1/*	$NetBSD: uturn.c,v 1.18 2012/01/12 23:10:27 skrll Exp $	*/
2
3/*	$OpenBSD: uturn.c,v 1.6 2007/12/29 01:26:14 kettenis Exp $	*/
4
5/*-
6 * Copyright (c) 2012 The NetBSD Foundation, Inc.
7 * All rights reserved.
8 *
9 * This code is derived from software contributed to The NetBSD Foundation
10 * by Nick Hudson.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/*
35 * Copyright (c) 2007 Mark Kettenis
36 *
37 * Permission to use, copy, modify, and distribute this software for any
38 * purpose with or without fee is hereby granted, provided that the above
39 * copyright notice and this permission notice appear in all copies.
40 *
41 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
42 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
43 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
44 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
45 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
46 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
47 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
48 */
49
50/*
51 * Copyright (c) 2004 Michael Shalayeff
52 * All rights reserved.
53 *
54 * Redistribution and use in source and binary forms, with or without
55 * modification, are permitted provided that the following conditions
56 * are met:
57 * 1. Redistributions of source code must retain the above copyright
58 *    notice, this list of conditions and the following disclaimer.
59 * 2. Redistributions in binary form must reproduce the above copyright
60 *    notice, this list of conditions and the following disclaimer in the
61 *    documentation and/or other materials provided with the distribution.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
64 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
65 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
66 * IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
67 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
69 * SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
70 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
71 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
72 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
73 * THE POSSIBILITY OF SUCH DAMAGE.
74 */
75
76/*
77 * References:
78 * 1. Hardware Cache Coherent Input/Output. Hewlett-Packard Journal, February
79 *    1996.
80 * 2. PA-RISC 1.1 Architecture and Instruction Set Reference Manual,
81 *    Hewlett-Packard, February 1994, Third Edition
82 */
83
84#include <sys/param.h>
85#include <sys/systm.h>
86#include <sys/device.h>
87#include <sys/reboot.h>
88#include <sys/malloc.h>
89#include <sys/extent.h>
90#include <sys/mbuf.h>
91#include <sys/tree.h>
92
93#include <uvm/uvm.h>
94
95#include <sys/bus.h>
96#include <machine/iomod.h>
97#include <machine/autoconf.h>
98
99#include <hp700/dev/cpudevs.h>
100
101#define UTURNDEBUG
102#ifdef UTURNDEBUG
103
104#define	DPRINTF(s)	do {	\
105	if (uturndebug)		\
106		printf s;	\
107} while(0)
108
109int uturndebug = 0;
110#else
111#define	DPRINTF(s)	/* */
112#endif
113
114struct uturn_regs {
115	/* Runway Supervisory Set */
116	int32_t		unused1[12];
117	uint32_t	io_command;		/* Offset 12 */
118#define	UTURN_CMD_TLB_PURGE		33	/* Purge I/O TLB entry */
119#define	UTURN_CMD_TLB_DIRECT_WRITE	35	/* I/O TLB Writes */
120
121	uint32_t	io_status;		/* Offset 13 */
122	uint32_t	io_control;		/* Offset 14 */
123#define	UTURN_IOCTRL_TLB_REAL		0x00000000
124#define	UTURN_IOCTRL_TLB_ERROR		0x00010000
125#define	UTURN_IOCTRL_TLB_NORMAL		0x00020000
126
127#define	UTURN_IOCTRL_MODE_OFF		0x00000000
128#define	UTURN_IOCTRL_MODE_INCLUDE	0x00000080
129#define	UTURN_IOCTRL_MODE_PEEK		0x00000180
130
131#define	UTURN_VIRTUAL_MODE	\
132	(UTURN_IOCTRL_TLB_NORMAL | UTURN_IOCTRL_MODE_INCLUDE)
133
134#define	UTURN_REAL_MODE		\
135	UTURN_IOCTRL_MODE_INCLUDE
136
137	int32_t		unused2[1];
138
139	/* Runway Auxiliary Register Set */
140	uint32_t	io_err_resp;		/* Offset  0 */
141	uint32_t	io_err_info;		/* Offset  1 */
142	uint32_t	io_err_req;		/* Offset  2 */
143	uint32_t	io_err_resp_hi;		/* Offset  3 */
144	uint32_t	io_tlb_entry_m;		/* Offset  4 */
145	uint32_t	io_tlb_entry_l;		/* Offset  5 */
146	uint32_t	unused3[1];
147	uint32_t	io_pdir_base;		/* Offset  7 */
148	uint32_t	io_io_low_hv;		/* Offset  8 */
149	uint32_t	io_io_high_hv;		/* Offset  9 */
150	uint32_t	unused4[1];
151	uint32_t	io_chain_id_mask;	/* Offset 11 */
152	uint32_t	unused5[2];
153	uint32_t	io_io_low;		/* Offset 14 */
154	uint32_t	io_io_high;		/* Offset 15 */
155};
156
157
158/* Uturn supports 256 TLB entries */
159#define	UTURN_CHAINID_SHIFT	8
160#define	UTURN_CHAINID_MASK	0xff
161#define	UTURN_TLB_ENTRIES	(1 << UTURN_CHAINID_SHIFT)
162
163#define	UTURN_IOVP_SIZE		PAGE_SIZE
164#define	UTURN_IOVP_SHIFT	PAGE_SHIFT
165#define	UTURN_IOVP_MASK		PAGE_MASK
166
167#define	UTURN_IOVA(iovp, off)	((iovp) | (off))
168#define	UTURN_IOVP(iova)	((iova) & UTURN_IOVP_MASK)
169#define	UTURN_IOVA_INDEX(iova)	((iova) >> UTURN_IOVP_SHIFT)
170
171struct uturn_softc {
172	device_t sc_dv;
173
174	bus_dma_tag_t sc_dmat;
175	struct uturn_regs volatile *sc_regs;
176	uint64_t *sc_pdir;
177	uint32_t sc_chainid_shift;
178
179	char sc_mapname[20];
180	struct extent *sc_map;
181
182	struct hppa_bus_dma_tag sc_dmatag;
183};
184
185/*
186 * per-map IOVA page table
187 */
188struct uturn_page_entry {
189	SPLAY_ENTRY(uturn_page_entry) upe_node;
190	paddr_t	upe_pa;
191	vaddr_t	upe_va;
192	bus_addr_t upe_iova;
193};
194
195struct uturn_page_map {
196	SPLAY_HEAD(uturn_page_tree, uturn_page_entry) upm_tree;
197	int upm_maxpage;	/* Size of allocated page map */
198	int upm_pagecnt;	/* Number of entries in use */
199	struct uturn_page_entry	upm_map[1];
200};
201
202/*
203 * per-map UTURN state
204 */
205struct uturn_map_state {
206	struct uturn_softc *ums_sc;
207	bus_addr_t ums_iovastart;
208	bus_size_t ums_iovasize;
209	struct uturn_page_map ums_map;	/* map must be last (array at end) */
210};
211
212int	uturnmatch(device_t, cfdata_t, void *);
213void	uturnattach(device_t, device_t, void *);
214static device_t uturn_callback(device_t, struct confargs *);
215
216CFATTACH_DECL_NEW(uturn, sizeof(struct uturn_softc),
217    uturnmatch, uturnattach, NULL, NULL);
218
219extern struct cfdriver uturn_cd;
220
221int uturn_dmamap_create(void *, bus_size_t, int, bus_size_t, bus_size_t, int,
222    bus_dmamap_t *);
223void uturn_dmamap_destroy(void *, bus_dmamap_t);
224int uturn_dmamap_load(void *, bus_dmamap_t, void *, bus_size_t, struct proc *,
225    int);
226int uturn_dmamap_load_mbuf(void *, bus_dmamap_t, struct mbuf *, int);
227int uturn_dmamap_load_uio(void *, bus_dmamap_t, struct uio *, int);
228int uturn_dmamap_load_raw(void *, bus_dmamap_t, bus_dma_segment_t *, int,
229    bus_size_t, int);
230void uturn_dmamap_unload(void *, bus_dmamap_t);
231void uturn_dmamap_sync(void *, bus_dmamap_t, bus_addr_t, bus_size_t, int);
232int uturn_dmamem_alloc(void *, bus_size_t, bus_size_t, bus_size_t,
233    bus_dma_segment_t *, int, int *, int);
234void uturn_dmamem_free(void *, bus_dma_segment_t *, int);
235int uturn_dmamem_map(void *, bus_dma_segment_t *, int, size_t, void **, int);
236void uturn_dmamem_unmap(void *, void *, size_t);
237paddr_t uturn_dmamem_mmap(void *, bus_dma_segment_t *, int, off_t, int, int);
238
239static void uturn_iommu_enter(struct uturn_softc *, bus_addr_t, pa_space_t,
240    vaddr_t, paddr_t);
241static void uturn_iommu_remove(struct uturn_softc *, bus_addr_t, bus_size_t);
242
243struct uturn_map_state *uturn_iomap_create(int);
244void	uturn_iomap_destroy(struct uturn_map_state *);
245int	uturn_iomap_insert_page(struct uturn_map_state *, vaddr_t, paddr_t);
246bus_addr_t uturn_iomap_translate(struct uturn_map_state *, paddr_t);
247void	uturn_iomap_clear_pages(struct uturn_map_state *);
248
249static int uturn_iomap_load_map(struct uturn_softc *, bus_dmamap_t, int);
250
251const struct hppa_bus_dma_tag uturn_dmat = {
252	NULL,
253	uturn_dmamap_create, uturn_dmamap_destroy,
254	uturn_dmamap_load, uturn_dmamap_load_mbuf,
255	uturn_dmamap_load_uio, uturn_dmamap_load_raw,
256	uturn_dmamap_unload, uturn_dmamap_sync,
257
258	uturn_dmamem_alloc, uturn_dmamem_free, uturn_dmamem_map,
259	uturn_dmamem_unmap, uturn_dmamem_mmap
260};
261
262int
263uturnmatch(device_t parent, cfdata_t cf, void *aux)
264{
265	struct confargs *ca = aux;
266
267	/* there will be only one */
268	if (ca->ca_type.iodc_type != HPPA_TYPE_IOA ||
269	    ca->ca_type.iodc_sv_model != HPPA_IOA_UTURN)
270		return 0;
271
272	if (ca->ca_type.iodc_model == 0x58 &&
273	    ca->ca_type.iodc_revision >= 0x20)
274		return 0;
275
276	return 1;
277}
278
279void
280uturnattach(device_t parent, device_t self, void *aux)
281{
282	struct confargs *ca = aux, nca;
283	struct uturn_softc *sc = device_private(self);
284	bus_space_handle_t ioh;
285	volatile struct uturn_regs *r;
286	struct pglist pglist;
287	int iova_bits;
288	vaddr_t va;
289	psize_t size;
290	int i;
291
292	if (bus_space_map(ca->ca_iot, ca->ca_hpa, IOMOD_HPASIZE, 0, &ioh)) {
293		aprint_error(": can't map IO space\n");
294		return;
295	}
296
297	sc->sc_dv = self;
298	sc->sc_dmat = ca->ca_dmatag;
299	sc->sc_regs = r = bus_space_vaddr(ca->ca_iot, ioh);
300
301	aprint_normal(": %x-%x", r->io_io_low << 16, r->io_io_high << 16);
302	aprint_normal(": %x-%x", r->io_io_low_hv << 16, r->io_io_high_hv << 16);
303
304	aprint_normal(": %s rev %d\n",
305	    ca->ca_type.iodc_revision < 0x10 ? "U2" : "UTurn",
306	    ca->ca_type.iodc_revision & 0xf);
307
308	/*
309	 * Setup the iommu.
310	 */
311
312	/* XXX 28 bits gives us 256Mb of iova space */
313	/* Calculate based on %age of RAM */
314	iova_bits = 28;
315
316	/*
317	 * size is # of pdir entries (64bits) in bytes.  1 entry per IOVA
318	 * page.
319	 */
320	size = (1 << (iova_bits - UTURN_IOVP_SHIFT)) * sizeof(uint64_t);
321
322	/*
323	 * Chainid is the upper most bits of an IOVP used to determine which
324	 * TLB entry an IOVP will use.
325	 */
326	sc->sc_chainid_shift = iova_bits - UTURN_CHAINID_SHIFT;
327
328	/*
329	 * Allocate memory for I/O pagetables.  They need to be physically
330	 * contiguous.
331	 */
332
333	if (uvm_pglistalloc(size, 0, -1, PAGE_SIZE, 0, &pglist, 1, 0) != 0)
334		panic("%s: no memory", __func__);
335
336	va = (vaddr_t)VM_PAGE_TO_PHYS(TAILQ_FIRST(&pglist));
337	sc->sc_pdir = (int64_t *)va;
338
339	memset(sc->sc_pdir, 0, size);
340
341	r->io_chain_id_mask = UTURN_CHAINID_MASK << sc->sc_chainid_shift;
342	r->io_pdir_base = VM_PAGE_TO_PHYS(TAILQ_FIRST(&pglist));
343
344	r->io_tlb_entry_m = 0;
345	r->io_tlb_entry_l = 0;
346
347	/* for (i = UTURN_TLB_ENTRIES; i != 0; i--) { */
348	for (i = 0; i < UTURN_TLB_ENTRIES; i++) {
349		r->io_command =
350		    UTURN_CMD_TLB_DIRECT_WRITE | (i << sc->sc_chainid_shift);
351	}
352	/*
353	 * Go to "Virtual Mode"
354	 */
355	r->io_control = UTURN_VIRTUAL_MODE;
356
357	snprintf(sc->sc_mapname, sizeof(sc->sc_mapname), "%s_map",
358	    device_xname(sc->sc_dv));
359	sc->sc_map = extent_create(sc->sc_mapname, 0, (1 << iova_bits),
360	    0, 0, EX_NOWAIT);
361
362	sc->sc_dmatag = uturn_dmat;
363	sc->sc_dmatag._cookie = sc;
364
365	/*
366	 * U2/UTurn is actually a combination of an Upper Bus Converter (UBC)
367	 * and a Lower Bus Converter (LBC).  This driver attaches to the UBC;
368	 * the LBC isn't very interesting, so we skip it.  This is easy, since
369	 * it always is module 63, hence the MAXMODBUS - 1 below.
370	 */
371	nca = *ca;
372	nca.ca_hpabase = r->io_io_low << 16;
373	nca.ca_dmatag = &sc->sc_dmatag;
374	nca.ca_nmodules = MAXMODBUS - 1;
375	pdc_scanbus(self, &nca, uturn_callback);
376}
377
378static device_t
379uturn_callback(device_t self, struct confargs *ca)
380{
381
382	return config_found_sm_loc(self, "gedoens", NULL, ca, mbprint,
383	    mbsubmatch);
384}
385
386/*
387 * PDIR entry format (HP bit number)
388 *
389 * +-------+----------------+----------------------------------------------+
390 * |0     3|4             15|16                                          31|
391 * | PPN   | Virtual Index  |         Physical Page Number (PPN)           |
392 * | [0:3] |    [0:11]      |                 [4:19]                       |
393 * +-------+----------------+----------------------------------------------+
394 *
395 * +-----------------------+-----------------------------------------------+
396 * |0           19|20    24|   25   |       |       |      |  30   |   31  |
397 * |     PPN      |  Rsvd  | PH     |Update | Rsvd  |Lock  | Safe  | Valid |
398 * |    [20:39    |        | Enable |Enable |       |Enable| DMA   |       |
399 * +-----------------------+-----------------------------------------------+
400 *
401 */
402
403#define UTURN_PENTRY_PREFETCH	0x40
404#define UTURN_PENTRY_UPDATE	0x20
405#define UTURN_PENTRY_LOCK	0x04	/* eisa devices only */
406#define UTURN_PENTRY_SAFEDMA	0x02	/* use safe dma - for subcacheline */
407#define UTURN_PENTRY_VALID	0x01
408
409static void
410uturn_iommu_enter(struct uturn_softc *sc, bus_addr_t iova, pa_space_t sp,
411    vaddr_t va, paddr_t pa)
412{
413	uint64_t pdir_entry;
414	uint64_t *pdirp;
415	uint32_t ci; /* coherent index */
416
417	pdirp = &sc->sc_pdir[UTURN_IOVA_INDEX(iova)];
418
419	DPRINTF(("%s: iova %lx pdir %p pdirp %p pa %lx", __func__, iova,
420	    sc->sc_pdir, pdirp, pa));
421
422	ci = lci(HPPA_SID_KERNEL, va);
423
424	/* setup hints, etc */
425	pdir_entry = (UTURN_PENTRY_LOCK | UTURN_PENTRY_SAFEDMA |
426	     UTURN_PENTRY_VALID);
427
428	/*
429	 * bottom 36 bits of pa map directly into entry to form PPN[4:39]
430	 * leaving last 12 bits for hints, etc.
431	 */
432	pdir_entry |= (pa & ~PAGE_MASK);
433
434	/* mask off top PPN bits */
435	pdir_entry &= 0x0000ffffffffffffUL;
436
437	/* insert the virtual index bits */
438	pdir_entry |= (((uint64_t)ci >> 12) << 48);
439
440	/* PPN[0:3] of the 40bit PPN go in entry[0:3] */
441	pdir_entry |= ((((uint64_t)pa & 0x000f000000000000UL) >> 48) << 60);
442
443	*pdirp = pdir_entry;
444
445	DPRINTF((": pdir_entry %llx\n", pdir_entry));
446
447	/*
448	 * We could use PDC_MODEL_CAPABILITIES here
449	 */
450 	fdcache(HPPA_SID_KERNEL, (vaddr_t)pdirp, sizeof(uint64_t));
451}
452
453
454static void
455uturn_iommu_remove(struct uturn_softc *sc, bus_addr_t iova, bus_size_t size)
456{
457	uint32_t chain_size = 1 << sc->sc_chainid_shift;
458	bus_size_t len;
459
460	KASSERT((iova & PAGE_MASK) == 0);
461	KASSERT((size & PAGE_MASK) == 0);
462
463	DPRINTF(("%s: sc %p iova %lx size %lx\n", __func__, sc, iova, size));
464	len = size;
465	while (len != 0) {
466		uint64_t *pdirp = &sc->sc_pdir[UTURN_IOVA_INDEX(iova)];
467
468		/* XXX Just the valid bit??? */
469		*pdirp = 0;
470
471		/*
472		* We could use PDC_MODEL_CAPABILITIES here
473		*/
474	 	fdcache(HPPA_SID_KERNEL, (vaddr_t)pdirp, sizeof(uint64_t));
475
476		iova += PAGE_SIZE;
477		len -= PAGE_SIZE;
478	}
479
480	len = size + chain_size;
481
482	while (len > chain_size) {
483		sc->sc_regs->io_command = UTURN_CMD_TLB_PURGE | iova;
484		iova += chain_size;
485		len -= chain_size;
486	}
487}
488
489int
490uturn_dmamap_create(void *v, bus_size_t size, int nsegments,
491    bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamap)
492{
493	struct uturn_softc *sc = v;
494	bus_dmamap_t map;
495	struct uturn_map_state *ums;
496	int error;
497
498	error = bus_dmamap_create(sc->sc_dmat, size, nsegments, maxsegsz,
499	    boundary, flags, &map);
500	if (error)
501		return (error);
502
503	ums = uturn_iomap_create(atop(round_page(size)));
504	if (ums == NULL) {
505		bus_dmamap_destroy(sc->sc_dmat, map);
506		return (ENOMEM);
507	}
508
509	ums->ums_sc = sc;
510	map->_dm_cookie = ums;
511	*dmamap = map;
512
513	return (0);
514}
515
516void
517uturn_dmamap_destroy(void *v, bus_dmamap_t map)
518{
519	struct uturn_softc *sc = v;
520
521	/*
522	 * The specification (man page) requires a loaded
523	 * map to be unloaded before it is destroyed.
524	 */
525	if (map->dm_nsegs)
526		uturn_dmamap_unload(sc, map);
527
528	if (map->_dm_cookie)
529		uturn_iomap_destroy(map->_dm_cookie);
530	map->_dm_cookie = NULL;
531
532	bus_dmamap_destroy(sc->sc_dmat, map);
533}
534
535static int
536uturn_iomap_load_map(struct uturn_softc *sc, bus_dmamap_t map, int flags)
537{
538	struct uturn_map_state *ums = map->_dm_cookie;
539	struct uturn_page_map *upm = &ums->ums_map;
540	struct uturn_page_entry *e;
541	int err, seg, s;
542	paddr_t pa, paend;
543	vaddr_t va;
544	bus_size_t sgsize;
545	bus_size_t align, boundary;
546	u_long iovaddr;
547	bus_addr_t iova;
548	int i;
549
550	/* XXX */
551	boundary = map->_dm_boundary;
552	align = PAGE_SIZE;
553
554	uturn_iomap_clear_pages(ums);
555
556	for (seg = 0; seg < map->dm_nsegs; seg++) {
557		struct hppa_bus_dma_segment *ds = &map->dm_segs[seg];
558
559		paend = round_page(ds->ds_addr + ds->ds_len);
560		for (pa = trunc_page(ds->ds_addr), va = trunc_page(ds->_ds_va);
561		     pa < paend; pa += PAGE_SIZE, va += PAGE_SIZE) {
562			err = uturn_iomap_insert_page(ums, va, pa);
563			if (err) {
564				printf("iomap insert error: %d for "
565				    "va 0x%lx pa 0x%lx\n", err, va, pa);
566				bus_dmamap_unload(sc->sc_dmat, map);
567				uturn_iomap_clear_pages(ums);
568			}
569		}
570	}
571
572	sgsize = ums->ums_map.upm_pagecnt * PAGE_SIZE;
573	/* XXXNH */
574	s = splhigh();
575	err = extent_alloc(sc->sc_map, sgsize, align, boundary,
576	    EX_NOWAIT | EX_BOUNDZERO, &iovaddr);
577	splx(s);
578	if (err)
579		return (err);
580
581	ums->ums_iovastart = iovaddr;
582	ums->ums_iovasize = sgsize;
583
584	iova = iovaddr;
585	for (i = 0, e = upm->upm_map; i < upm->upm_pagecnt; ++i, ++e) {
586		e->upe_iova = iova;
587		uturn_iommu_enter(sc, e->upe_iova, HPPA_SID_KERNEL, e->upe_va,
588		    e->upe_pa);
589		iova += PAGE_SIZE;
590	}
591
592	for (seg = 0; seg < map->dm_nsegs; seg++) {
593		struct hppa_bus_dma_segment *ds = &map->dm_segs[seg];
594		ds->ds_addr = uturn_iomap_translate(ums, ds->ds_addr);
595	}
596
597	return (0);
598}
599
600int
601uturn_dmamap_load(void *v, bus_dmamap_t map, void *addr, bus_size_t size,
602    struct proc *p, int flags)
603{
604	struct uturn_softc *sc = v;
605	int err;
606
607	err = bus_dmamap_load(sc->sc_dmat, map, addr, size, p, flags);
608	if (err)
609		return (err);
610
611	return uturn_iomap_load_map(sc, map, flags);
612}
613
614int
615uturn_dmamap_load_mbuf(void *v, bus_dmamap_t map, struct mbuf *m, int flags)
616{
617	struct uturn_softc *sc = v;
618	int err;
619
620	err = bus_dmamap_load_mbuf(sc->sc_dmat, map, m, flags);
621	if (err)
622		return (err);
623
624	return uturn_iomap_load_map(sc, map, flags);
625}
626
627int
628uturn_dmamap_load_uio(void *v, bus_dmamap_t map, struct uio *uio, int flags)
629{
630	struct uturn_softc *sc = v;
631
632	printf("load_uio\n");
633
634	return (bus_dmamap_load_uio(sc->sc_dmat, map, uio, flags));
635}
636
637int
638uturn_dmamap_load_raw(void *v, bus_dmamap_t map, bus_dma_segment_t *segs,
639    int nsegs, bus_size_t size, int flags)
640{
641	struct uturn_softc *sc = v;
642
643	printf("load_raw\n");
644
645	return (bus_dmamap_load_raw(sc->sc_dmat, map, segs, nsegs, size, flags));
646}
647
648void
649uturn_dmamap_unload(void *v, bus_dmamap_t map)
650{
651	struct uturn_softc *sc = v;
652	struct uturn_map_state *ums = map->_dm_cookie;
653	struct uturn_page_map *upm = &ums->ums_map;
654	struct uturn_page_entry *e;
655	int err, i, s;
656
657	/* Remove the IOMMU entries. */
658	for (i = 0, e = upm->upm_map; i < upm->upm_pagecnt; ++i, ++e)
659		uturn_iommu_remove(sc, e->upe_iova, PAGE_SIZE);
660
661	/* Clear the iomap. */
662	uturn_iomap_clear_pages(ums);
663
664	bus_dmamap_unload(sc->sc_dmat, map);
665
666	s = splhigh();
667	err = extent_free(sc->sc_map, ums->ums_iovastart,
668	    ums->ums_iovasize, EX_NOWAIT);
669	ums->ums_iovastart = 0;
670	ums->ums_iovasize = 0;
671	splx(s);
672	if (err)
673		printf("warning: %ld of IOVA space lost\n", ums->ums_iovasize);
674}
675
676void
677uturn_dmamap_sync(void *v, bus_dmamap_t map, bus_addr_t off,
678    bus_size_t len, int ops)
679{
680	/* Nothing to do; DMA is cache-coherent. */
681}
682
683int
684uturn_dmamem_alloc(void *v, bus_size_t size, bus_size_t alignment,
685    bus_size_t boundary, bus_dma_segment_t *segs,
686    int nsegs, int *rsegs, int flags)
687{
688	struct uturn_softc *sc = v;
689
690	return (bus_dmamem_alloc(sc->sc_dmat, size, alignment, boundary,
691	    segs, nsegs, rsegs, flags));
692}
693
694void
695uturn_dmamem_free(void *v, bus_dma_segment_t *segs, int nsegs)
696{
697	struct uturn_softc *sc = v;
698
699	bus_dmamem_free(sc->sc_dmat, segs, nsegs);
700}
701
702int
703uturn_dmamem_map(void *v, bus_dma_segment_t *segs, int nsegs, size_t size,
704    void **kvap, int flags)
705{
706	struct uturn_softc *sc = v;
707
708	return (bus_dmamem_map(sc->sc_dmat, segs, nsegs, size, kvap, flags));
709}
710
711void
712uturn_dmamem_unmap(void *v, void *kva, size_t size)
713{
714	struct uturn_softc *sc = v;
715
716	bus_dmamem_unmap(sc->sc_dmat, kva, size);
717}
718
719paddr_t
720uturn_dmamem_mmap(void *v, bus_dma_segment_t *segs, int nsegs, off_t off,
721    int prot, int flags)
722{
723	struct uturn_softc *sc = v;
724
725	return (bus_dmamem_mmap(sc->sc_dmat, segs, nsegs, off, prot, flags));
726}
727
728/*
729 * Utility function used by splay tree to order page entries by pa.
730 */
731static inline int
732upe_compare(struct uturn_page_entry *a, struct uturn_page_entry *b)
733{
734	return ((a->upe_pa > b->upe_pa) ? 1 :
735		(a->upe_pa < b->upe_pa) ? -1 : 0);
736}
737
738SPLAY_PROTOTYPE(uturn_page_tree, uturn_page_entry, upe_node, upe_compare);
739
740SPLAY_GENERATE(uturn_page_tree, uturn_page_entry, upe_node, upe_compare);
741
742/*
743 * Create a new iomap.
744 */
745struct uturn_map_state *
746uturn_iomap_create(int n)
747{
748	struct uturn_map_state *ums;
749
750	/* Safety for heavily fragmented data, such as mbufs */
751	n += 4;
752	if (n < 16)
753		n = 16;
754
755	ums = malloc(sizeof(*ums) + (n - 1) * sizeof(ums->ums_map.upm_map[0]),
756	    M_DEVBUF, M_NOWAIT | M_ZERO);
757	if (ums == NULL)
758		return (NULL);
759
760	/* Initialize the map. */
761	ums->ums_map.upm_maxpage = n;
762	SPLAY_INIT(&ums->ums_map.upm_tree);
763
764	return (ums);
765}
766
767/*
768 * Destroy an iomap.
769 */
770void
771uturn_iomap_destroy(struct uturn_map_state *ums)
772{
773	KASSERT(ums->ums_map.upm_pagecnt == 0);
774
775	free(ums, M_DEVBUF);
776}
777
778/*
779 * Insert a pa entry in the iomap.
780 */
781int
782uturn_iomap_insert_page(struct uturn_map_state *ums, vaddr_t va, paddr_t pa)
783{
784	struct uturn_page_map *upm = &ums->ums_map;
785	struct uturn_page_entry *e;
786
787	if (upm->upm_pagecnt >= upm->upm_maxpage) {
788		struct uturn_page_entry upe;
789
790		upe.upe_pa = pa;
791		if (SPLAY_FIND(uturn_page_tree, &upm->upm_tree, &upe))
792			return (0);
793
794		return (ENOMEM);
795	}
796
797	e = &upm->upm_map[upm->upm_pagecnt];
798
799	e->upe_pa = pa;
800	e->upe_va = va;
801	e->upe_iova = 0;
802
803	e = SPLAY_INSERT(uturn_page_tree, &upm->upm_tree, e);
804
805	/* Duplicates are okay, but only count them once. */
806	if (e)
807		return (0);
808
809	++upm->upm_pagecnt;
810
811	return (0);
812}
813
814/*
815 * Translate a physical address (pa) into a IOVA address.
816 */
817bus_addr_t
818uturn_iomap_translate(struct uturn_map_state *ums, paddr_t pa)
819{
820	struct uturn_page_map *upm = &ums->ums_map;
821	struct uturn_page_entry *e;
822	struct uturn_page_entry pe;
823	paddr_t offset = pa & PAGE_MASK;
824
825	pe.upe_pa = trunc_page(pa);
826
827	e = SPLAY_FIND(uturn_page_tree, &upm->upm_tree, &pe);
828
829	if (e == NULL) {
830		panic("couldn't find pa %lx\n", pa);
831		return 0;
832	}
833
834	return (e->upe_iova | offset);
835}
836
837/*
838 * Clear the iomap table and tree.
839 */
840void
841uturn_iomap_clear_pages(struct uturn_map_state *ums)
842{
843	ums->ums_map.upm_pagecnt = 0;
844	SPLAY_INIT(&ums->ums_map.upm_tree);
845}
846