blob: 0eb009104631dd70c7a11e232ac0186d32ba61dd [file] [log] [blame]
/******************************************************************************
* Copyright (c) 2011 IBM Corporation
* All rights reserved.
* This program and the accompanying materials
* are made available under the terms of the BSD License
* which accompanies this distribution, and is available at
* http://www.opensource.org/licenses/bsd-license.php
*
* Contributors:
* IBM Corporation - initial implementation
*****************************************************************************/
#include <stdarg.h>
#include <string.h>
#include <cache.h>
#include <byteorder.h>
#include <netdriver_int.h>
#include <libhvcall.h>
#include <virtio.h>
#include "virtio-net.h"
#define VIRTIOHDR_DEVICE_FEATURES 0
#define VIRTIOHDR_GUEST_FEATURES 4
#define VIRTIOHDR_QUEUE_ADDRESS 8
#define VIRTIOHDR_QUEUE_SIZE 12
#define VIRTIOHDR_QUEUE_SELECT 14
#define VIRTIOHDR_QUEUE_NOTIFY 16
#define VIRTIOHDR_DEVICE_STATUS 18
#define VIRTIOHDR_ISR_STATUS 19
#define VIRTIOHDR_MAC_ADDRESS 20
/**
* Module init for virtio via PCI.
* Checks whether we're reponsible for the given device and set up
* the virtqueue configuration.
*/
int
vn_module_init_pci(snk_kernel_t *snk_kernel_int, pci_config_t *conf)
{
uint64_t bar;
int i;
dprintk("virtionet: doing virtionet_module_init_pci!\n");
virtiodev.type = VIRTIO_TYPE_PCI;
/* Check whether the driver can handle this device by verifying vendor,
* device id and class code. */
if (conf->vendor_id != 0x1af4) {
dprintk("virtionet: unsupported vendor id\n");
return -1;
}
if (conf->device_id < 0x1000 || conf->device_id > 0x103f) {
dprintk("virtionet: unsupported device id\n");
return -1;
}
if (conf->class_code != 0x20000) {
dprintk("virtionet: unsupported class code\n");
return -1;
}
bar = snk_kernel_interface->pci_config_read(conf->puid, 4, conf->bus,
conf->devfn, 0x10);
if (!(bar & 1)) {
printk("First BAR is not an I/O BAR!\n");
return -1;
}
bar &= ~3ULL;
dprintk("untranslated bar = %llx\n", bar);
snk_kernel_interface->translate_addr((void *)&bar);
dprintk("translated bar = %llx\n", bar);
virtiodev.base = (void*)bar;
/* Reset device */
virtio_reset_device(&virtiodev);
/* The queue information can be retrieved via the virtio header that
* can be found in the I/O BAR. First queue is the receive queue,
* second the transmit queue, and the forth is the control queue for
* networking options.
* We are only interested in the receive and transmit queue here. */
for (i=VQ_RX; i<=VQ_TX; i++) {
/* Select ring (0=RX, 1=TX): */
vq[i].id = i-VQ_RX;
ci_write_16(virtiodev.base+VIRTIOHDR_QUEUE_SELECT,
cpu_to_le16(vq[i].id));
vq[i].size = le16_to_cpu(ci_read_16(virtiodev.base+VIRTIOHDR_QUEUE_SIZE));
vq[i].desc = malloc_aligned(virtio_vring_size(vq[i].size), 4096);
if (!vq[i].desc) {
printk("malloc failed!\n");
return -1;
}
memset(vq[i].desc, 0, virtio_vring_size(vq[i].size));
ci_write_32(virtiodev.base+VIRTIOHDR_QUEUE_ADDRESS,
cpu_to_le32((long)vq[i].desc / 4096));
vq[i].avail = (void*)vq[i].desc
+ vq[i].size * sizeof(struct vring_desc);
vq[i].used = (void*)VQ_ALIGN((long)vq[i].avail
+ vq[i].size * sizeof(struct vring_avail));
dprintk("%i: vq.id = %lx\nvq.size =%lx\n vq.avail =%lx\nvq.used=%lx\n",
i, vq[i].id, vq[i].size, vq[i].avail, vq[i].used);
}
/* Copy MAC address */
for (i = 0; i < 6; i++) {
virtionet_interface.mac_addr[i]
= ci_read_8(virtiodev.base+VIRTIOHDR_MAC_ADDRESS+i);
}
/* Acknowledge device. */
virtio_set_status(&virtiodev, VIRTIO_STAT_ACKNOWLEDGE);
return 0;
}