misc: mic: MIC VOP Bus

The Virtio Over PCIe (VOP) bus abstracts the low level hardware
details like interrupts and mapping remote memory so that the same VOP
driver can work without changes with different MIC host or card
drivers as long as the hardware bus operations are implemented. The
VOP driver registers itself on the VOP bus. The base PCIe drivers
implement the bus ops and register VOP devices on the bus, resulting
in the VOP driver being probed with the VOP devices. This allows the
VOP functionality to be shared between multiple generations of Intel
MIC products.

Reviewed-by: Ashutosh Dixit <ashutosh.dixit@intel.com>
Signed-off-by: Sudeep Dutt <sudeep.dutt@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Sudeep Dutt 2016-02-08 15:48:13 -08:00 committed by Greg Kroah-Hartman
parent b73c295833
commit a19ddd6fd2
4 changed files with 361 additions and 0 deletions

View File

@ -32,6 +32,23 @@ config SCIF_BUS
OS and tools for MIC to use with this driver are available from
<http://software.intel.com/en-us/mic-developer>.
comment "VOP Bus Driver"
config VOP_BUS
tristate "VOP Bus Driver"
depends on 64BIT && PCI && X86 && X86_DEV_DMA_OPS
help
This option is selected by any driver which registers a
device or driver on the VOP Bus, such as CONFIG_INTEL_MIC_HOST
and CONFIG_INTEL_MIC_CARD.
If you are building a host/card kernel with an Intel MIC device
then say M (recommended) or Y, else say N. If unsure say N.
More information about the Intel MIC family as well as the Linux
OS and tools for MIC to use with this driver are available from
<http://software.intel.com/en-us/mic-developer>.
comment "Intel MIC Host Driver"
config INTEL_MIC_HOST

View File

@ -5,3 +5,4 @@
obj-$(CONFIG_INTEL_MIC_BUS) += mic_bus.o
obj-$(CONFIG_SCIF_BUS) += scif_bus.o
obj-$(CONFIG_MIC_COSM) += cosm_bus.o
obj-$(CONFIG_VOP_BUS) += vop_bus.o

View File

