Module Name: src Committed By: mrg Date: Sun Mar 20 21:26:36 UTC 2011
Modified Files: src/sys/arch/sparc64/dev: iommu.c Log Message: re-add most of the pryo-supporting code, and some general clean up: - handle setting tsb size in the ptsb via IOMMU_TSBSIZE_IN_PTSB (partly from openbsd) - fix IDB_INFO for systems without a STC that fault on accesses to the flush register (like pyro) - move iommu_reset() to the end of iommu_init() - use IOMMUREG_WRITE() in a few places - add a missing membar_lookaside() (from openbsd) - if pmap_extract() on the flush buffer fails, disable flushing (from openbsd) - flush the pyro-style iommu when IOMMU_FLUSH_CACHE is set (partly from openbsd) - clear up a bit of debugging code so it spew a little less (sometimes you will get kernel lock spinouts due to long scrolling printfs) - use __func__ in several places To generate a diff of this commit: cvs rdiff -u -r1.102 -r1.103 src/sys/arch/sparc64/dev/iommu.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/sparc64/dev/iommu.c diff -u src/sys/arch/sparc64/dev/iommu.c:1.102 src/sys/arch/sparc64/dev/iommu.c:1.103 --- src/sys/arch/sparc64/dev/iommu.c:1.102 Wed Mar 16 05:49:43 2011 +++ src/sys/arch/sparc64/dev/iommu.c Sun Mar 20 21:26:36 2011 @@ -1,4 +1,4 @@ -/* $NetBSD: iommu.c,v 1.102 2011/03/16 05:49:43 mrg Exp $ */ +/* $NetBSD: iommu.c,v 1.103 2011/03/20 21:26:36 mrg Exp $ */ /* * Copyright (c) 1999, 2000 Matthew R. Green @@ -59,7 +59,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: iommu.c,v 1.102 2011/03/16 05:49:43 mrg Exp $"); +__KERNEL_RCSID(0, "$NetBSD: iommu.c,v 1.103 2011/03/20 21:26:36 mrg Exp $"); #include "opt_ddb.h" @@ -134,7 +134,7 @@ * be hard-wired, so we read the start and size from the PROM and * just use those values. */ - is->is_cr = (tsbsize << 16) | IOMMUCR_EN; + is->is_cr = IOMMUCR_EN; is->is_tsbsize = tsbsize; if (iovabase == -1) { is->is_dvmabase = IOTSB_VSTART(is->is_tsbsize); @@ -176,20 +176,13 @@ { /* Probe the iommu */ - printf("iommu regs at: cr=%lx tsb=%lx flush=%lx\n", - (u_long)bus_space_read_8(is->is_bustag, is->is_iommu, - offsetof (struct iommureg, iommu_cr)), - (u_long)bus_space_read_8(is->is_bustag, is->is_iommu, - offsetof (struct iommureg, iommu_tsb)), - (u_long)bus_space_read_8(is->is_bustag, is->is_iommu, - offsetof (struct iommureg, iommu_flush))); printf("iommu cr=%llx tsb=%llx\n", (unsigned long long)bus_space_read_8(is->is_bustag, is->is_iommu, - offsetof (struct iommureg, iommu_cr)), + offsetof(struct iommureg, iommu_cr)), (unsigned long long)bus_space_read_8(is->is_bustag, is->is_iommu, - offsetof (struct iommureg, iommu_tsb))); + offsetof(struct iommureg, iommu_tsb))); printf("TSB base %p phys %llx\n", (void *)is->is_tsb, (unsigned long long)is->is_ptsb); delay(1000000); /* 1 s */ @@ -197,11 +190,6 @@ #endif /* - * now actually start up the IOMMU - */ - iommu_reset(is); - - /* * Now all the hardware's working we need to allocate a dvma map. */ aprint_debug("DVMA map: %x to %x\n", @@ -214,6 +202,20 @@ is->is_dvmabase, is->is_dvmaend, M_DEVBUF, 0, 0, EX_NOWAIT); /* XXXMRG Check is_dvmamap is valid. */ + + /* + * Set the TSB size. The relevant bits were moved to the TSB + * base register in the PCIe host bridges. + */ + if (is->is_flags & IOMMU_TSBSIZE_IN_PTSB) + is->is_ptsb |= is->is_tsbsize; + else + is->is_cr |= (is->is_tsbsize << 16); + + /* + * now actually start up the IOMMU + */ + iommu_reset(is); } /* @@ -227,13 +229,10 @@ int i; struct strbuf_ctl *sb; - /* Need to do 64-bit stores */ - bus_space_write_8(is->is_bustag, is->is_iommu, IOMMUREG(iommu_tsb), - is->is_ptsb); + IOMMUREG_WRITE(is, iommu_tsb, is->is_ptsb); /* Enable IOMMU in diagnostic mode */ - bus_space_write_8(is->is_bustag, is->is_iommu, IOMMUREG(iommu_cr), - is->is_cr|IOMMUCR_DE); + IOMMUREG_WRITE(is, iommu_cr, is->is_cr|IOMMUCR_DE); for (i = 0; i < 2; i++) { if ((sb = is->is_sb[i])) { @@ -242,6 +241,8 @@ bus_space_write_8(is->is_bustag, is->is_sb[i]->sb_sb, STRBUFREG(strbuf_ctl), STRBUF_EN); + membar_lookaside(); + /* No streaming buffers? Disable them */ if (bus_space_read_8(is->is_bustag, is->is_sb[i]->sb_sb, @@ -252,12 +253,16 @@ /* * locate the pa of the flush buffer. */ - (void)pmap_extract(pmap_kernel(), - (vaddr_t)is->is_sb[i]->sb_flush, - &is->is_sb[i]->sb_flushpa); + if (pmap_extract(pmap_kernel(), + (vaddr_t)is->is_sb[i]->sb_flush, + &is->is_sb[i]->sb_flushpa) == FALSE) + is->is_sb[i]->sb_flush = NULL; } } } + + if (is->is_flags & IOMMU_FLUSH_CACHE) + IOMMUREG_WRITE(is, iommu_cache_invalidate, -1ULL); } /* @@ -288,12 +293,11 @@ tte |= (flags & 0xff000LL)<<(4*8); #endif - DPRINTF(IDB_IOMMU, ("Clearing TSB slot %d for va %p\n", - (int)IOTSBSLOT(va,is->is_tsbsize), (void *)(u_long)va)); is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)] = tte; bus_space_write_8(is->is_bustag, is->is_iommu, IOMMUREG(iommu_flush), va); - DPRINTF(IDB_IOMMU, ("iommu_enter: va %lx pa %lx TSB[%lx]@%p=%lx\n", + DPRINTF(IDB_IOMMU, ("iommu_enter: slot %d va %lx pa %lx " + "TSB[%lx]@%p=%lx\n", (int)IOTSBSLOT(va,is->is_tsbsize), va, (long)pa, (u_long)IOTSBSLOT(va,is->is_tsbsize), (void *)(u_long)&is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)], (u_long)tte)); @@ -325,6 +329,7 @@ void iommu_remove(struct iommu_state *is, vaddr_t va, size_t len) { + int slot; #ifdef DIAGNOSTIC if (va < is->is_dvmabase || va > is->is_dvmaend) @@ -363,8 +368,15 @@ is->is_tsb[IOTSBSLOT(va,is->is_tsbsize)] &= ~IOTTE_V; membar_storestore(); #endif - bus_space_write_8(is->is_bustag, is->is_iommu, - IOMMUREG(iommu_flush), va); + IOMMUREG_WRITE(is, iommu_flush, va); + + /* Flush cache if necessary. */ + slot = IOTSBSLOT(trunc_page(va), is->is_tsbsize); + if ((is->is_flags & IOMMU_FLUSH_CACHE) && + (len == 0 || (slot % 8) == 7)) + IOMMUREG_WRITE(is, iommu_cache_flush, + is->is_ptsb + slot * 8); + va += PAGE_SIZE; } } @@ -410,8 +422,8 @@ cur = flushtimeout; BUMPTIME(&flushtimeout, 500000); /* 1/2 sec */ - DPRINTF(IDB_IOMMU, ("iommu_strbuf_flush_done: flush = %lx " - "at va = %lx pa = %lx now=%"PRIx64":%"PRIx32" until = %"PRIx64":%"PRIx32"\n", + DPRINTF(IDB_IOMMU, ("%s: flush = %lx at va = %lx pa = %lx now=" + "%"PRIx64":%"PRIx32" until = %"PRIx64":%"PRIx32"\n", __func__, (long)*sb->sb_flush, (long)sb->sb_flush, (long)sb->sb_flushpa, cur.tv_sec, cur.tv_usec, flushtimeout.tv_sec, flushtimeout.tv_usec)); @@ -423,7 +435,7 @@ #ifdef DIAGNOSTIC if (!ldxa(sb->sb_flushpa, ASI_PHYS_CACHED)) { - printf("iommu_strbuf_flush_done: flush timeout %p, at %p\n", + printf("%s: flush timeout %p, at %p\n", __func__, (void *)(u_long)*sb->sb_flush, (void *)(u_long)sb->sb_flushpa); /* panic? */ #ifdef DDB @@ -431,7 +443,7 @@ #endif } #endif - DPRINTF(IDB_IOMMU, ("iommu_strbuf_flush_done: flushed\n")); + DPRINTF(IDB_IOMMU, ("%s: flushed\n", __func__)); return (*sb->sb_flush); } @@ -453,6 +465,7 @@ vaddr_t vaddr = (vaddr_t)buf; int seg; struct pmap *pmap; + int slot; if (map->dm_nsegs) { /* Already in use?? */ @@ -489,6 +502,7 @@ * If our segment size is larger than the boundary we need to * split the transfer up int little pieces ourselves. */ + KASSERT(is->is_dvmamap); s = splhigh(); err = extent_alloc(is->is_dvmamap, sgsize, align, (sgsize > boundary) ? 0 : boundary, @@ -597,9 +611,17 @@ flags | IOTTE_DEBUG(0x4000)); needsflush = 1; - dvmaddr += PAGE_SIZE; vaddr += sgsize; buflen -= sgsize; + + /* Flush cache if necessary. */ + slot = IOTSBSLOT(trunc_page(dvmaddr), is->is_tsbsize); + if ((is->is_flags & IOMMU_FLUSH_CACHE) && + (buflen <= 0 || (slot % 8) == 7)) + IOMMUREG_WRITE(is, iommu_cache_flush, + is->is_ptsb + slot * 8); + + dvmaddr += PAGE_SIZE; } if (needsflush) iommu_strbuf_flush_done(sb); @@ -672,6 +694,7 @@ u_long dvmaddr, sgstart, sgend, bmask; struct pglist *pglist; const int pagesz = PAGE_SIZE; + int slot; #ifdef DEBUG int npg = 0; #endif @@ -841,7 +864,15 @@ prev_pa = pa, flags | IOTTE_DEBUG(++npg << 12)); needsflush = 1; + + /* Flush cache if necessary. */ + slot = IOTSBSLOT(trunc_page(dvmaddr), is->is_tsbsize); + if ((is->is_flags & IOMMU_FLUSH_CACHE) && + ((dvmaddr + pagesz) > sgend || (slot % 8) == 7)) + IOMMUREG_WRITE(is, iommu_cache_flush, + is->is_ptsb + slot * 8); } + dvmaddr += pagesz; pa += pagesz; last_va = dvmaddr; @@ -859,7 +890,7 @@ { int seg; for (seg = 0; seg < map->dm_nsegs; seg++) { if (map->dm_segs[seg].ds_addr < is->is_dvmabase || - map->dm_segs[seg].ds_addr > is->is_dvmaend) { + map->dm_segs[seg].ds_addr > is->is_dvmaend) { printf("seg %d dvmaddr %lx out of range %x - %x\n", seg, (long)map->dm_segs[seg].ds_addr, is->is_dvmabase, is->is_dvmaend); @@ -912,8 +943,16 @@ iommu_enter(sb, dvmaddr, pa, flags | IOTTE_DEBUG(0x8000)); needsflush = 1; - dvmaddr += pagesz; sgsize -= pagesz; + + /* Flush cache if necessary. */ + slot = IOTSBSLOT(trunc_page(dvmaddr), is->is_tsbsize); + if ((is->is_flags & IOMMU_FLUSH_CACHE) && + (sgsize == 0 || (slot % 8) == 7)) + IOMMUREG_WRITE(is, iommu_cache_flush, + is->is_ptsb + slot * 8); + + dvmaddr += pagesz; } if (needsflush) iommu_strbuf_flush_done(sb); @@ -966,7 +1005,7 @@ #endif if ((is->is_tsb[IOTSBSLOT(va, is->is_tsbsize)] & IOTTE_STREAM) == 0) { - DPRINTF(IDB_BUSDMA, + DPRINTF(IDB_SYNC, ("iommu_dvmamap_sync_range: attempting to flush " "non-streaming entry\n")); return (0); @@ -984,7 +1023,7 @@ #endif for ( ; va <= vaend; va += PAGE_SIZE) { - DPRINTF(IDB_BUSDMA, + DPRINTF(IDB_SYNC, ("iommu_dvmamap_sync_range: flushing va %p\n", (void *)(u_long)va)); iommu_strbuf_flush(sb, va); @@ -1011,7 +1050,7 @@ } if (i == map->dm_nsegs) - panic("iommu_dvmamap_sync: segment too short %llu", + panic("%s: segment too short %llu", __func__, (unsigned long long)offset); if (ops & (BUS_DMASYNC_PREREAD | BUS_DMASYNC_POSTWRITE)) { @@ -1031,7 +1070,7 @@ } #ifdef DIAGNOSTIC if (i == map->dm_nsegs && len > 0) - panic("iommu_dvmamap_sync: leftover %llu", + panic("%s: leftover %llu", __func__, (unsigned long long)len); #endif