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