xref: /netbsd-src/sys/arch/hppa/dev/astro.c (revision 318c13d4b111e3161f647e0f7ab79314ab94e38a)
1*318c13d4Sthorpej /*	$NetBSD: astro.c,v 1.6 2023/12/03 02:03:18 thorpej Exp $	*/
26d3ceb1dSskrll 
36d3ceb1dSskrll /*	$OpenBSD: astro.c,v 1.8 2007/10/06 23:50:54 krw Exp $	*/
46d3ceb1dSskrll 
56d3ceb1dSskrll /*
66d3ceb1dSskrll  * Copyright (c) 2007 Mark Kettenis
76d3ceb1dSskrll  *
86d3ceb1dSskrll  * Permission to use, copy, modify, and distribute this software for any
96d3ceb1dSskrll  * purpose with or without fee is hereby granted, provided that the above
106d3ceb1dSskrll  * copyright notice and this permission notice appear in all copies.
116d3ceb1dSskrll  *
126d3ceb1dSskrll  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
136d3ceb1dSskrll  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
146d3ceb1dSskrll  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
156d3ceb1dSskrll  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
166d3ceb1dSskrll  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
176d3ceb1dSskrll  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
186d3ceb1dSskrll  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
196d3ceb1dSskrll  */
206d3ceb1dSskrll 
216d3ceb1dSskrll #include <sys/param.h>
221be97a72Sskrll 
236d3ceb1dSskrll #include <sys/systm.h>
246d3ceb1dSskrll #include <sys/device.h>
25*318c13d4Sthorpej #include <sys/vmem.h>
261be97a72Sskrll #include <sys/kmem.h>
276d3ceb1dSskrll #include <sys/reboot.h>
286d3ceb1dSskrll #include <sys/tree.h>
296d3ceb1dSskrll 
306d3ceb1dSskrll #include <uvm/uvm.h>
316d3ceb1dSskrll 
326d3ceb1dSskrll #include <machine/iomod.h>
336d3ceb1dSskrll #include <machine/autoconf.h>
346d3ceb1dSskrll #include <machine/pdc.h>
356d3ceb1dSskrll #include <machine/endian.h>
366d3ceb1dSskrll 
376d3ceb1dSskrll #include <hppa/dev/cpudevs.h>
386d3ceb1dSskrll #include <hppa/hppa/machdep.h>
396d3ceb1dSskrll 
406d3ceb1dSskrll struct astro_regs {
416d3ceb1dSskrll 	uint32_t	rid;
426d3ceb1dSskrll 	uint32_t	pad0000;
436d3ceb1dSskrll 	uint32_t	ioc_ctrl;
446d3ceb1dSskrll 	uint32_t	pad0008;
456d3ceb1dSskrll 	uint8_t		resv1[0x0300 - 0x0010];
466d3ceb1dSskrll 	uint64_t	lmmio_direct0_base;
476d3ceb1dSskrll 	uint64_t	lmmio_direct0_mask;
486d3ceb1dSskrll 	uint64_t	lmmio_direct0_route;
496d3ceb1dSskrll 	uint64_t	lmmio_direct1_base;
506d3ceb1dSskrll 	uint64_t	lmmio_direct1_mask;
516d3ceb1dSskrll 	uint64_t	lmmio_direct1_route;
526d3ceb1dSskrll 	uint64_t	lmmio_direct2_base;
536d3ceb1dSskrll 	uint64_t	lmmio_direct2_mask;
546d3ceb1dSskrll 	uint64_t	lmmio_direct2_route;
556d3ceb1dSskrll 	uint64_t	lmmio_direct3_base;
566d3ceb1dSskrll 	uint64_t	lmmio_direct3_mask;
576d3ceb1dSskrll 	uint64_t	lmmio_direct3_route;
586d3ceb1dSskrll 	uint64_t	lmmio_dist_base;
596d3ceb1dSskrll 	uint64_t	lmmio_dist_mask;
606d3ceb1dSskrll 	uint64_t	lmmio_dist_route;
616d3ceb1dSskrll 	uint64_t	gmmio_dist_base;
626d3ceb1dSskrll 	uint64_t	gmmio_dist_mask;
636d3ceb1dSskrll 	uint64_t	gmmio_dist_route;
646d3ceb1dSskrll 	uint64_t	ios_dist_base;
656d3ceb1dSskrll 	uint64_t	ios_dist_mask;
666d3ceb1dSskrll 	uint64_t	ios_dist_route;
676d3ceb1dSskrll 	uint8_t		resv2[0x03c0 - 0x03a8];
686d3ceb1dSskrll 	uint64_t	ios_direct_base;
696d3ceb1dSskrll 	uint64_t	ios_direct_mask;
706d3ceb1dSskrll 	uint64_t	ios_direct_route;
716d3ceb1dSskrll 	uint8_t		resv3[0x22000 - 0x03d8];
726d3ceb1dSskrll 	uint64_t	func_id;
736d3ceb1dSskrll 	uint64_t	func_class;
746d3ceb1dSskrll 	uint8_t		resv4[0x22040 - 0x22010];
756d3ceb1dSskrll 	uint64_t	rope_config;
766d3ceb1dSskrll 	uint8_t		resv5[0x22050 - 0x22048];
776d3ceb1dSskrll 	uint64_t	rope_debug;
786d3ceb1dSskrll 	uint8_t		resv6[0x22200 - 0x22058];
796d3ceb1dSskrll 	uint64_t	rope0_control;
806d3ceb1dSskrll 	uint64_t	rope1_control;
816d3ceb1dSskrll 	uint64_t	rope2_control;
826d3ceb1dSskrll 	uint64_t	rope3_control;
836d3ceb1dSskrll 	uint64_t	rope4_control;
846d3ceb1dSskrll 	uint64_t	rope5_control;
856d3ceb1dSskrll 	uint64_t	rope6_control;
866d3ceb1dSskrll 	uint64_t	rope7_control;
876d3ceb1dSskrll 	uint8_t		resv7[0x22300 - 0x22240];
886d3ceb1dSskrll 	uint32_t	tlb_ibase;
896d3ceb1dSskrll 	uint32_t	pad22300;
906d3ceb1dSskrll 	uint32_t	tlb_imask;
916d3ceb1dSskrll 	uint32_t	pad22308;
926d3ceb1dSskrll 	uint32_t	tlb_pcom;
936d3ceb1dSskrll 	uint32_t	pad22310;
946d3ceb1dSskrll 	uint32_t	tlb_tcnfg;
956d3ceb1dSskrll 	uint32_t	pad22318;
966d3ceb1dSskrll 	uint64_t	tlb_pdir_base;
976d3ceb1dSskrll };
986d3ceb1dSskrll 
996d3ceb1dSskrll #define ASTRO_IOC_CTRL_TE	0x0001	/* TOC Enable */
1006d3ceb1dSskrll #define ASTRO_IOC_CTRL_CE	0x0002	/* Coalesce Enable */
1016d3ceb1dSskrll #define ASTRO_IOC_CTRL_DE	0x0004	/* Dillon Enable */
1026d3ceb1dSskrll #define ASTRO_IOC_CTRL_IE	0x0008	/* IOS Enable */
1036d3ceb1dSskrll #define ASTRO_IOC_CTRL_OS	0x0010	/* Outbound Synchronous */
1046d3ceb1dSskrll #define ASTRO_IOC_CTRL_IS	0x0020	/* Inbound Synchronous */
1056d3ceb1dSskrll #define ASTRO_IOC_CTRL_RC	0x0040	/* Read Current Enable */
1066d3ceb1dSskrll #define ASTRO_IOC_CTRL_L0	0x0080	/* 0-length Read Enable */
1076d3ceb1dSskrll #define ASTRO_IOC_CTRL_RM	0x0100	/* Real Mode */
1086d3ceb1dSskrll #define ASTRO_IOC_CTRL_NC	0x0200	/* Non-coherent Mode */
1096d3ceb1dSskrll #define ASTRO_IOC_CTRL_ID	0x0400	/* Interrupt Disable */
1106d3ceb1dSskrll #define ASTRO_IOC_CTRL_D4	0x0800	/* Disable 4-byte Coalescing */
1116d3ceb1dSskrll #define ASTRO_IOC_CTRL_CC	0x1000	/* Increase Coalescing counter value */
1126d3ceb1dSskrll #define ASTRO_IOC_CTRL_DD	0x2000	/* Disable distr. range coalescing */
1136d3ceb1dSskrll #define ASTRO_IOC_CTRL_DC	0x4000	/* Disable the coalescing counter */
1146d3ceb1dSskrll 
1156d3ceb1dSskrll #define IOTTE_V		0x8000000000000000LL	/* Entry valid */
1166d3ceb1dSskrll #define IOTTE_PAMASK	0x000000fffffff000LL
1176d3ceb1dSskrll #define IOTTE_CI	0x00000000000000ffLL	/* Coherent index */
1186d3ceb1dSskrll 
1196d3ceb1dSskrll struct astro_softc {
1206d3ceb1dSskrll 	device_t sc_dv;
1216d3ceb1dSskrll 
1226d3ceb1dSskrll 	bus_dma_tag_t sc_dmat;
1236d3ceb1dSskrll 	struct astro_regs volatile *sc_regs;
1246d3ceb1dSskrll 	uint64_t *sc_pdir;
1256d3ceb1dSskrll 
1266d3ceb1dSskrll 	char sc_dvmamapname[20];
127*318c13d4Sthorpej 	vmem_t *sc_dvmamap;
1286d3ceb1dSskrll 	struct hppa_bus_dma_tag sc_dmatag;
1296d3ceb1dSskrll };
1306d3ceb1dSskrll 
1316d3ceb1dSskrll /*
1326d3ceb1dSskrll  * per-map DVMA page table
1336d3ceb1dSskrll  */
1346d3ceb1dSskrll struct iommu_page_entry {
1356d3ceb1dSskrll 	SPLAY_ENTRY(iommu_page_entry) ipe_node;
1366d3ceb1dSskrll 	paddr_t	ipe_pa;
1376d3ceb1dSskrll 	vaddr_t	ipe_va;
1386d3ceb1dSskrll 	bus_addr_t ipe_dva;
1396d3ceb1dSskrll };
1406d3ceb1dSskrll 
1416d3ceb1dSskrll struct iommu_page_map {
1426d3ceb1dSskrll 	SPLAY_HEAD(iommu_page_tree, iommu_page_entry) ipm_tree;
1436d3ceb1dSskrll 	int ipm_maxpage;	/* Size of allocated page map */
1446d3ceb1dSskrll 	int ipm_pagecnt;	/* Number of entries in use */
1456d3ceb1dSskrll 	struct iommu_page_entry	ipm_map[1];
1466d3ceb1dSskrll };
1476d3ceb1dSskrll 
1486d3ceb1dSskrll /*
1496d3ceb1dSskrll  * per-map IOMMU state
1506d3ceb1dSskrll  */
1516d3ceb1dSskrll struct iommu_map_state {
1526d3ceb1dSskrll 	struct astro_softc *ims_sc;
1536d3ceb1dSskrll 	bus_addr_t ims_dvmastart;
1546d3ceb1dSskrll 	bus_size_t ims_dvmasize;
1556d3ceb1dSskrll 	struct iommu_page_map ims_map;	/* map must be last (array at end) */
1566d3ceb1dSskrll };
1576d3ceb1dSskrll 
1586d3ceb1dSskrll int	astro_match(device_t, cfdata_t, void *);
1596d3ceb1dSskrll void	astro_attach(device_t, device_t, void *);
1606d3ceb1dSskrll static device_t astro_callback(device_t self, struct confargs *ca);
1616d3ceb1dSskrll 
1626d3ceb1dSskrll CFATTACH_DECL_NEW(astro, sizeof(struct astro_softc),
1636d3ceb1dSskrll     astro_match, astro_attach, NULL, NULL);
1646d3ceb1dSskrll 
1656d3ceb1dSskrll extern struct cfdriver astro_cd;
1666d3ceb1dSskrll 
1676d3ceb1dSskrll int	iommu_dvmamap_create(void *, bus_size_t, int, bus_size_t, bus_size_t,
1686d3ceb1dSskrll 	    int, bus_dmamap_t *);
1696d3ceb1dSskrll void	iommu_dvmamap_destroy(void *, bus_dmamap_t);
1706d3ceb1dSskrll int	iommu_dvmamap_load(void *, bus_dmamap_t, void *, bus_size_t,
1716d3ceb1dSskrll 	    struct proc *, int);
1726d3ceb1dSskrll int	iommu_dvmamap_load_mbuf(void *, bus_dmamap_t, struct mbuf *, int);
1736d3ceb1dSskrll int	iommu_dvmamap_load_uio(void *, bus_dmamap_t, struct uio *, int);
1746d3ceb1dSskrll int	iommu_dvmamap_load_raw(void *, bus_dmamap_t, bus_dma_segment_t *,
1756d3ceb1dSskrll 	    int, bus_size_t, int);
1766d3ceb1dSskrll void	iommu_dvmamap_unload(void *, bus_dmamap_t);
1776d3ceb1dSskrll void	iommu_dvmamap_sync(void *, bus_dmamap_t, bus_addr_t, bus_size_t, int);
1786d3ceb1dSskrll int	iommu_dvmamem_alloc(void *, bus_size_t, bus_size_t, bus_size_t,
1796d3ceb1dSskrll 	    bus_dma_segment_t *, int, int *, int);
1806d3ceb1dSskrll void	iommu_dvmamem_free(void *, bus_dma_segment_t *, int);
1816d3ceb1dSskrll int	iommu_dvmamem_map(void *, bus_dma_segment_t *, int, size_t,
1826d3ceb1dSskrll 	    void **, int);
1836d3ceb1dSskrll void	iommu_dvmamem_unmap(void *, void *, size_t);
1846d3ceb1dSskrll paddr_t	iommu_dvmamem_mmap(void *, bus_dma_segment_t *, int, off_t, int, int);
1856d3ceb1dSskrll 
1866d3ceb1dSskrll void	iommu_enter(struct astro_softc *, bus_addr_t, paddr_t, vaddr_t, int);
1876d3ceb1dSskrll void	iommu_remove(struct astro_softc *, bus_addr_t);
1886d3ceb1dSskrll 
1891be97a72Sskrll struct iommu_map_state *iommu_iomap_create(int, int);
1906d3ceb1dSskrll void	iommu_iomap_destroy(struct iommu_map_state *);
1916d3ceb1dSskrll int	iommu_iomap_insert_page(struct iommu_map_state *, vaddr_t, paddr_t);
1926d3ceb1dSskrll bus_addr_t iommu_iomap_translate(struct iommu_map_state *, paddr_t);
1936d3ceb1dSskrll void	iommu_iomap_clear_pages(struct iommu_map_state *);
1946d3ceb1dSskrll 
1956d3ceb1dSskrll static int iommu_iomap_load_map(struct astro_softc *, bus_dmamap_t, int);
1966d3ceb1dSskrll 
1976d3ceb1dSskrll const struct hppa_bus_dma_tag astro_dmat = {
1986d3ceb1dSskrll 	NULL,
1996d3ceb1dSskrll 	iommu_dvmamap_create, iommu_dvmamap_destroy,
2006d3ceb1dSskrll 	iommu_dvmamap_load, iommu_dvmamap_load_mbuf,
2016d3ceb1dSskrll 	iommu_dvmamap_load_uio, iommu_dvmamap_load_raw,
2026d3ceb1dSskrll 	iommu_dvmamap_unload, iommu_dvmamap_sync,
2036d3ceb1dSskrll 
2046d3ceb1dSskrll 	iommu_dvmamem_alloc, iommu_dvmamem_free, iommu_dvmamem_map,
2056d3ceb1dSskrll 	iommu_dvmamem_unmap, iommu_dvmamem_mmap
2066d3ceb1dSskrll };
2076d3ceb1dSskrll 
2086d3ceb1dSskrll int
astro_match(device_t parent,cfdata_t cf,void * aux)2096d3ceb1dSskrll astro_match(device_t parent, cfdata_t cf, void *aux)
2106d3ceb1dSskrll {
2116d3ceb1dSskrll 	struct confargs *ca = aux;
2126d3ceb1dSskrll 
2136d3ceb1dSskrll 	/* Astro is a U-Turn variant. */
2146d3ceb1dSskrll 	if (ca->ca_type.iodc_type != HPPA_TYPE_IOA ||
2156d3ceb1dSskrll 	    ca->ca_type.iodc_sv_model != HPPA_IOA_UTURN)
2166d3ceb1dSskrll 		return 0;
2176d3ceb1dSskrll 
2186d3ceb1dSskrll 	if (ca->ca_type.iodc_model == 0x58 &&
2196d3ceb1dSskrll 	    ca->ca_type.iodc_revision >= 0x20)
2206d3ceb1dSskrll 		return 1;
2216d3ceb1dSskrll 
2226d3ceb1dSskrll 	return 0;
2236d3ceb1dSskrll }
2246d3ceb1dSskrll 
2256d3ceb1dSskrll void
astro_attach(device_t parent,device_t self,void * aux)2266d3ceb1dSskrll astro_attach(device_t parent, device_t self, void *aux)
2276d3ceb1dSskrll {
2286d3ceb1dSskrll 	struct confargs *ca = aux, nca;
2296d3ceb1dSskrll 	struct astro_softc *sc = device_private(self);
2306d3ceb1dSskrll 	volatile struct astro_regs *r;
2316d3ceb1dSskrll 	bus_space_handle_t ioh;
2326d3ceb1dSskrll 	uint32_t rid, ioc_ctrl;
2336d3ceb1dSskrll 	psize_t size;
2346d3ceb1dSskrll 	vaddr_t va;
2356d3ceb1dSskrll 	paddr_t pa;
2366d3ceb1dSskrll 	void *p;
2376d3ceb1dSskrll 	struct vm_page *m;
2386d3ceb1dSskrll 	struct pglist mlist;
2396d3ceb1dSskrll 	int iova_bits;
2406d3ceb1dSskrll 	int pagezero_cookie;
2416d3ceb1dSskrll 
2426d3ceb1dSskrll 	sc->sc_dv = self;
2436d3ceb1dSskrll 	sc->sc_dmat = ca->ca_dmatag;
2446d3ceb1dSskrll 	if (bus_space_map(ca->ca_iot, ca->ca_hpa, sizeof(struct astro_regs),
2456d3ceb1dSskrll 	    0, &ioh)) {
2466d3ceb1dSskrll 		aprint_error(": can't map IO space\n");
2476d3ceb1dSskrll 		return;
2486d3ceb1dSskrll 	}
2496d3ceb1dSskrll 	p = bus_space_vaddr(ca->ca_iot, ioh);
2506d3ceb1dSskrll 	sc->sc_regs = r = p;
2516d3ceb1dSskrll 	rid = le32toh(r->rid);
2526d3ceb1dSskrll 	aprint_normal(": Astro rev %d.%d\n", (rid & 7) + 1, (rid >> 3) & 3);
2536d3ceb1dSskrll 
2546d3ceb1dSskrll 	ioc_ctrl = le32toh(r->ioc_ctrl);
2556d3ceb1dSskrll 	ioc_ctrl &= ~ASTRO_IOC_CTRL_CE;
2566d3ceb1dSskrll 	ioc_ctrl &= ~ASTRO_IOC_CTRL_RM;
2576d3ceb1dSskrll 	ioc_ctrl &= ~ASTRO_IOC_CTRL_NC;
2586d3ceb1dSskrll 	r->ioc_ctrl = htole32(ioc_ctrl);
2596d3ceb1dSskrll 
2606d3ceb1dSskrll 	/*
2616d3ceb1dSskrll 	 * Setup the iommu.
2626d3ceb1dSskrll 	 */
2636d3ceb1dSskrll 
2646d3ceb1dSskrll 	/* XXX This gives us 256MB of iova space. */
2656d3ceb1dSskrll 	iova_bits = 28;
2666d3ceb1dSskrll 
2676d3ceb1dSskrll 	r->tlb_ibase = htole32(0);
2686d3ceb1dSskrll 	r->tlb_imask = htole32(0xffffffff << iova_bits);
2696d3ceb1dSskrll 
2706d3ceb1dSskrll 	/* Page size is 4K. */
2716d3ceb1dSskrll 	r->tlb_tcnfg = htole32(0);
2726d3ceb1dSskrll 
2736d3ceb1dSskrll 	/* Flush TLB. */
2746d3ceb1dSskrll 	r->tlb_pcom = htole32(31);
2756d3ceb1dSskrll 
2766d3ceb1dSskrll 	/*
2776d3ceb1dSskrll 	 * Allocate memory for I/O pagetables.  They need to be physically
2786d3ceb1dSskrll 	 * contiguous.
2796d3ceb1dSskrll 	 */
2806d3ceb1dSskrll 
2816d3ceb1dSskrll 	size = (1 << (iova_bits - PAGE_SHIFT)) * sizeof(uint64_t);
2826d3ceb1dSskrll 	TAILQ_INIT(&mlist);
2836d3ceb1dSskrll 	if (uvm_pglistalloc(size, 0, -1, PAGE_SIZE, 0, &mlist, 1, 0) != 0) {
2846d3ceb1dSskrll 		aprint_error(": can't allocate PDIR\n");
2856d3ceb1dSskrll 		return;
2866d3ceb1dSskrll 	}
2876d3ceb1dSskrll 
2886d3ceb1dSskrll 	va = uvm_km_alloc(kernel_map, size, 0, UVM_KMF_VAONLY | UVM_KMF_NOWAIT);
2896d3ceb1dSskrll 
2906d3ceb1dSskrll 	if (va == 0) {
2916d3ceb1dSskrll 		aprint_error(": can't map PDIR\n");
2926d3ceb1dSskrll 		return;
2936d3ceb1dSskrll 	}
2946d3ceb1dSskrll 	sc->sc_pdir = (uint64_t *)va;
2956d3ceb1dSskrll 
2966d3ceb1dSskrll 	m = TAILQ_FIRST(&mlist);
2976d3ceb1dSskrll 	r->tlb_pdir_base = htole64(VM_PAGE_TO_PHYS(m));
2986d3ceb1dSskrll 
2996d3ceb1dSskrll 	/* Map the pages. */
3006d3ceb1dSskrll 	for (; m != NULL; m = TAILQ_NEXT(m, pageq.queue)) {
3016d3ceb1dSskrll 		pa = VM_PAGE_TO_PHYS(m);
3026d3ceb1dSskrll 		pmap_enter(pmap_kernel(), va, pa,
3036d3ceb1dSskrll 		    VM_PROT_READ|VM_PROT_WRITE, PMAP_WIRED);
3046d3ceb1dSskrll 		va += PAGE_SIZE;
3056d3ceb1dSskrll 	}
3066d3ceb1dSskrll 	pmap_update(pmap_kernel());
3076d3ceb1dSskrll 	memset(sc->sc_pdir, 0, size);
3086d3ceb1dSskrll 
3096d3ceb1dSskrll 	/*
3106d3ceb1dSskrll 	 * The PDC might have set up some devices to do DMA.  It will do
3116d3ceb1dSskrll 	 * this for the onboard USB controller if an USB keyboard is used
3126d3ceb1dSskrll 	 * for console input.  In that case, bad things will happen if we
3136d3ceb1dSskrll 	 * enable iova space.  So reset the PDC devices before we do that.
3146d3ceb1dSskrll 	 * Don't do this if we're using a serial console though, since it
3156d3ceb1dSskrll 	 * will stop working if we do.  This is fine since the serial port
3166d3ceb1dSskrll 	 * doesn't do DMA.
3176d3ceb1dSskrll 	 */
3186d3ceb1dSskrll 	pagezero_cookie = hppa_pagezero_map();
3196d3ceb1dSskrll 	if (PAGE0->mem_cons.pz_class != PCL_DUPLEX)
3206d3ceb1dSskrll 		pdcproc_ioreset();
3216d3ceb1dSskrll 	hppa_pagezero_unmap(pagezero_cookie);
3226d3ceb1dSskrll 
3236d3ceb1dSskrll 	/* Enable iova space. */
3246d3ceb1dSskrll 	r->tlb_ibase = htole32(1);
3256d3ceb1dSskrll 
3266d3ceb1dSskrll 	/*
3276d3ceb1dSskrll 	 * Now all the hardware's working we need to allocate a dvma map.
3286d3ceb1dSskrll 	 */
3296d3ceb1dSskrll 	snprintf(sc->sc_dvmamapname, sizeof(sc->sc_dvmamapname),
3306d3ceb1dSskrll 	    "%s_dvma", device_xname(sc->sc_dv));
331*318c13d4Sthorpej 	sc->sc_dvmamap = vmem_create(sc->sc_dvmamapname,
332*318c13d4Sthorpej 				     0,			/* base */
333*318c13d4Sthorpej 				     (1 << iova_bits),	/* size */
334*318c13d4Sthorpej 				     PAGE_SIZE,		/* quantum */
335*318c13d4Sthorpej 				     NULL,		/* allocfn */
336*318c13d4Sthorpej 				     NULL,		/* freefn */
337*318c13d4Sthorpej 				     NULL,		/* source */
338*318c13d4Sthorpej 				     0,			/* qcache_max */
339*318c13d4Sthorpej 				     VM_SLEEP,
340*318c13d4Sthorpej 				     IPL_VM);
341*318c13d4Sthorpej 	KASSERT(sc->sc_dvmamap != NULL);
3426d3ceb1dSskrll 
3436d3ceb1dSskrll 	sc->sc_dmatag = astro_dmat;
3446d3ceb1dSskrll 	sc->sc_dmatag._cookie = sc;
3456d3ceb1dSskrll 
3466d3ceb1dSskrll 	nca = *ca;	/* clone from us */
3476d3ceb1dSskrll 	nca.ca_dmatag = &sc->sc_dmatag;
3486d3ceb1dSskrll 	nca.ca_hpabase = IOMOD_IO_IO_LOW(p);
3496d3ceb1dSskrll 	nca.ca_nmodules = MAXMODBUS;
3506d3ceb1dSskrll 	pdc_scanbus(self, &nca, astro_callback);
3516d3ceb1dSskrll }
3526d3ceb1dSskrll 
3536d3ceb1dSskrll static device_t
astro_callback(device_t self,struct confargs * ca)3546d3ceb1dSskrll astro_callback(device_t self, struct confargs *ca)
3556d3ceb1dSskrll {
3566d3ceb1dSskrll 
3572685996bSthorpej 	return config_found(self, ca, mbprint,
358c7fb772bSthorpej 	    CFARGS(.submatch = mbsubmatch));
3596d3ceb1dSskrll }
3606d3ceb1dSskrll 
3616d3ceb1dSskrll int
iommu_dvmamap_create(void * v,bus_size_t size,int nsegments,bus_size_t maxsegsz,bus_size_t boundary,int flags,bus_dmamap_t * dmamap)3626d3ceb1dSskrll iommu_dvmamap_create(void *v, bus_size_t size, int nsegments,
3636d3ceb1dSskrll     bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamap)
3646d3ceb1dSskrll {
3656d3ceb1dSskrll 	struct astro_softc *sc = v;
3666d3ceb1dSskrll 	bus_dmamap_t map;
3676d3ceb1dSskrll 	struct iommu_map_state *ims;
3686d3ceb1dSskrll 	int error;
3696d3ceb1dSskrll 
3706d3ceb1dSskrll 	error = bus_dmamap_create(sc->sc_dmat, size, nsegments, maxsegsz,
3716d3ceb1dSskrll 	    boundary, flags, &map);
3726d3ceb1dSskrll 	if (error)
3736d3ceb1dSskrll 		return (error);
3746d3ceb1dSskrll 
3751be97a72Sskrll 	ims = iommu_iomap_create(atop(round_page(size)), flags);
3766d3ceb1dSskrll 	if (ims == NULL) {
3776d3ceb1dSskrll 		bus_dmamap_destroy(sc->sc_dmat, map);
3786d3ceb1dSskrll 		return (ENOMEM);
3796d3ceb1dSskrll 	}
3806d3ceb1dSskrll 
3816d3ceb1dSskrll 	ims->ims_sc = sc;
3826d3ceb1dSskrll 	map->_dm_cookie = ims;
3836d3ceb1dSskrll 	*dmamap = map;
3846d3ceb1dSskrll 
3856d3ceb1dSskrll 	return (0);
3866d3ceb1dSskrll }
3876d3ceb1dSskrll 
3886d3ceb1dSskrll void
iommu_dvmamap_destroy(void * v,bus_dmamap_t map)3896d3ceb1dSskrll iommu_dvmamap_destroy(void *v, bus_dmamap_t map)
3906d3ceb1dSskrll {
3916d3ceb1dSskrll 	struct astro_softc *sc = v;
3926d3ceb1dSskrll 
3936d3ceb1dSskrll 	/*
3946d3ceb1dSskrll 	 * The specification (man page) requires a loaded
3956d3ceb1dSskrll 	 * map to be unloaded before it is destroyed.
3966d3ceb1dSskrll 	 */
3976d3ceb1dSskrll 	if (map->dm_nsegs)
3986d3ceb1dSskrll 		iommu_dvmamap_unload(sc, map);
3996d3ceb1dSskrll 
4006d3ceb1dSskrll 	if (map->_dm_cookie)
4016d3ceb1dSskrll 		iommu_iomap_destroy(map->_dm_cookie);
4026d3ceb1dSskrll 	map->_dm_cookie = NULL;
4036d3ceb1dSskrll 
4046d3ceb1dSskrll 	bus_dmamap_destroy(sc->sc_dmat, map);
4056d3ceb1dSskrll }
4066d3ceb1dSskrll 
4076d3ceb1dSskrll static int
iommu_iomap_load_map(struct astro_softc * sc,bus_dmamap_t map,int flags)4086d3ceb1dSskrll iommu_iomap_load_map(struct astro_softc *sc, bus_dmamap_t map, int flags)
4096d3ceb1dSskrll {
4106d3ceb1dSskrll 	struct iommu_map_state *ims = map->_dm_cookie;
4116d3ceb1dSskrll 	struct iommu_page_map *ipm = &ims->ims_map;
4126d3ceb1dSskrll 	struct iommu_page_entry *e;
413*318c13d4Sthorpej 	int err, seg;
4146d3ceb1dSskrll 	paddr_t pa, paend;
4156d3ceb1dSskrll 	vaddr_t va;
4166d3ceb1dSskrll 	bus_size_t sgsize;
4176d3ceb1dSskrll 	bus_size_t align, boundary;
418*318c13d4Sthorpej 	vmem_addr_t dvmaddr;
4196d3ceb1dSskrll 	bus_addr_t dva;
4206d3ceb1dSskrll 	int i;
4216d3ceb1dSskrll 
4226d3ceb1dSskrll 	/* XXX */
4236d3ceb1dSskrll 	boundary = map->_dm_boundary;
424*318c13d4Sthorpej 	align = 0;	/* align to quantum */
4256d3ceb1dSskrll 
4266d3ceb1dSskrll 	iommu_iomap_clear_pages(ims);
4276d3ceb1dSskrll 
4286d3ceb1dSskrll 	for (seg = 0; seg < map->dm_nsegs; seg++) {
4296d3ceb1dSskrll 		struct hppa_bus_dma_segment *ds = &map->dm_segs[seg];
4306d3ceb1dSskrll 
4316d3ceb1dSskrll 		paend = round_page(ds->ds_addr + ds->ds_len);
4326d3ceb1dSskrll 		for (pa = trunc_page(ds->ds_addr), va = trunc_page(ds->_ds_va);
4336d3ceb1dSskrll 		     pa < paend; pa += PAGE_SIZE, va += PAGE_SIZE) {
4346d3ceb1dSskrll 			err = iommu_iomap_insert_page(ims, va, pa);
4356d3ceb1dSskrll 			if (err) {
4366d3ceb1dSskrll 				printf("iomap insert error: %d for "
4376d3ceb1dSskrll 				    "va 0x%lx pa 0x%lx\n", err, va, pa);
4386d3ceb1dSskrll 				bus_dmamap_unload(sc->sc_dmat, map);
4396d3ceb1dSskrll 				iommu_iomap_clear_pages(ims);
4406d3ceb1dSskrll 			}
4416d3ceb1dSskrll 		}
4426d3ceb1dSskrll 	}
4436d3ceb1dSskrll 
444*318c13d4Sthorpej 	const vm_flag_t vmflags = VM_BESTFIT |
445*318c13d4Sthorpej 	    ((flags & BUS_DMA_NOWAIT) ? VM_NOSLEEP : VM_SLEEP);
446*318c13d4Sthorpej 
4476d3ceb1dSskrll 	sgsize = ims->ims_map.ipm_pagecnt * PAGE_SIZE;
448*318c13d4Sthorpej 	err = vmem_xalloc(sc->sc_dvmamap, sgsize,
449*318c13d4Sthorpej 			  align,		/* align */
450*318c13d4Sthorpej 			  0,			/* phase */
451*318c13d4Sthorpej 			  boundary,		/* nocross */
452*318c13d4Sthorpej 			  VMEM_ADDR_MIN,	/* minaddr */
453*318c13d4Sthorpej 			  VMEM_ADDR_MAX,	/* maxaddr */
454*318c13d4Sthorpej 			  vmflags,
455*318c13d4Sthorpej 			  &dvmaddr);
4566d3ceb1dSskrll 	if (err)
4576d3ceb1dSskrll 		return (err);
4586d3ceb1dSskrll 
4596d3ceb1dSskrll 	ims->ims_dvmastart = dvmaddr;
4606d3ceb1dSskrll 	ims->ims_dvmasize = sgsize;
4616d3ceb1dSskrll 
4626d3ceb1dSskrll 	dva = dvmaddr;
4636d3ceb1dSskrll 	for (i = 0, e = ipm->ipm_map; i < ipm->ipm_pagecnt; ++i, ++e) {
4646d3ceb1dSskrll 		e->ipe_dva = dva;
4656d3ceb1dSskrll 		iommu_enter(sc, e->ipe_dva, e->ipe_pa, e->ipe_va, flags);
4666d3ceb1dSskrll 		dva += PAGE_SIZE;
4676d3ceb1dSskrll 	}
4686d3ceb1dSskrll 
4696d3ceb1dSskrll 	for (seg = 0; seg < map->dm_nsegs; seg++) {
4706d3ceb1dSskrll 		struct hppa_bus_dma_segment *ds = &map->dm_segs[seg];
4716d3ceb1dSskrll 		ds->ds_addr = iommu_iomap_translate(ims, ds->ds_addr);
4726d3ceb1dSskrll 	}
4736d3ceb1dSskrll 
4746d3ceb1dSskrll 	return (0);
4756d3ceb1dSskrll }
4766d3ceb1dSskrll 
4776d3ceb1dSskrll int
iommu_dvmamap_load(void * v,bus_dmamap_t map,void * addr,bus_size_t size,struct proc * p,int flags)4786d3ceb1dSskrll iommu_dvmamap_load(void *v, bus_dmamap_t map, void *addr, bus_size_t size,
4796d3ceb1dSskrll     struct proc *p, int flags)
4806d3ceb1dSskrll {
4816d3ceb1dSskrll 	struct astro_softc *sc = v;
4826d3ceb1dSskrll 	int err;
4836d3ceb1dSskrll 
4846d3ceb1dSskrll 	err = bus_dmamap_load(sc->sc_dmat, map, addr, size, p, flags);
4856d3ceb1dSskrll 	if (err)
4866d3ceb1dSskrll 		return (err);
4876d3ceb1dSskrll 
4886d3ceb1dSskrll 	return iommu_iomap_load_map(sc, map, flags);
4896d3ceb1dSskrll }
4906d3ceb1dSskrll 
4916d3ceb1dSskrll int
iommu_dvmamap_load_mbuf(void * v,bus_dmamap_t map,struct mbuf * m,int flags)4926d3ceb1dSskrll iommu_dvmamap_load_mbuf(void *v, bus_dmamap_t map, struct mbuf *m, int flags)
4936d3ceb1dSskrll {
4946d3ceb1dSskrll 	struct astro_softc *sc = v;
4956d3ceb1dSskrll 	int err;
4966d3ceb1dSskrll 
4976d3ceb1dSskrll 	err = bus_dmamap_load_mbuf(sc->sc_dmat, map, m, flags);
4986d3ceb1dSskrll 	if (err)
4996d3ceb1dSskrll 		return (err);
5006d3ceb1dSskrll 
5016d3ceb1dSskrll 	return iommu_iomap_load_map(sc, map, flags);
5026d3ceb1dSskrll }
5036d3ceb1dSskrll 
5046d3ceb1dSskrll int
iommu_dvmamap_load_uio(void * v,bus_dmamap_t map,struct uio * uio,int flags)5056d3ceb1dSskrll iommu_dvmamap_load_uio(void *v, bus_dmamap_t map, struct uio *uio, int flags)
5066d3ceb1dSskrll {
5076d3ceb1dSskrll 	struct astro_softc *sc = v;
5086d3ceb1dSskrll 
5096d3ceb1dSskrll 	printf("load_uio\n");
5106d3ceb1dSskrll 
5116d3ceb1dSskrll 	return (bus_dmamap_load_uio(sc->sc_dmat, map, uio, flags));
5126d3ceb1dSskrll }
5136d3ceb1dSskrll 
5146d3ceb1dSskrll int
iommu_dvmamap_load_raw(void * v,bus_dmamap_t map,bus_dma_segment_t * segs,int nsegs,bus_size_t size,int flags)5156d3ceb1dSskrll iommu_dvmamap_load_raw(void *v, bus_dmamap_t map, bus_dma_segment_t *segs,
5166d3ceb1dSskrll     int nsegs, bus_size_t size, int flags)
5176d3ceb1dSskrll {
5186d3ceb1dSskrll 	struct astro_softc *sc = v;
5196d3ceb1dSskrll 
5206d3ceb1dSskrll 	printf("load_raw\n");
5216d3ceb1dSskrll 
5226d3ceb1dSskrll 	return (bus_dmamap_load_raw(sc->sc_dmat, map, segs, nsegs, size, flags));
5236d3ceb1dSskrll }
5246d3ceb1dSskrll 
5256d3ceb1dSskrll void
iommu_dvmamap_unload(void * v,bus_dmamap_t map)5266d3ceb1dSskrll iommu_dvmamap_unload(void *v, bus_dmamap_t map)
5276d3ceb1dSskrll {
5286d3ceb1dSskrll 	struct astro_softc *sc = v;
5296d3ceb1dSskrll 	struct iommu_map_state *ims = map->_dm_cookie;
5306d3ceb1dSskrll 	struct iommu_page_map *ipm = &ims->ims_map;
5316d3ceb1dSskrll 	struct iommu_page_entry *e;
532*318c13d4Sthorpej 	int i;
5336d3ceb1dSskrll 
5346d3ceb1dSskrll 	/* Remove the IOMMU entries. */
5356d3ceb1dSskrll 	for (i = 0, e = ipm->ipm_map; i < ipm->ipm_pagecnt; ++i, ++e)
5366d3ceb1dSskrll 		iommu_remove(sc, e->ipe_dva);
5376d3ceb1dSskrll 
5386d3ceb1dSskrll 	/* Clear the iomap. */
5396d3ceb1dSskrll 	iommu_iomap_clear_pages(ims);
5406d3ceb1dSskrll 
5416d3ceb1dSskrll 	bus_dmamap_unload(sc->sc_dmat, map);
5426d3ceb1dSskrll 
543*318c13d4Sthorpej 	vmem_xfree(sc->sc_dvmamap, ims->ims_dvmastart, ims->ims_dvmasize);
5446d3ceb1dSskrll 	ims->ims_dvmastart = 0;
5456d3ceb1dSskrll 	ims->ims_dvmasize = 0;
5466d3ceb1dSskrll }
5476d3ceb1dSskrll 
5486d3ceb1dSskrll void
iommu_dvmamap_sync(void * v,bus_dmamap_t map,bus_addr_t off,bus_size_t len,int ops)5496d3ceb1dSskrll iommu_dvmamap_sync(void *v, bus_dmamap_t map, bus_addr_t off,
5506d3ceb1dSskrll     bus_size_t len, int ops)
5516d3ceb1dSskrll {
5526d3ceb1dSskrll 	/* Nothing to do; DMA is cache-coherent. */
5536d3ceb1dSskrll }
5546d3ceb1dSskrll 
5556d3ceb1dSskrll int
iommu_dvmamem_alloc(void * v,bus_size_t size,bus_size_t alignment,bus_size_t boundary,bus_dma_segment_t * segs,int nsegs,int * rsegs,int flags)5566d3ceb1dSskrll iommu_dvmamem_alloc(void *v, bus_size_t size, bus_size_t alignment,
5576d3ceb1dSskrll     bus_size_t boundary, bus_dma_segment_t *segs,
5586d3ceb1dSskrll     int nsegs, int *rsegs, int flags)
5596d3ceb1dSskrll {
5606d3ceb1dSskrll 	struct astro_softc *sc = v;
5616d3ceb1dSskrll 
5626d3ceb1dSskrll 	return (bus_dmamem_alloc(sc->sc_dmat, size, alignment, boundary,
5636d3ceb1dSskrll 	    segs, nsegs, rsegs, flags));
5646d3ceb1dSskrll }
5656d3ceb1dSskrll 
5666d3ceb1dSskrll void
iommu_dvmamem_free(void * v,bus_dma_segment_t * segs,int nsegs)5676d3ceb1dSskrll iommu_dvmamem_free(void *v, bus_dma_segment_t *segs, int nsegs)
5686d3ceb1dSskrll {
5696d3ceb1dSskrll 	struct astro_softc *sc = v;
5706d3ceb1dSskrll 
5716d3ceb1dSskrll 	bus_dmamem_free(sc->sc_dmat, segs, nsegs);
5726d3ceb1dSskrll }
5736d3ceb1dSskrll 
5746d3ceb1dSskrll int
iommu_dvmamem_map(void * v,bus_dma_segment_t * segs,int nsegs,size_t size,void ** kvap,int flags)5756d3ceb1dSskrll iommu_dvmamem_map(void *v, bus_dma_segment_t *segs, int nsegs, size_t size,
5766d3ceb1dSskrll     void **kvap, int flags)
5776d3ceb1dSskrll {
5786d3ceb1dSskrll 	struct astro_softc *sc = v;
5796d3ceb1dSskrll 
5806d3ceb1dSskrll 	return (bus_dmamem_map(sc->sc_dmat, segs, nsegs, size, kvap, flags));
5816d3ceb1dSskrll }
5826d3ceb1dSskrll 
5836d3ceb1dSskrll void
iommu_dvmamem_unmap(void * v,void * kva,size_t size)5846d3ceb1dSskrll iommu_dvmamem_unmap(void *v, void *kva, size_t size)
5856d3ceb1dSskrll {
5866d3ceb1dSskrll 	struct astro_softc *sc = v;
5876d3ceb1dSskrll 
5886d3ceb1dSskrll 	bus_dmamem_unmap(sc->sc_dmat, kva, size);
5896d3ceb1dSskrll }
5906d3ceb1dSskrll 
5916d3ceb1dSskrll paddr_t
iommu_dvmamem_mmap(void * v,bus_dma_segment_t * segs,int nsegs,off_t off,int prot,int flags)5926d3ceb1dSskrll iommu_dvmamem_mmap(void *v, bus_dma_segment_t *segs, int nsegs, off_t off,
5936d3ceb1dSskrll     int prot, int flags)
5946d3ceb1dSskrll {
5956d3ceb1dSskrll 	struct astro_softc *sc = v;
5966d3ceb1dSskrll 
5976d3ceb1dSskrll 	return (bus_dmamem_mmap(sc->sc_dmat, segs, nsegs, off, prot, flags));
5986d3ceb1dSskrll }
5996d3ceb1dSskrll 
6006d3ceb1dSskrll /*
6016d3ceb1dSskrll  * Utility function used by splay tree to order page entries by pa.
6026d3ceb1dSskrll  */
6036d3ceb1dSskrll static inline int
iomap_compare(struct iommu_page_entry * a,struct iommu_page_entry * b)6046d3ceb1dSskrll iomap_compare(struct iommu_page_entry *a, struct iommu_page_entry *b)
6056d3ceb1dSskrll {
6066d3ceb1dSskrll 	return ((a->ipe_pa > b->ipe_pa) ? 1 :
6076d3ceb1dSskrll 		(a->ipe_pa < b->ipe_pa) ? -1 : 0);
6086d3ceb1dSskrll }
6096d3ceb1dSskrll 
6106d3ceb1dSskrll SPLAY_PROTOTYPE(iommu_page_tree, iommu_page_entry, ipe_node, iomap_compare);
6116d3ceb1dSskrll 
6126d3ceb1dSskrll SPLAY_GENERATE(iommu_page_tree, iommu_page_entry, ipe_node, iomap_compare);
6136d3ceb1dSskrll 
6146d3ceb1dSskrll /*
6156d3ceb1dSskrll  * Create a new iomap.
6166d3ceb1dSskrll  */
6176d3ceb1dSskrll struct iommu_map_state *
iommu_iomap_create(int n,int flags)6181be97a72Sskrll iommu_iomap_create(int n, int flags)
6196d3ceb1dSskrll {
6206d3ceb1dSskrll 	struct iommu_map_state *ims;
6216d3ceb1dSskrll 
6226d3ceb1dSskrll 	/* Safety for heavily fragmented data, such as mbufs */
6236d3ceb1dSskrll 	n += 4;
6246d3ceb1dSskrll 	if (n < 16)
6256d3ceb1dSskrll 		n = 16;
6266d3ceb1dSskrll 
6271be97a72Sskrll 	const size_t sz =
6281be97a72Sskrll 	    sizeof(*ims) + (n - 1) * sizeof(ims->ims_map.ipm_map[0]);
6291be97a72Sskrll 
6301be97a72Sskrll 	ims = kmem_zalloc(sz, (flags & BUS_DMA_NOWAIT) ? KM_NOSLEEP : KM_SLEEP);
6316d3ceb1dSskrll 	if (ims == NULL)
6326d3ceb1dSskrll 		return (NULL);
6336d3ceb1dSskrll 
6346d3ceb1dSskrll 	/* Initialize the map. */
6356d3ceb1dSskrll 	ims->ims_map.ipm_maxpage = n;
6366d3ceb1dSskrll 	SPLAY_INIT(&ims->ims_map.ipm_tree);
6376d3ceb1dSskrll 
6386d3ceb1dSskrll 	return (ims);
6396d3ceb1dSskrll }
6406d3ceb1dSskrll 
6416d3ceb1dSskrll /*
6426d3ceb1dSskrll  * Destroy an iomap.
6436d3ceb1dSskrll  */
6446d3ceb1dSskrll void
iommu_iomap_destroy(struct iommu_map_state * ims)6456d3ceb1dSskrll iommu_iomap_destroy(struct iommu_map_state *ims)
6466d3ceb1dSskrll {
6476d3ceb1dSskrll #ifdef DIAGNOSTIC
6486d3ceb1dSskrll 	if (ims->ims_map.ipm_pagecnt > 0)
6496d3ceb1dSskrll 		printf("iommu_iomap_destroy: %d page entries in use\n",
6506d3ceb1dSskrll 		    ims->ims_map.ipm_pagecnt);
6516d3ceb1dSskrll #endif
6521be97a72Sskrll 	const int n = ims->ims_map.ipm_maxpage;
6531be97a72Sskrll 	const size_t sz =
6541be97a72Sskrll 	    sizeof(*ims) + (n - 1) * sizeof(ims->ims_map.ipm_map[0]);
6556d3ceb1dSskrll 
6561be97a72Sskrll 	kmem_free(ims, sz);
6576d3ceb1dSskrll }
6586d3ceb1dSskrll 
6596d3ceb1dSskrll /*
6606d3ceb1dSskrll  * Insert a pa entry in the iomap.
6616d3ceb1dSskrll  */
6626d3ceb1dSskrll int
iommu_iomap_insert_page(struct iommu_map_state * ims,vaddr_t va,paddr_t pa)6636d3ceb1dSskrll iommu_iomap_insert_page(struct iommu_map_state *ims, vaddr_t va, paddr_t pa)
6646d3ceb1dSskrll {
6656d3ceb1dSskrll 	struct iommu_page_map *ipm = &ims->ims_map;
6666d3ceb1dSskrll 	struct iommu_page_entry *e;
6676d3ceb1dSskrll 
6686d3ceb1dSskrll 	if (ipm->ipm_pagecnt >= ipm->ipm_maxpage) {
6696d3ceb1dSskrll 		struct iommu_page_entry ipe;
6706d3ceb1dSskrll 
6716d3ceb1dSskrll 		ipe.ipe_pa = pa;
6726d3ceb1dSskrll 		if (SPLAY_FIND(iommu_page_tree, &ipm->ipm_tree, &ipe))
6736d3ceb1dSskrll 			return (0);
6746d3ceb1dSskrll 
6756d3ceb1dSskrll 		return (ENOMEM);
6766d3ceb1dSskrll 	}
6776d3ceb1dSskrll 
6786d3ceb1dSskrll 	e = &ipm->ipm_map[ipm->ipm_pagecnt];
6796d3ceb1dSskrll 
6806d3ceb1dSskrll 	e->ipe_pa = pa;
6816d3ceb1dSskrll 	e->ipe_va = va;
6826d3ceb1dSskrll 	e->ipe_dva = 0;
6836d3ceb1dSskrll 
6846d3ceb1dSskrll 	e = SPLAY_INSERT(iommu_page_tree, &ipm->ipm_tree, e);
6856d3ceb1dSskrll 
6866d3ceb1dSskrll 	/* Duplicates are okay, but only count them once. */
6876d3ceb1dSskrll 	if (e)
6886d3ceb1dSskrll 		return (0);
6896d3ceb1dSskrll 
6906d3ceb1dSskrll 	++ipm->ipm_pagecnt;
6916d3ceb1dSskrll 
6926d3ceb1dSskrll 	return (0);
6936d3ceb1dSskrll }
6946d3ceb1dSskrll 
6956d3ceb1dSskrll /*
6966d3ceb1dSskrll  * Translate a physical address (pa) into a DVMA address.
6976d3ceb1dSskrll  */
6986d3ceb1dSskrll bus_addr_t
iommu_iomap_translate(struct iommu_map_state * ims,paddr_t pa)6996d3ceb1dSskrll iommu_iomap_translate(struct iommu_map_state *ims, paddr_t pa)
7006d3ceb1dSskrll {
7016d3ceb1dSskrll 	struct iommu_page_map *ipm = &ims->ims_map;
7026d3ceb1dSskrll 	struct iommu_page_entry *e;
7036d3ceb1dSskrll 	struct iommu_page_entry pe;
7046d3ceb1dSskrll 	paddr_t offset = pa & PAGE_MASK;
7056d3ceb1dSskrll 
7066d3ceb1dSskrll 	pe.ipe_pa = trunc_page(pa);
7076d3ceb1dSskrll 
7086d3ceb1dSskrll 	e = SPLAY_FIND(iommu_page_tree, &ipm->ipm_tree, &pe);
7096d3ceb1dSskrll 
7106d3ceb1dSskrll 	if (e == NULL) {
7116d3ceb1dSskrll 		panic("couldn't find pa %lx\n", pa);
7126d3ceb1dSskrll 		return 0;
7136d3ceb1dSskrll 	}
7146d3ceb1dSskrll 
7156d3ceb1dSskrll 	return (e->ipe_dva | offset);
7166d3ceb1dSskrll }
7176d3ceb1dSskrll 
7186d3ceb1dSskrll /*
7196d3ceb1dSskrll  * Clear the iomap table and tree.
7206d3ceb1dSskrll  */
7216d3ceb1dSskrll void
iommu_iomap_clear_pages(struct iommu_map_state * ims)7226d3ceb1dSskrll iommu_iomap_clear_pages(struct iommu_map_state *ims)
7236d3ceb1dSskrll {
7246d3ceb1dSskrll 	ims->ims_map.ipm_pagecnt = 0;
7256d3ceb1dSskrll 	SPLAY_INIT(&ims->ims_map.ipm_tree);
7266d3ceb1dSskrll }
7276d3ceb1dSskrll 
7286d3ceb1dSskrll /*
7296d3ceb1dSskrll  * Add an entry to the IOMMU table.
7306d3ceb1dSskrll  */
7316d3ceb1dSskrll void
iommu_enter(struct astro_softc * sc,bus_addr_t dva,paddr_t pa,vaddr_t va,int flags)7326d3ceb1dSskrll iommu_enter(struct astro_softc *sc, bus_addr_t dva, paddr_t pa, vaddr_t va,
7336d3ceb1dSskrll     int flags)
7346d3ceb1dSskrll {
7356d3ceb1dSskrll 	volatile uint64_t *tte_ptr = &sc->sc_pdir[dva >> PAGE_SHIFT];
7366d3ceb1dSskrll 	uint64_t tte;
7376d3ceb1dSskrll 	uint32_t ci;
7386d3ceb1dSskrll 
7396d3ceb1dSskrll #ifdef ASTRODEBUG
7406d3ceb1dSskrll 	printf("iommu_enter dva %lx, pa %lx, va %lx\n", dva, pa, va);
7416d3ceb1dSskrll #endif
7426d3ceb1dSskrll 
7436d3ceb1dSskrll #ifdef DIAGNOSTIC
7446d3ceb1dSskrll 	tte = le64toh(*tte_ptr);
7456d3ceb1dSskrll 
7466d3ceb1dSskrll 	if (tte & IOTTE_V) {
7476d3ceb1dSskrll 		printf("Overwriting valid tte entry (dva %lx pa %lx "
7486d3ceb1dSskrll 		    "&tte %p tte %llx)\n", dva, pa, tte_ptr, tte);
749*318c13d4Sthorpej 		/* vmem_print(sc->sc_dvmamap);		XXX */
7506d3ceb1dSskrll 		panic("IOMMU overwrite");
7516d3ceb1dSskrll 	}
7526d3ceb1dSskrll #endif
7536d3ceb1dSskrll 
7546d3ceb1dSskrll 	ci = lci(HPPA_SID_KERNEL, va);
7556d3ceb1dSskrll 
7566d3ceb1dSskrll 	tte = (pa & IOTTE_PAMASK) | ((ci >> 12) & IOTTE_CI);
7576d3ceb1dSskrll 	tte |= IOTTE_V;
7586d3ceb1dSskrll 
7596d3ceb1dSskrll 	*tte_ptr = htole64(tte);
7606d3ceb1dSskrll 	fdcache(HPPA_SID_KERNEL, (vaddr_t)tte_ptr, sizeof(*tte_ptr));
7616d3ceb1dSskrll }
7626d3ceb1dSskrll 
7636d3ceb1dSskrll /*
7646d3ceb1dSskrll  * Remove an entry from the IOMMU table.
7656d3ceb1dSskrll  */
7666d3ceb1dSskrll void
iommu_remove(struct astro_softc * sc,bus_addr_t dva)7676d3ceb1dSskrll iommu_remove(struct astro_softc *sc, bus_addr_t dva)
7686d3ceb1dSskrll {
7696d3ceb1dSskrll 	volatile struct astro_regs *r = sc->sc_regs;
7706d3ceb1dSskrll 	uint64_t *tte_ptr = &sc->sc_pdir[dva >> PAGE_SHIFT];
7716d3ceb1dSskrll 	uint64_t tte;
7726d3ceb1dSskrll 
7736d3ceb1dSskrll #ifdef DIAGNOSTIC
7746d3ceb1dSskrll 	if (dva != trunc_page(dva)) {
7756d3ceb1dSskrll 		printf("iommu_remove: unaligned dva: %lx\n", dva);
7766d3ceb1dSskrll 		dva = trunc_page(dva);
7776d3ceb1dSskrll 	}
7786d3ceb1dSskrll #endif
7796d3ceb1dSskrll 
7806d3ceb1dSskrll 	tte = le64toh(*tte_ptr);
7816d3ceb1dSskrll 
7826d3ceb1dSskrll #ifdef DIAGNOSTIC
7836d3ceb1dSskrll 	if ((tte & IOTTE_V) == 0) {
7846d3ceb1dSskrll 		printf("Removing invalid tte entry (dva %lx &tte %p "
7856d3ceb1dSskrll 		    "tte %llx)\n", dva, tte_ptr, tte);
786*318c13d4Sthorpej 		/* vmem_print(sc->sc_dvmamap);		XXX */
7876d3ceb1dSskrll 		panic("IOMMU remove overwrite");
7886d3ceb1dSskrll 	}
7896d3ceb1dSskrll #endif
7906d3ceb1dSskrll 
7916d3ceb1dSskrll 	*tte_ptr = htole64(tte & ~IOTTE_V);
7926d3ceb1dSskrll 
7936d3ceb1dSskrll 	/* Flush IOMMU. */
7946d3ceb1dSskrll 	r->tlb_pcom = htole32(dva | PAGE_SHIFT);
7956d3ceb1dSskrll }
796