/*-
* Copyright (c) 2012 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Nick Hudson.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 2007 Mark Kettenis
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Copyright (c) 2004 Michael Shalayeff
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR OR HIS RELATIVES BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* References:
* 1. Hardware Cache Coherent Input/Output. Hewlett-Packard Journal, February
* 1996.
* 2. PA-RISC 1.1 Architecture and Instruction Set Reference Manual,
* Hewlett-Packard, February 1994, Third Edition
*/
struct uturn_page_map {
SPLAY_HEAD(uturn_page_tree, uturn_page_entry) upm_tree;
int upm_maxpage; /* Size of allocated page map */
int upm_pagecnt; /* Number of entries in use */
struct uturn_page_entry upm_map[1];
};
/*
* per-map UTURN state
*/
struct uturn_map_state {
struct uturn_softc *ums_sc;
bus_addr_t ums_iovastart;
bus_size_t ums_iovasize;
struct uturn_page_map ums_map; /* map must be last (array at end) */
};
/* XXX 28 bits gives us 256Mb of iova space */
/* Calculate based on %age of RAM */
iova_bits = 28;
/*
* size is # of pdir entries (64bits) in bytes. 1 entry per IOVA
* page.
*/
size = (1 << (iova_bits - UTURN_IOVP_SHIFT)) * sizeof(uint64_t);
/*
* Chainid is the upper most bits of an IOVP used to determine which
* TLB entry an IOVP will use.
*/
sc->sc_chainid_shift = iova_bits - UTURN_CHAINID_SHIFT;
/*
* Allocate memory for I/O pagetables. They need to be physically
* contiguous.
*/
if (uvm_pglistalloc(size, 0, -1, PAGE_SIZE, 0, &pglist, 1, 0) != 0)
panic("%s: no memory", __func__);
va = (vaddr_t)VM_PAGE_TO_PHYS(TAILQ_FIRST(&pglist));
sc->sc_pdir = (int64_t *)va;
/* for (i = UTURN_TLB_ENTRIES; i != 0; i--) { */
for (i = 0; i < UTURN_TLB_ENTRIES; i++) {
r->io_command =
UTURN_CMD_TLB_DIRECT_WRITE | (i << sc->sc_chainid_shift);
}
/*
* Go to "Virtual Mode"
*/
r->io_control = UTURN_VIRTUAL_MODE;
/*
* U2/UTurn is actually a combination of an Upper Bus Converter (UBC)
* and a Lower Bus Converter (LBC). This driver attaches to the UBC;
* the LBC isn't very interesting, so we skip it. This is easy, since
* it always is module 63, hence the MAXMODBUS - 1 below.
*/
nca = *ca;
nca.ca_hpabase = r->io_io_low << 16;
nca.ca_dmatag = &sc->sc_dmatag;
nca.ca_nmodules = MAXMODBUS - 1;
pdc_scanbus(self, &nca, uturn_callback);
}
for (seg = 0; seg < map->dm_nsegs; seg++) {
struct hppa_bus_dma_segment *ds = &map->dm_segs[seg];
paend = round_page(ds->ds_addr + ds->ds_len);
for (pa = trunc_page(ds->ds_addr), va = trunc_page(ds->_ds_va);
pa < paend; pa += PAGE_SIZE, va += PAGE_SIZE) {
err = uturn_iomap_insert_page(ums, va, pa);
if (err) {
printf("iomap insert error: %d for "
"va 0x%lx pa 0x%lx\n", err, va, pa);
bus_dmamap_unload(sc->sc_dmat, map);
uturn_iomap_clear_pages(ums);
}
}
}
/*
* Utility function used by splay tree to order page entries by pa.
*/
static inline int
upe_compare(struct uturn_page_entry *a, struct uturn_page_entry *b)
{
return ((a->upe_pa > b->upe_pa) ? 1 :
(a->upe_pa < b->upe_pa) ? -1 : 0);
}
/*
* Create a new iomap.
*/
struct uturn_map_state *
uturn_iomap_create(int n, int flags)
{
struct uturn_map_state *ums;
/* Safety for heavily fragmented data, such as mbufs */
n += 4;
if (n < 16)
n = 16;
const size_t sz =
sizeof(*ums) + (n - 1) * sizeof(ums->ums_map.upm_map[0]);
ums = kmem_zalloc(sz, (flags & BUS_DMA_NOWAIT) ? KM_NOSLEEP : KM_SLEEP);
if (ums == NULL)
return (NULL);
/* Initialize the map. */
ums->ums_map.upm_maxpage = n;
SPLAY_INIT(&ums->ums_map.upm_tree);
return (ums);
}
/*
* Destroy an iomap.
*/
void
uturn_iomap_destroy(struct uturn_map_state *ums)
{
KASSERT(ums->ums_map.upm_pagecnt == 0);
const int n = ums->ums_map.upm_maxpage;
const size_t sz =
sizeof(*ums) + (n - 1) * sizeof(ums->ums_map.upm_map[0]);
kmem_free(ums, sz);
}
/*
* Insert a pa entry in the iomap.
*/
int
uturn_iomap_insert_page(struct uturn_map_state *ums, vaddr_t va, paddr_t pa)
{
struct uturn_page_map *upm = &ums->ums_map;
struct uturn_page_entry *e;
if (upm->upm_pagecnt >= upm->upm_maxpage) {
struct uturn_page_entry upe;
upe.upe_pa = pa;
if (SPLAY_FIND(uturn_page_tree, &upm->upm_tree, &upe))
return (0);
return (ENOMEM);
}
e = &upm->upm_map[upm->upm_pagecnt];
e->upe_pa = pa;
e->upe_va = va;
e->upe_iova = 0;
e = SPLAY_INSERT(uturn_page_tree, &upm->upm_tree, e);
/* Duplicates are okay, but only count them once. */
if (e)
return (0);
++upm->upm_pagecnt;
return (0);
}
/*
* Translate a physical address (pa) into a IOVA address.
*/
bus_addr_t
uturn_iomap_translate(struct uturn_map_state *ums, paddr_t pa)
{
struct uturn_page_map *upm = &ums->ums_map;
struct uturn_page_entry *e;
struct uturn_page_entry pe;
paddr_t offset = pa & PAGE_MASK;
pe.upe_pa = trunc_page(pa);
e = SPLAY_FIND(uturn_page_tree, &upm->upm_tree, &pe);
if (e == NULL) {
panic("couldn't find pa %lx\n", pa);
return 0;
}
return (e->upe_iova | offset);
}
/*
* Clear the iomap table and tree.
*/
void
uturn_iomap_clear_pages(struct uturn_map_state *ums)
{
ums->ums_map.upm_pagecnt = 0;
SPLAY_INIT(&ums->ums_map.upm_tree);
}