if_vtbe.c revision 276354
1275647Sbr/*-
2275647Sbr * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
3275647Sbr * All rights reserved.
4275647Sbr *
5275647Sbr * This software was developed by SRI International and the University of
6275647Sbr * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
7275647Sbr * ("CTSRD"), as part of the DARPA CRASH research programme.
8275647Sbr *
9275647Sbr * Redistribution and use in source and binary forms, with or without
10275647Sbr * modification, are permitted provided that the following conditions
11275647Sbr * are met:
12275647Sbr * 1. Redistributions of source code must retain the above copyright
13275647Sbr *    notice, this list of conditions and the following disclaimer.
14275647Sbr * 2. Redistributions in binary form must reproduce the above copyright
15275647Sbr *    notice, this list of conditions and the following disclaimer in the
16275647Sbr *    documentation and/or other materials provided with the distribution.
17275647Sbr *
18275647Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19275647Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20275647Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21275647Sbr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22275647Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23275647Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24275647Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25275647Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26275647Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27275647Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28275647Sbr * SUCH DAMAGE.
29275647Sbr */
30275647Sbr
31275647Sbr/*
32275647Sbr * BERI Virtio Networking Frontend
33275647Sbr */
34275647Sbr
35275647Sbr#include <sys/cdefs.h>
36275647Sbr__FBSDID("$FreeBSD: head/sys/dev/beri/virtio/network/if_vtbe.c 276354 2014-12-29 00:35:44Z bryanv $");
37275647Sbr
38275647Sbr#include <sys/param.h>
39275647Sbr#include <sys/systm.h>
40275647Sbr#include <sys/bus.h>
41275647Sbr#include <sys/kernel.h>
42275647Sbr#include <sys/module.h>
43275647Sbr#include <sys/malloc.h>
44275647Sbr#include <sys/rman.h>
45275647Sbr#include <sys/timeet.h>
46275647Sbr#include <sys/timetc.h>
47275647Sbr#include <sys/endian.h>
48275647Sbr#include <sys/lock.h>
49275647Sbr#include <sys/mbuf.h>
50275647Sbr#include <sys/mutex.h>
51275647Sbr#include <sys/socket.h>
52275647Sbr#include <sys/sockio.h>
53275647Sbr#include <sys/sysctl.h>
54275647Sbr#include <sys/mdioctl.h>
55275647Sbr#include <sys/conf.h>
56275647Sbr#include <sys/stat.h>
57275647Sbr#include <sys/uio.h>
58275647Sbr
59275647Sbr#include <dev/fdt/fdt_common.h>
60275647Sbr#include <dev/ofw/openfirm.h>
61275647Sbr#include <dev/ofw/ofw_bus.h>
62275647Sbr#include <dev/ofw/ofw_bus_subr.h>
63275647Sbr
64275647Sbr#include <net/bpf.h>
65275647Sbr#include <net/if.h>
66275647Sbr#include <net/ethernet.h>
67275647Sbr#include <net/if_dl.h>
68275647Sbr#include <net/if_media.h>
69275647Sbr#include <net/if_types.h>
70275647Sbr#include <net/if_var.h>
71275647Sbr#include <net/if_vlan_var.h>
72275647Sbr
73275647Sbr#include <machine/bus.h>
74275647Sbr#include <machine/fdt.h>
75275647Sbr#include <machine/cpu.h>
76275647Sbr#include <machine/intr.h>
77275647Sbr
78275647Sbr#include <dev/beri/virtio/virtio.h>
79275647Sbr#include <dev/beri/virtio/virtio_mmio_platform.h>
80275647Sbr
81275647Sbr#include <dev/altera/pio/pio.h>
82275647Sbr
83275647Sbr#include <dev/virtio/mmio/virtio_mmio.h>
84275647Sbr#include <dev/virtio/network/virtio_net.h>
85276354Sbryanv#include <dev/virtio/virtio_ids.h>
86276354Sbryanv#include <dev/virtio/virtio_config.h>
87275647Sbr#include <dev/virtio/virtio_ring.h>
88275647Sbr
89275647Sbr#include "pio_if.h"
90275647Sbr
91275647Sbr#define	DPRINTF(fmt, args...)	printf(fmt, ##args)
92275647Sbr
93275647Sbr#define	READ4(_sc, _reg) \
94275647Sbr	bus_read_4((_sc)->res[0], _reg)
95275647Sbr#define	WRITE4(_sc, _reg, _val) \
96275647Sbr	bus_write_4((_sc)->res[0], _reg, _val)
97275647Sbr
98275647Sbr#define	VTBE_LOCK(sc)			mtx_lock(&(sc)->mtx)
99275647Sbr#define	VTBE_UNLOCK(sc)			mtx_unlock(&(sc)->mtx)
100275647Sbr#define	VTBE_ASSERT_LOCKED(sc)		mtx_assert(&(sc)->mtx, MA_OWNED);
101275647Sbr#define	VTBE_ASSERT_UNLOCKED(sc)	mtx_assert(&(sc)->mtx, MA_NOTOWNED);
102275647Sbr
103275647Sbr/*
104275647Sbr * Driver data and defines.
105275647Sbr */
106275647Sbr#define	DESC_COUNT	256
107275647Sbr
108275647Sbrstruct vtbe_softc {
109275647Sbr	struct resource		*res[2];
110275647Sbr	bus_space_tag_t		bst;
111275647Sbr	bus_space_handle_t	bsh;
112275647Sbr	device_t		dev;
113275647Sbr	struct ifnet		*ifp;
114275647Sbr	int			if_flags;
115275647Sbr	struct mtx		mtx;
116275647Sbr	boolean_t		is_attached;
117275647Sbr
118275647Sbr	int			beri_mem_offset;
119275647Sbr	device_t		pio_send;
120275647Sbr	device_t		pio_recv;
121275647Sbr	int			opened;
122275647Sbr
123275647Sbr	struct vqueue_info	vs_queues[2];
124275647Sbr	int			vs_curq;
125275647Sbr	int			hdrsize;
126275647Sbr};
127275647Sbr
128275647Sbrstatic struct resource_spec vtbe_spec[] = {
129275647Sbr	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
130275647Sbr	{ -1, 0 }
131275647Sbr};
132275647Sbr
133275647Sbrstatic void vtbe_txfinish_locked(struct vtbe_softc *sc);
134275647Sbrstatic void vtbe_rxfinish_locked(struct vtbe_softc *sc);
135275647Sbrstatic void vtbe_stop_locked(struct vtbe_softc *sc);
136275647Sbrstatic int pio_enable_irq(struct vtbe_softc *sc, int enable);
137275647Sbr
138275647Sbrstatic void
139275647Sbrvtbe_txstart_locked(struct vtbe_softc *sc)
140275647Sbr{
141275647Sbr	struct virtio_net_hdr_mrg_rxbuf *vnh;
142275647Sbr	struct iovec iov[DESC_COUNT];
143275647Sbr	struct vqueue_info *vq;
144275647Sbr	struct iovec *riov;
145275647Sbr	struct ifnet *ifp;
146275647Sbr	struct mbuf *m;
147275647Sbr	struct uio uio;
148275647Sbr	int enqueued;
149275647Sbr	int iolen;
150275647Sbr	int error;
151275647Sbr	int *addr;
152275647Sbr	int reg;
153275647Sbr	int len;
154275647Sbr	int n;
155275647Sbr
156275647Sbr	VTBE_ASSERT_LOCKED(sc);
157275647Sbr
158275647Sbr	/* RX queue */
159275647Sbr	vq = &sc->vs_queues[0];
160275647Sbr	if (!vq_has_descs(vq)) {
161275647Sbr		return;
162275647Sbr	}
163275647Sbr
164275647Sbr	ifp = sc->ifp;
165275647Sbr	if (ifp->if_drv_flags & IFF_DRV_OACTIVE) {
166275647Sbr		return;
167275647Sbr	}
168275647Sbr
169275647Sbr	enqueued = 0;
170275647Sbr
171275647Sbr	if (!vq_ring_ready(vq))
172275647Sbr		return;
173275647Sbr
174275647Sbr	vq->vq_save_used = be16toh(vq->vq_used->idx);
175275647Sbr
176275647Sbr	for (;;) {
177275647Sbr		if (!vq_has_descs(vq)) {
178275647Sbr			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
179275647Sbr			break;
180275647Sbr		}
181275647Sbr
182275647Sbr		IFQ_DRV_DEQUEUE(&ifp->if_snd, m);
183275647Sbr		if (m == NULL) {
184275647Sbr			break;
185275647Sbr		}
186275647Sbr
187275647Sbr		n = vq_getchain(sc->beri_mem_offset, vq, iov,
188275647Sbr			DESC_COUNT, NULL);
189275647Sbr
190275647Sbr		KASSERT(n >= 1 && n <= DESC_COUNT,
191275647Sbr			("wrong descriptors num %d", n));
192275647Sbr
193275647Sbr		addr = iov[0].iov_base;
194275647Sbr		len = iov[0].iov_len;
195275647Sbr
196275647Sbr		vnh = iov[0].iov_base;
197275647Sbr		memset(vnh, 0, sc->hdrsize);
198275647Sbr		vnh->num_buffers = htobe16(1);
199275647Sbr
200275647Sbr		iov[0].iov_len -= sc->hdrsize;
201275647Sbr		iov[0].iov_base = (void *)((uintptr_t)iov[0].iov_base +
202275647Sbr					sc->hdrsize);
203275647Sbr		riov = &iov[0];
204275647Sbr
205275647Sbr		uio.uio_resid = iov[0].iov_len;
206275647Sbr		uio.uio_iov = riov;
207275647Sbr		uio.uio_segflg = UIO_SYSSPACE;
208275647Sbr		uio.uio_iovcnt = 1;
209275647Sbr		uio.uio_offset = 0;
210275647Sbr		uio.uio_rw = UIO_READ;
211275647Sbr
212275647Sbr		error = m_mbuftouio(&uio, m, 0);
213275647Sbr		if (error)
214275647Sbr			panic("m_mbuftouio failed\n");
215275647Sbr
216275647Sbr		iolen = (len - iov[0].iov_len - sc->hdrsize);
217275647Sbr		vq_relchain(vq, iov, 0, iolen + sc->hdrsize);
218275647Sbr		paddr_unmap((void *)addr, len);
219275647Sbr
220275647Sbr		if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1);
221275647Sbr
222275647Sbr		BPF_MTAP(ifp, m);
223275647Sbr		m_freem(m);
224275647Sbr
225275647Sbr		++enqueued;
226275647Sbr	}
227275647Sbr
228275647Sbr	if (enqueued != 0) {
229275647Sbr		reg = htobe32(VIRTIO_MMIO_INT_VRING);
230275647Sbr		WRITE4(sc, VIRTIO_MMIO_INTERRUPT_STATUS, reg);
231275647Sbr
232275647Sbr		PIO_SET(sc->pio_send, Q_INTR, 1);
233275647Sbr	}
234275647Sbr}
235275647Sbr
236275647Sbrstatic void
237275647Sbrvtbe_txstart(struct ifnet *ifp)
238275647Sbr{
239275647Sbr	struct vtbe_softc *sc = ifp->if_softc;
240275647Sbr
241275647Sbr	VTBE_LOCK(sc);
242275647Sbr	vtbe_txstart_locked(sc);
243275647Sbr	VTBE_UNLOCK(sc);
244275647Sbr}
245275647Sbr
246275647Sbrstatic void
247275647Sbrvtbe_stop_locked(struct vtbe_softc *sc)
248275647Sbr{
249275647Sbr	struct ifnet *ifp;
250275647Sbr
251275647Sbr	VTBE_ASSERT_LOCKED(sc);
252275647Sbr
253275647Sbr	ifp = sc->ifp;
254275647Sbr	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
255275647Sbr}
256275647Sbr
257275647Sbrstatic void
258275647Sbrvtbe_init_locked(struct vtbe_softc *sc)
259275647Sbr{
260275647Sbr	struct ifnet *ifp = sc->ifp;
261275647Sbr
262275647Sbr	VTBE_ASSERT_LOCKED(sc);
263275647Sbr
264275647Sbr	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
265275647Sbr		return;
266275647Sbr
267275647Sbr	ifp->if_drv_flags |= IFF_DRV_RUNNING;
268275647Sbr}
269275647Sbr
270275647Sbrstatic void
271275647Sbrvtbe_init(void *if_softc)
272275647Sbr{
273275647Sbr	struct vtbe_softc *sc = if_softc;
274275647Sbr
275275647Sbr	VTBE_LOCK(sc);
276275647Sbr	vtbe_init_locked(sc);
277275647Sbr	VTBE_UNLOCK(sc);
278275647Sbr}
279275647Sbr
280275647Sbrstatic int
281275647Sbrvtbe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
282275647Sbr{
283275647Sbr	struct ifmediareq *ifmr;
284275647Sbr	struct vtbe_softc *sc;
285275647Sbr	struct ifreq *ifr;
286275647Sbr	int mask, error;
287275647Sbr
288275647Sbr	sc = ifp->if_softc;
289275647Sbr	ifr = (struct ifreq *)data;
290275647Sbr
291275647Sbr	error = 0;
292275647Sbr	switch (cmd) {
293275647Sbr	case SIOCSIFFLAGS:
294275647Sbr		VTBE_LOCK(sc);
295275647Sbr		if (ifp->if_flags & IFF_UP) {
296275647Sbr			pio_enable_irq(sc, 1);
297275647Sbr
298275647Sbr			if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) {
299275647Sbr				vtbe_init_locked(sc);
300275647Sbr			}
301275647Sbr		} else {
302275647Sbr			pio_enable_irq(sc, 0);
303275647Sbr
304275647Sbr			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
305275647Sbr				vtbe_stop_locked(sc);
306275647Sbr			}
307275647Sbr		}
308275647Sbr		sc->if_flags = ifp->if_flags;
309275647Sbr		VTBE_UNLOCK(sc);
310275647Sbr		break;
311275647Sbr	case SIOCADDMULTI:
312275647Sbr	case SIOCDELMULTI:
313275647Sbr		break;
314275647Sbr	case SIOCSIFMEDIA:
315275647Sbr	case SIOCGIFMEDIA:
316275647Sbr		ifmr = (struct ifmediareq *)data;
317275647Sbr		ifmr->ifm_count = 1;
318275647Sbr		ifmr->ifm_status = (IFM_AVALID | IFM_ACTIVE);
319275647Sbr		ifmr->ifm_active = (IFM_ETHER | IFM_10G_T | IFM_FDX);
320275647Sbr		ifmr->ifm_current = ifmr->ifm_active;
321275647Sbr		break;
322275647Sbr	case SIOCSIFCAP:
323275647Sbr		mask = ifp->if_capenable ^ ifr->ifr_reqcap;
324275647Sbr		if (mask & IFCAP_VLAN_MTU) {
325275647Sbr			ifp->if_capenable ^= IFCAP_VLAN_MTU;
326275647Sbr		}
327275647Sbr		break;
328275647Sbr
329275647Sbr	case SIOCSIFADDR:
330275647Sbr		pio_enable_irq(sc, 1);
331275647Sbr	default:
332275647Sbr		error = ether_ioctl(ifp, cmd, data);
333275647Sbr		break;
334275647Sbr	}
335275647Sbr
336275647Sbr	return (error);
337275647Sbr}
338275647Sbr
339275647Sbrstatic void
340275647Sbrvtbe_txfinish_locked(struct vtbe_softc *sc)
341275647Sbr{
342275647Sbr	struct ifnet *ifp;
343275647Sbr
344275647Sbr	VTBE_ASSERT_LOCKED(sc);
345275647Sbr
346275647Sbr	ifp = sc->ifp;
347275647Sbr}
348275647Sbr
349275647Sbrstatic int
350275647Sbrvq_init(struct vtbe_softc *sc)
351275647Sbr{
352275647Sbr	struct vqueue_info *vq;
353275647Sbr	uint8_t *base;
354275647Sbr	int size;
355275647Sbr	int reg;
356275647Sbr	int pfn;
357275647Sbr
358275647Sbr	vq = &sc->vs_queues[sc->vs_curq];
359275647Sbr	vq->vq_qsize = DESC_COUNT;
360275647Sbr
361275647Sbr	reg = READ4(sc, VIRTIO_MMIO_QUEUE_PFN);
362275647Sbr	pfn = be32toh(reg);
363275647Sbr	vq->vq_pfn = pfn;
364275647Sbr
365275647Sbr	size = vring_size(vq->vq_qsize, VRING_ALIGN);
366275647Sbr	base = paddr_map(sc->beri_mem_offset,
367275647Sbr		(pfn << PAGE_SHIFT), size);
368275647Sbr
369275647Sbr	/* First pages are descriptors */
370275647Sbr	vq->vq_desc = (struct vring_desc *)base;
371275647Sbr	base += vq->vq_qsize * sizeof(struct vring_desc);
372275647Sbr
373275647Sbr	/* Then avail ring */
374275647Sbr	vq->vq_avail = (struct vring_avail *)base;
375275647Sbr	base += (2 + vq->vq_qsize + 1) * sizeof(uint16_t);
376275647Sbr
377275647Sbr	/* Then it's rounded up to the next page */
378275647Sbr	base = (uint8_t *)roundup2((uintptr_t)base, VRING_ALIGN);
379275647Sbr
380275647Sbr	/* And the last pages are the used ring */
381275647Sbr	vq->vq_used = (struct vring_used *)base;
382275647Sbr
383275647Sbr	/* Mark queue as allocated, and start at 0 when we use it. */
384275647Sbr	vq->vq_flags = VQ_ALLOC;
385275647Sbr	vq->vq_last_avail = 0;
386275647Sbr
387275647Sbr	return (0);
388275647Sbr}
389275647Sbr
390275647Sbrstatic void
391275647Sbrvtbe_proc_rx(struct vtbe_softc *sc, struct vqueue_info *vq)
392275647Sbr{
393275647Sbr	struct iovec iov[DESC_COUNT];
394275647Sbr	struct ifnet *ifp;
395275647Sbr	struct uio uio;
396275647Sbr	struct mbuf *m;
397275647Sbr	int iolen;
398275647Sbr	int i;
399275647Sbr	int n;
400275647Sbr
401275647Sbr	ifp = sc->ifp;
402275647Sbr
403275647Sbr	n = vq_getchain(sc->beri_mem_offset, vq, iov,
404275647Sbr		DESC_COUNT, NULL);
405275647Sbr
406275647Sbr	KASSERT(n >= 1 && n <= DESC_COUNT,
407275647Sbr		("wrong n %d", n));
408275647Sbr
409275647Sbr	iolen = 0;
410275647Sbr	for (i = 1; i < n; i++) {
411275647Sbr		iolen += iov[i].iov_len;
412275647Sbr	}
413275647Sbr
414275647Sbr	uio.uio_resid = iolen;
415275647Sbr	uio.uio_iov = &iov[1];
416275647Sbr	uio.uio_segflg = UIO_SYSSPACE;
417275647Sbr	uio.uio_iovcnt = (n - 1);
418275647Sbr	uio.uio_rw = UIO_WRITE;
419275647Sbr
420275647Sbr	if ((m = m_uiotombuf(&uio, M_NOWAIT, 0, ETHER_ALIGN,
421275647Sbr	    M_PKTHDR)) == NULL) {
422275647Sbr		if_inc_counter(ifp, IFCOUNTER_IERRORS, 1);
423275647Sbr		goto done;
424275647Sbr	}
425275647Sbr
426275647Sbr	m->m_pkthdr.rcvif = ifp;
427275647Sbr
428275647Sbr	if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1);
429275647Sbr
430275647Sbr	CURVNET_SET(ifp->if_vnet);
431275647Sbr	VTBE_UNLOCK(sc);
432275647Sbr	(*ifp->if_input)(ifp, m);
433275647Sbr	VTBE_LOCK(sc);
434275647Sbr	CURVNET_RESTORE();
435275647Sbr
436275647Sbrdone:
437275647Sbr	vq_relchain(vq, iov, n, iolen + sc->hdrsize);
438275647Sbr}
439275647Sbr
440275647Sbrstatic void
441275647Sbrvtbe_rxfinish_locked(struct vtbe_softc *sc)
442275647Sbr{
443275647Sbr	struct vqueue_info *vq;
444275647Sbr	int reg;
445275647Sbr
446275647Sbr	/* TX queue */
447275647Sbr	vq = &sc->vs_queues[1];
448275647Sbr	if (!vq_ring_ready(vq))
449275647Sbr		return;
450275647Sbr
451275647Sbr	/* Process new descriptors */
452275647Sbr	vq->vq_save_used = be16toh(vq->vq_used->idx);
453275647Sbr
454275647Sbr	while (vq_has_descs(vq)) {
455275647Sbr		vtbe_proc_rx(sc, vq);
456275647Sbr	}
457275647Sbr
458275647Sbr	/* Interrupt the other side */
459275647Sbr	reg = htobe32(VIRTIO_MMIO_INT_VRING);
460275647Sbr	WRITE4(sc, VIRTIO_MMIO_INTERRUPT_STATUS, reg);
461275647Sbr
462275647Sbr	PIO_SET(sc->pio_send, Q_INTR, 1);
463275647Sbr}
464275647Sbr
465275647Sbrstatic void
466275647Sbrvtbe_intr(void *arg)
467275647Sbr{
468275647Sbr	struct vtbe_softc *sc;
469275647Sbr	int pending;
470275647Sbr	uint32_t reg;
471275647Sbr
472275647Sbr	sc = arg;
473275647Sbr
474275647Sbr	VTBE_LOCK(sc);
475275647Sbr
476275647Sbr	reg = PIO_READ(sc->pio_recv);
477275647Sbr
478275647Sbr	/* Ack */
479275647Sbr	PIO_SET(sc->pio_recv, reg, 0);
480275647Sbr
481275647Sbr	pending = htobe32(reg);
482275647Sbr	if (pending & Q_SEL) {
483275647Sbr		reg = READ4(sc, VIRTIO_MMIO_QUEUE_SEL);
484275647Sbr		sc->vs_curq = be32toh(reg);
485275647Sbr	}
486275647Sbr
487275647Sbr	if (pending & Q_PFN) {
488275647Sbr		vq_init(sc);
489275647Sbr	}
490275647Sbr
491275647Sbr	if (pending & Q_NOTIFY) {
492275647Sbr		/* beri rx / arm tx notify */
493275647Sbr		vtbe_txfinish_locked(sc);
494275647Sbr	}
495275647Sbr
496275647Sbr	if (pending & Q_NOTIFY1) {
497275647Sbr		vtbe_rxfinish_locked(sc);
498275647Sbr	}
499275647Sbr
500275647Sbr	VTBE_UNLOCK(sc);
501275647Sbr}
502275647Sbr
503275647Sbrstatic int
504275647Sbrvtbe_get_hwaddr(struct vtbe_softc *sc, uint8_t *hwaddr)
505275647Sbr{
506275647Sbr	int rnd;
507275647Sbr
508275647Sbr	/*
509275647Sbr	 * Generate MAC address, use 'bsd' + random 24 low-order bits.
510275647Sbr	 */
511275647Sbr
512275647Sbr	rnd = arc4random() & 0x00ffffff;
513275647Sbr
514275647Sbr	hwaddr[0] = 'b';
515275647Sbr	hwaddr[1] = 's';
516275647Sbr	hwaddr[2] = 'd';
517275647Sbr	hwaddr[3] = rnd >> 16;
518275647Sbr	hwaddr[4] = rnd >>  8;
519275647Sbr	hwaddr[5] = rnd >>  0;
520275647Sbr
521275647Sbr	return (0);
522275647Sbr}
523275647Sbr
524275647Sbrstatic int
525275647Sbrpio_enable_irq(struct vtbe_softc *sc, int enable)
526275647Sbr{
527275647Sbr
528275647Sbr	/*
529275647Sbr	 * IRQ lines should be disabled while reprogram FPGA core.
530275647Sbr	 */
531275647Sbr
532275647Sbr	if (enable) {
533275647Sbr		if (sc->opened == 0) {
534275647Sbr			sc->opened = 1;
535275647Sbr			PIO_SETUP_IRQ(sc->pio_recv, vtbe_intr, sc);
536275647Sbr		}
537275647Sbr	} else {
538275647Sbr		if (sc->opened == 1) {
539275647Sbr			PIO_TEARDOWN_IRQ(sc->pio_recv);
540275647Sbr			sc->opened = 0;
541275647Sbr		}
542275647Sbr	}
543275647Sbr
544275647Sbr	return (0);
545275647Sbr}
546275647Sbr
547275647Sbrstatic int
548275647Sbrvtbe_probe(device_t dev)
549275647Sbr{
550275647Sbr
551275647Sbr	if (!ofw_bus_status_okay(dev))
552275647Sbr		return (ENXIO);
553275647Sbr
554275647Sbr	if (!ofw_bus_is_compatible(dev, "sri-cambridge,beri-vtnet"))
555275647Sbr		return (ENXIO);
556275647Sbr
557275647Sbr	device_set_desc(dev, "Virtio BERI Ethernet Controller");
558275647Sbr	return (BUS_PROBE_DEFAULT);
559275647Sbr}
560275647Sbr
561275647Sbrstatic int
562275647Sbrvtbe_attach(device_t dev)
563275647Sbr{
564275647Sbr	uint8_t macaddr[ETHER_ADDR_LEN];
565275647Sbr	struct vtbe_softc *sc;
566275647Sbr	struct ifnet *ifp;
567275647Sbr	int reg;
568275647Sbr
569275647Sbr	sc = device_get_softc(dev);
570275647Sbr	sc->dev = dev;
571275647Sbr
572275647Sbr	sc->hdrsize = sizeof(struct virtio_net_hdr_mrg_rxbuf);
573275647Sbr
574275647Sbr	if (bus_alloc_resources(dev, vtbe_spec, sc->res)) {
575275647Sbr		device_printf(dev, "could not allocate resources\n");
576275647Sbr		return (ENXIO);
577275647Sbr	}
578275647Sbr
579275647Sbr	/* Memory interface */
580275647Sbr	sc->bst = rman_get_bustag(sc->res[0]);
581275647Sbr	sc->bsh = rman_get_bushandle(sc->res[0]);
582275647Sbr
583275647Sbr	mtx_init(&sc->mtx, device_get_nameunit(sc->dev),
584275647Sbr	    MTX_NETWORK_LOCK, MTX_DEF);
585275647Sbr
586275647Sbr	if (setup_offset(dev, &sc->beri_mem_offset) != 0)
587275647Sbr		return (ENXIO);
588275647Sbr	if (setup_pio(dev, "pio-send", &sc->pio_send) != 0)
589275647Sbr		return (ENXIO);
590275647Sbr	if (setup_pio(dev, "pio-recv", &sc->pio_recv) != 0)
591275647Sbr		return (ENXIO);
592275647Sbr
593275647Sbr	/* Setup MMIO */
594275647Sbr
595275647Sbr	/* Specify that we provide network device */
596275647Sbr	reg = htobe32(VIRTIO_ID_NETWORK);
597275647Sbr	WRITE4(sc, VIRTIO_MMIO_DEVICE_ID, reg);
598275647Sbr
599275647Sbr	/* The number of desc we support */
600275647Sbr	reg = htobe32(DESC_COUNT);
601275647Sbr	WRITE4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX, reg);
602275647Sbr
603275647Sbr	/* Our features */
604275647Sbr	reg = htobe32(VIRTIO_NET_F_MAC |
605275647Sbr			VIRTIO_NET_F_MRG_RXBUF |
606275647Sbr    			VIRTIO_F_NOTIFY_ON_EMPTY);
607275647Sbr	WRITE4(sc, VIRTIO_MMIO_HOST_FEATURES, reg);
608275647Sbr
609275647Sbr	/* Get MAC */
610275647Sbr	if (vtbe_get_hwaddr(sc, macaddr)) {
611275647Sbr		device_printf(sc->dev, "can't get mac\n");
612275647Sbr		return (ENXIO);
613275647Sbr	}
614275647Sbr
615275647Sbr	/* Set up the ethernet interface. */
616275647Sbr	sc->ifp = ifp = if_alloc(IFT_ETHER);
617275647Sbr	ifp->if_baudrate = IF_Gbps(10);
618275647Sbr	ifp->if_softc = sc;
619275647Sbr	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
620275647Sbr	ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX |
621275647Sbr			 IFF_MULTICAST | IFF_PROMISC);
622275647Sbr	ifp->if_capabilities = IFCAP_VLAN_MTU;
623275647Sbr	ifp->if_capenable = ifp->if_capabilities;
624275647Sbr	ifp->if_start = vtbe_txstart;
625275647Sbr	ifp->if_ioctl = vtbe_ioctl;
626275647Sbr	ifp->if_init = vtbe_init;
627275647Sbr	IFQ_SET_MAXLEN(&ifp->if_snd, DESC_COUNT - 1);
628275647Sbr	ifp->if_snd.ifq_drv_maxlen = DESC_COUNT - 1;
629275647Sbr	IFQ_SET_READY(&ifp->if_snd);
630275647Sbr	ifp->if_hdrlen = sizeof(struct ether_vlan_header);
631275647Sbr
632275647Sbr	/* All ready to run, attach the ethernet interface. */
633275647Sbr	ether_ifattach(ifp, macaddr);
634275647Sbr
635275647Sbr	sc->is_attached = true;
636275647Sbr
637275647Sbr	return (0);
638275647Sbr}
639275647Sbr
640275647Sbrstatic device_method_t vtbe_methods[] = {
641275647Sbr	DEVMETHOD(device_probe,		vtbe_probe),
642275647Sbr	DEVMETHOD(device_attach,	vtbe_attach),
643275647Sbr
644275647Sbr	{ 0, 0 }
645275647Sbr};
646275647Sbr
647275647Sbrstatic driver_t vtbe_driver = {
648275647Sbr	"vtbe",
649275647Sbr	vtbe_methods,
650275647Sbr	sizeof(struct vtbe_softc),
651275647Sbr};
652275647Sbr
653275647Sbrstatic devclass_t vtbe_devclass;
654275647Sbr
655275647SbrDRIVER_MODULE(vtbe, simplebus, vtbe_driver, vtbe_devclass, 0, 0);
656275647SbrMODULE_DEPEND(vtbe, ether, 1, 1, 1);
657