1*4ea367cbSthorpej /* $NetBSD: uturn.c,v 1.7 2023/12/03 02:17:06 thorpej Exp $ */
26d3ceb1dSskrll
36d3ceb1dSskrll /* $OpenBSD: uturn.c,v 1.6 2007/12/29 01:26:14 kettenis Exp $ */
46d3ceb1dSskrll
56d3ceb1dSskrll /*-
66d3ceb1dSskrll * Copyright (c) 2012 The NetBSD Foundation, Inc.
76d3ceb1dSskrll * All rights reserved.
86d3ceb1dSskrll *
96d3ceb1dSskrll * This code is derived from software contributed to The NetBSD Foundation
106d3ceb1dSskrll * by Nick Hudson.
116d3ceb1dSskrll *
126d3ceb1dSskrll * Redistribution and use in source and binary forms, with or without
136d3ceb1dSskrll * modification, are permitted provided that the following conditions
146d3ceb1dSskrll * are met:
156d3ceb1dSskrll * 1. Redistributions of source code must retain the above copyright
166d3ceb1dSskrll * notice, this list of conditions and the following disclaimer.
176d3ceb1dSskrll * 2. Redistributions in binary form must reproduce the above copyright
186d3ceb1dSskrll * notice, this list of conditions and the following disclaimer in the
196d3ceb1dSskrll * documentation and/or other materials provided with the distribution.
206d3ceb1dSskrll *
216d3ceb1dSskrll * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
226d3ceb1dSskrll * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
236d3ceb1dSskrll * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
246d3ceb1dSskrll * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
256d3ceb1dSskrll * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
266d3ceb1dSskrll * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
276d3ceb1dSskrll * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
286d3ceb1dSskrll * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
296d3ceb1dSskrll * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
306d3ceb1dSskrll * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
316d3ceb1dSskrll * POSSIBILITY OF SUCH DAMAGE.
326d3ceb1dSskrll */
336d3ceb1dSskrll
346d3ceb1dSskrll /*
356d3ceb1dSskrll * Copyright (c) 2007 Mark Kettenis
366d3ceb1dSskrll *
376d3ceb1dSskrll * Permission to use, copy, modify, and distribute this software for any
386d3ceb1dSskrll * purpose with or without fee is hereby granted, provided that the above
396d3ceb1dSskrll * copyright notice and this permission notice appear in all copies.
406d3ceb1dSskrll *
416d3ceb1dSskrll * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
426d3ceb1dSskrll * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
436d3ceb1dSskrll * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
446d3ceb1dSskrll * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
456d3ceb1dSskrll * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
466d3ceb1dSskrll * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
476d3ceb1dSskrll * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
486d3ceb1dSskrll */
496d3ceb1dSskrll
506d3ceb1dSskrll /*
516d3ceb1dSskrll * Copyright (c) 2004 Michael Shalayeff
526d3ceb1dSskrll * All rights reserved.
536d3ceb1dSskrll *
546d3ceb1dSskrll * Redistribution and use in source and binary forms, with or without
556d3ceb1dSskrll * modification, are permitted provided that the following conditions
566d3ceb1dSskrll * are met:
576d3ceb1dSskrll * 1. Redistributions of source code must retain the above copyright
586d3ceb1dSskrll * notice, this list of conditions and the following disclaimer.
596d3ceb1dSskrll * 2. Redistributions in binary form must reproduce the above copyright
606d3ceb1dSskrll * notice, this list of conditions and the following disclaimer in the
616d3ceb1dSskrll * documentation and/or other materials provided with the distribution.
626d3ceb1dSskrll *
636d3ceb1dSskrll * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
646d3ceb1dSskrll * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
656d3ceb1dSskrll * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
666d3ceb1dSskrll * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
676d3ceb1dSskrll * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
686d3ceb1dSskrll * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
696d3ceb1dSskrll * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
706d3ceb1dSskrll * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
716d3ceb1dSskrll * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
726d3ceb1dSskrll * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
736d3ceb1dSskrll * THE POSSIBILITY OF SUCH DAMAGE.
746d3ceb1dSskrll */
756d3ceb1dSskrll
766d3ceb1dSskrll /*
776d3ceb1dSskrll * References:
786d3ceb1dSskrll * 1. Hardware Cache Coherent Input/Output. Hewlett-Packard Journal, February
796d3ceb1dSskrll * 1996.
806d3ceb1dSskrll * 2. PA-RISC 1.1 Architecture and Instruction Set Reference Manual,
816d3ceb1dSskrll * Hewlett-Packard, February 1994, Third Edition
826d3ceb1dSskrll */
836d3ceb1dSskrll
846d3ceb1dSskrll #include <sys/param.h>
851be97a72Sskrll
866d3ceb1dSskrll #include <sys/systm.h>
876d3ceb1dSskrll #include <sys/device.h>
88*4ea367cbSthorpej #include <sys/vmem.h>
891be97a72Sskrll #include <sys/kmem.h>
906d3ceb1dSskrll #include <sys/mbuf.h>
911be97a72Sskrll #include <sys/reboot.h>
926d3ceb1dSskrll #include <sys/tree.h>
936d3ceb1dSskrll
946d3ceb1dSskrll #include <uvm/uvm.h>
956d3ceb1dSskrll
966d3ceb1dSskrll #include <sys/bus.h>
976d3ceb1dSskrll #include <machine/iomod.h>
986d3ceb1dSskrll #include <machine/autoconf.h>
996d3ceb1dSskrll
1006d3ceb1dSskrll #include <hppa/dev/cpudevs.h>
1016d3ceb1dSskrll
1026d3ceb1dSskrll #define UTURNDEBUG
1036d3ceb1dSskrll #ifdef UTURNDEBUG
1046d3ceb1dSskrll
1056d3ceb1dSskrll #define DPRINTF(s) do { \
1066d3ceb1dSskrll if (uturndebug) \
1076d3ceb1dSskrll printf s; \
1086d3ceb1dSskrll } while(0)
1096d3ceb1dSskrll
1106d3ceb1dSskrll int uturndebug = 0;
1116d3ceb1dSskrll #else
1126d3ceb1dSskrll #define DPRINTF(s) /* */
1136d3ceb1dSskrll #endif
1146d3ceb1dSskrll
1156d3ceb1dSskrll struct uturn_regs {
1166d3ceb1dSskrll /* Runway Supervisory Set */
1176d3ceb1dSskrll int32_t unused1[12];
1186d3ceb1dSskrll uint32_t io_command; /* Offset 12 */
1196d3ceb1dSskrll #define UTURN_CMD_TLB_PURGE 33 /* Purge I/O TLB entry */
1206d3ceb1dSskrll #define UTURN_CMD_TLB_DIRECT_WRITE 35 /* I/O TLB Writes */
1216d3ceb1dSskrll
1226d3ceb1dSskrll uint32_t io_status; /* Offset 13 */
1236d3ceb1dSskrll uint32_t io_control; /* Offset 14 */
1246d3ceb1dSskrll #define UTURN_IOCTRL_TLB_REAL 0x00000000
1256d3ceb1dSskrll #define UTURN_IOCTRL_TLB_ERROR 0x00010000
1266d3ceb1dSskrll #define UTURN_IOCTRL_TLB_NORMAL 0x00020000
1276d3ceb1dSskrll
1286d3ceb1dSskrll #define UTURN_IOCTRL_MODE_OFF 0x00000000
1296d3ceb1dSskrll #define UTURN_IOCTRL_MODE_INCLUDE 0x00000080
1306d3ceb1dSskrll #define UTURN_IOCTRL_MODE_PEEK 0x00000180
1316d3ceb1dSskrll
1326d3ceb1dSskrll #define UTURN_VIRTUAL_MODE \
1336d3ceb1dSskrll (UTURN_IOCTRL_TLB_NORMAL | UTURN_IOCTRL_MODE_INCLUDE)
1346d3ceb1dSskrll
1356d3ceb1dSskrll #define UTURN_REAL_MODE \
1366d3ceb1dSskrll UTURN_IOCTRL_MODE_INCLUDE
1376d3ceb1dSskrll
1386d3ceb1dSskrll int32_t unused2[1];
1396d3ceb1dSskrll
1406d3ceb1dSskrll /* Runway Auxiliary Register Set */
1416d3ceb1dSskrll uint32_t io_err_resp; /* Offset 0 */
1426d3ceb1dSskrll uint32_t io_err_info; /* Offset 1 */
1436d3ceb1dSskrll uint32_t io_err_req; /* Offset 2 */
1446d3ceb1dSskrll uint32_t io_err_resp_hi; /* Offset 3 */
1456d3ceb1dSskrll uint32_t io_tlb_entry_m; /* Offset 4 */
1466d3ceb1dSskrll uint32_t io_tlb_entry_l; /* Offset 5 */
1476d3ceb1dSskrll uint32_t unused3[1];
1486d3ceb1dSskrll uint32_t io_pdir_base; /* Offset 7 */
1496d3ceb1dSskrll uint32_t io_io_low_hv; /* Offset 8 */
1506d3ceb1dSskrll uint32_t io_io_high_hv; /* Offset 9 */
1516d3ceb1dSskrll uint32_t unused4[1];
1526d3ceb1dSskrll uint32_t io_chain_id_mask; /* Offset 11 */
1536d3ceb1dSskrll uint32_t unused5[2];
1546d3ceb1dSskrll uint32_t io_io_low; /* Offset 14 */
1556d3ceb1dSskrll uint32_t io_io_high; /* Offset 15 */
1566d3ceb1dSskrll };
1576d3ceb1dSskrll
1586d3ceb1dSskrll
1596d3ceb1dSskrll /* Uturn supports 256 TLB entries */
1606d3ceb1dSskrll #define UTURN_CHAINID_SHIFT 8
1616d3ceb1dSskrll #define UTURN_CHAINID_MASK 0xff
1626d3ceb1dSskrll #define UTURN_TLB_ENTRIES (1 << UTURN_CHAINID_SHIFT)
1636d3ceb1dSskrll
1646d3ceb1dSskrll #define UTURN_IOVP_SIZE PAGE_SIZE
1656d3ceb1dSskrll #define UTURN_IOVP_SHIFT PAGE_SHIFT
1666d3ceb1dSskrll #define UTURN_IOVP_MASK PAGE_MASK
1676d3ceb1dSskrll
1686d3ceb1dSskrll #define UTURN_IOVA(iovp, off) ((iovp) | (off))
1696d3ceb1dSskrll #define UTURN_IOVP(iova) ((iova) & UTURN_IOVP_MASK)
1706d3ceb1dSskrll #define UTURN_IOVA_INDEX(iova) ((iova) >> UTURN_IOVP_SHIFT)
1716d3ceb1dSskrll
1726d3ceb1dSskrll struct uturn_softc {
1736d3ceb1dSskrll device_t sc_dv;
1746d3ceb1dSskrll
1756d3ceb1dSskrll bus_dma_tag_t sc_dmat;
1766d3ceb1dSskrll struct uturn_regs volatile *sc_regs;
1776d3ceb1dSskrll uint64_t *sc_pdir;
1786d3ceb1dSskrll uint32_t sc_chainid_shift;
1796d3ceb1dSskrll
1806d3ceb1dSskrll char sc_mapname[20];
181*4ea367cbSthorpej vmem_t *sc_map;
1826d3ceb1dSskrll
1836d3ceb1dSskrll struct hppa_bus_dma_tag sc_dmatag;
1846d3ceb1dSskrll };
1856d3ceb1dSskrll
1866d3ceb1dSskrll /*
1876d3ceb1dSskrll * per-map IOVA page table
1886d3ceb1dSskrll */
1896d3ceb1dSskrll struct uturn_page_entry {
1906d3ceb1dSskrll SPLAY_ENTRY(uturn_page_entry) upe_node;
1916d3ceb1dSskrll paddr_t upe_pa;
1926d3ceb1dSskrll vaddr_t upe_va;
1936d3ceb1dSskrll bus_addr_t upe_iova;
1946d3ceb1dSskrll };
1956d3ceb1dSskrll
1966d3ceb1dSskrll struct uturn_page_map {
1976d3ceb1dSskrll SPLAY_HEAD(uturn_page_tree, uturn_page_entry) upm_tree;
1986d3ceb1dSskrll int upm_maxpage; /* Size of allocated page map */
1996d3ceb1dSskrll int upm_pagecnt; /* Number of entries in use */
2006d3ceb1dSskrll struct uturn_page_entry upm_map[1];
2016d3ceb1dSskrll };
2026d3ceb1dSskrll
2036d3ceb1dSskrll /*
2046d3ceb1dSskrll * per-map UTURN state
2056d3ceb1dSskrll */
2066d3ceb1dSskrll struct uturn_map_state {
2076d3ceb1dSskrll struct uturn_softc *ums_sc;
2086d3ceb1dSskrll bus_addr_t ums_iovastart;
2096d3ceb1dSskrll bus_size_t ums_iovasize;
2106d3ceb1dSskrll struct uturn_page_map ums_map; /* map must be last (array at end) */
2116d3ceb1dSskrll };
2126d3ceb1dSskrll
2136d3ceb1dSskrll int uturnmatch(device_t, cfdata_t, void *);
2146d3ceb1dSskrll void uturnattach(device_t, device_t, void *);
2156d3ceb1dSskrll static device_t uturn_callback(device_t, struct confargs *);
2166d3ceb1dSskrll
2176d3ceb1dSskrll CFATTACH_DECL_NEW(uturn, sizeof(struct uturn_softc),
2186d3ceb1dSskrll uturnmatch, uturnattach, NULL, NULL);
2196d3ceb1dSskrll
2206d3ceb1dSskrll extern struct cfdriver uturn_cd;
2216d3ceb1dSskrll
2226d3ceb1dSskrll int uturn_dmamap_create(void *, bus_size_t, int, bus_size_t, bus_size_t, int,
2236d3ceb1dSskrll bus_dmamap_t *);
2246d3ceb1dSskrll void uturn_dmamap_destroy(void *, bus_dmamap_t);
2256d3ceb1dSskrll int uturn_dmamap_load(void *, bus_dmamap_t, void *, bus_size_t, struct proc *,
2266d3ceb1dSskrll int);
2276d3ceb1dSskrll int uturn_dmamap_load_mbuf(void *, bus_dmamap_t, struct mbuf *, int);
2286d3ceb1dSskrll int uturn_dmamap_load_uio(void *, bus_dmamap_t, struct uio *, int);
2296d3ceb1dSskrll int uturn_dmamap_load_raw(void *, bus_dmamap_t, bus_dma_segment_t *, int,
2306d3ceb1dSskrll bus_size_t, int);
2316d3ceb1dSskrll void uturn_dmamap_unload(void *, bus_dmamap_t);
2326d3ceb1dSskrll void uturn_dmamap_sync(void *, bus_dmamap_t, bus_addr_t, bus_size_t, int);
2336d3ceb1dSskrll int uturn_dmamem_alloc(void *, bus_size_t, bus_size_t, bus_size_t,
2346d3ceb1dSskrll bus_dma_segment_t *, int, int *, int);
2356d3ceb1dSskrll void uturn_dmamem_free(void *, bus_dma_segment_t *, int);
2366d3ceb1dSskrll int uturn_dmamem_map(void *, bus_dma_segment_t *, int, size_t, void **, int);
2376d3ceb1dSskrll void uturn_dmamem_unmap(void *, void *, size_t);
2386d3ceb1dSskrll paddr_t uturn_dmamem_mmap(void *, bus_dma_segment_t *, int, off_t, int, int);
2396d3ceb1dSskrll
2406d3ceb1dSskrll static void uturn_iommu_enter(struct uturn_softc *, bus_addr_t, pa_space_t,
2416d3ceb1dSskrll vaddr_t, paddr_t);
2426d3ceb1dSskrll static void uturn_iommu_remove(struct uturn_softc *, bus_addr_t, bus_size_t);
2436d3ceb1dSskrll
2441be97a72Sskrll struct uturn_map_state *uturn_iomap_create(int, int);
2456d3ceb1dSskrll void uturn_iomap_destroy(struct uturn_map_state *);
2466d3ceb1dSskrll int uturn_iomap_insert_page(struct uturn_map_state *, vaddr_t, paddr_t);
2476d3ceb1dSskrll bus_addr_t uturn_iomap_translate(struct uturn_map_state *, paddr_t);
2486d3ceb1dSskrll void uturn_iomap_clear_pages(struct uturn_map_state *);
2496d3ceb1dSskrll
2506d3ceb1dSskrll static int uturn_iomap_load_map(struct uturn_softc *, bus_dmamap_t, int);
2516d3ceb1dSskrll
2526d3ceb1dSskrll const struct hppa_bus_dma_tag uturn_dmat = {
2536d3ceb1dSskrll NULL,
2546d3ceb1dSskrll uturn_dmamap_create, uturn_dmamap_destroy,
2556d3ceb1dSskrll uturn_dmamap_load, uturn_dmamap_load_mbuf,
2566d3ceb1dSskrll uturn_dmamap_load_uio, uturn_dmamap_load_raw,
2576d3ceb1dSskrll uturn_dmamap_unload, uturn_dmamap_sync,
2586d3ceb1dSskrll
2596d3ceb1dSskrll uturn_dmamem_alloc, uturn_dmamem_free, uturn_dmamem_map,
2606d3ceb1dSskrll uturn_dmamem_unmap, uturn_dmamem_mmap
2616d3ceb1dSskrll };
2626d3ceb1dSskrll
2636d3ceb1dSskrll int
uturnmatch(device_t parent,cfdata_t cf,void * aux)2646d3ceb1dSskrll uturnmatch(device_t parent, cfdata_t cf, void *aux)
2656d3ceb1dSskrll {
2666d3ceb1dSskrll struct confargs *ca = aux;
2676d3ceb1dSskrll
2686d3ceb1dSskrll /* there will be only one */
2696d3ceb1dSskrll if (ca->ca_type.iodc_type != HPPA_TYPE_IOA ||
2706d3ceb1dSskrll ca->ca_type.iodc_sv_model != HPPA_IOA_UTURN)
2716d3ceb1dSskrll return 0;
2726d3ceb1dSskrll
2736d3ceb1dSskrll if (ca->ca_type.iodc_model == 0x58 &&
2746d3ceb1dSskrll ca->ca_type.iodc_revision >= 0x20)
2756d3ceb1dSskrll return 0;
2766d3ceb1dSskrll
2776d3ceb1dSskrll return 1;
2786d3ceb1dSskrll }
2796d3ceb1dSskrll
2806d3ceb1dSskrll void
uturnattach(device_t parent,device_t self,void * aux)2816d3ceb1dSskrll uturnattach(device_t parent, device_t self, void *aux)
2826d3ceb1dSskrll {
2836d3ceb1dSskrll struct confargs *ca = aux, nca;
2846d3ceb1dSskrll struct uturn_softc *sc = device_private(self);
2856d3ceb1dSskrll bus_space_handle_t ioh;
2866d3ceb1dSskrll volatile struct uturn_regs *r;
2876d3ceb1dSskrll struct pglist pglist;
2886d3ceb1dSskrll int iova_bits;
2896d3ceb1dSskrll vaddr_t va;
2906d3ceb1dSskrll psize_t size;
2916d3ceb1dSskrll int i;
2926d3ceb1dSskrll
2936d3ceb1dSskrll if (bus_space_map(ca->ca_iot, ca->ca_hpa, IOMOD_HPASIZE, 0, &ioh)) {
2946d3ceb1dSskrll aprint_error(": can't map IO space\n");
2956d3ceb1dSskrll return;
2966d3ceb1dSskrll }
2976d3ceb1dSskrll
2986d3ceb1dSskrll sc->sc_dv = self;
2996d3ceb1dSskrll sc->sc_dmat = ca->ca_dmatag;
3006d3ceb1dSskrll sc->sc_regs = r = bus_space_vaddr(ca->ca_iot, ioh);
3016d3ceb1dSskrll
3026d3ceb1dSskrll aprint_normal(": %x-%x", r->io_io_low << 16, r->io_io_high << 16);
3036d3ceb1dSskrll aprint_normal(": %x-%x", r->io_io_low_hv << 16, r->io_io_high_hv << 16);
3046d3ceb1dSskrll
3056d3ceb1dSskrll aprint_normal(": %s rev %d\n",
3066d3ceb1dSskrll ca->ca_type.iodc_revision < 0x10 ? "U2" : "UTurn",
3076d3ceb1dSskrll ca->ca_type.iodc_revision & 0xf);
3086d3ceb1dSskrll
3096d3ceb1dSskrll /*
3106d3ceb1dSskrll * Setup the iommu.
3116d3ceb1dSskrll */
3126d3ceb1dSskrll
3136d3ceb1dSskrll /* XXX 28 bits gives us 256Mb of iova space */
3146d3ceb1dSskrll /* Calculate based on %age of RAM */
3156d3ceb1dSskrll iova_bits = 28;
3166d3ceb1dSskrll
3176d3ceb1dSskrll /*
3186d3ceb1dSskrll * size is # of pdir entries (64bits) in bytes. 1 entry per IOVA
3196d3ceb1dSskrll * page.
3206d3ceb1dSskrll */
3216d3ceb1dSskrll size = (1 << (iova_bits - UTURN_IOVP_SHIFT)) * sizeof(uint64_t);
3226d3ceb1dSskrll
3236d3ceb1dSskrll /*
3246d3ceb1dSskrll * Chainid is the upper most bits of an IOVP used to determine which
3256d3ceb1dSskrll * TLB entry an IOVP will use.
3266d3ceb1dSskrll */
3276d3ceb1dSskrll sc->sc_chainid_shift = iova_bits - UTURN_CHAINID_SHIFT;
3286d3ceb1dSskrll
3296d3ceb1dSskrll /*
3306d3ceb1dSskrll * Allocate memory for I/O pagetables. They need to be physically
3316d3ceb1dSskrll * contiguous.
3326d3ceb1dSskrll */
3336d3ceb1dSskrll
3346d3ceb1dSskrll if (uvm_pglistalloc(size, 0, -1, PAGE_SIZE, 0, &pglist, 1, 0) != 0)
3356d3ceb1dSskrll panic("%s: no memory", __func__);
3366d3ceb1dSskrll
3376d3ceb1dSskrll va = (vaddr_t)VM_PAGE_TO_PHYS(TAILQ_FIRST(&pglist));
3386d3ceb1dSskrll sc->sc_pdir = (int64_t *)va;
3396d3ceb1dSskrll
3406d3ceb1dSskrll memset(sc->sc_pdir, 0, size);
3416d3ceb1dSskrll
3426d3ceb1dSskrll r->io_chain_id_mask = UTURN_CHAINID_MASK << sc->sc_chainid_shift;
3436d3ceb1dSskrll r->io_pdir_base = VM_PAGE_TO_PHYS(TAILQ_FIRST(&pglist));
3446d3ceb1dSskrll
3456d3ceb1dSskrll r->io_tlb_entry_m = 0;
3466d3ceb1dSskrll r->io_tlb_entry_l = 0;
3476d3ceb1dSskrll
3486d3ceb1dSskrll /* for (i = UTURN_TLB_ENTRIES; i != 0; i--) { */
3496d3ceb1dSskrll for (i = 0; i < UTURN_TLB_ENTRIES; i++) {
3506d3ceb1dSskrll r->io_command =
3516d3ceb1dSskrll UTURN_CMD_TLB_DIRECT_WRITE | (i << sc->sc_chainid_shift);
3526d3ceb1dSskrll }
3536d3ceb1dSskrll /*
3546d3ceb1dSskrll * Go to "Virtual Mode"
3556d3ceb1dSskrll */
3566d3ceb1dSskrll r->io_control = UTURN_VIRTUAL_MODE;
3576d3ceb1dSskrll
3586d3ceb1dSskrll snprintf(sc->sc_mapname, sizeof(sc->sc_mapname), "%s_map",
3596d3ceb1dSskrll device_xname(sc->sc_dv));
360*4ea367cbSthorpej sc->sc_map = vmem_create(sc->sc_mapname,
361*4ea367cbSthorpej 0, /* base */
362*4ea367cbSthorpej (1 << iova_bits), /* size */
363*4ea367cbSthorpej PAGE_SIZE, /* quantum */
364*4ea367cbSthorpej NULL, /* allocfn */
365*4ea367cbSthorpej NULL, /* freefn */
366*4ea367cbSthorpej NULL, /* source */
367*4ea367cbSthorpej 0, /* qcache_max */
368*4ea367cbSthorpej VM_SLEEP,
369*4ea367cbSthorpej IPL_VM);
370*4ea367cbSthorpej KASSERT(sc->sc_map != NULL);
3716d3ceb1dSskrll
3726d3ceb1dSskrll sc->sc_dmatag = uturn_dmat;
3736d3ceb1dSskrll sc->sc_dmatag._cookie = sc;
3746d3ceb1dSskrll
3756d3ceb1dSskrll /*
3766d3ceb1dSskrll * U2/UTurn is actually a combination of an Upper Bus Converter (UBC)
3776d3ceb1dSskrll * and a Lower Bus Converter (LBC). This driver attaches to the UBC;
3786d3ceb1dSskrll * the LBC isn't very interesting, so we skip it. This is easy, since
3796d3ceb1dSskrll * it always is module 63, hence the MAXMODBUS - 1 below.
3806d3ceb1dSskrll */
3816d3ceb1dSskrll nca = *ca;
3826d3ceb1dSskrll nca.ca_hpabase = r->io_io_low << 16;
3836d3ceb1dSskrll nca.ca_dmatag = &sc->sc_dmatag;
3846d3ceb1dSskrll nca.ca_nmodules = MAXMODBUS - 1;
3856d3ceb1dSskrll pdc_scanbus(self, &nca, uturn_callback);
3866d3ceb1dSskrll }
3876d3ceb1dSskrll
3886d3ceb1dSskrll static device_t
uturn_callback(device_t self,struct confargs * ca)3896d3ceb1dSskrll uturn_callback(device_t self, struct confargs *ca)
3906d3ceb1dSskrll {
3916d3ceb1dSskrll
3922685996bSthorpej return config_found(self, ca, mbprint,
393c7fb772bSthorpej CFARGS(.submatch = mbsubmatch));
3946d3ceb1dSskrll }
3956d3ceb1dSskrll
3966d3ceb1dSskrll /*
3976d3ceb1dSskrll * PDIR entry format (HP bit number)
3986d3ceb1dSskrll *
3996d3ceb1dSskrll * +-------+----------------+----------------------------------------------+
4006d3ceb1dSskrll * |0 3|4 15|16 31|
4016d3ceb1dSskrll * | PPN | Virtual Index | Physical Page Number (PPN) |
4026d3ceb1dSskrll * | [0:3] | [0:11] | [4:19] |
4036d3ceb1dSskrll * +-------+----------------+----------------------------------------------+
4046d3ceb1dSskrll *
4056d3ceb1dSskrll * +-----------------------+-----------------------------------------------+
4066d3ceb1dSskrll * |0 19|20 24| 25 | | | | 30 | 31 |
4076d3ceb1dSskrll * | PPN | Rsvd | PH |Update | Rsvd |Lock | Safe | Valid |
4086d3ceb1dSskrll * | [20:39 | | Enable |Enable | |Enable| DMA | |
4096d3ceb1dSskrll * +-----------------------+-----------------------------------------------+
4106d3ceb1dSskrll *
4116d3ceb1dSskrll */
4126d3ceb1dSskrll
4136d3ceb1dSskrll #define UTURN_PENTRY_PREFETCH 0x40
4146d3ceb1dSskrll #define UTURN_PENTRY_UPDATE 0x20
4156d3ceb1dSskrll #define UTURN_PENTRY_LOCK 0x04 /* eisa devices only */
4166d3ceb1dSskrll #define UTURN_PENTRY_SAFEDMA 0x02 /* use safe dma - for subcacheline */
4176d3ceb1dSskrll #define UTURN_PENTRY_VALID 0x01
4186d3ceb1dSskrll
4196d3ceb1dSskrll static void
uturn_iommu_enter(struct uturn_softc * sc,bus_addr_t iova,pa_space_t sp,vaddr_t va,paddr_t pa)4206d3ceb1dSskrll uturn_iommu_enter(struct uturn_softc *sc, bus_addr_t iova, pa_space_t sp,
4216d3ceb1dSskrll vaddr_t va, paddr_t pa)
4226d3ceb1dSskrll {
4236d3ceb1dSskrll uint64_t pdir_entry;
4246d3ceb1dSskrll uint64_t *pdirp;
4256d3ceb1dSskrll uint32_t ci; /* coherent index */
4266d3ceb1dSskrll
4276d3ceb1dSskrll pdirp = &sc->sc_pdir[UTURN_IOVA_INDEX(iova)];
4286d3ceb1dSskrll
4296d3ceb1dSskrll DPRINTF(("%s: iova %lx pdir %p pdirp %p pa %lx", __func__, iova,
4306d3ceb1dSskrll sc->sc_pdir, pdirp, pa));
4316d3ceb1dSskrll
4326d3ceb1dSskrll ci = lci(HPPA_SID_KERNEL, va);
4336d3ceb1dSskrll
4346d3ceb1dSskrll /* setup hints, etc */
4356d3ceb1dSskrll pdir_entry = (UTURN_PENTRY_LOCK | UTURN_PENTRY_SAFEDMA |
4366d3ceb1dSskrll UTURN_PENTRY_VALID);
4376d3ceb1dSskrll
4386d3ceb1dSskrll /*
4396d3ceb1dSskrll * bottom 36 bits of pa map directly into entry to form PPN[4:39]
4406d3ceb1dSskrll * leaving last 12 bits for hints, etc.
4416d3ceb1dSskrll */
4426d3ceb1dSskrll pdir_entry |= (pa & ~PAGE_MASK);
4436d3ceb1dSskrll
4446d3ceb1dSskrll /* mask off top PPN bits */
4456d3ceb1dSskrll pdir_entry &= 0x0000ffffffffffffUL;
4466d3ceb1dSskrll
4476d3ceb1dSskrll /* insert the virtual index bits */
4486d3ceb1dSskrll pdir_entry |= (((uint64_t)ci >> 12) << 48);
4496d3ceb1dSskrll
4506d3ceb1dSskrll /* PPN[0:3] of the 40bit PPN go in entry[0:3] */
4516d3ceb1dSskrll pdir_entry |= ((((uint64_t)pa & 0x000f000000000000UL) >> 48) << 60);
4526d3ceb1dSskrll
4536d3ceb1dSskrll *pdirp = pdir_entry;
4546d3ceb1dSskrll
4556d3ceb1dSskrll DPRINTF((": pdir_entry %llx\n", pdir_entry));
4566d3ceb1dSskrll
4576d3ceb1dSskrll /*
4586d3ceb1dSskrll * We could use PDC_MODEL_CAPABILITIES here
4596d3ceb1dSskrll */
4606d3ceb1dSskrll fdcache(HPPA_SID_KERNEL, (vaddr_t)pdirp, sizeof(uint64_t));
4616d3ceb1dSskrll }
4626d3ceb1dSskrll
4636d3ceb1dSskrll
4646d3ceb1dSskrll static void
uturn_iommu_remove(struct uturn_softc * sc,bus_addr_t iova,bus_size_t size)4656d3ceb1dSskrll uturn_iommu_remove(struct uturn_softc *sc, bus_addr_t iova, bus_size_t size)
4666d3ceb1dSskrll {
4676d3ceb1dSskrll uint32_t chain_size = 1 << sc->sc_chainid_shift;
4686d3ceb1dSskrll bus_size_t len;
4696d3ceb1dSskrll
4706d3ceb1dSskrll KASSERT((iova & PAGE_MASK) == 0);
4716d3ceb1dSskrll KASSERT((size & PAGE_MASK) == 0);
4726d3ceb1dSskrll
4736d3ceb1dSskrll DPRINTF(("%s: sc %p iova %lx size %lx\n", __func__, sc, iova, size));
4746d3ceb1dSskrll len = size;
4756d3ceb1dSskrll while (len != 0) {
4766d3ceb1dSskrll uint64_t *pdirp = &sc->sc_pdir[UTURN_IOVA_INDEX(iova)];
4776d3ceb1dSskrll
4786d3ceb1dSskrll /* XXX Just the valid bit??? */
4796d3ceb1dSskrll *pdirp = 0;
4806d3ceb1dSskrll
4816d3ceb1dSskrll /*
4826d3ceb1dSskrll * We could use PDC_MODEL_CAPABILITIES here
4836d3ceb1dSskrll */
4846d3ceb1dSskrll fdcache(HPPA_SID_KERNEL, (vaddr_t)pdirp, sizeof(uint64_t));
4856d3ceb1dSskrll
4866d3ceb1dSskrll iova += PAGE_SIZE;
4876d3ceb1dSskrll len -= PAGE_SIZE;
4886d3ceb1dSskrll }
4896d3ceb1dSskrll
4906d3ceb1dSskrll len = size + chain_size;
4916d3ceb1dSskrll
4926d3ceb1dSskrll while (len > chain_size) {
4936d3ceb1dSskrll sc->sc_regs->io_command = UTURN_CMD_TLB_PURGE | iova;
4946d3ceb1dSskrll iova += chain_size;
4956d3ceb1dSskrll len -= chain_size;
4966d3ceb1dSskrll }
4976d3ceb1dSskrll }
4986d3ceb1dSskrll
4996d3ceb1dSskrll int
uturn_dmamap_create(void * v,bus_size_t size,int nsegments,bus_size_t maxsegsz,bus_size_t boundary,int flags,bus_dmamap_t * dmamap)5006d3ceb1dSskrll uturn_dmamap_create(void *v, bus_size_t size, int nsegments,
5016d3ceb1dSskrll bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamap)
5026d3ceb1dSskrll {
5036d3ceb1dSskrll struct uturn_softc *sc = v;
5046d3ceb1dSskrll bus_dmamap_t map;
5056d3ceb1dSskrll struct uturn_map_state *ums;
5066d3ceb1dSskrll int error;
5076d3ceb1dSskrll
5086d3ceb1dSskrll error = bus_dmamap_create(sc->sc_dmat, size, nsegments, maxsegsz,
5096d3ceb1dSskrll boundary, flags, &map);
5106d3ceb1dSskrll if (error)
5116d3ceb1dSskrll return (error);
5126d3ceb1dSskrll
5131be97a72Sskrll ums = uturn_iomap_create(atop(round_page(size)), flags);
5146d3ceb1dSskrll if (ums == NULL) {
5156d3ceb1dSskrll bus_dmamap_destroy(sc->sc_dmat, map);
5166d3ceb1dSskrll return (ENOMEM);
5176d3ceb1dSskrll }
5186d3ceb1dSskrll
5196d3ceb1dSskrll ums->ums_sc = sc;
5206d3ceb1dSskrll map->_dm_cookie = ums;
5216d3ceb1dSskrll *dmamap = map;
5226d3ceb1dSskrll
5236d3ceb1dSskrll return (0);
5246d3ceb1dSskrll }
5256d3ceb1dSskrll
5266d3ceb1dSskrll void
uturn_dmamap_destroy(void * v,bus_dmamap_t map)5276d3ceb1dSskrll uturn_dmamap_destroy(void *v, bus_dmamap_t map)
5286d3ceb1dSskrll {
5296d3ceb1dSskrll struct uturn_softc *sc = v;
5306d3ceb1dSskrll
5316d3ceb1dSskrll /*
5326d3ceb1dSskrll * The specification (man page) requires a loaded
5336d3ceb1dSskrll * map to be unloaded before it is destroyed.
5346d3ceb1dSskrll */
5356d3ceb1dSskrll if (map->dm_nsegs)
5366d3ceb1dSskrll uturn_dmamap_unload(sc, map);
5376d3ceb1dSskrll
5386d3ceb1dSskrll if (map->_dm_cookie)
5396d3ceb1dSskrll uturn_iomap_destroy(map->_dm_cookie);
5406d3ceb1dSskrll map->_dm_cookie = NULL;
5416d3ceb1dSskrll
5426d3ceb1dSskrll bus_dmamap_destroy(sc->sc_dmat, map);
5436d3ceb1dSskrll }
5446d3ceb1dSskrll
5456d3ceb1dSskrll static int
uturn_iomap_load_map(struct uturn_softc * sc,bus_dmamap_t map,int flags)5466d3ceb1dSskrll uturn_iomap_load_map(struct uturn_softc *sc, bus_dmamap_t map, int flags)
5476d3ceb1dSskrll {
5486d3ceb1dSskrll struct uturn_map_state *ums = map->_dm_cookie;
5496d3ceb1dSskrll struct uturn_page_map *upm = &ums->ums_map;
5506d3ceb1dSskrll struct uturn_page_entry *e;
551*4ea367cbSthorpej int err, seg;
5526d3ceb1dSskrll paddr_t pa, paend;
5536d3ceb1dSskrll vaddr_t va;
5546d3ceb1dSskrll bus_size_t sgsize;
5556d3ceb1dSskrll bus_size_t align, boundary;
556*4ea367cbSthorpej vmem_addr_t iovaddr;
5576d3ceb1dSskrll bus_addr_t iova;
5586d3ceb1dSskrll int i;
5596d3ceb1dSskrll
5606d3ceb1dSskrll /* XXX */
5616d3ceb1dSskrll boundary = map->_dm_boundary;
562*4ea367cbSthorpej align = 0; /* align to quantum */
5636d3ceb1dSskrll
5646d3ceb1dSskrll uturn_iomap_clear_pages(ums);
5656d3ceb1dSskrll
5666d3ceb1dSskrll for (seg = 0; seg < map->dm_nsegs; seg++) {
5676d3ceb1dSskrll struct hppa_bus_dma_segment *ds = &map->dm_segs[seg];
5686d3ceb1dSskrll
5696d3ceb1dSskrll paend = round_page(ds->ds_addr + ds->ds_len);
5706d3ceb1dSskrll for (pa = trunc_page(ds->ds_addr), va = trunc_page(ds->_ds_va);
5716d3ceb1dSskrll pa < paend; pa += PAGE_SIZE, va += PAGE_SIZE) {
5726d3ceb1dSskrll err = uturn_iomap_insert_page(ums, va, pa);
5736d3ceb1dSskrll if (err) {
5746d3ceb1dSskrll printf("iomap insert error: %d for "
5756d3ceb1dSskrll "va 0x%lx pa 0x%lx\n", err, va, pa);
5766d3ceb1dSskrll bus_dmamap_unload(sc->sc_dmat, map);
5776d3ceb1dSskrll uturn_iomap_clear_pages(ums);
5786d3ceb1dSskrll }
5796d3ceb1dSskrll }
5806d3ceb1dSskrll }
5816d3ceb1dSskrll
582*4ea367cbSthorpej const vm_flag_t vmflags = VM_BESTFIT |
583*4ea367cbSthorpej ((flags & BUS_DMA_NOWAIT) ? VM_NOSLEEP : VM_SLEEP);
584*4ea367cbSthorpej
5856d3ceb1dSskrll sgsize = ums->ums_map.upm_pagecnt * PAGE_SIZE;
586*4ea367cbSthorpej err = vmem_xalloc(sc->sc_map, sgsize,
587*4ea367cbSthorpej align, /* align */
588*4ea367cbSthorpej 0, /* phase */
589*4ea367cbSthorpej boundary, /* nocross */
590*4ea367cbSthorpej VMEM_ADDR_MIN, /* minaddr */
591*4ea367cbSthorpej VMEM_ADDR_MAX, /* maxaddr */
592*4ea367cbSthorpej vmflags,
593*4ea367cbSthorpej &iovaddr);
5946d3ceb1dSskrll if (err)
5956d3ceb1dSskrll return (err);
5966d3ceb1dSskrll
5976d3ceb1dSskrll ums->ums_iovastart = iovaddr;
5986d3ceb1dSskrll ums->ums_iovasize = sgsize;
5996d3ceb1dSskrll
6006d3ceb1dSskrll iova = iovaddr;
6016d3ceb1dSskrll for (i = 0, e = upm->upm_map; i < upm->upm_pagecnt; ++i, ++e) {
6026d3ceb1dSskrll e->upe_iova = iova;
6036d3ceb1dSskrll uturn_iommu_enter(sc, e->upe_iova, HPPA_SID_KERNEL, e->upe_va,
6046d3ceb1dSskrll e->upe_pa);
6056d3ceb1dSskrll iova += PAGE_SIZE;
6066d3ceb1dSskrll }
6076d3ceb1dSskrll
6086d3ceb1dSskrll for (seg = 0; seg < map->dm_nsegs; seg++) {
6096d3ceb1dSskrll struct hppa_bus_dma_segment *ds = &map->dm_segs[seg];
6106d3ceb1dSskrll ds->ds_addr = uturn_iomap_translate(ums, ds->ds_addr);
6116d3ceb1dSskrll }
6126d3ceb1dSskrll
6136d3ceb1dSskrll return (0);
6146d3ceb1dSskrll }
6156d3ceb1dSskrll
6166d3ceb1dSskrll int
uturn_dmamap_load(void * v,bus_dmamap_t map,void * addr,bus_size_t size,struct proc * p,int flags)6176d3ceb1dSskrll uturn_dmamap_load(void *v, bus_dmamap_t map, void *addr, bus_size_t size,
6186d3ceb1dSskrll struct proc *p, int flags)
6196d3ceb1dSskrll {
6206d3ceb1dSskrll struct uturn_softc *sc = v;
6216d3ceb1dSskrll int err;
6226d3ceb1dSskrll
6236d3ceb1dSskrll err = bus_dmamap_load(sc->sc_dmat, map, addr, size, p, flags);
6246d3ceb1dSskrll if (err)
6256d3ceb1dSskrll return (err);
6266d3ceb1dSskrll
6276d3ceb1dSskrll return uturn_iomap_load_map(sc, map, flags);
6286d3ceb1dSskrll }
6296d3ceb1dSskrll
6306d3ceb1dSskrll int
uturn_dmamap_load_mbuf(void * v,bus_dmamap_t map,struct mbuf * m,int flags)6316d3ceb1dSskrll uturn_dmamap_load_mbuf(void *v, bus_dmamap_t map, struct mbuf *m, int flags)
6326d3ceb1dSskrll {
6336d3ceb1dSskrll struct uturn_softc *sc = v;
6346d3ceb1dSskrll int err;
6356d3ceb1dSskrll
6366d3ceb1dSskrll err = bus_dmamap_load_mbuf(sc->sc_dmat, map, m, flags);
6376d3ceb1dSskrll if (err)
6386d3ceb1dSskrll return (err);
6396d3ceb1dSskrll
6406d3ceb1dSskrll return uturn_iomap_load_map(sc, map, flags);
6416d3ceb1dSskrll }
6426d3ceb1dSskrll
6436d3ceb1dSskrll int
uturn_dmamap_load_uio(void * v,bus_dmamap_t map,struct uio * uio,int flags)6446d3ceb1dSskrll uturn_dmamap_load_uio(void *v, bus_dmamap_t map, struct uio *uio, int flags)
6456d3ceb1dSskrll {
6466d3ceb1dSskrll struct uturn_softc *sc = v;
6476d3ceb1dSskrll
6486d3ceb1dSskrll printf("load_uio\n");
6496d3ceb1dSskrll
6506d3ceb1dSskrll return (bus_dmamap_load_uio(sc->sc_dmat, map, uio, flags));
6516d3ceb1dSskrll }
6526d3ceb1dSskrll
6536d3ceb1dSskrll int
uturn_dmamap_load_raw(void * v,bus_dmamap_t map,bus_dma_segment_t * segs,int nsegs,bus_size_t size,int flags)6546d3ceb1dSskrll uturn_dmamap_load_raw(void *v, bus_dmamap_t map, bus_dma_segment_t *segs,
6556d3ceb1dSskrll int nsegs, bus_size_t size, int flags)
6566d3ceb1dSskrll {
6576d3ceb1dSskrll struct uturn_softc *sc = v;
6586d3ceb1dSskrll
6596d3ceb1dSskrll printf("load_raw\n");
6606d3ceb1dSskrll
6616d3ceb1dSskrll return (bus_dmamap_load_raw(sc->sc_dmat, map, segs, nsegs, size, flags));
6626d3ceb1dSskrll }
6636d3ceb1dSskrll
6646d3ceb1dSskrll void
uturn_dmamap_unload(void * v,bus_dmamap_t map)6656d3ceb1dSskrll uturn_dmamap_unload(void *v, bus_dmamap_t map)
6666d3ceb1dSskrll {
6676d3ceb1dSskrll struct uturn_softc *sc = v;
6686d3ceb1dSskrll struct uturn_map_state *ums = map->_dm_cookie;
6696d3ceb1dSskrll struct uturn_page_map *upm = &ums->ums_map;
6706d3ceb1dSskrll struct uturn_page_entry *e;
671*4ea367cbSthorpej int i;
6726d3ceb1dSskrll
6736d3ceb1dSskrll /* Remove the IOMMU entries. */
6746d3ceb1dSskrll for (i = 0, e = upm->upm_map; i < upm->upm_pagecnt; ++i, ++e)
6756d3ceb1dSskrll uturn_iommu_remove(sc, e->upe_iova, PAGE_SIZE);
6766d3ceb1dSskrll
6776d3ceb1dSskrll /* Clear the iomap. */
6786d3ceb1dSskrll uturn_iomap_clear_pages(ums);
6796d3ceb1dSskrll
6806d3ceb1dSskrll bus_dmamap_unload(sc->sc_dmat, map);
6816d3ceb1dSskrll
682*4ea367cbSthorpej vmem_xfree(sc->sc_map, ums->ums_iovastart, ums->ums_iovasize);
6836d3ceb1dSskrll ums->ums_iovastart = 0;
6846d3ceb1dSskrll ums->ums_iovasize = 0;
6856d3ceb1dSskrll }
6866d3ceb1dSskrll
6876d3ceb1dSskrll void
uturn_dmamap_sync(void * v,bus_dmamap_t map,bus_addr_t off,bus_size_t len,int ops)6886d3ceb1dSskrll uturn_dmamap_sync(void *v, bus_dmamap_t map, bus_addr_t off,
6896d3ceb1dSskrll bus_size_t len, int ops)
6906d3ceb1dSskrll {
6916d3ceb1dSskrll /* Nothing to do; DMA is cache-coherent. */
6926d3ceb1dSskrll }
6936d3ceb1dSskrll
6946d3ceb1dSskrll int
uturn_dmamem_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)6956d3ceb1dSskrll uturn_dmamem_alloc(void *v, bus_size_t size, bus_size_t alignment,
6966d3ceb1dSskrll bus_size_t boundary, bus_dma_segment_t *segs,
6976d3ceb1dSskrll int nsegs, int *rsegs, int flags)
6986d3ceb1dSskrll {
6996d3ceb1dSskrll struct uturn_softc *sc = v;
7006d3ceb1dSskrll
7016d3ceb1dSskrll return (bus_dmamem_alloc(sc->sc_dmat, size, alignment, boundary,
7026d3ceb1dSskrll segs, nsegs, rsegs, flags));
7036d3ceb1dSskrll }
7046d3ceb1dSskrll
7056d3ceb1dSskrll void
uturn_dmamem_free(void * v,bus_dma_segment_t * segs,int nsegs)7066d3ceb1dSskrll uturn_dmamem_free(void *v, bus_dma_segment_t *segs, int nsegs)
7076d3ceb1dSskrll {
7086d3ceb1dSskrll struct uturn_softc *sc = v;
7096d3ceb1dSskrll
7106d3ceb1dSskrll bus_dmamem_free(sc->sc_dmat, segs, nsegs);
7116d3ceb1dSskrll }
7126d3ceb1dSskrll
7136d3ceb1dSskrll int
uturn_dmamem_map(void * v,bus_dma_segment_t * segs,int nsegs,size_t size,void ** kvap,int flags)7146d3ceb1dSskrll uturn_dmamem_map(void *v, bus_dma_segment_t *segs, int nsegs, size_t size,
7156d3ceb1dSskrll void **kvap, int flags)
7166d3ceb1dSskrll {
7176d3ceb1dSskrll struct uturn_softc *sc = v;
7186d3ceb1dSskrll
7196d3ceb1dSskrll return (bus_dmamem_map(sc->sc_dmat, segs, nsegs, size, kvap, flags));
7206d3ceb1dSskrll }
7216d3ceb1dSskrll
7226d3ceb1dSskrll void
uturn_dmamem_unmap(void * v,void * kva,size_t size)7236d3ceb1dSskrll uturn_dmamem_unmap(void *v, void *kva, size_t size)
7246d3ceb1dSskrll {
7256d3ceb1dSskrll struct uturn_softc *sc = v;
7266d3ceb1dSskrll
7276d3ceb1dSskrll bus_dmamem_unmap(sc->sc_dmat, kva, size);
7286d3ceb1dSskrll }
7296d3ceb1dSskrll
7306d3ceb1dSskrll paddr_t
uturn_dmamem_mmap(void * v,bus_dma_segment_t * segs,int nsegs,off_t off,int prot,int flags)7316d3ceb1dSskrll uturn_dmamem_mmap(void *v, bus_dma_segment_t *segs, int nsegs, off_t off,
7326d3ceb1dSskrll int prot, int flags)
7336d3ceb1dSskrll {
7346d3ceb1dSskrll struct uturn_softc *sc = v;
7356d3ceb1dSskrll
7366d3ceb1dSskrll return (bus_dmamem_mmap(sc->sc_dmat, segs, nsegs, off, prot, flags));
7376d3ceb1dSskrll }
7386d3ceb1dSskrll
7396d3ceb1dSskrll /*
7406d3ceb1dSskrll * Utility function used by splay tree to order page entries by pa.
7416d3ceb1dSskrll */
7426d3ceb1dSskrll static inline int
upe_compare(struct uturn_page_entry * a,struct uturn_page_entry * b)7436d3ceb1dSskrll upe_compare(struct uturn_page_entry *a, struct uturn_page_entry *b)
7446d3ceb1dSskrll {
7456d3ceb1dSskrll return ((a->upe_pa > b->upe_pa) ? 1 :
7466d3ceb1dSskrll (a->upe_pa < b->upe_pa) ? -1 : 0);
7476d3ceb1dSskrll }
7486d3ceb1dSskrll
7496d3ceb1dSskrll SPLAY_PROTOTYPE(uturn_page_tree, uturn_page_entry, upe_node, upe_compare);
7506d3ceb1dSskrll
7516d3ceb1dSskrll SPLAY_GENERATE(uturn_page_tree, uturn_page_entry, upe_node, upe_compare);
7526d3ceb1dSskrll
7536d3ceb1dSskrll /*
7546d3ceb1dSskrll * Create a new iomap.
7556d3ceb1dSskrll */
7566d3ceb1dSskrll struct uturn_map_state *
uturn_iomap_create(int n,int flags)7571be97a72Sskrll uturn_iomap_create(int n, int flags)
7586d3ceb1dSskrll {
7596d3ceb1dSskrll struct uturn_map_state *ums;
7606d3ceb1dSskrll
7616d3ceb1dSskrll /* Safety for heavily fragmented data, such as mbufs */
7626d3ceb1dSskrll n += 4;
7636d3ceb1dSskrll if (n < 16)
7646d3ceb1dSskrll n = 16;
7651be97a72Sskrll const size_t sz =
7661be97a72Sskrll sizeof(*ums) + (n - 1) * sizeof(ums->ums_map.upm_map[0]);
7671be97a72Sskrll ums = kmem_zalloc(sz, (flags & BUS_DMA_NOWAIT) ? KM_NOSLEEP : KM_SLEEP);
7686d3ceb1dSskrll if (ums == NULL)
7696d3ceb1dSskrll return (NULL);
7706d3ceb1dSskrll
7716d3ceb1dSskrll /* Initialize the map. */
7726d3ceb1dSskrll ums->ums_map.upm_maxpage = n;
7736d3ceb1dSskrll SPLAY_INIT(&ums->ums_map.upm_tree);
7746d3ceb1dSskrll
7756d3ceb1dSskrll return (ums);
7766d3ceb1dSskrll }
7776d3ceb1dSskrll
7786d3ceb1dSskrll /*
7796d3ceb1dSskrll * Destroy an iomap.
7806d3ceb1dSskrll */
7816d3ceb1dSskrll void
uturn_iomap_destroy(struct uturn_map_state * ums)7826d3ceb1dSskrll uturn_iomap_destroy(struct uturn_map_state *ums)
7836d3ceb1dSskrll {
7846d3ceb1dSskrll KASSERT(ums->ums_map.upm_pagecnt == 0);
7851be97a72Sskrll const int n = ums->ums_map.upm_maxpage;
7861be97a72Sskrll const size_t sz =
7871be97a72Sskrll sizeof(*ums) + (n - 1) * sizeof(ums->ums_map.upm_map[0]);
7886d3ceb1dSskrll
7891be97a72Sskrll kmem_free(ums, sz);
7906d3ceb1dSskrll }
7916d3ceb1dSskrll
7926d3ceb1dSskrll /*
7936d3ceb1dSskrll * Insert a pa entry in the iomap.
7946d3ceb1dSskrll */
7956d3ceb1dSskrll int
uturn_iomap_insert_page(struct uturn_map_state * ums,vaddr_t va,paddr_t pa)7966d3ceb1dSskrll uturn_iomap_insert_page(struct uturn_map_state *ums, vaddr_t va, paddr_t pa)
7976d3ceb1dSskrll {
7986d3ceb1dSskrll struct uturn_page_map *upm = &ums->ums_map;
7996d3ceb1dSskrll struct uturn_page_entry *e;
8006d3ceb1dSskrll
8016d3ceb1dSskrll if (upm->upm_pagecnt >= upm->upm_maxpage) {
8026d3ceb1dSskrll struct uturn_page_entry upe;
8036d3ceb1dSskrll
8046d3ceb1dSskrll upe.upe_pa = pa;
8056d3ceb1dSskrll if (SPLAY_FIND(uturn_page_tree, &upm->upm_tree, &upe))
8066d3ceb1dSskrll return (0);
8076d3ceb1dSskrll
8086d3ceb1dSskrll return (ENOMEM);
8096d3ceb1dSskrll }
8106d3ceb1dSskrll
8116d3ceb1dSskrll e = &upm->upm_map[upm->upm_pagecnt];
8126d3ceb1dSskrll
8136d3ceb1dSskrll e->upe_pa = pa;
8146d3ceb1dSskrll e->upe_va = va;
8156d3ceb1dSskrll e->upe_iova = 0;
8166d3ceb1dSskrll
8176d3ceb1dSskrll e = SPLAY_INSERT(uturn_page_tree, &upm->upm_tree, e);
8186d3ceb1dSskrll
8196d3ceb1dSskrll /* Duplicates are okay, but only count them once. */
8206d3ceb1dSskrll if (e)
8216d3ceb1dSskrll return (0);
8226d3ceb1dSskrll
8236d3ceb1dSskrll ++upm->upm_pagecnt;
8246d3ceb1dSskrll
8256d3ceb1dSskrll return (0);
8266d3ceb1dSskrll }
8276d3ceb1dSskrll
8286d3ceb1dSskrll /*
8296d3ceb1dSskrll * Translate a physical address (pa) into a IOVA address.
8306d3ceb1dSskrll */
8316d3ceb1dSskrll bus_addr_t
uturn_iomap_translate(struct uturn_map_state * ums,paddr_t pa)8326d3ceb1dSskrll uturn_iomap_translate(struct uturn_map_state *ums, paddr_t pa)
8336d3ceb1dSskrll {
8346d3ceb1dSskrll struct uturn_page_map *upm = &ums->ums_map;
8356d3ceb1dSskrll struct uturn_page_entry *e;
8366d3ceb1dSskrll struct uturn_page_entry pe;
8376d3ceb1dSskrll paddr_t offset = pa & PAGE_MASK;
8386d3ceb1dSskrll
8396d3ceb1dSskrll pe.upe_pa = trunc_page(pa);
8406d3ceb1dSskrll
8416d3ceb1dSskrll e = SPLAY_FIND(uturn_page_tree, &upm->upm_tree, &pe);
8426d3ceb1dSskrll
8436d3ceb1dSskrll if (e == NULL) {
8446d3ceb1dSskrll panic("couldn't find pa %lx\n", pa);
8456d3ceb1dSskrll return 0;
8466d3ceb1dSskrll }
8476d3ceb1dSskrll
8486d3ceb1dSskrll return (e->upe_iova | offset);
8496d3ceb1dSskrll }
8506d3ceb1dSskrll
8516d3ceb1dSskrll /*
8526d3ceb1dSskrll * Clear the iomap table and tree.
8536d3ceb1dSskrll */
8546d3ceb1dSskrll void
uturn_iomap_clear_pages(struct uturn_map_state * ums)8556d3ceb1dSskrll uturn_iomap_clear_pages(struct uturn_map_state *ums)
8566d3ceb1dSskrll {
8576d3ceb1dSskrll ums->ums_map.upm_pagecnt = 0;
8586d3ceb1dSskrll SPLAY_INIT(&ums->ums_map.upm_tree);
8596d3ceb1dSskrll }
860