From a020bb21aa31f25d8afabdd26ae39c3be4e2f3e8 Mon Sep 17 00:00:00 2001 From: msmith Date: Wed, 25 Oct 2000 06:59:06 +0000 Subject: Major update to the 'twe' driver. - Layout reorganisation to enhance portability. The driver now has a relatively MI 'core' and a FreeBSD-specific layer over the top. Since the NetBSD people have already done their own port, this is largely just to help me with the BSD/OS port. - Request ID allocation changed to improve performance (I'd been considering switching to this approach after having failed to come up with a better way to dynamically allocate request IDs, and seeing Andy Doran use it in the NetBSD port of the driver convinced me that I was wasting my time doing it any other way). Now we just allocate all the requests up front. - Maximum request count bumped back to 255 after characterisation of a firmware issue (off-by-one causing it to crash with 256 outstanding commands). - Control interface implemented. This allows 3ware's '3dm' utility to talk to the controller. 3dm will be available from 3ware shortly. - Controller soft-reset feature added; if the controller signals a firmware or protocol error, the controller will be reset and all outstanding commands will be retried. --- sys/dev/twe/twe.c | 1847 ++++++++++++++++++++++++----------------------------- 1 file changed, 844 insertions(+), 1003 deletions(-) (limited to 'sys/dev/twe/twe.c') diff --git a/sys/dev/twe/twe.c b/sys/dev/twe/twe.c index 067e6f9..ecd4dfa 100644 --- a/sys/dev/twe/twe.c +++ b/sys/dev/twe/twe.c @@ -31,114 +31,73 @@ * Driver for the 3ware Escalade family of IDE RAID controllers. */ -#include -#include -#include -#include - -#if __FreeBSD_version < 500000 -# include -#else -# include -#endif -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - +#include #include +#include #include - -/* - * Initialisation, bus interface. - */ -static int twe_probe(device_t dev); -static int twe_attach(device_t dev); -static void twe_free(struct twe_softc *sc); -static void twe_startup(void *arg); -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_intr(void *arg); - -/* - * Control device. - */ -static d_open_t twe_open; -static d_close_t twe_close; -static d_ioctl_t twe_ioctl; +#define TWE_DEFINE_TABLES +#include /* * Command submission. */ -static void *twe_get_param(struct twe_softc *sc, int table_id, int parameter_id, size_t size, +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_init_connection(struct twe_softc *sc); -/*static int twe_wait_request(struct twe_request *tr);*/ -static int twe_immediate_request(struct twe_request *tr); -static void twe_startio(struct twe_softc *sc); -static void twe_completeio(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_startio(struct twe_softc *sc); +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_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); -static void twe_enable_interrupts(struct twe_softc *sc); -static void twe_disable_interrupts(struct twe_softc *sc); +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); +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 struct twe_request *twe_get_request(struct twe_softc *sc); -static void twe_release_request(struct twe_request *tr); -static void twe_free_request(struct twe_request *tr); -static int twe_get_requestid(struct twe_request *tr); -static void twe_release_requestid(struct twe_request *tr); -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); -static void twe_map_request(struct twe_request *tr); -static void twe_unmap_request(struct twe_request *tr); +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_name_aen(u_int16_t aen); -#if 0 -static void twe_print_request(struct twe_request *tr); -void twe_report(void); -#endif +static char *twe_format_aen(struct twe_softc *sc, u_int16_t aen); +static int twe_request_qlen(struct twe_request *tr); +static void twe_panic(struct twe_softc *sc, char *reason); /******************************************************************************** ******************************************************************************** @@ -146,228 +105,74 @@ void twe_report(void); ******************************************************************************** ********************************************************************************/ -devclass_t twe_devclass; - -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) -}; - -DRIVER_MODULE(tw, pci, twe_pci_driver, twe_devclass, 0, 0); - -#define TWE_CDEV_MAJOR 146 - -static struct cdevsw twe_cdevsw = { - /* open */ twe_open, - /* close */ twe_close, - /* read */ noread, - /* write */ nowrite, - /* ioctl */ twe_ioctl, - /* poll */ nopoll, - /* mmap */ nommap, - /* strategy */ nostrategy, - /* name */ "twe", - /* maj */ TWE_CDEV_MAJOR, - /* dump */ nodump, - /* psize */ nopsize, - /* flags */ 0, - /* bmaj */ -1 -}; - -/******************************************************************************** - * 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)) { - device_set_desc(dev, TWE_DEVICE_NAME); - return(0); - } - return(ENXIO); -} - /******************************************************************************** - * Free all of the resources associated with (sc). - * - * Should not be called if the controller is active. + * Initialise the controller, set up driver data structures. */ -static void -twe_free(struct twe_softc *sc) +int +twe_setup(struct twe_softc *sc) { struct twe_request *tr; + int i; debug_called(4); - /* throw away any command buffers */ - while ((tr = TAILQ_FIRST(&sc->twe_freecmds)) != NULL) { - TAILQ_REMOVE(&sc->twe_freecmds, tr, tr_link); - 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); -} - -/******************************************************************************** - * 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); - - /* - * 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) { - device_printf(dev, "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); - /* - * Initialise the softc structure. + * Initialise request queues. */ - sc = device_get_softc(dev); - bzero(sc, sizeof(*sc)); - sc->twe_dev = dev; - TAILQ_INIT(&sc->twe_work); - TAILQ_INIT(&sc->twe_freecmds); - bioq_init(&sc->twe_bioq); + 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 the PCI register window. + * Allocate request structures up front. */ - rid = TWE_IO_CONFIG_REG; - sc->twe_io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, 0, ~0, 1, RF_ACTIVE); - if (sc->twe_io == NULL) { - device_printf(sc->twe_dev, "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); + 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; - /* - * Allocate the parent bus DMA tag appropriate for PCI. - */ - error = 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); - if (error != 0) { - device_printf(dev, "can't allocate parent DMA tag\n"); - twe_free(sc); - return(ENOMEM); + /* + * Put command onto the freelist. + */ + twe_release_request(tr); } - /* - * Allocate and connect our interrupt. + /* + * Wait for the controller to come ready. */ - rid = 0; - sc->twe_irq = bus_alloc_resource(sc->twe_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); - if (sc->twe_irq == NULL) { - device_printf(sc->twe_dev, "can't allocate interrupt\n"); - twe_free(sc); - return(ENXIO); - } - error = bus_setup_intr(sc->twe_dev, sc->twe_irq, INTR_TYPE_BIO, twe_intr, sc, &sc->twe_intr); - if (error) { - device_printf(sc->twe_dev, "can't set up interrupt\n"); - twe_free(sc); + if (twe_wait_status(sc, TWE_STATUS_MICROCONTROLLER_READY, 60)) { + twe_printf(sc, "microcontroller not ready\n"); return(ENXIO); } /* - * Create DMA tag for mapping objects into controller-addressable space. + * Disable interrupts from the card. */ - error = 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); - if (error != 0) { - device_printf(sc->twe_dev, "can't allocate data buffer DMA tag\n"); - twe_free(sc); - return(ENOMEM); - } + twe_disable_interrupts(sc); /* - * Create the control device. + * Soft reset the controller, look for the AEN acknowledging the reset, + * check for errors, drain the response queue. */ - 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)); + for (i = 0; i < TWE_MAX_RESET_TRIES; i++) { - /* - * 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. - */ - bzero(&sc->twe_ich, sizeof(struct intr_config_hook)); - sc->twe_ich.ich_func = twe_startup; - sc->twe_ich.ich_arg = sc; - if (config_intrhook_establish(&sc->twe_ich) != 0) { - device_printf(sc->twe_dev, "can't establish configuration hook\n"); - twe_free(sc); + 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); } @@ -375,98 +180,57 @@ twe_attach(device_t dev) } /******************************************************************************** - * Initialise the controller, locate disk devices and attach children to them. + * Locate disk devices and attach children to them. */ -static void -twe_startup(void *arg) +void +twe_init(struct twe_softc *sc) { - struct twe_softc *sc = (struct twe_softc *)arg; struct twe_drive *dr; - int i, error; - u_int32_t status_reg; - TWE_Param *drives, *capacity; - - debug_called(4); + int i, table; + u_int16_t dsize; + TWE_Param *drives, *param; + TWE_Unit_Descriptor *ud; - /* pull ourselves off the intrhook chain */ - config_intrhook_disestablish(&sc->twe_ich); /* - * Wait for the controller to come ready. + * The controller is in a safe state, so try to find drives attached to it. */ - if (twe_wait_status(sc, TWE_STATUS_MICROCONTROLLER_READY, 60)) { - device_printf(sc->twe_dev, "microcontroller not ready\n"); + 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; } - - /* - * Disable interrupts from the card while we're getting it into a safe state. - */ - twe_disable_interrupts(sc); - + /* - * Soft reset the controller, look for the AEN acknowledging the reset, - * check for errors, drain the response queue. + * For each detected unit, create a child device. */ - for (i = 0; i < TWE_MAX_RESET_TRIES; i++) { + for (i = 0, dr = &sc->twe_drive[0]; i < TWE_MAX_UNITS; i++, dr++) { - if (i > 0) - device_printf(sc->twe_dev, "reset %d failed, trying again\n", i); + /* check that the drive is online */ + if (!(drives->data[i] & TWE_PARAM_UNITSTATUS_Online)) + continue; - TWE_SOFT_RESET(sc); + table = TWE_PARAM_UNITINFO + i; - if (twe_wait_status(sc, TWE_STATUS_ATTENTION_INTERRUPT, 15)) { - device_printf(sc->twe_dev, "no attention interrupt"); - continue; - } - if (twe_drain_aen_queue(sc)) { - device_printf(sc->twe_dev, "can't drain AEN queue\n"); + 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_find_aen(sc, TWE_AEN_SOFT_RESET)) { - device_printf(sc->twe_dev, "reset not reported\n"); - continue; - } - status_reg = TWE_STATUS(sc); - if (TWE_STATUS_ERRORS(status_reg) || twe_check_bits(sc, status_reg)) { - device_printf(sc->twe_dev, "controller errors detected\n"); + 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_drain_response_queue(sc)) { - device_printf(sc->twe_dev, "can't drain response queue\n"); + 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; } - break; /* reset process complete */ - } - /* did we give up? */ - if (i >= TWE_MAX_RESET_TRIES) { - device_printf(sc->twe_dev, "can't initialise controller, giving up\n"); - return; - } - - /* - * The controller is in a safe state, so try to find drives attached to it. - * XXX ick, magic numbers - */ - if ((drives = twe_get_param(sc, 3, 3, TWE_MAX_UNITS, NULL)) == NULL) { - device_printf(sc->twe_dev, "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++) { - - if (drives->data[i] == 0) /* unit not present */ - continue; - - if ((capacity = twe_get_param(sc, TWE_UNIT_INFORMATION_TABLE_BASE + i, 4, 4, NULL)) == NULL) { - device_printf(sc->twe_dev, "error fetching capacity for unit %d\n", i); + 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; } - dr->td_size = *(u_int32_t *)capacity->data; - free(capacity, M_DEVBUF); + 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) { @@ -477,25 +241,23 @@ twe_startup(void *arg) dr->td_sectors = 32; } dr->td_cylinders = dr->td_size / (dr->td_heads * dr->td_sectors); - dr->td_unit = i; - dr->td_state = TWE_DRIVE_UNKNOWN; /* XXX how do we find out what the real state is? */ - dr->td_raidlevel = TWE_DRIVE_UNKNOWN; /* XXX how do we find out what the real raidlevel is? */ - dr->td_disk = device_add_child(sc->twe_dev, NULL, -1); - if (dr->td_disk == 0) - device_printf(sc->twe_dev, "device_add_child failed\n"); - device_set_ivars(dr->td_disk, dr); + twe_attach_drive(sc, dr); } free(drives, M_DEVBUF); - if ((error = bus_generic_attach(sc->twe_dev)) != 0) - device_printf(sc->twe_dev, "bus_generic_attach returned %d\n", error); - /* * Initialise connection with controller. */ - twe_init_connection(sc); + 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. @@ -503,126 +265,38 @@ twe_startup(void *arg) sc->twe_state &= ~TWE_STATE_SHUTDOWN; /* - * Finally enable interrupts . + * Finally enable interrupts. */ twe_enable_interrupts(sc); } /******************************************************************************** - * 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. + * Stop the controller */ -static int -twe_shutdown(device_t dev) +void +twe_deinit(struct twe_softc *sc) { - struct twe_softc *sc = device_get_softc(dev); - int i, s, error; - - debug_called(4); - - s = splbio(); - error = 0; - /* * Mark the controller as shutting down, and disable any further interrupts. */ sc->twe_state |= TWE_STATE_SHUTDOWN; twe_disable_interrupts(sc); - /* - * Delete all our child devices. +#ifdef TWE_SHUTDOWN_NOTIFICATION + /* + * Disconnect from the controller */ - 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; - } - } - - out: - splx(s); - return(error); -} - -/******************************************************************************** - * Bring the controller to a quiescent state, ready for system suspend. - * - * XXX this isn't really very well implemented. - */ -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); + twe_init_connection(sc, TWE_SHUTDOWN_MESSAGE_CREDITS); +#endif } /******************************************************************************* * Take an interrupt, or be poked by other code to look for interrupt-worthy * status. */ -static void -twe_intr(void *arg) +void +twe_intr(struct twe_softc *sc) { - struct twe_softc *sc = (struct twe_softc *)arg; u_int32_t status_reg; debug_called(4); @@ -646,49 +320,178 @@ twe_intr(void *arg) twe_done(sc); }; -/******************************************************************************** - ******************************************************************************** - Control Device - ******************************************************************************** - ********************************************************************************/ - -/******************************************************************************** - * Accept an open operation on the control device. +/******************************************************************************* + * Receive a bio structure from a child device and queue it on a particular + * controller, then poke the controller to start as much work as it can. */ -static int -twe_open(dev_t dev, int flags, int fmt, struct proc *p) +int +twe_submit_bio(struct twe_softc *sc, twe_bio *bp) { - int unit = minor(dev); - struct twe_softc *sc = devclass_get_softc(twe_devclass, unit); + + debug_called(4); + + twe_enqueue_bio(sc, bp); - sc->twe_state |= TWE_STATE_OPEN; + twe_startio(sc); return(0); } /******************************************************************************** - * Accept the last close on the control device. + * Handle controller-specific control operations. */ -static int -twe_close(dev_t dev, int flags, int fmt, struct proc *p) +int +twe_ioctl(struct twe_softc *sc, int cmd, void *addr) { - int unit = minor(dev); - struct twe_softc *sc = devclass_get_softc(twe_devclass, unit); + 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; + 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; + } + + /* copy the user-supplied command */ + bcopy(&tu->tu_command, &tr->tr_command, sizeof(TWE_Command)); + + /* 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_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; + } - sc->twe_state &= ~TWE_STATE_OPEN; - return (0); + return(error); } /******************************************************************************** - * Handle controller-specific control operations. + * Enable the useful interrupts from the controller. */ -static int -twe_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) +void +twe_enable_interrupts(struct twe_softc *sc) { - - switch(cmd) { - default: - return(ENOTTY); - } + 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; } /******************************************************************************** @@ -697,22 +500,42 @@ twe_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p) ******************************************************************************** ********************************************************************************/ -/******************************************************************************* - * Receive a bio structure from a child device and queue it on a particular - * controller, then poke the controller to start as much work as it can. +/******************************************************************************** + * Read integer parameter table entries. */ -int -twe_submit_buf(struct twe_softc *sc, struct bio *bp) +static int +twe_get_param_1(struct twe_softc *sc, int table_id, int param_id, u_int8_t *result) { - int s; - - debug_called(4); + TWE_Param *param; - s = splbio(); - bioq_insert_tail(&sc->twe_bioq, bp); - splx(s); + 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); +} - twe_startio(sc); +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); } @@ -723,7 +546,8 @@ twe_submit_buf(struct twe_softc *sc, struct bio *bp) * 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 parameter_id, size_t size, void (* func)(struct twe_request *tr)) +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; @@ -736,7 +560,7 @@ twe_get_param(struct twe_softc *sc, int table_id, int parameter_id, size_t size, param = NULL; /* get a command */ - if ((tr = twe_get_request(sc)) == NULL) + if (twe_get_request(sc, &tr)) goto err; /* get a buffer */ @@ -745,37 +569,45 @@ twe_get_param(struct twe_softc *sc, int table_id, int parameter_id, size_t size, tr->tr_data = param; tr->tr_length = TWE_SECTOR_SIZE; tr->tr_flags = TWE_CMD_DATAIN | TWE_CMD_DATAOUT; - tr->tr_complete = NULL; - tr->tr_private = NULL; /* build the command for the controller */ cmd = &tr->tr_command; - cmd->opcode = TWE_OP_GET_PARAM; - cmd->sgl_offset = 2; - cmd->size = 2; - cmd->unit = 0; - cmd->count = 1; + 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 = parameter_id; - param->parameter_size_bytes = size; + 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 (tr->tr_command.status != 0) { - debug(2, "command failed - 0x%x", tr->tr_command.status); + switch (cmd->generic.flags) { + case TWE_FLAGS_SUCCESS: + 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)); + break; + + case TWE_FLAGS_FATAL: + default: + twe_printf(sc, "command failed - %s\n", + twe_describe_code(twe_table_status, cmd->generic.status)); goto err; } - twe_release_request(tr); - return(param); } + twe_release_request(tr); + return(param); } else { tr->tr_complete = func; error = twe_start(tr); @@ -794,12 +626,106 @@ err: } /******************************************************************************** + * 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) { + switch (cmd->generic.flags) { + case TWE_FLAGS_SUCCESS: + 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)); + break; + + case TWE_FLAGS_FATAL: + default: + twe_printf(sc, "command failed - %s\n", + twe_describe_code(twe_table_status, cmd->generic.status)); + error = EIO; + break; + } + } + +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) +twe_init_connection(struct twe_softc *sc, int mode) { struct twe_request *tr; TWE_Command *cmd; @@ -808,17 +734,16 @@ twe_init_connection(struct twe_softc *sc) debug_called(4); /* get a command */ - if ((tr = twe_get_request(sc)) == NULL) + if (twe_get_request(sc, &tr)) return(NULL); /* build the command */ cmd = &tr->tr_command; - cmd->opcode = TWE_OP_INIT_CONNECTION; - cmd->sgl_offset = 0; - cmd->size = 3; - cmd->unit = 0; - cmd->count = TWE_INIT_MESSAGE_CREDITS; - cmd->args.init_connection.response_queue_pointer = 0; + 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); @@ -829,10 +754,11 @@ twe_init_connection(struct twe_softc *sc) twe_unmap_request(tr); twe_release_request(tr); + if (mode == TWE_INIT_MESSAGE_CREDITS) + sc->twe_host_id = cmd->initconnection.host_id; return(error); } -#if 0 /******************************************************************************** * Start the command (tr) and sleep waiting for it to complete. * @@ -841,22 +767,21 @@ twe_init_connection(struct twe_softc *sc) static int twe_wait_request(struct twe_request *tr) { - int error, s; + int s; debug_called(4); - error = 0; - tr->tr_private = tr; /* our wait channel */ + tr->tr_flags |= TWE_CMD_SLEEPER; + tr->tr_status = TWE_CMD_BUSY; + twe_enqueue_ready(tr); + twe_startio(tr->tr_sc); s = splbio(); - if ((error = twe_start(tr)) != 0) - goto out; - tsleep(tr->tr_private, PUSER, "twwcmd", hz); /* XXX more sensible timeout than 1s? */ + while (tr->tr_status == TWE_CMD_BUSY) + tsleep(tr, PRIBIO, "twewait", 0); splx(s); - -out: - return(error); + + return(0); } -#endif /******************************************************************************** * Start the command (tr) and busy-wait for it to complete. @@ -889,77 +814,67 @@ twe_startio(struct twe_softc *sc) { struct twe_request *tr; TWE_Command *cmd; - struct bio *bp; - int s, error; + twe_bio *bp; + int error; debug_called(4); /* spin until something prevents us from doing any work */ - s = splbio(); for (;;) { - if (sc->twe_deferred == NULL) { + /* 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 = bioq_first(&sc->twe_bioq)) == NULL) + if ((bp = twe_dequeue_bio(sc)) == NULL) break; /* get a command */ - if ((tr = twe_get_request(sc)) == NULL) + if (twe_get_request(sc, &tr)) break; - /* get the bio containing our work */ - bioq_remove(&sc->twe_bioq, bp); - splx(s); - /* connect the bio to the command */ tr->tr_complete = twe_completeio; tr->tr_private = bp; - tr->tr_data = bp->bio_data; - tr->tr_length = bp->bio_bcount; + tr->tr_data = TWE_BIO_DATA(bp); + tr->tr_length = TWE_BIO_LENGTH(bp); cmd = &tr->tr_command; -#ifdef FREEBSD_4 - if (bp->bio_flags & B_READ) -#else - if (bp->bio_cmd == BIO_READ) -#endif - { + if (TWE_BIO_IS_READ(bp)) { tr->tr_flags |= TWE_CMD_DATAIN; - cmd->opcode = TWE_OP_READ; + cmd->io.opcode = TWE_OP_READ; } else { tr->tr_flags |= TWE_CMD_DATAOUT; - cmd->opcode = TWE_OP_WRITE; + cmd->io.opcode = TWE_OP_WRITE; } /* build a suitable I/O command (assumes 512-byte rounded transfers) */ - cmd->sgl_offset = 3; - cmd->size = 3; - cmd->unit = ((struct twed_softc *)bp->bio_dev->si_drv1)->twed_drive->td_unit; - cmd->args.io.lba = bp->bio_pblkno; - cmd->count = (bp->bio_bcount + TWE_BLOCK_SIZE - 1) / TWE_BLOCK_SIZE; + 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); - } else { - - /* we previously deferred a command, try to submit it again */ - tr = sc->twe_deferred; - sc->twe_deferred = NULL; } + /* 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) { - sc->twe_deferred = tr; /* try it again later */ + twe_requeue_ready(tr); /* try it again later */ break; /* don't try anything more for now */ } /* otherwise, fail the command */ tr->tr_status = TWE_CMD_FAILED; twe_completeio(tr); } - s = splbio(); } - splx(s); } /******************************************************************************** @@ -968,30 +883,104 @@ twe_startio(struct twe_softc *sc) static void twe_completeio(struct twe_request *tr) { - TWE_Command *cmd; + TWE_Command *cmd = &tr->tr_command; struct twe_softc *sc = tr->tr_sc; - struct bio *bp = (struct bio *)tr->tr_private; - struct twed_softc *twed = (struct twed_softc *)bp->bio_dev->si_drv1; + twe_bio *bp = (twe_bio *)tr->tr_private; debug_called(4); if (tr->tr_status == TWE_CMD_COMPLETE) { - cmd = &tr->tr_command; - if (cmd->status != 0) { - bp->bio_error = EIO; - bp->bio_flags |= BIO_ERROR; - device_printf(twed->twed_dev, "command failed - 0x%x\n", cmd->status); + + switch (cmd->generic.flags) { + case TWE_FLAGS_SUCCESS: + 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)); + break; + + case TWE_FLAGS_FATAL: + default: + twe_printf(sc, "command failed - %s\n", + twe_describe_code(twe_table_status, cmd->generic.status)); + TWE_BIO_SET_ERROR(bp, EIO); + + /* + * XXX some status values suggest that the controller should be reset and all outstanding + * commands retried. This might be a good place for that. + */ + break; } } else if (tr->tr_status == TWE_CMD_FAILED) { /* could be more verbose here? */ - bp->bio_error = EIO; - bp->bio_flags |= BIO_ERROR; - device_printf(sc->twe_dev, "command failed submission - controller wedged\n"); + TWE_BIO_SET_ERROR(bp, EIO); + twe_printf(sc, "command failed submission - controller wedged\n"); + /* + * XXX reset controller and retry? + */ } twe_release_request(tr); twed_intr(bp); } /******************************************************************************** + * 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 ******************************************************************************** @@ -1011,14 +1000,15 @@ twe_start(struct twe_request *tr) debug_called(4); - /* give the command a request ID */ - if (twe_get_requestid(tr)) - return(EBUSY); /* can't handle it now, try later */ - /* mark the command as currently being processed */ tr->tr_status = TWE_CMD_BUSY; - /* spin briefly waiting for the controller to come ready */ + /* + * 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(); @@ -1030,14 +1020,16 @@ twe_start(struct twe_request *tr) TWE_COMMAND_QUEUE(sc, tr->tr_cmdphys); done = 1; /* move command to work queue */ - TAILQ_INSERT_TAIL(&sc->twe_work, tr, tr_link); + twe_enqueue_busy(tr); +#ifdef TWE_DEBUG if (tr->tr_complete != NULL) { - debug(3, "queued request %d with callback %p", tr->tr_command.request_id, tr->tr_complete); - } else if (tr->tr_private != NULL) { - debug(3, "queued request %d with wait channel %p", tr->tr_command.request_id, tr->tr_private); + 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.request_id); + debug(3, "queued request %d for polling caller", tr->tr_command.generic.request_id); } +#endif } splx(s); /* drop spl to allow completion interrupts */ } @@ -1052,7 +1044,6 @@ twe_start(struct twe_request *tr) * overestimated the number of commands it can accept. (Should we actually reject * the command at this point?) */ - twe_release_requestid(tr); return(EBUSY); } @@ -1081,11 +1072,13 @@ twe_done(struct twe_softc *sc) if (!(status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY)) { found = 1; rq = TWE_RESPONSE_QUEUE(sc); - tr = sc->twe_cmdlookup[rq.u.response_id]; /* find command */ + tr = sc->twe_lookup[rq.u.response_id]; /* find command */ if (tr != NULL) { /* paranoia */ - tr->tr_status = TWE_CMD_COMPLETE; - debug(3, "completed request id %d with status %d", tr->tr_command.request_id, tr->tr_command.status); - twe_release_requestid(tr); + 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 { debug(2, "done event for nonbusy id %d\n", rq.u.response_id); } @@ -1100,7 +1093,7 @@ twe_done(struct twe_softc *sc) twe_startio(sc); /* handle completion and timeouts */ - twe_complete(sc); + twe_complete(sc); /* XXX use deferred completion? */ } /******************************************************************************** @@ -1112,45 +1105,34 @@ twe_done(struct twe_softc *sc) static void twe_complete(struct twe_softc *sc) { - struct twe_request *tr, *nr; - int s; + struct twe_request *tr; debug_called(5); - s = splbio(); - /* - * Scan the list of busy/done commands, dispatch them appropriately. + * Pull commands off the completed list, dispatch them appropriately */ - tr = TAILQ_FIRST(&sc->twe_work); - while (tr != NULL) { - nr = TAILQ_NEXT(tr, tr_link); + while ((tr = twe_dequeue_complete(sc)) != NULL) { - /* command has been completed in some fashion */ - if (tr->tr_status > TWE_CMD_BUSY) { - - /* unmap the command's data buffer */ - twe_unmap_request(tr); + /* unmap the command's data buffer */ + twe_unmap_request(tr); - /* remove from work list */ - TAILQ_REMOVE(&sc->twe_work, tr, tr_link); + /* mark command as complete */ + tr->tr_status = TWE_CMD_COMPLETE; - /* 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); + /* 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_private != NULL) { /* caller is asleep waiting */ - debug(2, "wake up command owner on %p", tr->tr_private); - wakeup_one(tr->tr_private); + } 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"); - } + } else { /* caller is polling command */ + debug(2, "command left for owner"); } - tr = nr; - } - splx(s); + } } /******************************************************************************** @@ -1203,6 +1185,42 @@ twe_drain_response_queue(struct twe_softc *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"); + 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 ******************************************************************************** @@ -1218,7 +1236,7 @@ twe_host_intr(struct twe_softc *sc) { debug_called(4); - device_printf(sc->twe_dev, "host interrupt\n"); + twe_printf(sc, "host interrupt\n"); TWE_CONTROL(sc, TWE_CONTROL_CLEAR_HOST_INTERRUPT); } @@ -1234,7 +1252,7 @@ twe_attention_intr(struct twe_softc *sc) /* instigate a poll for AENs */ if (twe_fetch_aen(sc)) { - device_printf(sc->twe_dev, "error polling for signalled AEN\n"); + twe_printf(sc, "error polling for signalled AEN\n"); } else { TWE_CONTROL(sc, TWE_CONTROL_CLEAR_ATTENTION_INTERRUPT); } @@ -1255,34 +1273,11 @@ twe_command_intr(struct twe_softc *sc) * them, and when other commands have completed. Mask it so we don't get * another one. */ - device_printf(sc->twe_dev, "command interrupt\n"); + twe_printf(sc, "command interrupt\n"); TWE_CONTROL(sc, TWE_CONTROL_MASK_COMMAND_INTERRUPT); } /******************************************************************************** - * Enable the useful interrupts from the controller. - */ -static 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. - */ -static void -twe_disable_interrupts(struct twe_softc *sc) -{ - TWE_CONTROL(sc, TWE_CONTROL_DISABLE_INTERRUPTS); - sc->twe_state &= ~TWE_STATE_INTEN; -} - -/******************************************************************************** ******************************************************************************** Asynchronous Event Handling ******************************************************************************** @@ -1297,8 +1292,7 @@ twe_fetch_aen(struct twe_softc *sc) debug_called(4); - /* XXX ick, magic numbers */ - if ((twe_get_param(sc, 0x401, 2, 2, twe_handle_aen)) == NULL) + if ((twe_get_param(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, 2, twe_handle_aen)) == NULL) return(EIO); return(0); } @@ -1335,15 +1329,11 @@ twe_handle_aen(struct twe_request *tr) static int twe_drain_aen_queue(struct twe_softc *sc) { - TWE_Param *param; u_int16_t aen; for (;;) { - /* XXX ick, magic numbers */ - param = twe_get_param(sc, 0x401, 2, 2, NULL); - if (param == NULL) + if (twe_get_param_2(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, &aen)) return(1); - aen = *(u_int16_t *)(param->data); if (aen == TWE_AEN_QUEUE_EMPTY) return(0); twe_enqueue_aen(sc, aen); @@ -1361,22 +1351,32 @@ twe_drain_aen_queue(struct twe_softc *sc) static void twe_enqueue_aen(struct twe_softc *sc, u_int16_t aen) { - int s, next; + char *msg; + int s, next, nextnext; debug_called(4); - debug(1, "queueing AEN <%s>", twe_name_aen(aen)); + 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; - } else { - device_printf(sc->twe_dev, "AEN queue overflow, lost AEN <%s>\n", twe_name_aen(aen)); } + /* 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; @@ -1385,7 +1385,6 @@ twe_enqueue_aen(struct twe_softc *sc, u_int16_t aen) splx(s); } -#if 0 /******************************************************************************** * Pop an AEN off the queue, or return -1 if there are none left. * @@ -1406,7 +1405,6 @@ twe_dequeue_aen(struct twe_softc *sc) } return(result); } -#endif /******************************************************************************** * Check to see if the requested AEN is in the queue. @@ -1424,6 +1422,7 @@ twe_find_aen(struct twe_softc *sc, u_int16_t aen) if (sc->twe_aen_queue[i] == aen) missing = 0; } + splx(s); return(missing); } @@ -1469,429 +1468,271 @@ twe_wait_aen(struct twe_softc *sc, int aen, int timeout) /******************************************************************************** * Get a new command buffer. * - * This may return NULL in low-memory cases. - * - * Note that using malloc() is expensive (the command buffer is << 1 page) but - * necessary if we are to be a loadable module before the zone allocator is fixed. - * On the other hand, using malloc ensures that the command structure at the top - * of the request is aligned within the controller's constraints (64 bytes). - * - * If possible, we recycle a command buffer that's been used before. - * - * XXX currently, commands are mapped into controller space just before being - * handed to the controller. It may be more efficient to do that here. + * This will return NULL if all command buffers are in use. */ -static struct twe_request * -twe_get_request(struct twe_softc *sc) +static int +twe_get_request(struct twe_softc *sc, struct twe_request **tr) { - struct twe_request *tr; - int s, error; - debug_called(4); /* try to reuse an old buffer */ - s = splbio(); - if ((tr = TAILQ_FIRST(&sc->twe_freecmds)) != NULL) - TAILQ_REMOVE(&sc->twe_freecmds, tr, tr_link); - splx(s); - - /* allocate a new command buffer? */ - if (tr == NULL) { - tr = (struct twe_request *)malloc(sizeof(*tr), M_DEVBUF, M_NOWAIT); - if (tr != NULL) { - bzero(tr, sizeof(*tr)); - tr->tr_sc = sc; - error = bus_dmamap_create(sc->twe_buffer_dmat, 0, &tr->tr_cmdmap); - if (error) { - free(tr, M_DEVBUF); - return(NULL); - } - error = bus_dmamap_create(sc->twe_buffer_dmat, 0, &tr->tr_dmamap); - if (error) { - bus_dmamap_destroy(sc->twe_buffer_dmat, tr->tr_cmdmap); - free(tr, M_DEVBUF); - return(NULL); - } - } - } + *tr = twe_dequeue_free(sc); /* initialise some fields to their defaults */ - tr->tr_status = TWE_CMD_SETUP; /* command is in setup phase */ - tr->tr_flags = 0; - tr->tr_command.host_id = 0; /* not used */ - tr->tr_command.status = 0; /* before submission to controller */ - tr->tr_command.flags = 0; /* not used */ - return(tr); + if (*tr != NULL) { + (*tr)->tr_data = 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 recycling. + * Release a command buffer for reuse. * - * XXX It might be a good idea to limit the number of commands we save for reuse - * if it's shown that this list bloats out massively. */ static void twe_release_request(struct twe_request *tr) { - int s; - debug_called(4); - s = splbio(); - TAILQ_INSERT_HEAD(&tr->tr_sc->twe_freecmds, tr, tr_link); - splx(s); + twe_enqueue_free(tr); } /******************************************************************************** - * Permanently discard a command buffer. - */ -static 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, M_DEVBUF); -} + ******************************************************************************** + Debugging + ******************************************************************************** + ********************************************************************************/ /******************************************************************************** - * Allocate a request ID for a command about to be submitted. + * Print some information about the controller */ -static int -twe_get_requestid(struct twe_request *tr) +void +twe_describe_controller(struct twe_softc *sc) { - struct twe_softc *sc = tr->tr_sc; - int i, s, result; - - debug_called(4); - - s = splbio(); - result = 1; + TWE_Param *p[6]; + u_int8_t ports; + u_int32_t size; + int i; - /* XXX linear search is slow */ - for (i = 0; i < TWE_Q_LENGTH; i++) { - if (sc->twe_cmdlookup[i] == NULL) { - tr->tr_command.request_id = i; - sc->twe_cmdlookup[i] = tr; - result = 0; - break; + 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); } - splx(s); - - return(result); } /******************************************************************************** - * Free a command's request ID for reuse. - */ -static void -twe_release_requestid(struct twe_request *tr) -{ - struct twe_softc *sc = tr->tr_sc; - - debug_called(4); - - sc->twe_cmdlookup[tr->tr_command.request_id] = 0; /* XXX atomic? */ -} - -/******************************************************************************** - * Map/unmap (tr)'s command and data in the controller's addressable space. + * Complain if the status bits aren't what we're expecting. * - * 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. + * Rate-limit the complaints to at most one of each every five seconds, but + * always return the correct status. */ -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.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->sgl_offset) { - case 2: - for (i = 0; i < nsegments; i++) { - cmd->args.param.sgl[i].address = segs[i].ds_addr; - cmd->args.param.sgl[i].length = segs[i].ds_len; - } - for (; i < TWE_MAX_SGL_LENGTH; i++) { /* XXX necessary? */ - cmd->args.param.sgl[i].address = 0; - cmd->args.param.sgl[i].length = 0; - } - break; - case 3: - for (i = 0; i < nsegments; i++) { - cmd->args.io.sgl[i].address = segs[i].ds_addr; - cmd->args.io.sgl[i].length = segs[i].ds_len; - } - for (; i < TWE_MAX_SGL_LENGTH; i++) { /* XXX necessary? */ - cmd->args.io.sgl[i].address = 0; - cmd->args.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; -} - -static 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, M_DEVBUF, 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); - } - } -} - -static void -twe_unmap_request(struct twe_request *tr) +static int +twe_check_bits(struct twe_softc *sc, u_int32_t status_reg) { - 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); + int result; + static time_t lastwarn[2] = {0, 0}; /* - * If the command involved data, unmap that too. + * 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 (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, M_DEVBUF); - tr->tr_data = tr->tr_realdata; /* restore 'real' data pointer */ + 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"); } -} - -/******************************************************************************** - ******************************************************************************** - Debugging - ******************************************************************************** - ********************************************************************************/ - -/******************************************************************************** - * Complain if the status bits aren't what we're expecting. - */ -static int -twe_check_bits(struct twe_softc *sc, u_int32_t status_reg) -{ - int result; result = 0; if ((status_reg & TWE_STATUS_EXPECTED_BITS) != TWE_STATUS_EXPECTED_BITS) { - device_printf(sc->twe_dev, "missing expected status bit(s) %b\n", ~status_reg & TWE_STATUS_EXPECTED_BITS, - TWE_STATUS_BITS_DESCRIPTION); + 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) { - device_printf(sc->twe_dev, "unexpected status bit(s) %b\n", status_reg & TWE_STATUS_UNEXPECTED_BITS, - TWE_STATUS_BITS_DESCRIPTION); + 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 naming (aen). + * 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 struct { - u_int16_t aen; - char *desc; -} twe_aen_names[] = { - {0x0000, "queue empty"}, - {0x0001, "soft reset"}, - {0x0002, "degraded mirror"}, - {0x0003, "controller error"}, - {0x0004, "rebuild fail"}, - {0x0005, "rebuild done"}, - {0x00ff, "aen queue full"}, - {0, NULL} -}; static char * -twe_name_aen(u_int16_t aen) +twe_format_aen(struct twe_softc *sc, u_int16_t aen) { - int i; - static char buf[16]; - - for (i = 0; twe_aen_names[i].desc != NULL; i++) - if (twe_aen_names[i].aen == aen) - return(twe_aen_names[i].desc); - - sprintf(buf, "0x%x", 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); } -#if 0 -/******************************************************************************** - * Return a string naming (opcode). - */ -static struct { - u_int8_t opcode; - char *desc; -} twe_opcode_names[] = { - {0x00, "nop"}, - {0x01, "init connection"}, - {0x02, "read"}, - {0x03, "write"}, - {0x04, "verify"}, - {0x12, "get param"}, - {0x13, "set param"}, - {0x1a, "sector info"}, - {0x1c, "listen"}, - {0, NULL} -}; +static int +twe_request_qlen(struct twe_request *tr) +{ + int len = 0; -static char * -twe_name_opcode(u_int8_t opcode) + while (tr != NULL) { + tr = TAILQ_NEXT(tr, tr_link); + len++; + } + return(len); +} + +void +twe_print_controller(struct twe_softc *sc) { - int i; - static char buf[16]; + u_int32_t status_reg; - for (i = 0; twe_opcode_names[i].desc != NULL; i++) - if (twe_opcode_names[i].opcode == opcode) - return(twe_opcode_names[i].desc); + status_reg = TWE_STATUS(sc); + twe_printf(sc, "status %b\n", status_reg, TWE_STATUS_BITS_DESCRIPTION); + twe_printf(sc, "free %d\n", twe_request_qlen(TAILQ_FIRST(&sc->twe_free))); + twe_printf(sc, "ready %d\n", twe_request_qlen(TAILQ_FIRST(&sc->twe_ready))); + twe_printf(sc, "busy %d\n", twe_request_qlen(TAILQ_FIRST(&sc->twe_busy))); + twe_printf(sc, "complete %d\n", twe_request_qlen(TAILQ_FIRST(&sc->twe_complete))); + twe_printf(sc, "AEN queue head %d tail %d\n", sc->twe_aen_head, sc->twe_aen_tail); +} - sprintf(buf, "0x%x", opcode); - return(buf); +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) { - device_t dev = tr->tr_sc->twe_dev; + struct twe_softc *sc = tr->tr_sc; TWE_Command *cmd = &tr->tr_command; int i; - device_printf(dev, "CMD: request_id %d opcode <%s> size %d unit %d host_id %d\n", - cmd->request_id, twe_name_opcode(cmd->opcode), cmd->size, cmd->unit, cmd->host_id); - device_printf(dev, " status %d flags 0x%x count %d sgl_offset %d\n", - cmd->status, cmd->flags, cmd->count, cmd->sgl_offset); - switch(cmd->sgl_offset) { - case 3: - device_printf(dev, " lba %d\n", cmd->args.io.lba); - for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->args.io.sgl[i].length != 0); i++) - device_printf(dev, " %d: 0x%x/%d\n", - i, cmd->args.io.sgl[i].address, cmd->args.io.sgl[i].length); + 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 2: - for (i = 0; (i < TWE_MAX_SGL_LENGTH) && (cmd->args.param.sgl[i].length != 0); i++) - device_printf(dev, " %d: 0x%x/%d\n", - i, cmd->args.param.sgl[i].address, cmd->args.param.sgl[i].length); + case TWE_OP_INIT_CONNECTION: + twe_printf(sc, " response queue pointer 0x%x\n", + cmd->initconnection.response_queue_pointer); break; default: - device_printf(dev, " response queue pointer 0x%x\n", - cmd->args.init_connection.response_queue_pointer); + break; } - device_printf(dev, " 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); - device_printf(dev, " 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); + 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); } -/******************************************************************************** - * Print current controller status, call from DDB. - */ -void -twe_report(void) -{ - u_int32_t status_reg; - struct twe_softc *sc; - int i, s; - - s = splbio(); - for (i = 0; (sc = devclass_get_softc(twe_devclass, i)) != NULL; i++) { - status_reg = TWE_STATUS(sc); - device_printf(sc->twe_dev, "status %b\n", status_reg, TWE_STATUS_BITS_DESCRIPTION); - } - splx(s); -} #endif -- cgit v1.1