@ -0,0 +1,203 @@
/*
* Intel MIC Platform Software Stack (MPSS)
*
* Copyright(c) 2016 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
* Intel Virtio Over PCIe (VOP) Bus driver.
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/idr.h>
#include <linux/dma-mapping.h>
#include "vop_bus.h"
static ssize_t device_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct vop_device *dev = dev_to_vop(d);
return sprintf(buf, "0x%04x\n", dev->id.device);
}
static DEVICE_ATTR_RO(device);
static ssize_t vendor_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct vop_device *dev = dev_to_vop(d);
return sprintf(buf, "0x%04x\n", dev->id.vendor);
}
static DEVICE_ATTR_RO(vendor);
static ssize_t modalias_show(struct device *d,
struct device_attribute *attr, char *buf)
{
struct vop_device *dev = dev_to_vop(d);
return sprintf(buf, "vop:d%08Xv%08X\n",
dev->id.device, dev->id.vendor);
}
static DEVICE_ATTR_RO(modalias);
static struct attribute *vop_dev_attrs[] = {
&dev_attr_device.attr,
&dev_attr_vendor.attr,
&dev_attr_modalias.attr,
NULL,
};
ATTRIBUTE_GROUPS(vop_dev);
static inline int vop_id_match(const struct vop_device *dev,
const struct vop_device_id *id)
{
if (id->device != dev->id.device && id->device != VOP_DEV_ANY_ID)
return 0;
return id->vendor == VOP_DEV_ANY_ID || id->vendor == dev->id.vendor;
}
/*
* This looks through all the IDs a driver claims to support. If any of them
* match, we return 1 and the kernel will call vop_dev_probe().
*/
static int vop_dev_match(struct device *dv, struct device_driver *dr)
{
unsigned int i;
struct vop_device *dev = dev_to_vop(dv);
const struct vop_device_id *ids;
ids = drv_to_vop(dr)->id_table;
for (i = 0; ids[i].device; i++)
if (vop_id_match(dev, &ids[i]))
return 1;
return 0;
}
static int vop_uevent(struct device *dv, struct kobj_uevent_env *env)
{
struct vop_device *dev = dev_to_vop(dv);
return add_uevent_var(env, "MODALIAS=vop:d%08Xv%08X",
dev->id.device, dev->id.vendor);
}
static int vop_dev_probe(struct device *d)
{
struct vop_device *dev = dev_to_vop(d);
struct vop_driver *drv = drv_to_vop(dev->dev.driver);
return drv->probe(dev);
}
static int vop_dev_remove(struct device *d)
{
struct vop_device *dev = dev_to_vop(d);
struct vop_driver *drv = drv_to_vop(dev->dev.driver);
drv->remove(dev);
return 0;
}
static struct bus_type vop_bus = {
.name = "vop_bus",
.match = vop_dev_match,
.dev_groups = vop_dev_groups,
.uevent = vop_uevent,
.probe = vop_dev_probe,
.remove = vop_dev_remove,
};
int vop_register_driver(struct vop_driver *driver)
{
driver->driver.bus = &vop_bus;
return driver_register(&driver->driver);
}
EXPORT_SYMBOL_GPL(vop_register_driver);
void vop_unregister_driver(struct vop_driver *driver)
{
driver_unregister(&driver->driver);
}
EXPORT_SYMBOL_GPL(vop_unregister_driver);
static void vop_release_dev(struct device *d)
{
put_device(d);
}
struct vop_device *
vop_register_device(struct device *pdev, int id,
const struct dma_map_ops *dma_ops,
struct vop_hw_ops *hw_ops, u8 dnode, struct mic_mw *aper,
struct dma_chan *chan)
{
int ret;
struct vop_device *vdev;
vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
if (!vdev)
return ERR_PTR(-ENOMEM);
vdev->dev.parent = pdev;
vdev->id.device = id;
vdev->id.vendor = VOP_DEV_ANY_ID;
vdev->dev.archdata.dma_ops = (struct dma_map_ops *)dma_ops;
vdev->dev.dma_mask = &vdev->dev.coherent_dma_mask;
dma_set_mask(&vdev->dev, DMA_BIT_MASK(64));
vdev->dev.release = vop_release_dev;
vdev->hw_ops = hw_ops;
vdev->dev.bus = &vop_bus;
vdev->dnode = dnode;
vdev->aper = aper;
vdev->dma_ch = chan;
vdev->index = dnode - 1;
dev_set_name(&vdev->dev, "vop-dev%u", vdev->index);
/*
* device_register() causes the bus infrastructure to look for a
* matching driver.
*/
ret = device_register(&vdev->dev);
if (ret)
goto free_vdev;
return vdev;
free_vdev:
kfree(vdev);
return ERR_PTR(ret);
}
EXPORT_SYMBOL_GPL(vop_register_device);
void vop_unregister_device(struct vop_device *dev)
{
device_unregister(&dev->dev);
}
EXPORT_SYMBOL_GPL(vop_unregister_device);
static int __init vop_init(void)
{
return bus_register(&vop_bus);
}
static void __exit vop_exit(void)
{
bus_unregister(&vop_bus);
}
core_initcall(vop_init);
module_exit(vop_exit);
MODULE_AUTHOR("Intel Corporation");
MODULE_DESCRIPTION("Intel(R) VOP Bus driver");
MODULE_LICENSE("GPL v2");

View File

