xref: /netbsd-src/sys/arch/hppa/dev/uturn.c (revision 4ea367cb79c0df2560e7e5cd3173da7c272b8f7e)
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