diff --git a/sys/dev/mpi3mr/mpi3mr.c b/sys/dev/mpi3mr/mpi3mr.c --- a/sys/dev/mpi3mr/mpi3mr.c +++ b/sys/dev/mpi3mr/mpi3mr.c @@ -980,6 +980,11 @@ sc->admin_reply_ephase = 1; if (!sc->admin_req) { + /* + * We need to create the tag for the admin queue to get the + * iofacts to see how many bits the controller decodes. Solve + * this chicken and egg problem by only doing lower 4GB DMA. + */ if (bus_dma_tag_create(sc->mpi3mr_parent_dmat, /* parent */ 4, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ @@ -1434,6 +1439,12 @@ MPI3_SGE_FLAGS_END_OF_LIST); + /* + * We can't use sc->dma_loaddr / hiaddr here. We set those only after + * we get the iocfacts. So allocate in the lower 4GB. The amount of + * data is tiny and we don't do this that often, so any bouncing we + * might have to do isn't a cause for concern. + */ if (bus_dma_tag_create(sc->mpi3mr_parent_dmat, /* parent */ 4, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ @@ -1668,6 +1679,22 @@ sc->max_host_ios = sc->facts.max_reqs - (MPI3MR_INTERNALCMDS_RESVD + 1); + /* + * Set the DMA mask for the card. dma_mask is the number of bits that + * can have bits set in them. Translate this into bus_dma loaddr/hiaddr + * args. Add sanity for more bits than address space or other overflow + * situations. + */ + if (sc->facts.dma_mask == 0 || + (sc->facts.dma_mask >= sizeof(bus_addr_t) * 8)) + sc->dma_loaddr = BUS_SPACE_MAXADDR; + else + sc->dma_loaddr = ~((1ull << sc->facts.dma_mask) - 1); + sc->dma_hiaddr = BUS_SPACE_MAXADDR; + mpi3mr_dprint(sc, MPI3MR_INFO, + "dma_mask bits: %d loaddr 0x%jx hiaddr 0x%jx\n", + sc->facts.dma_mask, sc->dma_loaddr, sc->dma_hiaddr); + return retval; } diff --git a/sys/dev/mpi3mr/mpi3mr_pci.c b/sys/dev/mpi3mr/mpi3mr_pci.c --- a/sys/dev/mpi3mr/mpi3mr_pci.c +++ b/sys/dev/mpi3mr/mpi3mr_pci.c @@ -273,6 +273,16 @@ sc->mpi3mr_btag = rman_get_bustag(sc->mpi3mr_regs_resource); sc->mpi3mr_bhandle = rman_get_bushandle(sc->mpi3mr_regs_resource); + /* + * XXX Perhaps we should move this to after we read iocfacts and use + * that to create the proper parent tag. However, to get the iocfacts + * we need to have a dmatag for both the admin queue and the iocfacts + * DMA transfer. So for now, we just create a 'no restriction' tag and + * use sc->dma_loaddr for all the other tag_create calls to get the + * right value. It would be nice if one could retroactively adjust a + * created tag. The Linux driver effectively does this by setting the + * dma_mask on the device. + */ /* Allocate the parent DMA tag */ if (bus_dma_tag_create(bus_get_dma_tag(dev), /* parent */ 1, 0, /* algnmnt, boundary */