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$"); 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 iovec iov[DESC_COUNT]; 142276710Sbr struct virtio_net_hdr *vnh; 143275647Sbr struct vqueue_info *vq; 144276710Sbr struct iovec *tiov; 145275647Sbr struct ifnet *ifp; 146275647Sbr struct mbuf *m; 147275647Sbr struct uio uio; 148275647Sbr int enqueued; 149275647Sbr int iolen; 150275647Sbr int error; 151275647Sbr int reg; 152275647Sbr int len; 153275647Sbr int n; 154275647Sbr 155275647Sbr VTBE_ASSERT_LOCKED(sc); 156275647Sbr 157275647Sbr /* RX queue */ 158275647Sbr vq = &sc->vs_queues[0]; 159275647Sbr if (!vq_has_descs(vq)) { 160275647Sbr return; 161275647Sbr } 162275647Sbr 163275647Sbr ifp = sc->ifp; 164275647Sbr if (ifp->if_drv_flags & IFF_DRV_OACTIVE) { 165275647Sbr return; 166275647Sbr } 167275647Sbr 168275647Sbr enqueued = 0; 169275647Sbr 170275647Sbr if (!vq_ring_ready(vq)) 171275647Sbr return; 172275647Sbr 173275647Sbr vq->vq_save_used = be16toh(vq->vq_used->idx); 174275647Sbr 175275647Sbr for (;;) { 176275647Sbr if (!vq_has_descs(vq)) { 177275647Sbr ifp->if_drv_flags |= IFF_DRV_OACTIVE; 178275647Sbr break; 179275647Sbr } 180275647Sbr 181275647Sbr IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 182275647Sbr if (m == NULL) { 183275647Sbr break; 184275647Sbr } 185275647Sbr 186275647Sbr n = vq_getchain(sc->beri_mem_offset, vq, iov, 187275647Sbr DESC_COUNT, NULL); 188276710Sbr KASSERT(n == 2, 189276710Sbr ("Unexpected amount of descriptors (%d)", n)); 190275647Sbr 191276710Sbr tiov = getcopy(iov, n); 192275647Sbr vnh = iov[0].iov_base; 193275647Sbr memset(vnh, 0, sc->hdrsize); 194275647Sbr 195276710Sbr len = iov[1].iov_len; 196276710Sbr uio.uio_resid = len; 197276710Sbr uio.uio_iov = &tiov[1]; 198275647Sbr uio.uio_segflg = UIO_SYSSPACE; 199275647Sbr uio.uio_iovcnt = 1; 200275647Sbr uio.uio_offset = 0; 201275647Sbr uio.uio_rw = UIO_READ; 202275647Sbr 203275647Sbr error = m_mbuftouio(&uio, m, 0); 204275647Sbr if (error) 205275647Sbr panic("m_mbuftouio failed\n"); 206275647Sbr 207276710Sbr iolen = (len - uio.uio_resid + sc->hdrsize); 208275647Sbr 209276710Sbr free(tiov, M_DEVBUF); 210276710Sbr vq_relchain(vq, iov, n, iolen); 211276710Sbr 212275647Sbr if_inc_counter(ifp, IFCOUNTER_OPACKETS, 1); 213275647Sbr 214275647Sbr BPF_MTAP(ifp, m); 215275647Sbr m_freem(m); 216275647Sbr 217275647Sbr ++enqueued; 218275647Sbr } 219275647Sbr 220275647Sbr if (enqueued != 0) { 221275647Sbr reg = htobe32(VIRTIO_MMIO_INT_VRING); 222275647Sbr WRITE4(sc, VIRTIO_MMIO_INTERRUPT_STATUS, reg); 223275647Sbr 224275647Sbr PIO_SET(sc->pio_send, Q_INTR, 1); 225275647Sbr } 226275647Sbr} 227275647Sbr 228275647Sbrstatic void 229275647Sbrvtbe_txstart(struct ifnet *ifp) 230275647Sbr{ 231275647Sbr struct vtbe_softc *sc = ifp->if_softc; 232275647Sbr 233275647Sbr VTBE_LOCK(sc); 234275647Sbr vtbe_txstart_locked(sc); 235275647Sbr VTBE_UNLOCK(sc); 236275647Sbr} 237275647Sbr 238275647Sbrstatic void 239275647Sbrvtbe_stop_locked(struct vtbe_softc *sc) 240275647Sbr{ 241275647Sbr struct ifnet *ifp; 242275647Sbr 243275647Sbr VTBE_ASSERT_LOCKED(sc); 244275647Sbr 245275647Sbr ifp = sc->ifp; 246275647Sbr ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 247275647Sbr} 248275647Sbr 249275647Sbrstatic void 250275647Sbrvtbe_init_locked(struct vtbe_softc *sc) 251275647Sbr{ 252275647Sbr struct ifnet *ifp = sc->ifp; 253275647Sbr 254275647Sbr VTBE_ASSERT_LOCKED(sc); 255275647Sbr 256275647Sbr if (ifp->if_drv_flags & IFF_DRV_RUNNING) 257275647Sbr return; 258275647Sbr 259275647Sbr ifp->if_drv_flags |= IFF_DRV_RUNNING; 260275647Sbr} 261275647Sbr 262275647Sbrstatic void 263275647Sbrvtbe_init(void *if_softc) 264275647Sbr{ 265275647Sbr struct vtbe_softc *sc = if_softc; 266275647Sbr 267275647Sbr VTBE_LOCK(sc); 268275647Sbr vtbe_init_locked(sc); 269275647Sbr VTBE_UNLOCK(sc); 270275647Sbr} 271275647Sbr 272275647Sbrstatic int 273275647Sbrvtbe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 274275647Sbr{ 275275647Sbr struct ifmediareq *ifmr; 276275647Sbr struct vtbe_softc *sc; 277275647Sbr struct ifreq *ifr; 278275647Sbr int mask, error; 279275647Sbr 280275647Sbr sc = ifp->if_softc; 281275647Sbr ifr = (struct ifreq *)data; 282275647Sbr 283275647Sbr error = 0; 284275647Sbr switch (cmd) { 285275647Sbr case SIOCSIFFLAGS: 286275647Sbr VTBE_LOCK(sc); 287275647Sbr if (ifp->if_flags & IFF_UP) { 288275647Sbr pio_enable_irq(sc, 1); 289275647Sbr 290275647Sbr if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 291275647Sbr vtbe_init_locked(sc); 292275647Sbr } 293275647Sbr } else { 294275647Sbr pio_enable_irq(sc, 0); 295275647Sbr 296275647Sbr if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 297275647Sbr vtbe_stop_locked(sc); 298275647Sbr } 299275647Sbr } 300275647Sbr sc->if_flags = ifp->if_flags; 301275647Sbr VTBE_UNLOCK(sc); 302275647Sbr break; 303275647Sbr case SIOCADDMULTI: 304275647Sbr case SIOCDELMULTI: 305275647Sbr break; 306275647Sbr case SIOCSIFMEDIA: 307275647Sbr case SIOCGIFMEDIA: 308275647Sbr ifmr = (struct ifmediareq *)data; 309275647Sbr ifmr->ifm_count = 1; 310275647Sbr ifmr->ifm_status = (IFM_AVALID | IFM_ACTIVE); 311275647Sbr ifmr->ifm_active = (IFM_ETHER | IFM_10G_T | IFM_FDX); 312275647Sbr ifmr->ifm_current = ifmr->ifm_active; 313275647Sbr break; 314275647Sbr case SIOCSIFCAP: 315275647Sbr mask = ifp->if_capenable ^ ifr->ifr_reqcap; 316275647Sbr if (mask & IFCAP_VLAN_MTU) { 317275647Sbr ifp->if_capenable ^= IFCAP_VLAN_MTU; 318275647Sbr } 319275647Sbr break; 320275647Sbr 321275647Sbr case SIOCSIFADDR: 322275647Sbr pio_enable_irq(sc, 1); 323275647Sbr default: 324275647Sbr error = ether_ioctl(ifp, cmd, data); 325275647Sbr break; 326275647Sbr } 327275647Sbr 328275647Sbr return (error); 329275647Sbr} 330275647Sbr 331275647Sbrstatic void 332275647Sbrvtbe_txfinish_locked(struct vtbe_softc *sc) 333275647Sbr{ 334275647Sbr struct ifnet *ifp; 335275647Sbr 336275647Sbr VTBE_ASSERT_LOCKED(sc); 337275647Sbr 338275647Sbr ifp = sc->ifp; 339275647Sbr} 340275647Sbr 341275647Sbrstatic int 342275647Sbrvq_init(struct vtbe_softc *sc) 343275647Sbr{ 344275647Sbr struct vqueue_info *vq; 345275647Sbr uint8_t *base; 346275647Sbr int size; 347275647Sbr int reg; 348275647Sbr int pfn; 349275647Sbr 350275647Sbr vq = &sc->vs_queues[sc->vs_curq]; 351275647Sbr vq->vq_qsize = DESC_COUNT; 352275647Sbr 353275647Sbr reg = READ4(sc, VIRTIO_MMIO_QUEUE_PFN); 354275647Sbr pfn = be32toh(reg); 355275647Sbr vq->vq_pfn = pfn; 356275647Sbr 357275647Sbr size = vring_size(vq->vq_qsize, VRING_ALIGN); 358275647Sbr base = paddr_map(sc->beri_mem_offset, 359275647Sbr (pfn << PAGE_SHIFT), size); 360275647Sbr 361275647Sbr /* First pages are descriptors */ 362275647Sbr vq->vq_desc = (struct vring_desc *)base; 363275647Sbr base += vq->vq_qsize * sizeof(struct vring_desc); 364275647Sbr 365275647Sbr /* Then avail ring */ 366275647Sbr vq->vq_avail = (struct vring_avail *)base; 367275647Sbr base += (2 + vq->vq_qsize + 1) * sizeof(uint16_t); 368275647Sbr 369275647Sbr /* Then it's rounded up to the next page */ 370275647Sbr base = (uint8_t *)roundup2((uintptr_t)base, VRING_ALIGN); 371275647Sbr 372275647Sbr /* And the last pages are the used ring */ 373275647Sbr vq->vq_used = (struct vring_used *)base; 374275647Sbr 375275647Sbr /* Mark queue as allocated, and start at 0 when we use it. */ 376275647Sbr vq->vq_flags = VQ_ALLOC; 377275647Sbr vq->vq_last_avail = 0; 378275647Sbr 379275647Sbr return (0); 380275647Sbr} 381275647Sbr 382275647Sbrstatic void 383275647Sbrvtbe_proc_rx(struct vtbe_softc *sc, struct vqueue_info *vq) 384275647Sbr{ 385275647Sbr struct iovec iov[DESC_COUNT]; 386276710Sbr struct iovec *tiov; 387275647Sbr struct ifnet *ifp; 388275647Sbr struct uio uio; 389275647Sbr struct mbuf *m; 390275647Sbr int iolen; 391275647Sbr int i; 392275647Sbr int n; 393275647Sbr 394275647Sbr ifp = sc->ifp; 395275647Sbr 396275647Sbr n = vq_getchain(sc->beri_mem_offset, vq, iov, 397275647Sbr DESC_COUNT, NULL); 398275647Sbr 399275647Sbr KASSERT(n >= 1 && n <= DESC_COUNT, 400275647Sbr ("wrong n %d", n)); 401275647Sbr 402276710Sbr tiov = getcopy(iov, n); 403276710Sbr 404275647Sbr iolen = 0; 405275647Sbr for (i = 1; i < n; i++) { 406275647Sbr iolen += iov[i].iov_len; 407275647Sbr } 408275647Sbr 409275647Sbr uio.uio_resid = iolen; 410276710Sbr uio.uio_iov = &tiov[1]; 411275647Sbr uio.uio_segflg = UIO_SYSSPACE; 412275647Sbr uio.uio_iovcnt = (n - 1); 413275647Sbr uio.uio_rw = UIO_WRITE; 414275647Sbr 415275647Sbr if ((m = m_uiotombuf(&uio, M_NOWAIT, 0, ETHER_ALIGN, 416275647Sbr M_PKTHDR)) == NULL) { 417275647Sbr if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); 418275647Sbr goto done; 419275647Sbr } 420275647Sbr 421275647Sbr m->m_pkthdr.rcvif = ifp; 422275647Sbr 423275647Sbr if_inc_counter(ifp, IFCOUNTER_IPACKETS, 1); 424275647Sbr 425275647Sbr CURVNET_SET(ifp->if_vnet); 426275647Sbr VTBE_UNLOCK(sc); 427275647Sbr (*ifp->if_input)(ifp, m); 428275647Sbr VTBE_LOCK(sc); 429275647Sbr CURVNET_RESTORE(); 430275647Sbr 431275647Sbrdone: 432276710Sbr free(tiov, M_DEVBUF); 433275647Sbr vq_relchain(vq, iov, n, iolen + sc->hdrsize); 434275647Sbr} 435275647Sbr 436275647Sbrstatic void 437275647Sbrvtbe_rxfinish_locked(struct vtbe_softc *sc) 438275647Sbr{ 439275647Sbr struct vqueue_info *vq; 440275647Sbr int reg; 441275647Sbr 442275647Sbr /* TX queue */ 443275647Sbr vq = &sc->vs_queues[1]; 444275647Sbr if (!vq_ring_ready(vq)) 445275647Sbr return; 446275647Sbr 447275647Sbr /* Process new descriptors */ 448275647Sbr vq->vq_save_used = be16toh(vq->vq_used->idx); 449275647Sbr 450275647Sbr while (vq_has_descs(vq)) { 451275647Sbr vtbe_proc_rx(sc, vq); 452275647Sbr } 453275647Sbr 454275647Sbr /* Interrupt the other side */ 455275647Sbr reg = htobe32(VIRTIO_MMIO_INT_VRING); 456275647Sbr WRITE4(sc, VIRTIO_MMIO_INTERRUPT_STATUS, reg); 457275647Sbr 458275647Sbr PIO_SET(sc->pio_send, Q_INTR, 1); 459275647Sbr} 460275647Sbr 461275647Sbrstatic void 462275647Sbrvtbe_intr(void *arg) 463275647Sbr{ 464275647Sbr struct vtbe_softc *sc; 465275647Sbr int pending; 466275647Sbr uint32_t reg; 467275647Sbr 468275647Sbr sc = arg; 469275647Sbr 470275647Sbr VTBE_LOCK(sc); 471275647Sbr 472275647Sbr reg = PIO_READ(sc->pio_recv); 473275647Sbr 474275647Sbr /* Ack */ 475275647Sbr PIO_SET(sc->pio_recv, reg, 0); 476275647Sbr 477275647Sbr pending = htobe32(reg); 478275647Sbr if (pending & Q_SEL) { 479275647Sbr reg = READ4(sc, VIRTIO_MMIO_QUEUE_SEL); 480275647Sbr sc->vs_curq = be32toh(reg); 481275647Sbr } 482275647Sbr 483275647Sbr if (pending & Q_PFN) { 484275647Sbr vq_init(sc); 485275647Sbr } 486275647Sbr 487275647Sbr if (pending & Q_NOTIFY) { 488275647Sbr /* beri rx / arm tx notify */ 489275647Sbr vtbe_txfinish_locked(sc); 490275647Sbr } 491275647Sbr 492275647Sbr if (pending & Q_NOTIFY1) { 493275647Sbr vtbe_rxfinish_locked(sc); 494275647Sbr } 495275647Sbr 496275647Sbr VTBE_UNLOCK(sc); 497275647Sbr} 498275647Sbr 499275647Sbrstatic int 500275647Sbrvtbe_get_hwaddr(struct vtbe_softc *sc, uint8_t *hwaddr) 501275647Sbr{ 502275647Sbr int rnd; 503275647Sbr 504275647Sbr /* 505275647Sbr * Generate MAC address, use 'bsd' + random 24 low-order bits. 506275647Sbr */ 507275647Sbr 508275647Sbr rnd = arc4random() & 0x00ffffff; 509275647Sbr 510275647Sbr hwaddr[0] = 'b'; 511275647Sbr hwaddr[1] = 's'; 512275647Sbr hwaddr[2] = 'd'; 513275647Sbr hwaddr[3] = rnd >> 16; 514275647Sbr hwaddr[4] = rnd >> 8; 515275647Sbr hwaddr[5] = rnd >> 0; 516275647Sbr 517275647Sbr return (0); 518275647Sbr} 519275647Sbr 520275647Sbrstatic int 521275647Sbrpio_enable_irq(struct vtbe_softc *sc, int enable) 522275647Sbr{ 523275647Sbr 524275647Sbr /* 525275647Sbr * IRQ lines should be disabled while reprogram FPGA core. 526275647Sbr */ 527275647Sbr 528275647Sbr if (enable) { 529275647Sbr if (sc->opened == 0) { 530275647Sbr sc->opened = 1; 531275647Sbr PIO_SETUP_IRQ(sc->pio_recv, vtbe_intr, sc); 532275647Sbr } 533275647Sbr } else { 534275647Sbr if (sc->opened == 1) { 535275647Sbr PIO_TEARDOWN_IRQ(sc->pio_recv); 536275647Sbr sc->opened = 0; 537275647Sbr } 538275647Sbr } 539275647Sbr 540275647Sbr return (0); 541275647Sbr} 542275647Sbr 543275647Sbrstatic int 544275647Sbrvtbe_probe(device_t dev) 545275647Sbr{ 546275647Sbr 547275647Sbr if (!ofw_bus_status_okay(dev)) 548275647Sbr return (ENXIO); 549275647Sbr 550275647Sbr if (!ofw_bus_is_compatible(dev, "sri-cambridge,beri-vtnet")) 551275647Sbr return (ENXIO); 552275647Sbr 553275647Sbr device_set_desc(dev, "Virtio BERI Ethernet Controller"); 554275647Sbr return (BUS_PROBE_DEFAULT); 555275647Sbr} 556275647Sbr 557275647Sbrstatic int 558275647Sbrvtbe_attach(device_t dev) 559275647Sbr{ 560275647Sbr uint8_t macaddr[ETHER_ADDR_LEN]; 561275647Sbr struct vtbe_softc *sc; 562275647Sbr struct ifnet *ifp; 563275647Sbr int reg; 564275647Sbr 565275647Sbr sc = device_get_softc(dev); 566275647Sbr sc->dev = dev; 567275647Sbr 568276710Sbr sc->hdrsize = sizeof(struct virtio_net_hdr); 569275647Sbr 570275647Sbr if (bus_alloc_resources(dev, vtbe_spec, sc->res)) { 571275647Sbr device_printf(dev, "could not allocate resources\n"); 572275647Sbr return (ENXIO); 573275647Sbr } 574275647Sbr 575275647Sbr /* Memory interface */ 576275647Sbr sc->bst = rman_get_bustag(sc->res[0]); 577275647Sbr sc->bsh = rman_get_bushandle(sc->res[0]); 578275647Sbr 579275647Sbr mtx_init(&sc->mtx, device_get_nameunit(sc->dev), 580275647Sbr MTX_NETWORK_LOCK, MTX_DEF); 581275647Sbr 582275647Sbr if (setup_offset(dev, &sc->beri_mem_offset) != 0) 583275647Sbr return (ENXIO); 584275647Sbr if (setup_pio(dev, "pio-send", &sc->pio_send) != 0) 585275647Sbr return (ENXIO); 586275647Sbr if (setup_pio(dev, "pio-recv", &sc->pio_recv) != 0) 587275647Sbr return (ENXIO); 588275647Sbr 589275647Sbr /* Setup MMIO */ 590275647Sbr 591275647Sbr /* Specify that we provide network device */ 592275647Sbr reg = htobe32(VIRTIO_ID_NETWORK); 593275647Sbr WRITE4(sc, VIRTIO_MMIO_DEVICE_ID, reg); 594275647Sbr 595275647Sbr /* The number of desc we support */ 596275647Sbr reg = htobe32(DESC_COUNT); 597275647Sbr WRITE4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX, reg); 598275647Sbr 599275647Sbr /* Our features */ 600275647Sbr reg = htobe32(VIRTIO_NET_F_MAC | 601275647Sbr VIRTIO_F_NOTIFY_ON_EMPTY); 602275647Sbr WRITE4(sc, VIRTIO_MMIO_HOST_FEATURES, reg); 603275647Sbr 604275647Sbr /* Get MAC */ 605275647Sbr if (vtbe_get_hwaddr(sc, macaddr)) { 606275647Sbr device_printf(sc->dev, "can't get mac\n"); 607275647Sbr return (ENXIO); 608275647Sbr } 609275647Sbr 610275647Sbr /* Set up the ethernet interface. */ 611275647Sbr sc->ifp = ifp = if_alloc(IFT_ETHER); 612275647Sbr ifp->if_baudrate = IF_Gbps(10); 613275647Sbr ifp->if_softc = sc; 614275647Sbr if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 615275647Sbr ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | 616275647Sbr IFF_MULTICAST | IFF_PROMISC); 617275647Sbr ifp->if_capabilities = IFCAP_VLAN_MTU; 618275647Sbr ifp->if_capenable = ifp->if_capabilities; 619275647Sbr ifp->if_start = vtbe_txstart; 620275647Sbr ifp->if_ioctl = vtbe_ioctl; 621275647Sbr ifp->if_init = vtbe_init; 622275647Sbr IFQ_SET_MAXLEN(&ifp->if_snd, DESC_COUNT - 1); 623275647Sbr ifp->if_snd.ifq_drv_maxlen = DESC_COUNT - 1; 624275647Sbr IFQ_SET_READY(&ifp->if_snd); 625275647Sbr ifp->if_hdrlen = sizeof(struct ether_vlan_header); 626275647Sbr 627275647Sbr /* All ready to run, attach the ethernet interface. */ 628275647Sbr ether_ifattach(ifp, macaddr); 629275647Sbr 630275647Sbr sc->is_attached = true; 631275647Sbr 632275647Sbr return (0); 633275647Sbr} 634275647Sbr 635275647Sbrstatic device_method_t vtbe_methods[] = { 636275647Sbr DEVMETHOD(device_probe, vtbe_probe), 637275647Sbr DEVMETHOD(device_attach, vtbe_attach), 638275647Sbr 639275647Sbr { 0, 0 } 640275647Sbr}; 641275647Sbr 642275647Sbrstatic driver_t vtbe_driver = { 643275647Sbr "vtbe", 644275647Sbr vtbe_methods, 645275647Sbr sizeof(struct vtbe_softc), 646275647Sbr}; 647275647Sbr 648275647Sbrstatic devclass_t vtbe_devclass; 649275647Sbr 650275647SbrDRIVER_MODULE(vtbe, simplebus, vtbe_driver, vtbe_devclass, 0, 0); 651275647SbrMODULE_DEPEND(vtbe, ether, 1, 1, 1); 652