1275048Sbr/*-
2275048Sbr * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3275048Sbr * All rights reserved.
4275048Sbr *
5275048Sbr * This software was developed by SRI International and the University of
6275048Sbr * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7275048Sbr * ("CTSRD"), as part of the DARPA CRASH research programme.
8275048Sbr *
9275048Sbr * Redistribution and use in source and binary forms, with or without
10275048Sbr * modification, are permitted provided that the following conditions
11275048Sbr * are met:
12275048Sbr * 1. Redistributions of source code must retain the above copyright
13275048Sbr *    notice, this list of conditions and the following disclaimer.
14275048Sbr * 2. Redistributions in binary form must reproduce the above copyright
15275048Sbr *    notice, this list of conditions and the following disclaimer in the
16275048Sbr *    documentation and/or other materials provided with the distribution.
17275048Sbr *
18275048Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19275048Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20275048Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21275048Sbr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22275048Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23275048Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24275048Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25275048Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26275048Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27275048Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28275048Sbr * SUCH DAMAGE.
29275048Sbr */
30275048Sbr
31275048Sbr/*
32275048Sbr * BERI virtio mmio backend common methods
33275048Sbr */
34275048Sbr
35275048Sbr#include <sys/cdefs.h>
36275048Sbr__FBSDID("$FreeBSD$");
37275048Sbr
38275048Sbr#include <sys/param.h>
39275048Sbr#include <sys/systm.h>
40275048Sbr#include <sys/bus.h>
41276031Sian#include <sys/cdefs.h>
42275048Sbr#include <sys/kernel.h>
43275048Sbr#include <sys/module.h>
44275048Sbr#include <sys/malloc.h>
45275048Sbr#include <sys/rman.h>
46275048Sbr#include <sys/timeet.h>
47275048Sbr#include <sys/timetc.h>
48275048Sbr#include <sys/conf.h>
49275048Sbr#include <sys/uio.h>
50275048Sbr#include <sys/stat.h>
51275048Sbr#include <sys/time.h>
52275048Sbr#include <sys/event.h>
53275048Sbr#include <sys/selinfo.h>
54275048Sbr#include <sys/endian.h>
55275048Sbr#include <sys/rwlock.h>
56275048Sbr
57275048Sbr#include <machine/bus.h>
58275048Sbr#include <machine/fdt.h>
59275048Sbr#include <machine/cpu.h>
60275048Sbr#include <machine/intr.h>
61275048Sbr
62275647Sbr#include <dev/fdt/fdt_common.h>
63275647Sbr#include <dev/ofw/openfirm.h>
64275647Sbr#include <dev/ofw/ofw_bus.h>
65275647Sbr#include <dev/ofw/ofw_bus_subr.h>
66275647Sbr
67275048Sbr#include <dev/beri/virtio/virtio.h>
68275048Sbr#include <dev/virtio/virtqueue.h>
69275048Sbr#include <dev/virtio/virtio_ring.h>
70275647Sbr#include <dev/altera/pio/pio.h>
71275048Sbr
72275647Sbr#include "pio_if.h"
73275647Sbr
74275048Sbrint
75275048Sbrvq_ring_ready(struct vqueue_info *vq)
76275048Sbr{
77275048Sbr
78275048Sbr	return (vq->vq_flags & VQ_ALLOC);
79275048Sbr}
80275048Sbr
81275048Sbrint
82275048Sbrvq_has_descs(struct vqueue_info *vq)
83275048Sbr{
84275048Sbr
85275048Sbr	return (vq_ring_ready(vq) && vq->vq_last_avail !=
86275048Sbr		be16toh(vq->vq_avail->idx));
87275048Sbr}
88275048Sbr
89275048Sbrvoid *
90275048Sbrpaddr_map(uint32_t offset, uint32_t phys, uint32_t size)
91275048Sbr{
92275048Sbr	bus_space_handle_t bsh;
93275048Sbr
94275048Sbr	if (bus_space_map(fdtbus_bs_tag, (phys + offset),
95275048Sbr			size, 0, &bsh) != 0) {
96275048Sbr		panic("Couldn't map 0x%08x\n", (phys + offset));
97275048Sbr	}
98275048Sbr
99275048Sbr	return (void *)(bsh);
100275048Sbr}
101275048Sbr
102275048Sbrvoid
103275048Sbrpaddr_unmap(void *phys, uint32_t size)
104275048Sbr{
105275048Sbr
106275048Sbr	bus_space_unmap(fdtbus_bs_tag, (bus_space_handle_t)phys, size);
107275048Sbr}
108275048Sbr
109275048Sbrstatic inline void
110275048Sbr_vq_record(uint32_t offs, int i, volatile struct vring_desc *vd,
111275048Sbr	struct iovec *iov, int n_iov, uint16_t *flags) {
112275048Sbr
113275048Sbr	if (i >= n_iov)
114275048Sbr		return;
115275048Sbr
116275048Sbr	iov[i].iov_base = paddr_map(offs, be64toh(vd->addr),
117275048Sbr				be32toh(vd->len));
118275048Sbr	iov[i].iov_len = be32toh(vd->len);
119275048Sbr	if (flags != NULL)
120275048Sbr		flags[i] = be16toh(vd->flags);
121275048Sbr}
122275048Sbr
123275048Sbrint
124275048Sbrvq_getchain(uint32_t offs, struct vqueue_info *vq,
125275048Sbr	struct iovec *iov, int n_iov, uint16_t *flags)
126275048Sbr{
127275048Sbr	volatile struct vring_desc *vdir, *vindir, *vp;
128275048Sbr	int idx, ndesc, n_indir;
129275048Sbr	int head, next;
130275048Sbr	int i;
131275048Sbr
132275048Sbr	idx = vq->vq_last_avail;
133275048Sbr	ndesc = (be16toh(vq->vq_avail->idx) - idx);
134275048Sbr	if (ndesc == 0)
135275048Sbr		return (0);
136275048Sbr
137275048Sbr	head = be16toh(vq->vq_avail->ring[idx & (vq->vq_qsize - 1)]);
138275048Sbr	next = head;
139275048Sbr
140275048Sbr	for (i = 0; i < VQ_MAX_DESCRIPTORS; next = be16toh(vdir->next)) {
141275048Sbr		vdir = &vq->vq_desc[next];
142275048Sbr		if ((be16toh(vdir->flags) & VRING_DESC_F_INDIRECT) == 0) {
143275048Sbr			_vq_record(offs, i, vdir, iov, n_iov, flags);
144275048Sbr			i++;
145275048Sbr		} else {
146275048Sbr			n_indir = be32toh(vdir->len) / 16;
147275048Sbr			vindir = paddr_map(offs, be64toh(vdir->addr),
148275048Sbr					be32toh(vdir->len));
149275048Sbr			next = 0;
150275048Sbr			for (;;) {
151275048Sbr				vp = &vindir[next];
152275048Sbr				_vq_record(offs, i, vp, iov, n_iov, flags);
153275048Sbr				i+=1;
154275048Sbr				if ((be16toh(vp->flags) & \
155275048Sbr					VRING_DESC_F_NEXT) == 0)
156275048Sbr					break;
157275048Sbr				next = be16toh(vp->next);
158275048Sbr			}
159276031Sian			paddr_unmap(__DEVOLATILE(void *, vindir), be32toh(vdir->len));
160275048Sbr		}
161275048Sbr
162275048Sbr		if ((be16toh(vdir->flags) & VRING_DESC_F_NEXT) == 0)
163275048Sbr			return (i);
164275048Sbr	}
165275048Sbr
166275048Sbr	return (i);
167275048Sbr}
168275048Sbr
169275048Sbrvoid
170275048Sbrvq_relchain(struct vqueue_info *vq, struct iovec *iov, int n, uint32_t iolen)
171275048Sbr{
172275048Sbr	volatile struct vring_used_elem *vue;
173275048Sbr	volatile struct vring_used *vu;
174275048Sbr	uint16_t head, uidx, mask;
175275048Sbr	int i;
176275048Sbr
177275048Sbr	mask = vq->vq_qsize - 1;
178275647Sbr	vu = vq->vq_used;
179275048Sbr	head = be16toh(vq->vq_avail->ring[vq->vq_last_avail++ & mask]);
180275048Sbr
181275048Sbr	uidx = be16toh(vu->idx);
182275048Sbr	vue = &vu->ring[uidx++ & mask];
183275647Sbr	vue->id = htobe32(head);
184275647Sbr
185275048Sbr	vue->len = htobe32(iolen);
186275048Sbr	vu->idx = htobe16(uidx);
187275048Sbr
188275048Sbr	/* Clean up */
189276710Sbr	for (i = 0; i < n; i++) {
190275048Sbr		paddr_unmap((void *)iov[i].iov_base, iov[i].iov_len);
191275048Sbr	}
192275048Sbr}
193275647Sbr
194275647Sbrint
195275647Sbrsetup_pio(device_t dev, char *name, device_t *pio_dev)
196275647Sbr{
197275647Sbr	phandle_t pio_node;
198275647Sbr	struct fdt_ic *ic;
199275647Sbr	phandle_t xref;
200275647Sbr	phandle_t node;
201275647Sbr
202275647Sbr	if ((node = ofw_bus_get_node(dev)) == -1)
203275647Sbr		return (ENXIO);
204275647Sbr
205275647Sbr	if (OF_searchencprop(node, name, &xref,
206275647Sbr		sizeof(xref)) == -1) {
207275647Sbr		return (ENXIO);
208275647Sbr	}
209275647Sbr
210275647Sbr	pio_node = OF_node_from_xref(xref);
211275647Sbr	SLIST_FOREACH(ic, &fdt_ic_list_head, fdt_ics) {
212275647Sbr		if (ic->iph == pio_node) {
213275647Sbr			*pio_dev = ic->dev;
214275647Sbr			return (0);
215275647Sbr		}
216275647Sbr	}
217275647Sbr
218275647Sbr	return (ENXIO);
219275647Sbr}
220275647Sbr
221275647Sbrint
222275647Sbrsetup_offset(device_t dev, uint32_t *offset)
223275647Sbr{
224275647Sbr	pcell_t dts_value[2];
225275647Sbr	phandle_t mem_node;
226275647Sbr	phandle_t xref;
227275647Sbr	phandle_t node;
228275647Sbr	int len;
229275647Sbr
230275647Sbr	if ((node = ofw_bus_get_node(dev)) == -1)
231275647Sbr		return (ENXIO);
232275647Sbr
233275647Sbr	if (OF_searchencprop(node, "beri-mem", &xref,
234275647Sbr		sizeof(xref)) == -1) {
235275647Sbr		return (ENXIO);
236275647Sbr	}
237275647Sbr
238275647Sbr	mem_node = OF_node_from_xref(xref);
239275647Sbr	if ((len = OF_getproplen(mem_node, "reg")) <= 0)
240275647Sbr		return (ENXIO);
241275647Sbr	OF_getencprop(mem_node, "reg", dts_value, len);
242275647Sbr	*offset = dts_value[0];
243275647Sbr
244275647Sbr	return (0);
245275647Sbr}
246275647Sbr
247276710Sbrstruct iovec *
248276710Sbrgetcopy(struct iovec *iov, int n)
249276710Sbr{
250276710Sbr	struct iovec *tiov;
251276710Sbr	int i;
252276710Sbr
253276710Sbr	tiov = malloc(n * sizeof(struct iovec), M_DEVBUF, M_NOWAIT);
254276710Sbr	for (i = 0; i < n; i++) {
255276710Sbr		tiov[i].iov_base = iov[i].iov_base;
256276710Sbr		tiov[i].iov_len = iov[i].iov_len;
257276710Sbr	}
258276710Sbr
259276710Sbr	return (tiov);
260276710Sbr}
261