diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2014-03-15 21:49:48 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2014-03-15 21:49:48 +0000 |
commit | f39b4ef058b8673a7bbdd44c530626bff37d2664 (patch) | |
tree | e543a44da1f0c814f74b6f4fcf1ab6113f1a795d /sys/dev | |
parent | abf1b580c65c2435367d4cb233afd06d59175bd1 (diff) |
ISP1000 SBus support for qlw(4).
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ic/qlw.c | 174 | ||||
-rw-r--r-- | sys/dev/ic/qlwreg.h | 60 | ||||
-rw-r--r-- | sys/dev/sbus/files.sbus | 5 | ||||
-rw-r--r-- | sys/dev/sbus/qlw_sbus.c | 147 |
4 files changed, 297 insertions, 89 deletions
diff --git a/sys/dev/ic/qlw.c b/sys/dev/ic/qlw.c index 35d5eabd78c..e6e0df29832 100644 --- a/sys/dev/ic/qlw.c +++ b/sys/dev/ic/qlw.c @@ -1,4 +1,4 @@ -/* $OpenBSD: qlw.c,v 1.16 2014/03/15 13:08:52 kettenis Exp $ */ +/* $OpenBSD: qlw.c,v 1.17 2014/03/15 21:49:47 kettenis Exp $ */ /* * Copyright (c) 2011 David Gwynne <dlg@openbsd.org> @@ -91,7 +91,12 @@ void qlw_put_cmd(struct qlw_softc *, void *, struct scsi_xfer *, void qlw_put_cont(struct qlw_softc *, void *, struct scsi_xfer *, struct qlw_ccb *, int); struct qlw_ccb *qlw_handle_resp(struct qlw_softc *, u_int16_t); -void qlw_put_data_seg(struct qlw_iocb_seg *, bus_dmamap_t, int); +void qlw_get_header(struct qlw_softc *, struct qlw_iocb_hdr *, + int *, int *); +void qlw_put_header(struct qlw_softc *, struct qlw_iocb_hdr *, + int, int); +void qlw_put_data_seg(struct qlw_softc *, struct qlw_iocb_seg *, + bus_dmamap_t, int); int qlw_softreset(struct qlw_softc *); void qlw_dma_burst_enable(struct qlw_softc *); @@ -124,6 +129,24 @@ qlw_xs_bus(struct qlw_softc *sc, struct scsi_xfer *xs) } static inline u_int16_t +qlw_swap16(struct qlw_softc *sc, u_int16_t value) +{ + if (sc->sc_isp_gen == QLW_GEN_ISP1000) + return htobe16(value); + else + return htole16(value); +} + +static inline u_int32_t +qlw_swap32(struct qlw_softc *sc, u_int32_t value) +{ + if (sc->sc_isp_gen == QLW_GEN_ISP1000) + return htobe32(value); + else + return htole32(value); +} + +static inline u_int16_t qlw_queue_read(struct qlw_softc *sc, bus_size_t offset) { return qlw_read(sc, sc->sc_mbox_base + offset); @@ -522,24 +545,28 @@ struct qlw_ccb * qlw_handle_resp(struct qlw_softc *sc, u_int16_t id) { struct qlw_ccb *ccb; - struct qla_iocb_status *status; + struct qlw_iocb_hdr *hdr; + struct qlw_iocb_status *status; struct scsi_xfer *xs; u_int32_t handle; - u_int8_t *entry; + int entry_type; + int flags; int bus; ccb = NULL; - entry = QLW_DMA_KVA(sc->sc_responses) + (id * QLW_QUEUE_ENTRY_SIZE); + hdr = QLW_DMA_KVA(sc->sc_responses) + (id * QLW_QUEUE_ENTRY_SIZE); bus_dmamap_sync(sc->sc_dmat, QLW_DMA_MAP(sc->sc_responses), id * QLW_QUEUE_ENTRY_SIZE, QLW_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTREAD); - qlw_dump_iocb(sc, entry); - switch (entry[0]) { + qlw_dump_iocb(sc, hdr); + + qlw_get_header(sc, hdr, &entry_type, &flags); + switch (entry_type) { case QLW_IOCB_STATUS: - status = (struct qla_iocb_status *)entry; - handle = letoh32(status->handle); + status = (struct qlw_iocb_status *)hdr; + handle = qlw_swap32(sc, status->handle); if (handle > sc->sc_maxccbs) { panic("bad completed command handle: %d (> %d)", handle, sc->sc_maxccbs); @@ -568,10 +595,10 @@ qlw_handle_resp(struct qlw_softc *sc, u_int16_t id) } bus = qlw_xs_bus(sc, xs); - xs->status = letoh16(status->scsi_status); - switch (letoh16(status->completion)) { + xs->status = qlw_swap16(sc, status->scsi_status); + switch (qlw_swap16(sc, status->completion)) { case QLW_IOCB_STATUS_COMPLETE: - if (letoh16(status->scsi_status) & + if (qlw_swap16(sc, status->scsi_status) & QLW_SCSI_STATUS_SENSE_VALID) { memcpy(&xs->sense, status->sense_data, sizeof(xs->sense)); @@ -583,8 +610,7 @@ qlw_handle_resp(struct qlw_softc *sc, u_int16_t id) break; case QLW_IOCB_STATUS_INCOMPLETE: - if (letoh16(status->flags) & - QLW_STATE_GOT_TARGET) { + if (flags & QLW_STATE_GOT_TARGET) { xs->error = XS_DRIVER_STUFFUP; } else { xs->error = XS_SELTIMEOUT; @@ -617,7 +643,7 @@ qlw_handle_resp(struct qlw_softc *sc, u_int16_t id) case QLW_IOCB_STATUS_DATA_OVERRUN: case QLW_IOCB_STATUS_DATA_UNDERRUN: - xs->resid = letoh32(status->resid); + xs->resid = qlw_swap32(sc, status->resid); xs->error = XS_NOERROR; break; @@ -632,7 +658,7 @@ qlw_handle_resp(struct qlw_softc *sc, u_int16_t id) atomic_setbits_int(&sc->sc_update_required[bus], 1 << xs->sc_link->target); task_add(systq, &sc->sc_update_task); - xs->resid = letoh32(status->resid); + xs->resid = qlw_swap32(sc, status->resid); xs->error = XS_NOERROR; break; @@ -642,14 +668,14 @@ qlw_handle_resp(struct qlw_softc *sc, u_int16_t id) atomic_setbits_int(&sc->sc_update_required[bus], 1 << xs->sc_link->target); task_add(systq, &sc->sc_update_task); - xs->resid = letoh32(status->resid); + xs->resid = qlw_swap32(sc, status->resid); xs->error = XS_NOERROR; break; default: DPRINTF(QLW_D_INTR, "%s: unexpected completion" " status %x\n", DEVNAME(sc), - letoh16(status->completion)); + qlw_swap16(sc, status->completion)); xs->error = XS_DRIVER_STUFFUP; break; } @@ -657,7 +683,7 @@ qlw_handle_resp(struct qlw_softc *sc, u_int16_t id) default: DPRINTF(QLW_D_INTR, "%s: unexpected response entry type %x\n", - DEVNAME(sc), entry[0]); + DEVNAME(sc), entry_type); break; } @@ -774,7 +800,7 @@ qlw_scsi_cmd(struct scsi_xfer *xs) int bus; int seg; - if (xs->cmdlen > sizeof(iocb->req_cdb)) { + if (xs->cmdlen > sizeof(iocb->cdb)) { DPRINTF(QLW_D_IO, "%s: cdb too big (%d)\n", DEVNAME(sc), xs->cmdlen); memset(&xs->sense, 0, sizeof(xs->sense)); @@ -1252,27 +1278,63 @@ qlw_dump_iocb_segs(struct qlw_softc *sc, void *segs, int n) #endif } +/* + * The PCI bus is little-endian whereas SBus is big-endian. This + * leads to some differences in byte twisting of DMA transfers of + * request and response queue entries. Most fields can be treated as + * 16-bit or 32-bit with the endianness of the bus, but the header + * fields end up being swapped by the ISP1000's SBus interface. + */ + void -qlw_put_marker(struct qlw_softc *sc, int bus, void *buf) +qlw_get_header(struct qlw_softc *sc, struct qlw_iocb_hdr *hdr, + int *type, int *flags) { - struct qlw_iocb_marker *marker = buf; + if (sc->sc_isp_gen == QLW_GEN_ISP1000) { + *type = hdr->entry_count; + *flags = hdr->seqno; + } else { + *type = hdr->entry_type; + *flags = hdr->flags; + } +} - marker->entry_type = QLW_IOCB_MARKER; - marker->entry_count = 1; - marker->seqno = 0; - marker->flags = 0; +void +qlw_put_header(struct qlw_softc *sc, struct qlw_iocb_hdr *hdr, + int type, int count) +{ + if (sc->sc_isp_gen == QLW_GEN_ISP1000) { + hdr->entry_type = count; + hdr->entry_count = type; + hdr->seqno = 0; + hdr->flags = 0; + } else { + hdr->entry_type = type; + hdr->entry_count = count; + hdr->seqno = 0; + hdr->flags = 0; + } +} - /* could be more specific here; isp(4) isn't */ - marker->target = (bus << 7); - marker->modifier = QLW_IOCB_MARKER_SYNC_ALL; - qlw_dump_iocb(sc, buf); +void +qlw_put_data_seg(struct qlw_softc *sc, struct qlw_iocb_seg *seg, + bus_dmamap_t dmap, int num) +{ + seg->seg_addr = qlw_swap32(sc, dmap->dm_segs[num].ds_addr); + seg->seg_len = qlw_swap32(sc, dmap->dm_segs[num].ds_len); } void -qlw_put_data_seg(struct qlw_iocb_seg *seg, bus_dmamap_t dmap, int num) +qlw_put_marker(struct qlw_softc *sc, int bus, void *buf) { - seg->seg_addr = htole32(dmap->dm_segs[num].ds_addr); - seg->seg_len = htole32(dmap->dm_segs[num].ds_len); + struct qlw_iocb_marker *marker = buf; + + qlw_put_header(sc, &marker->hdr, QLW_IOCB_MARKER, 1); + + /* could be more specific here; isp(4) isn't */ + marker->device = qlw_swap16(sc, (bus << 7) << 8); + marker->modifier = qlw_swap16(sc, QLW_IOCB_MARKER_SYNC_ALL); + qlw_dump_iocb(sc, buf); } void @@ -1280,30 +1342,29 @@ qlw_put_cmd(struct qlw_softc *sc, void *buf, struct scsi_xfer *xs, struct qlw_ccb *ccb) { struct qlw_iocb_req0 *req = buf; + int entry_count = 1; u_int16_t dir; int seg, nsegs; - - req->entry_type = QLW_IOCB_CMD_TYPE_0; - req->entry_count = 1; - req->seqno = 0; - req->flags = 0; + int seg_count; + int timeout = 0; + int bus, target, lun; if (xs->datalen == 0) { dir = QLW_IOCB_CMD_NO_DATA; - req->req_seg_count = htole16(1); + seg_count = 1; } else { dir = xs->flags & SCSI_DATA_IN ? QLW_IOCB_CMD_READ_DATA : QLW_IOCB_CMD_WRITE_DATA; - req->req_seg_count = htole16(ccb->ccb_dmamap->dm_nsegs); + seg_count = ccb->ccb_dmamap->dm_nsegs; nsegs = ccb->ccb_dmamap->dm_nsegs - QLW_IOCB_SEGS_PER_CMD; while (nsegs > 0) { - req->entry_count++; + entry_count++; nsegs -= QLW_IOCB_SEGS_PER_CONT; } for (seg = 0; seg < ccb->ccb_dmamap->dm_nsegs; seg++) { if (seg >= QLW_IOCB_SEGS_PER_CMD) break; - qlw_put_data_seg(&req->req0_segs[seg], + qlw_put_data_seg(sc, &req->segs[seg], ccb->ccb_dmamap, seg); } } @@ -1311,42 +1372,45 @@ qlw_put_cmd(struct qlw_softc *sc, void *buf, struct scsi_xfer *xs, if (sc->sc_running && (xs->sc_link->quirks & SDEV_NOTAGS) == 0) dir |= QLW_IOCB_CMD_SIMPLE_QUEUE; - req->req_flags = htole16(dir); + qlw_put_header(sc, &req->hdr, QLW_IOCB_CMD_TYPE_0, entry_count); /* * timeout is in seconds. make sure it's at least 1 if a timeout * was specified in xs */ if (xs->timeout != 0) - req->req_time = htole16(MAX(1, xs->timeout/1000)); + timeout = MAX(1, xs->timeout/1000); - req->req_target = (qlw_xs_bus(sc, xs) << 7) | xs->sc_link->target; - req->req_lun_trn = xs->sc_link->lun; + req->flags = qlw_swap16(sc, dir); + req->seg_count = qlw_swap16(sc, seg_count); + req->timeout = qlw_swap16(sc, timeout); + + bus = qlw_xs_bus(sc, xs); + target = xs->sc_link->target; + lun = xs->sc_link->lun; + req->device = qlw_swap16(sc, (((bus << 7) | target) << 8) | lun); - memcpy(req->req_cdb, xs->cmd, xs->cmdlen); - req->req_ccblen = htole16(xs->cmdlen); + memcpy(req->cdb, xs->cmd, xs->cmdlen); + req->ccblen = qlw_swap16(sc, xs->cmdlen); - req->req_handle = htole32(ccb->ccb_id); + req->handle = qlw_swap32(sc, ccb->ccb_id); qlw_dump_iocb(sc, buf); } void qlw_put_cont(struct qlw_softc *sc, void *buf, struct scsi_xfer *xs, - struct qlw_ccb *ccb, int seg0) + struct qlw_ccb *ccb, int seg0) { struct qlw_iocb_cont0 *cont = buf; int seg; - cont->entry_type = QLW_IOCB_CONT_TYPE_0; - cont->entry_count = 1; - cont->seqno = 0; - cont->flags = 0; + qlw_put_header(sc, &cont->hdr, QLW_IOCB_CONT_TYPE_0, 1); for (seg = seg0; seg < ccb->ccb_dmamap->dm_nsegs; seg++) { if ((seg - seg0) >= QLW_IOCB_SEGS_PER_CONT) break; - qlw_put_data_seg(&cont->segs[seg - seg0], + qlw_put_data_seg(sc, &cont->segs[seg - seg0], ccb->ccb_dmamap, seg); } } diff --git a/sys/dev/ic/qlwreg.h b/sys/dev/ic/qlwreg.h index b3602dbb139..65f761565a5 100644 --- a/sys/dev/ic/qlwreg.h +++ b/sys/dev/ic/qlwreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: qlwreg.h,v 1.6 2014/03/15 13:08:52 kettenis Exp $ */ +/* $OpenBSD: qlwreg.h,v 1.7 2014/03/15 21:49:47 kettenis Exp $ */ /* * Copyright (c) 2013, 2014 Jonathan Matthew <jmatthew@openbsd.org> @@ -245,7 +245,12 @@ struct qlw_nvram { #define QLW_IOCB_CMD_WRITE_DATA 0x0040 #define QLW_IOCB_CMD_NO_FAST_POST 0x0080 -#define QLW_IOCB_MARKER_SYNC_ALL 2 +struct qlw_iocb_hdr { + u_int8_t entry_type; + u_int8_t entry_count; + u_int8_t seqno; + u_int8_t flags; +} __packed; #define QLW_IOCB_SEGS_PER_CMD 4 #define QLW_IOCB_SEGS_PER_CONT 7 @@ -262,38 +267,28 @@ struct qlw_iocb_seg { #define QLW_IOCB_MARKER 0x04 struct qlw_iocb_req0 { - u_int8_t entry_type; /* QLW_IOCB_CMD_TYPE_0 */ - u_int8_t entry_count; - u_int8_t seqno; - u_int8_t flags; + struct qlw_iocb_hdr hdr; /* QLW_IOCB_REQ_TYPE0 */ - u_int32_t req_handle; - u_int8_t req_lun_trn; - u_int8_t req_target; - u_int16_t req_ccblen; - u_int16_t req_flags; - u_int16_t req_reserved; - u_int16_t req_time; - u_int16_t req_seg_count; - u_int8_t req_cdb[12]; - struct qlw_iocb_seg req0_segs[4]; + u_int32_t handle; + u_int16_t device; + u_int16_t ccblen; + u_int16_t flags; + u_int16_t reserved; + u_int16_t timeout; + u_int16_t seg_count; + u_int8_t cdb[12]; + struct qlw_iocb_seg segs[4]; } __packed; struct qlw_iocb_cont0 { - u_int8_t entry_type; /* QLA_IOCB_CONT_TYPE_0 */ - u_int8_t entry_count; - u_int8_t seqno; - u_int8_t flags; + struct qlw_iocb_hdr hdr; /* QLW_IOCB_CONT_TYPE_0 */ u_int32_t reserved; struct qlw_iocb_seg segs[7]; } __packed; -struct qla_iocb_status { - u_int8_t entry_type; /* QLA_IOCB_STATUS */ - u_int8_t entry_count; - u_int8_t seqno; - u_int8_t flags; +struct qlw_iocb_status { + struct qlw_iocb_hdr hdr; u_int32_t handle; u_int16_t scsi_status; @@ -326,14 +321,13 @@ struct qla_iocb_status { #define QLW_SCSI_STATUS_SENSE_VALID 0x0200 struct qlw_iocb_marker { - u_int8_t entry_type; /* QLW_IOCB_MARKER */ - u_int8_t entry_count; - u_int8_t seqno; - u_int8_t flags; + struct qlw_iocb_hdr hdr; /* QLW_IOCB_MARKER */ u_int32_t handle; - u_int8_t lun; - u_int8_t target; - u_int8_t modifier; - u_int8_t reserved2[53]; + u_int16_t device; + u_int16_t modifier; + u_int8_t reserved2[52]; + } __packed; + +#define QLW_IOCB_MARKER_SYNC_ALL 2 diff --git a/sys/dev/sbus/files.sbus b/sys/dev/sbus/files.sbus index 1a12d6e7905..e5e767759ae 100644 --- a/sys/dev/sbus/files.sbus +++ b/sys/dev/sbus/files.sbus @@ -1,4 +1,4 @@ -# $OpenBSD: files.sbus,v 1.40 2014/02/02 13:01:58 kettenis Exp $ +# $OpenBSD: files.sbus,v 1.41 2014/03/15 21:49:47 kettenis Exp $ # $NetBSD: files.sbus,v 1.16 2000/12/08 17:29:12 martin Exp $ # # Config file and device description for machine-independent SBUS code. @@ -58,6 +58,9 @@ file dev/sbus/cs4231.c audiocs attach isp at sbus with isp_sbus file dev/sbus/isp_sbus.c isp_sbus +attach qlw at sbus with qlw_sbus +file dev/sbus/qlw_sbus.c qlw_sbus + attach qla at sbus with qla_sbus file dev/sbus/qla_sbus.c qla_sbus diff --git a/sys/dev/sbus/qlw_sbus.c b/sys/dev/sbus/qlw_sbus.c new file mode 100644 index 00000000000..c1e6a6a832d --- /dev/null +++ b/sys/dev/sbus/qlw_sbus.c @@ -0,0 +1,147 @@ +/* $OpenBSD: qlw_sbus.c,v 1.1 2014/03/15 21:49:47 kettenis Exp $ */ +/* + * Copyright (c) 2014 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#include <machine/bus.h> +#include <machine/intr.h> +#include <machine/autoconf.h> + +#include <dev/sbus/sbusvar.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <dev/ic/qlwreg.h> +#include <dev/ic/qlwvar.h> + +#ifndef ISP_NOFIRMWARE +#include <dev/microcode/isp/asm_sbus.h> +#endif + +int qlw_sbus_match(struct device *, void *, void *); +void qlw_sbus_attach(struct device *, struct device *, void *); + +struct cfattach qlw_sbus_ca = { + sizeof(struct qlw_softc), + qlw_sbus_match, + qlw_sbus_attach +}; + +int +qlw_sbus_match(struct device *parent, void *cf, void *aux) +{ + struct sbus_attach_args *sa = aux; + + if (strcmp("ptisp", sa->sa_name) == 0 || + strcmp("PTI,ptisp", sa->sa_name) == 0 || + strcmp("SUNW,isp", sa->sa_name) == 0 || + strcmp("QLGC,isp", sa->sa_name) == 0) + return 2; + + return 0; +} + +void +qlw_sbus_attach(struct device *parent, struct device *self, void *aux) +{ + struct qlw_softc *sc = (void *)self; + struct sbus_attach_args *sa = aux; + u_int32_t sbusburst, burst; + int freq; + + if (sa->sa_nintr < 1) { + printf(": no interrupt\n"); + return; + } + + if (sa->sa_nreg < 1) { + printf(": no registers\n"); + return; + } + + if (sbus_bus_map(sa->sa_bustag, sa->sa_slot, sa->sa_offset, + sa->sa_size, 0, 0, &sc->sc_ioh) != 0) { + printf(": can't map registers\n"); + return; + } + + printf(": %s\n", sa->sa_name); + + if (bus_intr_establish(sa->sa_bustag, sa->sa_pri, IPL_BIO, 0, + qlw_intr, sc, sc->sc_dev.dv_xname) == NULL) { + printf("%s: can't establish interrupt\n", DEVNAME(sc)); + goto unmap; + } + + /* + * Get transfer burst size from PROM + */ + sbusburst = ((struct sbus_softc *)parent)->sc_burst; + if (sbusburst == 0) + sbusburst = SBUS_BURST_32 - 1; /* 1->16 */ + + burst = getpropint(sa->sa_node, "burst-sizes", -1); + if (burst == -1) + burst = sbusburst; + + /* Clamp at parent's burst sizes */ + burst &= sbusburst; + + if ((burst & SBUS_BURST_32)) + sc->sc_isp_config = QLW_BURST_ENABLE | QLW_SBUS_FIFO_32; + else if ((burst & SBUS_BURST_16)) + sc->sc_isp_config = QLW_BURST_ENABLE | QLW_SBUS_FIFO_16; + else if ((burst & SBUS_BURST_8)) + sc->sc_isp_config = QLW_BURST_ENABLE | QLW_SBUS_BURST_8; + + sc->sc_iot = sa->sa_bustag; + sc->sc_ios = sa->sa_size; + sc->sc_dmat = sa->sa_dmatag; + + sc->sc_isp_gen = QLW_GEN_ISP1000; + sc->sc_isp_type = QLW_ISP1000; + sc->sc_numbusses = 1; + + freq = getpropint(sa->sa_node, "clock-frequency", 40000000); + sc->sc_clock = (freq + 500000) / 1000000; + +#ifndef ISP_NOFIRMWARE + /* + * Some early versions of the PTI cards don't support loading + * a new firmware, so only do this in the Sun or QLogic + * branded ones. + */ + if (strcmp("SUNW,isp", sa->sa_name) == 0 || + strcmp("QLGC,isp", sa->sa_name) == 0) + sc->sc_firmware = isp_1000_risc_code; +#endif + + sc->sc_initiator[0] = getpropint(sa->sa_node, "scsi-initiator-id", 6); + + sc->sc_host_cmd_ctrl = QLW_HOST_CMD_CTRL_SBUS; + sc->sc_mbox_base = QLW_MBOX_BASE_SBUS; + + qlw_attach(sc); + return; + +unmap: + bus_space_unmap(sa->sa_bustag, sc->sc_ioh, sa->sa_size); +} |