diff options
Diffstat (limited to 'sys/dev/twe/twe_freebsd.c')
-rw-r--r-- | sys/dev/twe/twe_freebsd.c | 224 |
1 files changed, 175 insertions, 49 deletions
diff --git a/sys/dev/twe/twe_freebsd.c b/sys/dev/twe/twe_freebsd.c index 75c5003..a1b871f 100644 --- a/sys/dev/twe/twe_freebsd.c +++ b/sys/dev/twe/twe_freebsd.c @@ -57,6 +57,9 @@ static u_int32_t twed_bio_out; #define TWED_BIO_OUT #endif +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); + /******************************************************************************** ******************************************************************************** Control device interface @@ -269,18 +272,50 @@ twe_attach(device_t dev) } /* + * Create DMA tag for mapping command's into controller-addressable space. + */ + if (bus_dma_tag_create(sc->twe_parent_dmat, /* parent */ + 1, 0, /* alignment, boundary */ + BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + sizeof(TWE_Command) * + TWE_Q_LENGTH, 1, /* maxsize, nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &sc->twe_cmd_dmat)) { + twe_printf(sc, "can't allocate data buffer DMA tag\n"); + twe_free(sc); + return(ENOMEM); + } + /* + * Allocate memory and make it available for DMA. + */ + if (bus_dmamem_alloc(sc->twe_cmd_dmat, (void **)&sc->twe_cmd, + BUS_DMA_NOWAIT, &sc->twe_cmdmap)) { + twe_printf(sc, "can't allocate command memory\n"); + return(ENOMEM); + } + bus_dmamap_load(sc->twe_cmd_dmat, sc->twe_cmdmap, sc->twe_cmd, + sizeof(TWE_Command) * TWE_Q_LENGTH, + twe_setup_request_dmamap, sc, 0); + bzero(sc->twe_cmd, sizeof(TWE_Command) * TWE_Q_LENGTH); + + /* * 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_32BIT, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ MAXBSIZE, TWE_MAX_SGL_LENGTH,/* maxsize, nsegments */ BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ - busdma_lock_mutex, /* lockfunc */ - &Giant, /* lockarg */ + busdma_lock_mutex, /* lockfunc */ + &Giant, /* lockarg */ &sc->twe_buffer_dmat)) { twe_printf(sc, "can't allocate data buffer DMA tag\n"); twe_free(sc); @@ -288,10 +323,39 @@ twe_attach(device_t dev) } /* + * 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_32BIT, /* lowaddr */ + BUS_SPACE_MAXADDR, /* highaddr */ + NULL, NULL, /* filter, filterarg */ + MAXBSIZE, 1, /* maxsize, nsegments */ + BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ + BUS_DMA_ALLOCNOW, /* flags */ + NULL, /* lockfunc */ + NULL, /* lockarg */ + &sc->twe_immediate_dmat)) { + twe_printf(sc, "can't allocate data buffer DMA tag\n"); + twe_free(sc); + return(ENOMEM); + } + /* + * Allocate memory for requests which cannot sleep or support continuation. + */ + if (bus_dmamem_alloc(sc->twe_immediate_dmat, (void **)&sc->twe_immediate, + BUS_DMA_NOWAIT, &sc->twe_immediate_map)) { + twe_printf(sc, "can't allocate memory for immediate requests\n"); + return(ENOMEM); + } + + /* * Initialise the controller and driver core. */ - if ((error = twe_setup(sc))) + if ((error = twe_setup(sc))) { + twe_free(sc); return(error); + } /* * Print some information about the controller and configuration. @@ -337,6 +401,20 @@ twe_free(struct twe_softc *sc) while ((tr = twe_dequeue_free(sc)) != NULL) twe_free_request(tr); + if (sc->twe_cmd != NULL) { + bus_dmamap_unload(sc->twe_cmd_dmat, sc->twe_cmdmap); + bus_dmamem_free(sc->twe_cmd_dmat, sc->twe_cmd, sc->twe_cmdmap); + } + + if (sc->twe_immediate != NULL) { + bus_dmamap_unload(sc->twe_immediate_dmat, sc->twe_immediate_map); + bus_dmamem_free(sc->twe_immediate_dmat, sc->twe_immediate, + sc->twe_immediate_map); + } + + if (sc->twe_immediate_dmat) + bus_dma_tag_destroy(sc->twe_immediate_dmat); + /* destroy the data-transfer DMA tag */ if (sc->twe_buffer_dmat) bus_dma_tag_destroy(sc->twe_buffer_dmat); @@ -486,7 +564,7 @@ twe_intrhook(void *arg) /******************************************************************************** * Given a detected drive, attach it to the bio interface. * - * This is called from twe_add_unit. + * This is called from twe_init. */ void twe_attach_drive(struct twe_softc *sc, struct twe_drive *dr) @@ -784,30 +862,26 @@ twed_detach(device_t dev) ******************************************************************************** ********************************************************************************/ -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) +twe_allocate_request(struct twe_softc *sc, int tag) { struct twe_request *tr; - if ((tr = malloc(sizeof(struct twe_request), TWE_MALLOC_CLASS, M_NOWAIT)) == NULL) + if ((tr = malloc(sizeof(struct twe_request), TWE_MALLOC_CLASS, M_WAITOK)) == NULL) { + twe_printf(sc, "unable to allocate memory for tag %d\n", tag); 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); - } + tr->tr_tag = tag; 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); + twe_printf(sc, "unable to allocate dmamap for tag %d\n", tag); return(NULL); } return(tr); @@ -823,7 +897,6 @@ twe_free_request(struct twe_request *tr) 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); } @@ -855,15 +928,21 @@ 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; + struct twe_softc *sc = tr->tr_sc; + TWE_Command *cmd = TWE_FIND_COMMAND(tr); debug_called(4); + if (tr->tr_flags & TWE_CMD_MAPPED) + panic("already mapped command"); + + tr->tr_flags |= TWE_CMD_MAPPED; + /* 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; + cmd->generic.size += 2 * nsegments; /* * Due to the fact that parameter and I/O commands have the scatter/gather list in @@ -904,38 +983,66 @@ twe_setup_data_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int err break; } } + + if (tr->tr_flags & TWE_CMD_DATAIN) { + if (tr->tr_flags & TWE_CMD_IMMEDIATE) { + bus_dmamap_sync(sc->twe_immediate_dmat, sc->twe_immediate_map, + BUS_DMASYNC_PREREAD); + } else { + 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); + + if (tr->tr_flags & TWE_CMD_IMMEDIATE) { + bus_dmamap_sync(sc->twe_immediate_dmat, sc->twe_immediate_map, + BUS_DMASYNC_PREWRITE); + } else { + bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, + BUS_DMASYNC_PREWRITE); + } + } + + if (twe_start(tr) == EBUSY) + panic("EBUSY should not happen"); } 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; + struct twe_softc *sc = (struct twe_softc *)arg; debug_called(4); /* command can't cross a page boundary */ - tr->tr_cmdphys = segs[0].ds_addr; + sc->twe_cmdphys = segs[0].ds_addr; } -void +int twe_map_request(struct twe_request *tr) { struct twe_softc *sc = tr->tr_sc; + int error = 0; debug_called(4); + if (sc->twe_state & TWE_STATE_FRZN) + return (EBUSY); - /* - * 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); + bus_dmamap_sync(sc->twe_cmd_dmat, sc->twe_cmdmap, BUS_DMASYNC_PREWRITE); /* * If the command involves data, map that too. */ - if (tr->tr_data != NULL) { + if (tr->tr_data != NULL && ((tr->tr_flags & TWE_CMD_MAPPED) == 0)) { /* * Data must be 64-byte aligned; allocate a fixup buffer if it's not. @@ -948,18 +1055,23 @@ twe_map_request(struct twe_request *tr) /* * 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); + */ + if (tr->tr_flags & TWE_CMD_IMMEDIATE) { + bcopy(tr->tr_data, sc->twe_immediate, tr->tr_length); + bus_dmamap_load(sc->twe_immediate_dmat, sc->twe_immediate_map, sc->twe_immediate, + tr->tr_length, twe_setup_data_dmamap, tr, 0); + } else { + error = bus_dmamap_load(sc->twe_buffer_dmat, tr->tr_dmamap, tr->tr_data, tr->tr_length, + twe_setup_data_dmamap, tr, 0); } - } + if (error == EINPROGRESS) { + sc->twe_state |= TWE_STATE_FRZN; + error = 0; + } + } else + error = twe_start(tr); + + return(error); } void @@ -969,27 +1081,41 @@ twe_unmap_request(struct twe_request *tr) 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); + bus_dmamap_sync(sc->twe_cmd_dmat, sc->twe_cmdmap, BUS_DMASYNC_POSTWRITE); /* * 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 (tr->tr_flags & TWE_CMD_IMMEDIATE) { + bus_dmamap_sync(sc->twe_immediate_dmat, sc->twe_immediate_map, + BUS_DMASYNC_POSTREAD); + } else { + 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); + if (tr->tr_flags & TWE_CMD_DATAOUT) { + if (tr->tr_flags & TWE_CMD_IMMEDIATE) { + bus_dmamap_sync(sc->twe_immediate_dmat, sc->twe_immediate_map, + BUS_DMASYNC_POSTWRITE); + } else { + bus_dmamap_sync(sc->twe_buffer_dmat, tr->tr_dmamap, + BUS_DMASYNC_POSTWRITE); + } + } - bus_dmamap_unload(sc->twe_buffer_dmat, tr->tr_dmamap); + if (tr->tr_flags & TWE_CMD_IMMEDIATE) { + bcopy(sc->twe_immediate, tr->tr_data, tr->tr_length); + bus_dmamap_unload(sc->twe_immediate_dmat, sc->twe_immediate_map); + } else { + bus_dmamap_unload(sc->twe_buffer_dmat, tr->tr_dmamap); + } } /* free alignment buffer if it was used */ |