@ -0,0 +1,140 @@
/*
* Intel MIC Platform Software Stack (MPSS)
*
* Copyright(c) 2016 Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2, as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
* Intel Virtio over PCIe Bus driver.
*/
#ifndef _VOP_BUS_H_
#define _VOP_BUS_H_
/*
* Everything a vop driver needs to work with any particular vop
* implementation.
*/
#include <linux/dmaengine.h>
#include <linux/interrupt.h>
#include "../common/mic_dev.h"
struct vop_device_id {
u32 device;
u32 vendor;
};
#define VOP_DEV_TRNSP 1
#define VOP_DEV_ANY_ID 0xffffffff
/*
* Size of the internal buffer used during DMA's as an intermediate buffer
* for copy to/from user. Must be an integral number of pages.
*/
#define VOP_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL)
/**
* vop_device - representation of a device using vop
* @hw_ops: the hardware ops supported by this device.
* @id: the device type identification (used to match it with a driver).
* @dev: underlying device.
* @dnode - The destination node which this device will communicate with.
* @aper: Aperture memory window
* @dma_ch - DMA channel
* @index: unique position on the vop bus
*/
struct vop_device {
struct vop_hw_ops *hw_ops;
struct vop_device_id id;
struct device dev;
u8 dnode;
struct mic_mw *aper;
struct dma_chan *dma_ch;
int index;
};
/**
* vop_driver - operations for a vop I/O driver
* @driver: underlying device driver (populate name and owner).
* @id_table: the ids serviced by this driver.
* @probe: the function to call when a device is found. Returns 0 or -errno.
* @remove: the function to call when a device is removed.
*/
struct vop_driver {
struct device_driver driver;
const struct vop_device_id *id_table;
int (*probe)(struct vop_device *dev);
void (*remove)(struct vop_device *dev);
};
/**
* vop_hw_ops - Hardware operations for accessing a VOP device on the VOP bus.
*
* @next_db: Obtain the next available doorbell.
* @request_irq: Request an interrupt on a particular doorbell.
* @free_irq: Free an interrupt requested previously.
* @ack_interrupt: acknowledge an interrupt in the ISR.
* @get_remote_dp: Get access to the virtio device page used by the remote
* node to add/remove/configure virtio devices.
* @get_dp: Get access to the virtio device page used by the self
* node to add/remove/configure virtio devices.
* @send_intr: Send an interrupt to the peer node on a specified doorbell.
* @ioremap: Map a buffer with the specified DMA address and length.
* @iounmap: Unmap a buffer previously mapped.
* @dma_filter: The DMA filter function to use for obtaining access to
* a DMA channel on the peer node.
*/
struct vop_hw_ops {
int (*next_db)(struct vop_device *vpdev);
struct mic_irq *(*request_irq)(struct vop_device *vpdev,
irqreturn_t (*func)(int irq, void *data),
const char *name, void *data,
int intr_src);
void (*free_irq)(struct vop_device *vpdev,
struct mic_irq *cookie, void *data);
void (*ack_interrupt)(struct vop_device *vpdev, int num);
void __iomem * (*get_remote_dp)(struct vop_device *vpdev);
void * (*get_dp)(struct vop_device *vpdev);
void (*send_intr)(struct vop_device *vpdev, int db);
void __iomem * (*ioremap)(struct vop_device *vpdev,
dma_addr_t pa, size_t len);
void (*iounmap)(struct vop_device *vpdev, void __iomem *va);
};
struct vop_device *
vop_register_device(struct device *pdev, int id,
const struct dma_map_ops *dma_ops,
struct vop_hw_ops *hw_ops, u8 dnode, struct mic_mw *aper,
struct dma_chan *chan);
void vop_unregister_device(struct vop_device *dev);
int vop_register_driver(struct vop_driver *drv);
void vop_unregister_driver(struct vop_driver *drv);
/*
* module_vop_driver() - Helper macro for drivers that don't do
* anything special in module init/exit. This eliminates a lot of
* boilerplate. Each module may only use this macro once, and
* calling it replaces module_init() and module_exit()
*/
#define module_vop_driver(__vop_driver) \
module_driver(__vop_driver, vop_register_driver, \
vop_unregister_driver)
static inline struct vop_device *dev_to_vop(struct device *dev)
{
return container_of(dev, struct vop_device, dev);
}
static inline struct vop_driver *drv_to_vop(struct device_driver *drv)
{
return container_of(drv, struct vop_driver, driver);
}
#endif /* _VOP_BUS_H */