Date: Wed, 23 Dec 2009 22:02:34 +0000 (UTC) From: Marius Strobl <marius@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r200923 - in head/sys/sparc64: include sparc64 Message-ID: <200912232202.nBNM2YcC010494@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: marius Date: Wed Dec 23 22:02:34 2009 New Revision: 200923 URL: http://svn.freebsd.org/changeset/base/200923 Log: - Add support for the IOMMUs of Fire JBus to PCIe and Oberon Uranus to PCIe bridges. - Add support for talking the PROM mappings over to the kernel IOTSB just like we do with the kernel TSB in order to allow OFW drivers to continue to work. - Change some members, parameters and variables to unsigned where more appropriate. Modified: head/sys/sparc64/include/iommureg.h head/sys/sparc64/include/iommuvar.h head/sys/sparc64/sparc64/iommu.c Modified: head/sys/sparc64/include/iommureg.h ============================================================================== --- head/sys/sparc64/include/iommureg.h Wed Dec 23 21:51:41 2009 (r200922) +++ head/sys/sparc64/include/iommureg.h Wed Dec 23 22:02:34 2009 (r200923) @@ -44,10 +44,13 @@ * controllers. */ -/* iommmu registers */ +/* IOMMU registers */ #define IMR_CTL 0x0000 /* IOMMU control register */ #define IMR_TSB 0x0008 /* IOMMU TSB base register */ #define IMR_FLUSH 0x0010 /* IOMMU flush register */ +/* The TTE Cache is Fire and Oberon only. */ +#define IMR_CACHE_FLUSH 0x0100 /* IOMMU TTE cache flush address register */ +#define IMR_CACHE_INVAL 0x0108 /* IOMMU TTE cache invalidate register */ /* streaming buffer registers */ #define ISR_CTL 0x0000 /* streaming buffer control reg */ @@ -70,28 +73,57 @@ /* * control register bits */ -/* Nummber of entries in IOTSB */ +/* Nummber of entries in the IOTSB - pre-Fire only */ +#define IOMMUCR_TSBSZ_MASK 0x0000000000070000UL #define IOMMUCR_TSBSZ_SHIFT 16 -#define IOMMUCR_TSB1K 0x0000000000000000UL -#define IOMMUCR_TSB2K 0x0000000000010000UL -#define IOMMUCR_TSB4K 0x0000000000020000UL -#define IOMMUCR_TSB8K 0x0000000000030000UL -#define IOMMUCR_TSB16K 0x0000000000040000UL -#define IOMMUCR_TSB32K 0x0000000000050000UL -#define IOMMUCR_TSB64K 0x0000000000060000UL -#define IOMMUCR_TSB128K 0x0000000000070000UL -/* Mask for above */ -#define IOMMUCR_TSBMASK 0xfffffffffff8ffffUL -/* 8K iommu page size */ +/* TSB cache snoop enable */ +#define IOMMUCR_SE 0x0000000000000400UL +/* Cache modes - Fire and Oberon */ +#define IOMMUCR_CM_NC_TLB_TBW 0x0000000000000000UL +#define IOMMUCR_CM_LC_NTLB_NTBW 0x0000000000000100UL +#define IOMMUCR_CM_LC_TLB_TBW 0x0000000000000200UL +#define IOMMUCR_CM_C_TLB_TBW 0x0000000000000300UL +/* IOMMU page size - pre-Fire only */ #define IOMMUCR_8KPG 0x0000000000000000UL -/* 64K iommu page size */ #define IOMMUCR_64KPG 0x0000000000000004UL -/* Diag enable */ +/* Bypass enable - Fire and Oberon */ +#define IOMMUCR_BE 0x0000000000000002UL +/* Diagnostic mode enable - pre-Fire only */ #define IOMMUCR_DE 0x0000000000000002UL -/* Enable IOMMU */ +/* IOMMU/translation enable */ #define IOMMUCR_EN 0x0000000000000001UL /* + * TSB base register bits + */ + /* TSB base address */ +#define IOMMUTB_TB_MASK 0x000007ffffffe000UL +#define IOMMUTB_TB_SHIFT 13 +/* IOMMU page size - Fire and Oberon */ +#define IOMMUTB_8KPG 0x0000000000000000UL +#define IOMMUTB_64KPG 0x0000000000000100UL +/* Nummber of entries in the IOTSB - Fire and Oberon */ +#define IOMMUTB_TSBSZ_MASK 0x0000000000000004UL +#define IOMMUTB_TSBSZ_SHIFT 0 + +/* + * TSB size definitions for both control and TSB base register */ +#define IOMMU_TSB1K 0 +#define IOMMU_TSB2K 1 +#define IOMMU_TSB4K 2 +#define IOMMU_TSB8K 3 +#define IOMMU_TSB16K 4 +#define IOMMU_TSB32K 5 +#define IOMMU_TSB64K 6 +#define IOMMU_TSB128K 7 +/* Fire and Oberon */ +#define IOMMU_TSB256K 8 +/* Fire and Oberon */ +#define IOMMU_TSB512K 9 +#define IOMMU_TSBENTRIES(tsbsz) \ + ((1 << (tsbsz)) << (IO_PAGE_SHIFT - IOTTE_SHIFT)) + +/* * Diagnostic register definitions */ #define IOMMU_DTAG_VPNBITS 19 @@ -113,16 +145,16 @@ */ /* Entry valid */ #define IOTTE_V 0x8000000000000000UL -/* 8K or 64K page? */ +/* Page size - pre-Fire only */ #define IOTTE_64K 0x2000000000000000UL #define IOTTE_8K 0x0000000000000000UL -/* Is page streamable? */ +/* Streamable page - streaming buffer equipped variants only */ #define IOTTE_STREAM 0x1000000000000000UL -/* Accesses to same bus segment? */ +/* Accesses to the same bus segment - SBus only */ #define IOTTE_LOCAL 0x0800000000000000UL -/* Let's assume this is correct */ -#define IOTTE_PAMASK 0x000007ffffffe000UL -/* Accesses to cacheable space */ +/* Physical address mask (based on Oberon) */ +#define IOTTE_PAMASK 0x00007fffffffe000UL +/* Accesses to cacheable space - pre-Fire only */ #define IOTTE_C 0x0000000000000010UL /* Writeable */ #define IOTTE_W 0x0000000000000002UL Modified: head/sys/sparc64/include/iommuvar.h ============================================================================== --- head/sys/sparc64/include/iommuvar.h Wed Dec 23 21:51:41 2009 (r200922) +++ head/sys/sparc64/include/iommuvar.h Wed Dec 23 22:02:34 2009 (r200923) @@ -66,10 +66,10 @@ struct iommu_state { int is_tsbsize; /* (r) 0 = 8K, ... */ uint64_t is_pmaxaddr; /* (r) max. physical address */ uint64_t is_dvmabase; /* (r) */ - int64_t is_cr; /* (r) Control reg value */ + uint64_t is_cr; /* (r) Control reg value */ vm_paddr_t is_flushpa[2]; /* (r) */ - volatile int64_t *is_flushva[2]; /* (r, *i) */ + volatile uint64_t *is_flushva[2]; /* (r, *i) */ /* * (i) * When a flush is completed, 64 bytes will be stored at the given @@ -99,11 +99,14 @@ struct iommu_state { /* behavior flags */ u_int is_flags; /* (r) */ #define IOMMU_RERUN_DISABLE (1 << 0) +#define IOMMU_FIRE (1 << 1) +#define IOMMU_FLUSH_CACHE (1 << 2) +#define IOMMU_PRESERVE_PROM (1 << 3) }; /* interfaces for PCI/SBus code */ -void iommu_init(const char *name, struct iommu_state *is, int tsbsize, - uint32_t iovabase, int resvpg); +void iommu_init(const char *name, struct iommu_state *is, u_int tsbsize, + uint32_t iovabase, u_int resvpg); void iommu_reset(struct iommu_state *is); void iommu_decode_fault(struct iommu_state *is, vm_offset_t phys); Modified: head/sys/sparc64/sparc64/iommu.c ============================================================================== --- head/sys/sparc64/sparc64/iommu.c Wed Dec 23 21:51:41 2009 (r200922) +++ head/sys/sparc64/sparc64/iommu.c Wed Dec 23 22:02:34 2009 (r200923) @@ -138,11 +138,13 @@ __FBSDID("$FreeBSD$"); #include <vm/pmap.h> #include <vm/vm_map.h> +#include <machine/asi.h> #include <machine/bus.h> #include <machine/bus_private.h> #include <machine/iommureg.h> #include <machine/pmap.h> #include <machine/resource.h> +#include <machine/ver.h> #include <sys/rman.h> @@ -212,6 +214,12 @@ static __inline void iommu_tlb_flush(struct iommu_state *is, bus_addr_t va) { + if ((is->is_flags & IOMMU_FIRE) != 0) + /* + * Direct page flushing is not supported and also not + * necessary due to cache snooping. + */ + return; IOMMU_WRITE8(is, is_iommu, IMR_FLUSH, va); } @@ -282,18 +290,19 @@ iommu_map_remq(struct iommu_state *is, b * - create a private DVMA map. */ void -iommu_init(const char *name, struct iommu_state *is, int tsbsize, - uint32_t iovabase, int resvpg) +iommu_init(const char *name, struct iommu_state *is, u_int tsbsize, + uint32_t iovabase, u_int resvpg) { vm_size_t size; vm_offset_t offs; - uint64_t end; + uint64_t end, obpmap, obpptsb, tte; + u_int maxtsbsize, obptsbentries, obptsbsize, slot, tsbentries; int i; /* - * Setup the iommu. + * Setup the IOMMU. * - * The sun4u iommu is part of the PCI or SBus controller so we + * The sun4u IOMMU is part of the PCI or SBus controller so we * will deal with it here.. * * The IOMMU address space always ends at 0xffffe000, but the starting @@ -301,16 +310,30 @@ iommu_init(const char *name, struct iomm * is->is_tsbsize entries, where each entry is 8 bytes. The start of * the map can be calculated by (0xffffe000 << (8 + is->is_tsbsize)). */ - is->is_cr = (tsbsize << IOMMUCR_TSBSZ_SHIFT) | IOMMUCR_EN; + if ((is->is_flags & IOMMU_FIRE) != 0) { + maxtsbsize = IOMMU_TSB512K; + /* + * We enable bypass in order to be able to use a physical + * address for the event queue base. + */ + is->is_cr = IOMMUCR_SE | IOMMUCR_CM_C_TLB_TBW | IOMMUCR_BE; + } else { + maxtsbsize = IOMMU_TSB128K; + is->is_cr = (tsbsize << IOMMUCR_TSBSZ_SHIFT) | IOMMUCR_DE; + } + if (tsbsize > maxtsbsize) + panic("%s: unsupported TSB size ", __func__); + tsbentries = IOMMU_TSBENTRIES(tsbsize); + is->is_cr |= IOMMUCR_EN; is->is_tsbsize = tsbsize; is->is_dvmabase = iovabase; if (iovabase == -1) is->is_dvmabase = IOTSB_VSTART(is->is_tsbsize); size = IOTSB_BASESZ << is->is_tsbsize; - printf("%s: DVMA map: %#lx to %#lx%s\n", name, + printf("%s: DVMA map: %#lx to %#lx %d entries%s\n", name, is->is_dvmabase, is->is_dvmabase + - (size << (IO_PAGE_SHIFT - IOTTE_SHIFT)) - 1, + (size << (IO_PAGE_SHIFT - IOTTE_SHIFT)) - 1, tsbentries, IOMMU_HAS_SB(is) ? ", streaming buffer" : ""); /* @@ -333,12 +356,54 @@ iommu_init(const char *name, struct iomm */ is->is_tsb = contigmalloc(size, M_DEVBUF, M_NOWAIT, 0, ~0UL, PAGE_SIZE, 0); - if (is->is_tsb == 0) + if (is->is_tsb == NULL) panic("%s: contigmalloc failed", __func__); is->is_ptsb = pmap_kextract((vm_offset_t)is->is_tsb); bzero(is->is_tsb, size); /* + * Add the PROM mappings to the kernel IOTSB if desired. + * Note that the firmware of certain Darwin boards doesn't set + * the TSB size correctly. + */ + if ((is->is_flags & IOMMU_FIRE) != 0) + obptsbsize = (IOMMU_READ8(is, is_iommu, IMR_TSB) & + IOMMUTB_TSBSZ_MASK) >> IOMMUTB_TSBSZ_SHIFT; + else + obptsbsize = (IOMMU_READ8(is, is_iommu, IMR_CTL) & + IOMMUCR_TSBSZ_MASK) >> IOMMUCR_TSBSZ_SHIFT; + obptsbentries = IOMMU_TSBENTRIES(obptsbsize); + if (bootverbose) + printf("%s: PROM IOTSB size: %d (%d entries)\n", name, + obptsbsize, obptsbentries); + if ((is->is_flags & IOMMU_PRESERVE_PROM) != 0 && + !(cpu_impl == CPU_IMPL_ULTRASPARCIIi && obptsbsize == 7)) { + if (obptsbentries > tsbentries) + panic("%s: PROM IOTSB entries exceed kernel", + __func__); + obpptsb = IOMMU_READ8(is, is_iommu, IMR_TSB) & + IOMMUTB_TB_MASK; + for (i = 0; i < obptsbentries; i++) { + tte = ldxa(obpptsb + i * 8, ASI_PHYS_USE_EC); + if ((tte & IOTTE_V) == 0) + continue; + slot = tsbentries - obptsbentries + i; + if (bootverbose) + printf("%s: adding PROM IOTSB slot %d " + "(kernel slot %d) TTE: %#lx\n", name, + i, slot, tte); + obpmap = (is->is_dvmabase + slot * IO_PAGE_SIZE) >> + IO_PAGE_SHIFT; + if (rman_reserve_resource(&is->is_dvma_rman, obpmap, + obpmap, IO_PAGE_SIZE >> IO_PAGE_SHIFT, RF_ACTIVE, + NULL) == NULL) + panic("%s: could not reserve PROM IOTSB slot " + "%d (kernel slot %d)", __func__, i, slot); + is->is_tsb[slot] = tte; + } + } + + /* * Initialize streaming buffer, if it is there. */ if (IOMMU_HAS_SB(is)) { @@ -349,7 +414,7 @@ iommu_init(const char *name, struct iomm offs = roundup2((vm_offset_t)is->is_flush, STRBUF_FLUSHSYNC_NBYTES); for (i = 0; i < 2; i++, offs += STRBUF_FLUSHSYNC_NBYTES) { - is->is_flushva[i] = (int64_t *)offs; + is->is_flushva[i] = (uint64_t *)offs; is->is_flushpa[i] = pmap_kextract(offs); } } @@ -368,11 +433,16 @@ iommu_init(const char *name, struct iomm void iommu_reset(struct iommu_state *is) { + uint64_t tsb; int i; - IOMMU_WRITE8(is, is_iommu, IMR_TSB, is->is_ptsb); - /* Enable IOMMU in diagnostic mode */ - IOMMU_WRITE8(is, is_iommu, IMR_CTL, is->is_cr | IOMMUCR_DE); + tsb = is->is_ptsb; + if ((is->is_flags & IOMMU_FIRE) != 0) { + tsb |= is->is_tsbsize; + IOMMU_WRITE8(is, is_iommu, IMR_CACHE_INVAL, ~0ULL); + } + IOMMU_WRITE8(is, is_iommu, IMR_TSB, tsb); + IOMMU_WRITE8(is, is_iommu, IMR_CTL, is->is_cr); for (i = 0; i < 2; i++) { if (is->is_sb[i] != 0) { @@ -386,6 +456,8 @@ iommu_reset(struct iommu_state *is) is->is_sb[i] = 0; } } + + (void)IOMMU_READ8(is, is_iommu, IMR_CTL); } /* @@ -396,7 +468,7 @@ static void iommu_enter(struct iommu_state *is, vm_offset_t va, vm_paddr_t pa, int stream, int flags) { - int64_t tte; + uint64_t tte; KASSERT(va >= is->is_dvmabase, ("%s: va %#lx not in DVMA space", __func__, va)); @@ -423,7 +495,7 @@ iommu_enter(struct iommu_state *is, vm_o static int iommu_remove(struct iommu_state *is, vm_offset_t va, vm_size_t len) { - int streamed = 0; + int slot, streamed = 0; #ifdef IOMMU_DIAG iommu_diag(is, va); @@ -443,6 +515,12 @@ iommu_remove(struct iommu_state *is, vm_ len -= ulmin(len, IO_PAGE_SIZE); IOMMU_SET_TTE(is, va, 0); iommu_tlb_flush(is, va); + if ((is->is_flags & IOMMU_FLUSH_CACHE) != 0) { + slot = IOTSBSLOT(va); + if (len <= IO_PAGE_SIZE || slot % 8 == 7) + IOMMU_WRITE8(is, is_iommu, IMR_CACHE_FLUSH, + is->is_ptsb + slot * 8); + } va += IO_PAGE_SIZE; } return (streamed); @@ -829,12 +907,13 @@ iommu_dvmamap_load_buffer(bus_dma_tag_t bus_dmamap_t map, void *buf, bus_size_t buflen, struct thread *td, int flags, bus_dma_segment_t *segs, int *segp, int align) { - bus_addr_t amask, dvmaddr; + bus_addr_t amask, dvmaddr, dvmoffs; bus_size_t sgsize, esize; vm_offset_t vaddr, voffs; vm_paddr_t curaddr; pmap_t pmap = NULL; int error, firstpg, sgcnt; + u_int slot; KASSERT(buflen != 0, ("%s: buflen == 0!", __func__)); if (buflen > dt->dt_maxsize) @@ -877,8 +956,15 @@ iommu_dvmamap_load_buffer(bus_dma_tag_t buflen -= sgsize; vaddr += sgsize; - iommu_enter(is, trunc_io_page(dvmaddr), trunc_io_page(curaddr), + dvmoffs = trunc_io_page(dvmaddr); + iommu_enter(is, dvmoffs, trunc_io_page(curaddr), (map->dm_flags & DMF_STREAMED) != 0, flags); + if ((is->is_flags & IOMMU_FLUSH_CACHE) != 0) { + slot = IOTSBSLOT(dvmoffs); + if (buflen <= 0 || slot % 8 == 7) + IOMMU_WRITE8(is, is_iommu, IMR_CACHE_FLUSH, + is->is_ptsb + slot * 8); + } /* * Chop the chunk up into segments of at most maxsegsz, but try @@ -1183,6 +1269,8 @@ iommu_diag(struct iommu_state *is, vm_of int i; uint64_t data, tag; + if ((is->is_flags & IOMMU_FIRE) != 0) + return; IS_LOCK_ASSERT(is); IOMMU_WRITE8(is, is_dva, 0, trunc_io_page(va)); membar(StoreStore | StoreLoad);
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200912232202.nBNM2YcC010494>