Home | History | Annotate | Line # | Download | only in dev
astro.c revision 1.1
      1  1.1  skrll /*	$NetBSD: astro.c,v 1.1 2014/02/24 07:23:42 skrll Exp $	*/
      2  1.1  skrll 
      3  1.1  skrll /*	$OpenBSD: astro.c,v 1.8 2007/10/06 23:50:54 krw Exp $	*/
      4  1.1  skrll 
      5  1.1  skrll /*
      6  1.1  skrll  * Copyright (c) 2007 Mark Kettenis
      7  1.1  skrll  *
      8  1.1  skrll  * Permission to use, copy, modify, and distribute this software for any
      9  1.1  skrll  * purpose with or without fee is hereby granted, provided that the above
     10  1.1  skrll  * copyright notice and this permission notice appear in all copies.
     11  1.1  skrll  *
     12  1.1  skrll  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     13  1.1  skrll  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     14  1.1  skrll  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     15  1.1  skrll  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     16  1.1  skrll  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     17  1.1  skrll  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     18  1.1  skrll  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     19  1.1  skrll  */
     20  1.1  skrll 
     21  1.1  skrll #include <sys/param.h>
     22  1.1  skrll #include <sys/systm.h>
     23  1.1  skrll #include <sys/device.h>
     24  1.1  skrll #include <sys/extent.h>
     25  1.1  skrll #include <sys/malloc.h>
     26  1.1  skrll #include <sys/reboot.h>
     27  1.1  skrll #include <sys/tree.h>
     28  1.1  skrll 
     29  1.1  skrll #include <uvm/uvm.h>
     30  1.1  skrll 
     31  1.1  skrll #include <machine/iomod.h>
     32  1.1  skrll #include <machine/autoconf.h>
     33  1.1  skrll #include <machine/pdc.h>
     34  1.1  skrll #include <machine/endian.h>
     35  1.1  skrll 
     36  1.1  skrll #include <hppa/dev/cpudevs.h>
     37  1.1  skrll #include <hppa/hppa/machdep.h>
     38  1.1  skrll 
     39  1.1  skrll struct astro_regs {
     40  1.1  skrll 	uint32_t	rid;
     41  1.1  skrll 	uint32_t	pad0000;
     42  1.1  skrll 	uint32_t	ioc_ctrl;
     43  1.1  skrll 	uint32_t	pad0008;
     44  1.1  skrll 	uint8_t		resv1[0x0300 - 0x0010];
     45  1.1  skrll 	uint64_t	lmmio_direct0_base;
     46  1.1  skrll 	uint64_t	lmmio_direct0_mask;
     47  1.1  skrll 	uint64_t	lmmio_direct0_route;
     48  1.1  skrll 	uint64_t	lmmio_direct1_base;
     49  1.1  skrll 	uint64_t	lmmio_direct1_mask;
     50  1.1  skrll 	uint64_t	lmmio_direct1_route;
     51  1.1  skrll 	uint64_t	lmmio_direct2_base;
     52  1.1  skrll 	uint64_t	lmmio_direct2_mask;
     53  1.1  skrll 	uint64_t	lmmio_direct2_route;
     54  1.1  skrll 	uint64_t	lmmio_direct3_base;
     55  1.1  skrll 	uint64_t	lmmio_direct3_mask;
     56  1.1  skrll 	uint64_t	lmmio_direct3_route;
     57  1.1  skrll 	uint64_t	lmmio_dist_base;
     58  1.1  skrll 	uint64_t	lmmio_dist_mask;
     59  1.1  skrll 	uint64_t	lmmio_dist_route;
     60  1.1  skrll 	uint64_t	gmmio_dist_base;
     61  1.1  skrll 	uint64_t	gmmio_dist_mask;
     62  1.1  skrll 	uint64_t	gmmio_dist_route;
     63  1.1  skrll 	uint64_t	ios_dist_base;
     64  1.1  skrll 	uint64_t	ios_dist_mask;
     65  1.1  skrll 	uint64_t	ios_dist_route;
     66  1.1  skrll 	uint8_t		resv2[0x03c0 - 0x03a8];
     67  1.1  skrll 	uint64_t	ios_direct_base;
     68  1.1  skrll 	uint64_t	ios_direct_mask;
     69  1.1  skrll 	uint64_t	ios_direct_route;
     70  1.1  skrll 	uint8_t		resv3[0x22000 - 0x03d8];
     71  1.1  skrll 	uint64_t	func_id;
     72  1.1  skrll 	uint64_t	func_class;
     73  1.1  skrll 	uint8_t		resv4[0x22040 - 0x22010];
     74  1.1  skrll 	uint64_t	rope_config;
     75  1.1  skrll 	uint8_t		resv5[0x22050 - 0x22048];
     76  1.1  skrll 	uint64_t	rope_debug;
     77  1.1  skrll 	uint8_t		resv6[0x22200 - 0x22058];
     78  1.1  skrll 	uint64_t	rope0_control;
     79  1.1  skrll 	uint64_t	rope1_control;
     80  1.1  skrll 	uint64_t	rope2_control;
     81  1.1  skrll 	uint64_t	rope3_control;
     82  1.1  skrll 	uint64_t	rope4_control;
     83  1.1  skrll 	uint64_t	rope5_control;
     84  1.1  skrll 	uint64_t	rope6_control;
     85  1.1  skrll 	uint64_t	rope7_control;
     86  1.1  skrll 	uint8_t		resv7[0x22300 - 0x22240];
     87  1.1  skrll 	uint32_t	tlb_ibase;
     88  1.1  skrll 	uint32_t	pad22300;
     89  1.1  skrll 	uint32_t	tlb_imask;
     90  1.1  skrll 	uint32_t	pad22308;
     91  1.1  skrll 	uint32_t	tlb_pcom;
     92  1.1  skrll 	uint32_t	pad22310;
     93  1.1  skrll 	uint32_t	tlb_tcnfg;
     94  1.1  skrll 	uint32_t	pad22318;
     95  1.1  skrll 	uint64_t	tlb_pdir_base;
     96  1.1  skrll };
     97  1.1  skrll 
     98  1.1  skrll #define ASTRO_IOC_CTRL_TE	0x0001	/* TOC Enable */
     99  1.1  skrll #define ASTRO_IOC_CTRL_CE	0x0002	/* Coalesce Enable */
    100  1.1  skrll #define ASTRO_IOC_CTRL_DE	0x0004	/* Dillon Enable */
    101  1.1  skrll #define ASTRO_IOC_CTRL_IE	0x0008	/* IOS Enable */
    102  1.1  skrll #define ASTRO_IOC_CTRL_OS	0x0010	/* Outbound Synchronous */
    103  1.1  skrll #define ASTRO_IOC_CTRL_IS	0x0020	/* Inbound Synchronous */
    104  1.1  skrll #define ASTRO_IOC_CTRL_RC	0x0040	/* Read Current Enable */
    105  1.1  skrll #define ASTRO_IOC_CTRL_L0	0x0080	/* 0-length Read Enable */
    106  1.1  skrll #define ASTRO_IOC_CTRL_RM	0x0100	/* Real Mode */
    107  1.1  skrll #define ASTRO_IOC_CTRL_NC	0x0200	/* Non-coherent Mode */
    108  1.1  skrll #define ASTRO_IOC_CTRL_ID	0x0400	/* Interrupt Disable */
    109  1.1  skrll #define ASTRO_IOC_CTRL_D4	0x0800	/* Disable 4-byte Coalescing */
    110  1.1  skrll #define ASTRO_IOC_CTRL_CC	0x1000	/* Increase Coalescing counter value */
    111  1.1  skrll #define ASTRO_IOC_CTRL_DD	0x2000	/* Disable distr. range coalescing */
    112  1.1  skrll #define ASTRO_IOC_CTRL_DC	0x4000	/* Disable the coalescing counter */
    113  1.1  skrll 
    114  1.1  skrll #define IOTTE_V		0x8000000000000000LL	/* Entry valid */
    115  1.1  skrll #define IOTTE_PAMASK	0x000000fffffff000LL
    116  1.1  skrll #define IOTTE_CI	0x00000000000000ffLL	/* Coherent index */
    117  1.1  skrll 
    118  1.1  skrll struct astro_softc {
    119  1.1  skrll 	device_t sc_dv;
    120  1.1  skrll 
    121  1.1  skrll 	bus_dma_tag_t sc_dmat;
    122  1.1  skrll 	struct astro_regs volatile *sc_regs;
    123  1.1  skrll 	uint64_t *sc_pdir;
    124  1.1  skrll 
    125  1.1  skrll 	char sc_dvmamapname[20];
    126  1.1  skrll 	struct extent *sc_dvmamap;
    127  1.1  skrll 	struct hppa_bus_dma_tag sc_dmatag;
    128  1.1  skrll };
    129  1.1  skrll 
    130  1.1  skrll /*
    131  1.1  skrll  * per-map DVMA page table
    132  1.1  skrll  */
    133  1.1  skrll struct iommu_page_entry {
    134  1.1  skrll 	SPLAY_ENTRY(iommu_page_entry) ipe_node;
    135  1.1  skrll 	paddr_t	ipe_pa;
    136  1.1  skrll 	vaddr_t	ipe_va;
    137  1.1  skrll 	bus_addr_t ipe_dva;
    138  1.1  skrll };
    139  1.1  skrll 
    140  1.1  skrll struct iommu_page_map {
    141  1.1  skrll 	SPLAY_HEAD(iommu_page_tree, iommu_page_entry) ipm_tree;
    142  1.1  skrll 	int ipm_maxpage;	/* Size of allocated page map */
    143  1.1  skrll 	int ipm_pagecnt;	/* Number of entries in use */
    144  1.1  skrll 	struct iommu_page_entry	ipm_map[1];
    145  1.1  skrll };
    146  1.1  skrll 
    147  1.1  skrll /*
    148  1.1  skrll  * per-map IOMMU state
    149  1.1  skrll  */
    150  1.1  skrll struct iommu_map_state {
    151  1.1  skrll 	struct astro_softc *ims_sc;
    152  1.1  skrll 	bus_addr_t ims_dvmastart;
    153  1.1  skrll 	bus_size_t ims_dvmasize;
    154  1.1  skrll 	struct iommu_page_map ims_map;	/* map must be last (array at end) */
    155  1.1  skrll };
    156  1.1  skrll 
    157  1.1  skrll int	astro_match(device_t, cfdata_t, void *);
    158  1.1  skrll void	astro_attach(device_t, device_t, void *);
    159  1.1  skrll static device_t astro_callback(device_t self, struct confargs *ca);
    160  1.1  skrll 
    161  1.1  skrll CFATTACH_DECL_NEW(astro, sizeof(struct astro_softc),
    162  1.1  skrll     astro_match, astro_attach, NULL, NULL);
    163  1.1  skrll 
    164  1.1  skrll extern struct cfdriver astro_cd;
    165  1.1  skrll 
    166  1.1  skrll int	iommu_dvmamap_create(void *, bus_size_t, int, bus_size_t, bus_size_t,
    167  1.1  skrll 	    int, bus_dmamap_t *);
    168  1.1  skrll void	iommu_dvmamap_destroy(void *, bus_dmamap_t);
    169  1.1  skrll int	iommu_dvmamap_load(void *, bus_dmamap_t, void *, bus_size_t,
    170  1.1  skrll 	    struct proc *, int);
    171  1.1  skrll int	iommu_dvmamap_load_mbuf(void *, bus_dmamap_t, struct mbuf *, int);
    172  1.1  skrll int	iommu_dvmamap_load_uio(void *, bus_dmamap_t, struct uio *, int);
    173  1.1  skrll int	iommu_dvmamap_load_raw(void *, bus_dmamap_t, bus_dma_segment_t *,
    174  1.1  skrll 	    int, bus_size_t, int);
    175  1.1  skrll void	iommu_dvmamap_unload(void *, bus_dmamap_t);
    176  1.1  skrll void	iommu_dvmamap_sync(void *, bus_dmamap_t, bus_addr_t, bus_size_t, int);
    177  1.1  skrll int	iommu_dvmamem_alloc(void *, bus_size_t, bus_size_t, bus_size_t,
    178  1.1  skrll 	    bus_dma_segment_t *, int, int *, int);
    179  1.1  skrll void	iommu_dvmamem_free(void *, bus_dma_segment_t *, int);
    180  1.1  skrll int	iommu_dvmamem_map(void *, bus_dma_segment_t *, int, size_t,
    181  1.1  skrll 	    void **, int);
    182  1.1  skrll void	iommu_dvmamem_unmap(void *, void *, size_t);
    183  1.1  skrll paddr_t	iommu_dvmamem_mmap(void *, bus_dma_segment_t *, int, off_t, int, int);
    184  1.1  skrll 
    185  1.1  skrll void	iommu_enter(struct astro_softc *, bus_addr_t, paddr_t, vaddr_t, int);
    186  1.1  skrll void	iommu_remove(struct astro_softc *, bus_addr_t);
    187  1.1  skrll 
    188  1.1  skrll struct iommu_map_state *iommu_iomap_create(int);
    189  1.1  skrll void	iommu_iomap_destroy(struct iommu_map_state *);
    190  1.1  skrll int	iommu_iomap_insert_page(struct iommu_map_state *, vaddr_t, paddr_t);
    191  1.1  skrll bus_addr_t iommu_iomap_translate(struct iommu_map_state *, paddr_t);
    192  1.1  skrll void	iommu_iomap_clear_pages(struct iommu_map_state *);
    193  1.1  skrll 
    194  1.1  skrll static int iommu_iomap_load_map(struct astro_softc *, bus_dmamap_t, int);
    195  1.1  skrll 
    196  1.1  skrll const struct hppa_bus_dma_tag astro_dmat = {
    197  1.1  skrll 	NULL,
    198  1.1  skrll 	iommu_dvmamap_create, iommu_dvmamap_destroy,
    199  1.1  skrll 	iommu_dvmamap_load, iommu_dvmamap_load_mbuf,
    200  1.1  skrll 	iommu_dvmamap_load_uio, iommu_dvmamap_load_raw,
    201  1.1  skrll 	iommu_dvmamap_unload, iommu_dvmamap_sync,
    202  1.1  skrll 
    203  1.1  skrll 	iommu_dvmamem_alloc, iommu_dvmamem_free, iommu_dvmamem_map,
    204  1.1  skrll 	iommu_dvmamem_unmap, iommu_dvmamem_mmap
    205  1.1  skrll };
    206  1.1  skrll 
    207  1.1  skrll int
    208  1.1  skrll astro_match(device_t parent, cfdata_t cf, void *aux)
    209  1.1  skrll {
    210  1.1  skrll 	struct confargs *ca = aux;
    211  1.1  skrll 
    212  1.1  skrll 	/* Astro is a U-Turn variant. */
    213  1.1  skrll 	if (ca->ca_type.iodc_type != HPPA_TYPE_IOA ||
    214  1.1  skrll 	    ca->ca_type.iodc_sv_model != HPPA_IOA_UTURN)
    215  1.1  skrll 		return 0;
    216  1.1  skrll 
    217  1.1  skrll 	if (ca->ca_type.iodc_model == 0x58 &&
    218  1.1  skrll 	    ca->ca_type.iodc_revision >= 0x20)
    219  1.1  skrll 		return 1;
    220  1.1  skrll 
    221  1.1  skrll 	return 0;
    222  1.1  skrll }
    223  1.1  skrll 
    224  1.1  skrll void
    225  1.1  skrll astro_attach(device_t parent, device_t self, void *aux)
    226  1.1  skrll {
    227  1.1  skrll 	struct confargs *ca = aux, nca;
    228  1.1  skrll 	struct astro_softc *sc = device_private(self);
    229  1.1  skrll 	volatile struct astro_regs *r;
    230  1.1  skrll 	bus_space_handle_t ioh;
    231  1.1  skrll 	uint32_t rid, ioc_ctrl;
    232  1.1  skrll 	psize_t size;
    233  1.1  skrll 	vaddr_t va;
    234  1.1  skrll 	paddr_t pa;
    235  1.1  skrll 	void *p;
    236  1.1  skrll 	struct vm_page *m;
    237  1.1  skrll 	struct pglist mlist;
    238  1.1  skrll 	int iova_bits;
    239  1.1  skrll 	int pagezero_cookie;
    240  1.1  skrll 
    241  1.1  skrll 	sc->sc_dv = self;
    242  1.1  skrll 	sc->sc_dmat = ca->ca_dmatag;
    243  1.1  skrll 	if (bus_space_map(ca->ca_iot, ca->ca_hpa, sizeof(struct astro_regs),
    244  1.1  skrll 	    0, &ioh)) {
    245  1.1  skrll 		aprint_error(": can't map IO space\n");
    246  1.1  skrll 		return;
    247  1.1  skrll 	}
    248  1.1  skrll 	p = bus_space_vaddr(ca->ca_iot, ioh);
    249  1.1  skrll 	sc->sc_regs = r = p;
    250  1.1  skrll 	rid = le32toh(r->rid);
    251  1.1  skrll 	aprint_normal(": Astro rev %d.%d\n", (rid & 7) + 1, (rid >> 3) & 3);
    252  1.1  skrll 
    253  1.1  skrll 	ioc_ctrl = le32toh(r->ioc_ctrl);
    254  1.1  skrll 	ioc_ctrl &= ~ASTRO_IOC_CTRL_CE;
    255  1.1  skrll 	ioc_ctrl &= ~ASTRO_IOC_CTRL_RM;
    256  1.1  skrll 	ioc_ctrl &= ~ASTRO_IOC_CTRL_NC;
    257  1.1  skrll 	r->ioc_ctrl = htole32(ioc_ctrl);
    258  1.1  skrll 
    259  1.1  skrll 	/*
    260  1.1  skrll 	 * Setup the iommu.
    261  1.1  skrll 	 */
    262  1.1  skrll 
    263  1.1  skrll 	/* XXX This gives us 256MB of iova space. */
    264  1.1  skrll 	iova_bits = 28;
    265  1.1  skrll 
    266  1.1  skrll 	r->tlb_ibase = htole32(0);
    267  1.1  skrll 	r->tlb_imask = htole32(0xffffffff << iova_bits);
    268  1.1  skrll 
    269  1.1  skrll 	/* Page size is 4K. */
    270  1.1  skrll 	r->tlb_tcnfg = htole32(0);
    271  1.1  skrll 
    272  1.1  skrll 	/* Flush TLB. */
    273  1.1  skrll 	r->tlb_pcom = htole32(31);
    274  1.1  skrll 
    275  1.1  skrll 	/*
    276  1.1  skrll 	 * Allocate memory for I/O pagetables.  They need to be physically
    277  1.1  skrll 	 * contiguous.
    278  1.1  skrll 	 */
    279  1.1  skrll 
    280  1.1  skrll 	size = (1 << (iova_bits - PAGE_SHIFT)) * sizeof(uint64_t);
    281  1.1  skrll 	TAILQ_INIT(&mlist);
    282  1.1  skrll 	if (uvm_pglistalloc(size, 0, -1, PAGE_SIZE, 0, &mlist, 1, 0) != 0) {
    283  1.1  skrll 		aprint_error(": can't allocate PDIR\n");
    284  1.1  skrll 		return;
    285  1.1  skrll 	}
    286  1.1  skrll 
    287  1.1  skrll 	va = uvm_km_alloc(kernel_map, size, 0, UVM_KMF_VAONLY | UVM_KMF_NOWAIT);
    288  1.1  skrll 
    289  1.1  skrll 	if (va == 0) {
    290  1.1  skrll 		aprint_error(": can't map PDIR\n");
    291  1.1  skrll 		return;
    292  1.1  skrll 	}
    293  1.1  skrll 	sc->sc_pdir = (uint64_t *)va;
    294  1.1  skrll 
    295  1.1  skrll 	m = TAILQ_FIRST(&mlist);
    296  1.1  skrll 	r->tlb_pdir_base = htole64(VM_PAGE_TO_PHYS(m));
    297  1.1  skrll 
    298  1.1  skrll 	/* Map the pages. */
    299  1.1  skrll 	for (; m != NULL; m = TAILQ_NEXT(m, pageq.queue)) {
    300  1.1  skrll 		pa = VM_PAGE_TO_PHYS(m);
    301  1.1  skrll 		pmap_enter(pmap_kernel(), va, pa,
    302  1.1  skrll 		    VM_PROT_READ|VM_PROT_WRITE, PMAP_WIRED);
    303  1.1  skrll 		va += PAGE_SIZE;
    304  1.1  skrll 	}
    305  1.1  skrll 	pmap_update(pmap_kernel());
    306  1.1  skrll 	memset(sc->sc_pdir, 0, size);
    307  1.1  skrll 
    308  1.1  skrll 	/*
    309  1.1  skrll 	 * The PDC might have set up some devices to do DMA.  It will do
    310  1.1  skrll 	 * this for the onboard USB controller if an USB keyboard is used
    311  1.1  skrll 	 * for console input.  In that case, bad things will happen if we
    312  1.1  skrll 	 * enable iova space.  So reset the PDC devices before we do that.
    313  1.1  skrll 	 * Don't do this if we're using a serial console though, since it
    314  1.1  skrll 	 * will stop working if we do.  This is fine since the serial port
    315  1.1  skrll 	 * doesn't do DMA.
    316  1.1  skrll 	 */
    317  1.1  skrll 	pagezero_cookie = hppa_pagezero_map();
    318  1.1  skrll 	if (PAGE0->mem_cons.pz_class != PCL_DUPLEX)
    319  1.1  skrll 		pdcproc_ioreset();
    320  1.1  skrll 	hppa_pagezero_unmap(pagezero_cookie);
    321  1.1  skrll 
    322  1.1  skrll 	/* Enable iova space. */
    323  1.1  skrll 	r->tlb_ibase = htole32(1);
    324  1.1  skrll 
    325  1.1  skrll 	/*
    326  1.1  skrll 	 * Now all the hardware's working we need to allocate a dvma map.
    327  1.1  skrll 	 */
    328  1.1  skrll 	snprintf(sc->sc_dvmamapname, sizeof(sc->sc_dvmamapname),
    329  1.1  skrll 	    "%s_dvma", device_xname(sc->sc_dv));
    330  1.1  skrll 	sc->sc_dvmamap = extent_create(sc->sc_dvmamapname, 0, (1 << iova_bits),
    331  1.1  skrll 	    0, 0, EX_NOWAIT);
    332  1.1  skrll 
    333  1.1  skrll 	sc->sc_dmatag = astro_dmat;
    334  1.1  skrll 	sc->sc_dmatag._cookie = sc;
    335  1.1  skrll 
    336  1.1  skrll 	nca = *ca;	/* clone from us */
    337  1.1  skrll 	nca.ca_dmatag = &sc->sc_dmatag;
    338  1.1  skrll 	nca.ca_hpabase = IOMOD_IO_IO_LOW(p);
    339  1.1  skrll 	nca.ca_nmodules = MAXMODBUS;
    340  1.1  skrll 	pdc_scanbus(self, &nca, astro_callback);
    341  1.1  skrll }
    342  1.1  skrll 
    343  1.1  skrll static device_t
    344  1.1  skrll astro_callback(device_t self, struct confargs *ca)
    345  1.1  skrll {
    346  1.1  skrll 
    347  1.1  skrll 	return config_found_sm_loc(self, "gedoens", NULL, ca, mbprint, mbsubmatch);
    348  1.1  skrll }
    349  1.1  skrll 
    350  1.1  skrll int
    351  1.1  skrll iommu_dvmamap_create(void *v, bus_size_t size, int nsegments,
    352  1.1  skrll     bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamap)
    353  1.1  skrll {
    354  1.1  skrll 	struct astro_softc *sc = v;
    355  1.1  skrll 	bus_dmamap_t map;
    356  1.1  skrll 	struct iommu_map_state *ims;
    357  1.1  skrll 	int error;
    358  1.1  skrll 
    359  1.1  skrll 	error = bus_dmamap_create(sc->sc_dmat, size, nsegments, maxsegsz,
    360  1.1  skrll 	    boundary, flags, &map);
    361  1.1  skrll 	if (error)
    362  1.1  skrll 		return (error);
    363  1.1  skrll 
    364  1.1  skrll 	ims = iommu_iomap_create(atop(round_page(size)));
    365  1.1  skrll 	if (ims == NULL) {
    366  1.1  skrll 		bus_dmamap_destroy(sc->sc_dmat, map);
    367  1.1  skrll 		return (ENOMEM);
    368  1.1  skrll 	}
    369  1.1  skrll 
    370  1.1  skrll 	ims->ims_sc = sc;
    371  1.1  skrll 	map->_dm_cookie = ims;
    372  1.1  skrll 	*dmamap = map;
    373  1.1  skrll 
    374  1.1  skrll 	return (0);
    375  1.1  skrll }
    376  1.1  skrll 
    377  1.1  skrll void
    378  1.1  skrll iommu_dvmamap_destroy(void *v, bus_dmamap_t map)
    379  1.1  skrll {
    380  1.1  skrll 	struct astro_softc *sc = v;
    381  1.1  skrll 
    382  1.1  skrll 	/*
    383  1.1  skrll 	 * The specification (man page) requires a loaded
    384  1.1  skrll 	 * map to be unloaded before it is destroyed.
    385  1.1  skrll 	 */
    386  1.1  skrll 	if (map->dm_nsegs)
    387  1.1  skrll 		iommu_dvmamap_unload(sc, map);
    388  1.1  skrll 
    389  1.1  skrll 	if (map->_dm_cookie)
    390  1.1  skrll 		iommu_iomap_destroy(map->_dm_cookie);
    391  1.1  skrll 	map->_dm_cookie = NULL;
    392  1.1  skrll 
    393  1.1  skrll 	bus_dmamap_destroy(sc->sc_dmat, map);
    394  1.1  skrll }
    395  1.1  skrll 
    396  1.1  skrll static int
    397  1.1  skrll iommu_iomap_load_map(struct astro_softc *sc, bus_dmamap_t map, int flags)
    398  1.1  skrll {
    399  1.1  skrll 	struct iommu_map_state *ims = map->_dm_cookie;
    400  1.1  skrll 	struct iommu_page_map *ipm = &ims->ims_map;
    401  1.1  skrll 	struct iommu_page_entry *e;
    402  1.1  skrll 	int err, seg, s;
    403  1.1  skrll 	paddr_t pa, paend;
    404  1.1  skrll 	vaddr_t va;
    405  1.1  skrll 	bus_size_t sgsize;
    406  1.1  skrll 	bus_size_t align, boundary;
    407  1.1  skrll 	u_long dvmaddr;
    408  1.1  skrll 	bus_addr_t dva;
    409  1.1  skrll 	int i;
    410  1.1  skrll 
    411  1.1  skrll 	/* XXX */
    412  1.1  skrll 	boundary = map->_dm_boundary;
    413  1.1  skrll 	align = PAGE_SIZE;
    414  1.1  skrll 
    415  1.1  skrll 	iommu_iomap_clear_pages(ims);
    416  1.1  skrll 
    417  1.1  skrll 	for (seg = 0; seg < map->dm_nsegs; seg++) {
    418  1.1  skrll 		struct hppa_bus_dma_segment *ds = &map->dm_segs[seg];
    419  1.1  skrll 
    420  1.1  skrll 		paend = round_page(ds->ds_addr + ds->ds_len);
    421  1.1  skrll 		for (pa = trunc_page(ds->ds_addr), va = trunc_page(ds->_ds_va);
    422  1.1  skrll 		     pa < paend; pa += PAGE_SIZE, va += PAGE_SIZE) {
    423  1.1  skrll 			err = iommu_iomap_insert_page(ims, va, pa);
    424  1.1  skrll 			if (err) {
    425  1.1  skrll 				printf("iomap insert error: %d for "
    426  1.1  skrll 				    "va 0x%lx pa 0x%lx\n", err, va, pa);
    427  1.1  skrll 				bus_dmamap_unload(sc->sc_dmat, map);
    428  1.1  skrll 				iommu_iomap_clear_pages(ims);
    429  1.1  skrll 			}
    430  1.1  skrll 		}
    431  1.1  skrll 	}
    432  1.1  skrll 
    433  1.1  skrll 	sgsize = ims->ims_map.ipm_pagecnt * PAGE_SIZE;
    434  1.1  skrll 	/* XXXNH */
    435  1.1  skrll 	s = splhigh();
    436  1.1  skrll 	err = extent_alloc(sc->sc_dvmamap, sgsize, align, boundary,
    437  1.1  skrll 	    EX_NOWAIT | EX_BOUNDZERO, &dvmaddr);
    438  1.1  skrll 	splx(s);
    439  1.1  skrll 	if (err)
    440  1.1  skrll 		return (err);
    441  1.1  skrll 
    442  1.1  skrll 	ims->ims_dvmastart = dvmaddr;
    443  1.1  skrll 	ims->ims_dvmasize = sgsize;
    444  1.1  skrll 
    445  1.1  skrll 	dva = dvmaddr;
    446  1.1  skrll 	for (i = 0, e = ipm->ipm_map; i < ipm->ipm_pagecnt; ++i, ++e) {
    447  1.1  skrll 		e->ipe_dva = dva;
    448  1.1  skrll 		iommu_enter(sc, e->ipe_dva, e->ipe_pa, e->ipe_va, flags);
    449  1.1  skrll 		dva += PAGE_SIZE;
    450  1.1  skrll 	}
    451  1.1  skrll 
    452  1.1  skrll 	for (seg = 0; seg < map->dm_nsegs; seg++) {
    453  1.1  skrll 		struct hppa_bus_dma_segment *ds = &map->dm_segs[seg];
    454  1.1  skrll 		ds->ds_addr = iommu_iomap_translate(ims, ds->ds_addr);
    455  1.1  skrll 	}
    456  1.1  skrll 
    457  1.1  skrll 	return (0);
    458  1.1  skrll }
    459  1.1  skrll 
    460  1.1  skrll int
    461  1.1  skrll iommu_dvmamap_load(void *v, bus_dmamap_t map, void *addr, bus_size_t size,
    462  1.1  skrll     struct proc *p, int flags)
    463  1.1  skrll {
    464  1.1  skrll 	struct astro_softc *sc = v;
    465  1.1  skrll 	int err;
    466  1.1  skrll 
    467  1.1  skrll 	err = bus_dmamap_load(sc->sc_dmat, map, addr, size, p, flags);
    468  1.1  skrll 	if (err)
    469  1.1  skrll 		return (err);
    470  1.1  skrll 
    471  1.1  skrll 	return iommu_iomap_load_map(sc, map, flags);
    472  1.1  skrll }
    473  1.1  skrll 
    474  1.1  skrll int
    475  1.1  skrll iommu_dvmamap_load_mbuf(void *v, bus_dmamap_t map, struct mbuf *m, int flags)
    476  1.1  skrll {
    477  1.1  skrll 	struct astro_softc *sc = v;
    478  1.1  skrll 	int err;
    479  1.1  skrll 
    480  1.1  skrll 	err = bus_dmamap_load_mbuf(sc->sc_dmat, map, m, flags);
    481  1.1  skrll 	if (err)
    482  1.1  skrll 		return (err);
    483  1.1  skrll 
    484  1.1  skrll 	return iommu_iomap_load_map(sc, map, flags);
    485  1.1  skrll }
    486  1.1  skrll 
    487  1.1  skrll int
    488  1.1  skrll iommu_dvmamap_load_uio(void *v, bus_dmamap_t map, struct uio *uio, int flags)
    489  1.1  skrll {
    490  1.1  skrll 	struct astro_softc *sc = v;
    491  1.1  skrll 
    492  1.1  skrll 	printf("load_uio\n");
    493  1.1  skrll 
    494  1.1  skrll 	return (bus_dmamap_load_uio(sc->sc_dmat, map, uio, flags));
    495  1.1  skrll }
    496  1.1  skrll 
    497  1.1  skrll int
    498  1.1  skrll iommu_dvmamap_load_raw(void *v, bus_dmamap_t map, bus_dma_segment_t *segs,
    499  1.1  skrll     int nsegs, bus_size_t size, int flags)
    500  1.1  skrll {
    501  1.1  skrll 	struct astro_softc *sc = v;
    502  1.1  skrll 
    503  1.1  skrll 	printf("load_raw\n");
    504  1.1  skrll 
    505  1.1  skrll 	return (bus_dmamap_load_raw(sc->sc_dmat, map, segs, nsegs, size, flags));
    506  1.1  skrll }
    507  1.1  skrll 
    508  1.1  skrll void
    509  1.1  skrll iommu_dvmamap_unload(void *v, bus_dmamap_t map)
    510  1.1  skrll {
    511  1.1  skrll 	struct astro_softc *sc = v;
    512  1.1  skrll 	struct iommu_map_state *ims = map->_dm_cookie;
    513  1.1  skrll 	struct iommu_page_map *ipm = &ims->ims_map;
    514  1.1  skrll 	struct iommu_page_entry *e;
    515  1.1  skrll 	int err, i, s;
    516  1.1  skrll 
    517  1.1  skrll 	/* Remove the IOMMU entries. */
    518  1.1  skrll 	for (i = 0, e = ipm->ipm_map; i < ipm->ipm_pagecnt; ++i, ++e)
    519  1.1  skrll 		iommu_remove(sc, e->ipe_dva);
    520  1.1  skrll 
    521  1.1  skrll 	/* Clear the iomap. */
    522  1.1  skrll 	iommu_iomap_clear_pages(ims);
    523  1.1  skrll 
    524  1.1  skrll 	bus_dmamap_unload(sc->sc_dmat, map);
    525  1.1  skrll 
    526  1.1  skrll 	s = splhigh();
    527  1.1  skrll 	err = extent_free(sc->sc_dvmamap, ims->ims_dvmastart,
    528  1.1  skrll 	    ims->ims_dvmasize, EX_NOWAIT);
    529  1.1  skrll 	ims->ims_dvmastart = 0;
    530  1.1  skrll 	ims->ims_dvmasize = 0;
    531  1.1  skrll 	splx(s);
    532  1.1  skrll 	if (err)
    533  1.1  skrll 		printf("warning: %ld of DVMA space lost\n", ims->ims_dvmasize);
    534  1.1  skrll }
    535  1.1  skrll 
    536  1.1  skrll void
    537  1.1  skrll iommu_dvmamap_sync(void *v, bus_dmamap_t map, bus_addr_t off,
    538  1.1  skrll     bus_size_t len, int ops)
    539  1.1  skrll {
    540  1.1  skrll 	/* Nothing to do; DMA is cache-coherent. */
    541  1.1  skrll }
    542  1.1  skrll 
    543  1.1  skrll int
    544  1.1  skrll iommu_dvmamem_alloc(void *v, bus_size_t size, bus_size_t alignment,
    545  1.1  skrll     bus_size_t boundary, bus_dma_segment_t *segs,
    546  1.1  skrll     int nsegs, int *rsegs, int flags)
    547  1.1  skrll {
    548  1.1  skrll 	struct astro_softc *sc = v;
    549  1.1  skrll 
    550  1.1  skrll 	return (bus_dmamem_alloc(sc->sc_dmat, size, alignment, boundary,
    551  1.1  skrll 	    segs, nsegs, rsegs, flags));
    552  1.1  skrll }
    553  1.1  skrll 
    554  1.1  skrll void
    555  1.1  skrll iommu_dvmamem_free(void *v, bus_dma_segment_t *segs, int nsegs)
    556  1.1  skrll {
    557  1.1  skrll 	struct astro_softc *sc = v;
    558  1.1  skrll 
    559  1.1  skrll 	bus_dmamem_free(sc->sc_dmat, segs, nsegs);
    560  1.1  skrll }
    561  1.1  skrll 
    562  1.1  skrll int
    563  1.1  skrll iommu_dvmamem_map(void *v, bus_dma_segment_t *segs, int nsegs, size_t size,
    564  1.1  skrll     void **kvap, int flags)
    565  1.1  skrll {
    566  1.1  skrll 	struct astro_softc *sc = v;
    567  1.1  skrll 
    568  1.1  skrll 	return (bus_dmamem_map(sc->sc_dmat, segs, nsegs, size, kvap, flags));
    569  1.1  skrll }
    570  1.1  skrll 
    571  1.1  skrll void
    572  1.1  skrll iommu_dvmamem_unmap(void *v, void *kva, size_t size)
    573  1.1  skrll {
    574  1.1  skrll 	struct astro_softc *sc = v;
    575  1.1  skrll 
    576  1.1  skrll 	bus_dmamem_unmap(sc->sc_dmat, kva, size);
    577  1.1  skrll }
    578  1.1  skrll 
    579  1.1  skrll paddr_t
    580  1.1  skrll iommu_dvmamem_mmap(void *v, bus_dma_segment_t *segs, int nsegs, off_t off,
    581  1.1  skrll     int prot, int flags)
    582  1.1  skrll {
    583  1.1  skrll 	struct astro_softc *sc = v;
    584  1.1  skrll 
    585  1.1  skrll 	return (bus_dmamem_mmap(sc->sc_dmat, segs, nsegs, off, prot, flags));
    586  1.1  skrll }
    587  1.1  skrll 
    588  1.1  skrll /*
    589  1.1  skrll  * Utility function used by splay tree to order page entries by pa.
    590  1.1  skrll  */
    591  1.1  skrll static inline int
    592  1.1  skrll iomap_compare(struct iommu_page_entry *a, struct iommu_page_entry *b)
    593  1.1  skrll {
    594  1.1  skrll 	return ((a->ipe_pa > b->ipe_pa) ? 1 :
    595  1.1  skrll 		(a->ipe_pa < b->ipe_pa) ? -1 : 0);
    596  1.1  skrll }
    597  1.1  skrll 
    598  1.1  skrll SPLAY_PROTOTYPE(iommu_page_tree, iommu_page_entry, ipe_node, iomap_compare);
    599  1.1  skrll 
    600  1.1  skrll SPLAY_GENERATE(iommu_page_tree, iommu_page_entry, ipe_node, iomap_compare);
    601  1.1  skrll 
    602  1.1  skrll /*
    603  1.1  skrll  * Create a new iomap.
    604  1.1  skrll  */
    605  1.1  skrll struct iommu_map_state *
    606  1.1  skrll iommu_iomap_create(int n)
    607  1.1  skrll {
    608  1.1  skrll 	struct iommu_map_state *ims;
    609  1.1  skrll 
    610  1.1  skrll 	/* Safety for heavily fragmented data, such as mbufs */
    611  1.1  skrll 	n += 4;
    612  1.1  skrll 	if (n < 16)
    613  1.1  skrll 		n = 16;
    614  1.1  skrll 
    615  1.1  skrll 	ims = malloc(sizeof(*ims) + (n - 1) * sizeof(ims->ims_map.ipm_map[0]),
    616  1.1  skrll 	    M_DEVBUF, M_NOWAIT | M_ZERO);
    617  1.1  skrll 	if (ims == NULL)
    618  1.1  skrll 		return (NULL);
    619  1.1  skrll 
    620  1.1  skrll 	/* Initialize the map. */
    621  1.1  skrll 	ims->ims_map.ipm_maxpage = n;
    622  1.1  skrll 	SPLAY_INIT(&ims->ims_map.ipm_tree);
    623  1.1  skrll 
    624  1.1  skrll 	return (ims);
    625  1.1  skrll }
    626  1.1  skrll 
    627  1.1  skrll /*
    628  1.1  skrll  * Destroy an iomap.
    629  1.1  skrll  */
    630  1.1  skrll void
    631  1.1  skrll iommu_iomap_destroy(struct iommu_map_state *ims)
    632  1.1  skrll {
    633  1.1  skrll #ifdef DIAGNOSTIC
    634  1.1  skrll 	if (ims->ims_map.ipm_pagecnt > 0)
    635  1.1  skrll 		printf("iommu_iomap_destroy: %d page entries in use\n",
    636  1.1  skrll 		    ims->ims_map.ipm_pagecnt);
    637  1.1  skrll #endif
    638  1.1  skrll 
    639  1.1  skrll 	free(ims, M_DEVBUF);
    640  1.1  skrll }
    641  1.1  skrll 
    642  1.1  skrll /*
    643  1.1  skrll  * Insert a pa entry in the iomap.
    644  1.1  skrll  */
    645  1.1  skrll int
    646  1.1  skrll iommu_iomap_insert_page(struct iommu_map_state *ims, vaddr_t va, paddr_t pa)
    647  1.1  skrll {
    648  1.1  skrll 	struct iommu_page_map *ipm = &ims->ims_map;
    649  1.1  skrll 	struct iommu_page_entry *e;
    650  1.1  skrll 
    651  1.1  skrll 	if (ipm->ipm_pagecnt >= ipm->ipm_maxpage) {
    652  1.1  skrll 		struct iommu_page_entry ipe;
    653  1.1  skrll 
    654  1.1  skrll 		ipe.ipe_pa = pa;
    655  1.1  skrll 		if (SPLAY_FIND(iommu_page_tree, &ipm->ipm_tree, &ipe))
    656  1.1  skrll 			return (0);
    657  1.1  skrll 
    658  1.1  skrll 		return (ENOMEM);
    659  1.1  skrll 	}
    660  1.1  skrll 
    661  1.1  skrll 	e = &ipm->ipm_map[ipm->ipm_pagecnt];
    662  1.1  skrll 
    663  1.1  skrll 	e->ipe_pa = pa;
    664  1.1  skrll 	e->ipe_va = va;
    665  1.1  skrll 	e->ipe_dva = 0;
    666  1.1  skrll 
    667  1.1  skrll 	e = SPLAY_INSERT(iommu_page_tree, &ipm->ipm_tree, e);
    668  1.1  skrll 
    669  1.1  skrll 	/* Duplicates are okay, but only count them once. */
    670  1.1  skrll 	if (e)
    671  1.1  skrll 		return (0);
    672  1.1  skrll 
    673  1.1  skrll 	++ipm->ipm_pagecnt;
    674  1.1  skrll 
    675  1.1  skrll 	return (0);
    676  1.1  skrll }
    677  1.1  skrll 
    678  1.1  skrll /*
    679  1.1  skrll  * Translate a physical address (pa) into a DVMA address.
    680  1.1  skrll  */
    681  1.1  skrll bus_addr_t
    682  1.1  skrll iommu_iomap_translate(struct iommu_map_state *ims, paddr_t pa)
    683  1.1  skrll {
    684  1.1  skrll 	struct iommu_page_map *ipm = &ims->ims_map;
    685  1.1  skrll 	struct iommu_page_entry *e;
    686  1.1  skrll 	struct iommu_page_entry pe;
    687  1.1  skrll 	paddr_t offset = pa & PAGE_MASK;
    688  1.1  skrll 
    689  1.1  skrll 	pe.ipe_pa = trunc_page(pa);
    690  1.1  skrll 
    691  1.1  skrll 	e = SPLAY_FIND(iommu_page_tree, &ipm->ipm_tree, &pe);
    692  1.1  skrll 
    693  1.1  skrll 	if (e == NULL) {
    694  1.1  skrll 		panic("couldn't find pa %lx\n", pa);
    695  1.1  skrll 		return 0;
    696  1.1  skrll 	}
    697  1.1  skrll 
    698  1.1  skrll 	return (e->ipe_dva | offset);
    699  1.1  skrll }
    700  1.1  skrll 
    701  1.1  skrll /*
    702  1.1  skrll  * Clear the iomap table and tree.
    703  1.1  skrll  */
    704  1.1  skrll void
    705  1.1  skrll iommu_iomap_clear_pages(struct iommu_map_state *ims)
    706  1.1  skrll {
    707  1.1  skrll 	ims->ims_map.ipm_pagecnt = 0;
    708  1.1  skrll 	SPLAY_INIT(&ims->ims_map.ipm_tree);
    709  1.1  skrll }
    710  1.1  skrll 
    711  1.1  skrll /*
    712  1.1  skrll  * Add an entry to the IOMMU table.
    713  1.1  skrll  */
    714  1.1  skrll void
    715  1.1  skrll iommu_enter(struct astro_softc *sc, bus_addr_t dva, paddr_t pa, vaddr_t va,
    716  1.1  skrll     int flags)
    717  1.1  skrll {
    718  1.1  skrll 	volatile uint64_t *tte_ptr = &sc->sc_pdir[dva >> PAGE_SHIFT];
    719  1.1  skrll 	uint64_t tte;
    720  1.1  skrll 	uint32_t ci;
    721  1.1  skrll 
    722  1.1  skrll #ifdef ASTRODEBUG
    723  1.1  skrll 	printf("iommu_enter dva %lx, pa %lx, va %lx\n", dva, pa, va);
    724  1.1  skrll #endif
    725  1.1  skrll 
    726  1.1  skrll #ifdef DIAGNOSTIC
    727  1.1  skrll 	tte = le64toh(*tte_ptr);
    728  1.1  skrll 
    729  1.1  skrll 	if (tte & IOTTE_V) {
    730  1.1  skrll 		printf("Overwriting valid tte entry (dva %lx pa %lx "
    731  1.1  skrll 		    "&tte %p tte %llx)\n", dva, pa, tte_ptr, tte);
    732  1.1  skrll 		extent_print(sc->sc_dvmamap);
    733  1.1  skrll 		panic("IOMMU overwrite");
    734  1.1  skrll 	}
    735  1.1  skrll #endif
    736  1.1  skrll 
    737  1.1  skrll 	ci = lci(HPPA_SID_KERNEL, va);
    738  1.1  skrll 
    739  1.1  skrll 	tte = (pa & IOTTE_PAMASK) | ((ci >> 12) & IOTTE_CI);
    740  1.1  skrll 	tte |= IOTTE_V;
    741  1.1  skrll 
    742  1.1  skrll 	*tte_ptr = htole64(tte);
    743  1.1  skrll 	fdcache(HPPA_SID_KERNEL, (vaddr_t)tte_ptr, sizeof(*tte_ptr));
    744  1.1  skrll }
    745  1.1  skrll 
    746  1.1  skrll /*
    747  1.1  skrll  * Remove an entry from the IOMMU table.
    748  1.1  skrll  */
    749  1.1  skrll void
    750  1.1  skrll iommu_remove(struct astro_softc *sc, bus_addr_t dva)
    751  1.1  skrll {
    752  1.1  skrll 	volatile struct astro_regs *r = sc->sc_regs;
    753  1.1  skrll 	uint64_t *tte_ptr = &sc->sc_pdir[dva >> PAGE_SHIFT];
    754  1.1  skrll 	uint64_t tte;
    755  1.1  skrll 
    756  1.1  skrll #ifdef DIAGNOSTIC
    757  1.1  skrll 	if (dva != trunc_page(dva)) {
    758  1.1  skrll 		printf("iommu_remove: unaligned dva: %lx\n", dva);
    759  1.1  skrll 		dva = trunc_page(dva);
    760  1.1  skrll 	}
    761  1.1  skrll #endif
    762  1.1  skrll 
    763  1.1  skrll 	tte = le64toh(*tte_ptr);
    764  1.1  skrll 
    765  1.1  skrll #ifdef DIAGNOSTIC
    766  1.1  skrll 	if ((tte & IOTTE_V) == 0) {
    767  1.1  skrll 		printf("Removing invalid tte entry (dva %lx &tte %p "
    768  1.1  skrll 		    "tte %llx)\n", dva, tte_ptr, tte);
    769  1.1  skrll 		extent_print(sc->sc_dvmamap);
    770  1.1  skrll 		panic("IOMMU remove overwrite");
    771  1.1  skrll 	}
    772  1.1  skrll #endif
    773  1.1  skrll 
    774  1.1  skrll 	*tte_ptr = htole64(tte & ~IOTTE_V);
    775  1.1  skrll 
    776  1.1  skrll 	/* Flush IOMMU. */
    777  1.1  skrll 	r->tlb_pcom = htole32(dva | PAGE_SHIFT);
    778  1.1  skrll }
    779