diff options
Diffstat (limited to 'drivers/misc/mic')
-rw-r--r-- | drivers/misc/mic/Kconfig | 21 | ||||
-rw-r--r-- | drivers/misc/mic/Makefile | 1 | ||||
-rw-r--r-- | drivers/misc/mic/bus/Makefile | 5 | ||||
-rw-r--r-- | drivers/misc/mic/bus/mic_bus.c | 218 | ||||
-rw-r--r-- | drivers/misc/mic/card/mic_device.c | 23 | ||||
-rw-r--r-- | drivers/misc/mic/card/mic_device.h | 9 | ||||
-rw-r--r-- | drivers/misc/mic/card/mic_virtio.c | 7 | ||||
-rw-r--r-- | drivers/misc/mic/card/mic_x100.c | 62 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_boot.c | 83 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_device.h | 24 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_intr.c | 121 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_intr.h | 27 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_main.c | 7 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_virtio.c | 187 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_virtio.h | 21 | ||||
-rw-r--r-- | drivers/misc/mic/host/mic_x100.c | 8 |
16 files changed, 700 insertions, 124 deletions
diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 462a5b1d8651..cc4eef040c14 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -1,8 +1,25 @@ +comment "Intel MIC Bus Driver" + +config INTEL_MIC_BUS + tristate "Intel MIC 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 MIC Bus, such as CONFIG_INTEL_MIC_HOST, + CONFIG_INTEL_MIC_CARD, CONFIG_INTEL_MIC_X100_DMA etc. + + 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 tristate "Intel MIC Host Driver" - depends on 64BIT && PCI && X86 + depends on 64BIT && PCI && X86 && INTEL_MIC_BUS select VHOST_RING help This enables Host Driver support for the Intel Many Integrated @@ -22,7 +39,7 @@ comment "Intel MIC Card Driver" config INTEL_MIC_CARD tristate "Intel MIC Card Driver" - depends on 64BIT && X86 + depends on 64BIT && X86 && INTEL_MIC_BUS select VIRTIO help This enables card driver support for the Intel Many Integrated diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile index 05b34d683a58..e9bf148755e2 100644 --- a/drivers/misc/mic/Makefile +++ b/drivers/misc/mic/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_INTEL_MIC_HOST) += host/ obj-$(CONFIG_INTEL_MIC_CARD) += card/ +obj-$(CONFIG_INTEL_MIC_BUS) += bus/ diff --git a/drivers/misc/mic/bus/Makefile b/drivers/misc/mic/bus/Makefile new file mode 100644 index 000000000000..d85c7f2a0af4 --- /dev/null +++ b/drivers/misc/mic/bus/Makefile @@ -0,0 +1,5 @@ +# +# Makefile - Intel MIC Linux driver. +# Copyright(c) 2014, Intel Corporation. +# +obj-$(CONFIG_INTEL_MIC_BUS) += mic_bus.o diff --git a/drivers/misc/mic/bus/mic_bus.c b/drivers/misc/mic/bus/mic_bus.c new file mode 100644 index 000000000000..961ae90aae47 --- /dev/null +++ b/drivers/misc/mic/bus/mic_bus.c @@ -0,0 +1,218 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2014 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 MIC Bus driver. + * + * This implementation is very similar to the the virtio bus driver + * implementation @ drivers/virtio/virtio.c + */ +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/idr.h> +#include <linux/mic_bus.h> + +/* Unique numbering for mbus devices. */ +static DEFINE_IDA(mbus_index_ida); + +static ssize_t device_show(struct device *d, + struct device_attribute *attr, char *buf) +{ + struct mbus_device *dev = dev_to_mbus(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 mbus_device *dev = dev_to_mbus(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 mbus_device *dev = dev_to_mbus(d); + return sprintf(buf, "mbus:d%08Xv%08X\n", + dev->id.device, dev->id.vendor); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *mbus_dev_attrs[] = { + &dev_attr_device.attr, + &dev_attr_vendor.attr, + &dev_attr_modalias.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mbus_dev); + +static inline int mbus_id_match(const struct mbus_device *dev, + const struct mbus_device_id *id) +{ + if (id->device != dev->id.device && id->device != MBUS_DEV_ANY_ID) + return 0; + + return id->vendor == MBUS_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 mbus_dev_probe(). + */ +static int mbus_dev_match(struct device *dv, struct device_driver *dr) +{ + unsigned int i; + struct mbus_device *dev = dev_to_mbus(dv); + const struct mbus_device_id *ids; + + ids = drv_to_mbus(dr)->id_table; + for (i = 0; ids[i].device; i++) + if (mbus_id_match(dev, &ids[i])) + return 1; + return 0; +} + +static int mbus_uevent(struct device *dv, struct kobj_uevent_env *env) +{ + struct mbus_device *dev = dev_to_mbus(dv); + + return add_uevent_var(env, "MODALIAS=mbus:d%08Xv%08X", + dev->id.device, dev->id.vendor); +} + +static int mbus_dev_probe(struct device *d) +{ + int err; + struct mbus_device *dev = dev_to_mbus(d); + struct mbus_driver *drv = drv_to_mbus(dev->dev.driver); + + err = drv->probe(dev); + if (!err) + if (drv->scan) + drv->scan(dev); + return err; +} + +static int mbus_dev_remove(struct device *d) +{ + struct mbus_device *dev = dev_to_mbus(d); + struct mbus_driver *drv = drv_to_mbus(dev->dev.driver); + + drv->remove(dev); + return 0; +} + +static struct bus_type mic_bus = { + .name = "mic_bus", + .match = mbus_dev_match, + .dev_groups = mbus_dev_groups, + .uevent = mbus_uevent, + .probe = mbus_dev_probe, + .remove = mbus_dev_remove, +}; + +int mbus_register_driver(struct mbus_driver *driver) +{ + driver->driver.bus = &mic_bus; + return driver_register(&driver->driver); +} +EXPORT_SYMBOL_GPL(mbus_register_driver); + +void mbus_unregister_driver(struct mbus_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_GPL(mbus_unregister_driver); + +static void mbus_release_dev(struct device *d) +{ + struct mbus_device *mbdev = dev_to_mbus(d); + kfree(mbdev); +} + +struct mbus_device * +mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops, + struct mbus_hw_ops *hw_ops, void __iomem *mmio_va) +{ + int ret; + struct mbus_device *mbdev; + + mbdev = kzalloc(sizeof(*mbdev), GFP_KERNEL); + if (!mbdev) + return ERR_PTR(-ENOMEM); + + mbdev->mmio_va = mmio_va; + mbdev->dev.parent = pdev; + mbdev->id.device = id; + mbdev->id.vendor = MBUS_DEV_ANY_ID; + mbdev->dev.archdata.dma_ops = dma_ops; + mbdev->dev.dma_mask = &mbdev->dev.coherent_dma_mask; + dma_set_mask(&mbdev->dev, DMA_BIT_MASK(64)); + mbdev->dev.release = mbus_release_dev; + mbdev->hw_ops = hw_ops; + mbdev->dev.bus = &mic_bus; + + /* Assign a unique device index and hence name. */ + ret = ida_simple_get(&mbus_index_ida, 0, 0, GFP_KERNEL); + if (ret < 0) + goto free_mbdev; + + mbdev->index = ret; + dev_set_name(&mbdev->dev, "mbus-dev%u", mbdev->index); + /* + * device_register() causes the bus infrastructure to look for a + * matching driver. + */ + ret = device_register(&mbdev->dev); + if (ret) + goto ida_remove; + return mbdev; +ida_remove: + ida_simple_remove(&mbus_index_ida, mbdev->index); +free_mbdev: + kfree(mbdev); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(mbus_register_device); + +void mbus_unregister_device(struct mbus_device *mbdev) +{ + int index = mbdev->index; /* save for after device release */ + + device_unregister(&mbdev->dev); + ida_simple_remove(&mbus_index_ida, index); +} +EXPORT_SYMBOL_GPL(mbus_unregister_device); + +static int __init mbus_init(void) +{ + return bus_register(&mic_bus); +} + +static void __exit mbus_exit(void) +{ + bus_unregister(&mic_bus); + ida_destroy(&mbus_index_ida); +} + +core_initcall(mbus_init); +module_exit(mbus_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) MIC Bus driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c index d0980ff96833..83819eee553b 100644 --- a/drivers/misc/mic/card/mic_device.c +++ b/drivers/misc/mic/card/mic_device.c @@ -83,8 +83,8 @@ static int mic_shutdown_init(void) int shutdown_db; shutdown_db = mic_next_card_db(); - shutdown_cookie = mic_request_card_irq(mic_shutdown_isr, - "Shutdown", mdrv, shutdown_db); + shutdown_cookie = mic_request_card_irq(mic_shutdown_isr, NULL, + "Shutdown", mdrv, shutdown_db); if (IS_ERR(shutdown_cookie)) rc = PTR_ERR(shutdown_cookie); else @@ -136,7 +136,8 @@ static void mic_dp_uninit(void) /** * mic_request_card_irq - request an irq. * - * @func: The callback function that handles the interrupt. + * @handler: interrupt handler passed to request_threaded_irq. + * @thread_fn: thread fn. passed to request_threaded_irq. * @name: The ASCII name of the callee requesting the irq. * @data: private data that is returned back when calling the * function handler. @@ -149,17 +150,19 @@ static void mic_dp_uninit(void) * error code. * */ -struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data), - const char *name, void *data, int index) +struct mic_irq * +mic_request_card_irq(irq_handler_t handler, + irq_handler_t thread_fn, const char *name, + void *data, int index) { int rc = 0; unsigned long cookie; struct mic_driver *mdrv = g_drv; - rc = request_irq(mic_db_to_irq(mdrv, index), func, - 0, name, data); + rc = request_threaded_irq(mic_db_to_irq(mdrv, index), handler, + thread_fn, 0, name, data); if (rc) { - dev_err(mdrv->dev, "request_irq failed rc = %d\n", rc); + dev_err(mdrv->dev, "request_threaded_irq failed rc = %d\n", rc); goto err; } mdrv->irq_info.irq_usage_count[index]++; @@ -172,9 +175,9 @@ err: /** * mic_free_card_irq - free irq. * - * @cookie: cookie obtained during a successful call to mic_request_irq + * @cookie: cookie obtained during a successful call to mic_request_threaded_irq * @data: private data specified by the calling function during the - * mic_request_irq + * mic_request_threaded_irq * * returns: none. */ diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h index 306f502be95e..844be8fc9b22 100644 --- a/drivers/misc/mic/card/mic_device.h +++ b/drivers/misc/mic/card/mic_device.h @@ -30,6 +30,8 @@ #include <linux/workqueue.h> #include <linux/io.h> #include <linux/irqreturn.h> +#include <linux/interrupt.h> +#include <linux/mic_bus.h> /** * struct mic_intr_info - Contains h/w specific interrupt sources info @@ -70,6 +72,7 @@ struct mic_device { * @hotplug_work: Hot plug work for adding/removing virtio devices. * @irq_info: The OS specific irq information * @intr_info: H/W specific interrupt information. + * @dma_mbdev: dma device on the MIC virtual bus. */ struct mic_driver { char name[20]; @@ -80,6 +83,7 @@ struct mic_driver { struct work_struct hotplug_work; struct mic_irq_info irq_info; struct mic_intr_info intr_info; + struct mbus_device *dma_mbdev; }; /** @@ -116,8 +120,9 @@ mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) int mic_driver_init(struct mic_driver *mdrv); void mic_driver_uninit(struct mic_driver *mdrv); int mic_next_card_db(void); -struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data), - const char *name, void *data, int intr_src); +struct mic_irq * +mic_request_card_irq(irq_handler_t handler, irq_handler_t thread_fn, + const char *name, void *data, int intr_src); void mic_free_card_irq(struct mic_irq *cookie, void *data); u32 mic_read_spad(struct mic_device *mdev, unsigned int idx); void mic_send_intr(struct mic_device *mdev, int doorbell); diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c index 653799b96bfa..f14b60080c21 100644 --- a/drivers/misc/mic/card/mic_virtio.c +++ b/drivers/misc/mic/card/mic_virtio.c @@ -417,7 +417,7 @@ static int mic_add_device(struct mic_device_desc __iomem *d, virtio_db = mic_next_card_db(); mvdev->virtio_cookie = mic_request_card_irq(mic_virtio_intr_handler, - "virtio intr", mvdev, virtio_db); + NULL, "virtio intr", mvdev, virtio_db); if (IS_ERR(mvdev->virtio_cookie)) { ret = PTR_ERR(mvdev->virtio_cookie); goto kfree; @@ -606,8 +606,9 @@ int mic_devices_init(struct mic_driver *mdrv) mic_scan_devices(mdrv, !REMOVE_DEVICES); config_db = mic_next_card_db(); - virtio_config_cookie = mic_request_card_irq(mic_extint_handler, - "virtio_config_intr", mdrv, config_db); + virtio_config_cookie = mic_request_card_irq(mic_extint_handler, NULL, + "virtio_config_intr", mdrv, + config_db); if (IS_ERR(virtio_config_cookie)) { rc = PTR_ERR(virtio_config_cookie); goto exit; diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c index 2868945c9a4d..9d57545d64f6 100644 --- a/drivers/misc/mic/card/mic_x100.c +++ b/drivers/misc/mic/card/mic_x100.c @@ -148,6 +148,47 @@ void mic_card_unmap(struct mic_device *mdev, void __iomem *addr) iounmap(addr); } +static inline struct mic_driver *mbdev_to_mdrv(struct mbus_device *mbdev) +{ + return dev_get_drvdata(mbdev->dev.parent); +} + +static struct mic_irq * +_mic_request_threaded_irq(struct mbus_device *mbdev, + irq_handler_t handler, irq_handler_t thread_fn, + const char *name, void *data, int intr_src) +{ + int rc = 0; + unsigned int irq = intr_src; + unsigned long cookie = irq; + + rc = request_threaded_irq(irq, handler, thread_fn, 0, name, data); + if (rc) { + dev_err(mbdev_to_mdrv(mbdev)->dev, + "request_threaded_irq failed rc = %d\n", rc); + return ERR_PTR(rc); + } + return (struct mic_irq *)cookie; +} + +static void _mic_free_irq(struct mbus_device *mbdev, + struct mic_irq *cookie, void *data) +{ + unsigned long irq = (unsigned long)cookie; + free_irq(irq, data); +} + +static void _mic_ack_interrupt(struct mbus_device *mbdev, int num) +{ + mic_ack_interrupt(&mbdev_to_mdrv(mbdev)->mdev); +} + +static struct mbus_hw_ops mbus_hw_ops = { + .request_threaded_irq = _mic_request_threaded_irq, + .free_irq = _mic_free_irq, + .ack_interrupt = _mic_ack_interrupt, +}; + static int __init mic_probe(struct platform_device *pdev) { struct mic_driver *mdrv = &g_drv; @@ -159,32 +200,41 @@ static int __init mic_probe(struct platform_device *pdev) mdev->mmio.pa = MIC_X100_MMIO_BASE; mdev->mmio.len = MIC_X100_MMIO_LEN; - mdev->mmio.va = ioremap(MIC_X100_MMIO_BASE, MIC_X100_MMIO_LEN); + mdev->mmio.va = devm_ioremap(&pdev->dev, MIC_X100_MMIO_BASE, + MIC_X100_MMIO_LEN); if (!mdev->mmio.va) { dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); rc = -EIO; goto done; } mic_hw_intr_init(mdrv); + platform_set_drvdata(pdev, mdrv); + mdrv->dma_mbdev = mbus_register_device(mdrv->dev, MBUS_DEV_DMA_MIC, + NULL, &mbus_hw_ops, + mdrv->mdev.mmio.va); + if (IS_ERR(mdrv->dma_mbdev)) { + rc = PTR_ERR(mdrv->dma_mbdev); + dev_err(&pdev->dev, "mbus_add_device failed rc %d\n", rc); + goto done; + } rc = mic_driver_init(mdrv); if (rc) { dev_err(&pdev->dev, "mic_driver_init failed rc %d\n", rc); - goto iounmap; + goto remove_dma; } done: return rc; -iounmap: - iounmap(mdev->mmio.va); +remove_dma: + mbus_unregister_device(mdrv->dma_mbdev); return rc; } static int mic_remove(struct platform_device *pdev) { struct mic_driver *mdrv = &g_drv; - struct mic_device *mdev = &mdrv->mdev; mic_driver_uninit(mdrv); - iounmap(mdev->mmio.va); + mbus_unregister_device(mdrv->dma_mbdev); return 0; } diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index b75c6b5cc20f..ff2b0fb1a6be 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -23,11 +23,70 @@ #include <linux/pci.h> #include <linux/mic_common.h> +#include <linux/mic_bus.h> #include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" #include "mic_virtio.h" +static inline struct mic_device *mbdev_to_mdev(struct mbus_device *mbdev) +{ + return dev_get_drvdata(mbdev->dev.parent); +} + +static dma_addr_t +mic_dma_map_page(struct device *dev, struct page *page, + unsigned long offset, size_t size, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + void *va = phys_to_virt(page_to_phys(page)) + offset; + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + return mic_map_single(mdev, va, size); +} + +static void +mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr, + size_t size, enum dma_data_direction dir, + struct dma_attrs *attrs) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + mic_unmap_single(mdev, dma_addr, size); +} + +static struct dma_map_ops mic_dma_ops = { + .map_page = mic_dma_map_page, + .unmap_page = mic_dma_unmap_page, +}; + +static struct mic_irq * +_mic_request_threaded_irq(struct mbus_device *mbdev, + irq_handler_t handler, irq_handler_t thread_fn, + const char *name, void *data, int intr_src) +{ + return mic_request_threaded_irq(mbdev_to_mdev(mbdev), handler, + thread_fn, name, data, + intr_src, MIC_INTR_DMA); +} + +static void _mic_free_irq(struct mbus_device *mbdev, + struct mic_irq *cookie, void *data) +{ + return mic_free_irq(mbdev_to_mdev(mbdev), cookie, data); +} + +static void _mic_ack_interrupt(struct mbus_device *mbdev, int num) +{ + struct mic_device *mdev = mbdev_to_mdev(mbdev); + mdev->ops->intr_workarounds(mdev); +} + +static struct mbus_hw_ops mbus_hw_ops = { + .request_threaded_irq = _mic_request_threaded_irq, + .free_irq = _mic_free_irq, + .ack_interrupt = _mic_ack_interrupt, +}; + /** * mic_reset - Reset the MIC device. * @mdev: pointer to mic_device instance @@ -95,9 +154,21 @@ retry: */ goto retry; } + mdev->dma_mbdev = mbus_register_device(mdev->sdev->parent, + MBUS_DEV_DMA_HOST, &mic_dma_ops, + &mbus_hw_ops, mdev->mmio.va); + if (IS_ERR(mdev->dma_mbdev)) { + rc = PTR_ERR(mdev->dma_mbdev); + goto unlock_ret; + } + mdev->dma_ch = mic_request_dma_chan(mdev); + if (!mdev->dma_ch) { + rc = -ENXIO; + goto dma_remove; + } rc = mdev->ops->load_mic_fw(mdev, buf); if (rc) - goto unlock_ret; + goto dma_release; mic_smpt_restore(mdev); mic_intr_restore(mdev); mdev->intr_ops->enable_interrupts(mdev); @@ -105,6 +176,11 @@ retry: mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); mdev->ops->send_firmware_intr(mdev); mic_set_state(mdev, MIC_ONLINE); + goto unlock_ret; +dma_release: + dma_release_channel(mdev->dma_ch); +dma_remove: + mbus_unregister_device(mdev->dma_mbdev); unlock_ret: mutex_unlock(&mdev->mic_mutex); return rc; @@ -122,6 +198,11 @@ void mic_stop(struct mic_device *mdev, bool force) mutex_lock(&mdev->mic_mutex); if (MIC_OFFLINE != mdev->state || force) { mic_virtio_reset_devices(mdev); + if (mdev->dma_ch) { + dma_release_channel(mdev->dma_ch); + mdev->dma_ch = NULL; + } + mbus_unregister_device(mdev->dma_mbdev); mic_bootparam_init(mdev); mic_reset(mdev); if (MIC_RESET_FAILED == mdev->state) diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 0398c696d257..016bd15a7bd1 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -25,6 +25,8 @@ #include <linux/idr.h> #include <linux/notifier.h> #include <linux/irqreturn.h> +#include <linux/dmaengine.h> +#include <linux/mic_bus.h> #include "mic_intr.h" @@ -87,6 +89,8 @@ enum mic_stepping { * @cdev: Character device for MIC. * @vdev_list: list of virtio devices. * @pm_notifier: Handles PM notifications from the OS. + * @dma_mbdev: MIC BUS DMA device. + * @dma_ch: DMA channel reserved by this driver for use by virtio devices. */ struct mic_device { struct mic_mw mmio; @@ -124,6 +128,8 @@ struct mic_device { struct cdev cdev; struct list_head vdev_list; struct notifier_block pm_notifier; + struct mbus_device *dma_mbdev; + struct dma_chan *dma_ch; }; /** @@ -144,6 +150,7 @@ struct mic_device { * @load_mic_fw: Load firmware segments required to boot the card * into card memory. This includes the kernel, command line, ramdisk etc. * @get_postcode: Get post code status from firmware. + * @dma_filter: DMA filter function to be used. */ struct mic_hw_ops { u8 aper_bar; @@ -159,6 +166,7 @@ struct mic_hw_ops { void (*send_firmware_intr)(struct mic_device *mdev); int (*load_mic_fw)(struct mic_device *mdev, const char *buf); u32 (*get_postcode)(struct mic_device *mdev); + bool (*dma_filter)(struct dma_chan *chan, void *param); }; /** @@ -187,6 +195,22 @@ mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) iowrite32(val, mw->va + offset); } +static inline struct dma_chan *mic_request_dma_chan(struct mic_device *mdev) +{ + dma_cap_mask_t mask; + struct dma_chan *chan; + + dma_cap_zero(mask); + dma_cap_set(DMA_MEMCPY, mask); + chan = dma_request_channel(mask, mdev->ops->dma_filter, + mdev->sdev->parent); + if (chan) + return chan; + dev_err(mdev->sdev->parent, "%s %d unable to acquire channel\n", + __func__, __LINE__); + return NULL; +} + void mic_sysfs_init(struct mic_device *mdev); int mic_start(struct mic_device *mdev, const char *buf); void mic_stop(struct mic_device *mdev, bool force); diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c index dbc5afde1392..d686f2846ac7 100644 --- a/drivers/misc/mic/host/mic_intr.c +++ b/drivers/misc/mic/host/mic_intr.c @@ -24,28 +24,29 @@ #include "../common/mic_dev.h" #include "mic_device.h" -/* - * mic_invoke_callback - Invoke callback functions registered for - * the corresponding source id. - * - * @mdev: pointer to the mic_device instance - * @idx: The interrupt source id. - * - * Returns none. - */ -static inline void mic_invoke_callback(struct mic_device *mdev, int idx) +static irqreturn_t mic_thread_fn(int irq, void *dev) { + struct mic_device *mdev = dev; + struct mic_intr_info *intr_info = mdev->intr_info; + struct mic_irq_info *irq_info = &mdev->irq_info; struct mic_intr_cb *intr_cb; struct pci_dev *pdev = container_of(mdev->sdev->parent, - struct pci_dev, dev); + struct pci_dev, dev); + int i; - spin_lock(&mdev->irq_info.mic_intr_lock); - list_for_each_entry(intr_cb, &mdev->irq_info.cb_list[idx], list) - if (intr_cb->func) - intr_cb->func(pdev->irq, intr_cb->data); - spin_unlock(&mdev->irq_info.mic_intr_lock); + spin_lock(&irq_info->mic_thread_lock); + for (i = intr_info->intr_start_idx[MIC_INTR_DB]; + i < intr_info->intr_len[MIC_INTR_DB]; i++) + if (test_and_clear_bit(i, &irq_info->mask)) { + list_for_each_entry(intr_cb, &irq_info->cb_list[i], + list) + if (intr_cb->thread_fn) + intr_cb->thread_fn(pdev->irq, + intr_cb->data); + } + spin_unlock(&irq_info->mic_thread_lock); + return IRQ_HANDLED; } - /** * mic_interrupt - Generic interrupt handler for * MSI and INTx based interrupts. @@ -53,7 +54,11 @@ static inline void mic_invoke_callback(struct mic_device *mdev, int idx) static irqreturn_t mic_interrupt(int irq, void *dev) { struct mic_device *mdev = dev; - struct mic_intr_info *info = mdev->intr_info; + struct mic_intr_info *intr_info = mdev->intr_info; + struct mic_irq_info *irq_info = &mdev->irq_info; + struct mic_intr_cb *intr_cb; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); u32 mask; int i; @@ -61,12 +66,19 @@ static irqreturn_t mic_interrupt(int irq, void *dev) if (!mask) return IRQ_NONE; - for (i = info->intr_start_idx[MIC_INTR_DB]; - i < info->intr_len[MIC_INTR_DB]; i++) - if (mask & BIT(i)) - mic_invoke_callback(mdev, i); - - return IRQ_HANDLED; + spin_lock(&irq_info->mic_intr_lock); + for (i = intr_info->intr_start_idx[MIC_INTR_DB]; + i < intr_info->intr_len[MIC_INTR_DB]; i++) + if (mask & BIT(i)) { + list_for_each_entry(intr_cb, &irq_info->cb_list[i], + list) + if (intr_cb->handler) + intr_cb->handler(pdev->irq, + intr_cb->data); + set_bit(i, &irq_info->mask); + } + spin_unlock(&irq_info->mic_intr_lock); + return IRQ_WAKE_THREAD; } /* Return the interrupt offset from the index. Index is 0 based. */ @@ -99,14 +111,15 @@ static struct msix_entry *mic_get_available_vector(struct mic_device *mdev) * * @mdev: pointer to the mic_device instance * @idx: The source id to be registered. - * @func: The function to be called when the source id receives + * @handler: The function to be called when the source id receives * the interrupt. + * @thread_fn: thread fn. corresponding to the handler * @data: Private data of the requester. * Return the callback structure that was registered or an * appropriate error on failure. */ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, - u8 idx, irqreturn_t (*func) (int irq, void *dev), + u8 idx, irq_handler_t handler, irq_handler_t thread_fn, void *data) { struct mic_intr_cb *intr_cb; @@ -117,7 +130,8 @@ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, if (!intr_cb) return ERR_PTR(-ENOMEM); - intr_cb->func = func; + intr_cb->handler = handler; + intr_cb->thread_fn = thread_fn; intr_cb->data = data; intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida, 0, 0, GFP_KERNEL); @@ -126,9 +140,11 @@ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, goto ida_fail; } + spin_lock(&mdev->irq_info.mic_thread_lock); spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]); spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); + spin_unlock(&mdev->irq_info.mic_thread_lock); return intr_cb; ida_fail: @@ -152,8 +168,9 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) unsigned long flags; int i; + spin_lock(&mdev->irq_info.mic_thread_lock); + spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); for (i = 0; i < MIC_NUM_OFFSETS; i++) { - spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { intr_cb = list_entry(pos, struct mic_intr_cb, list); if (intr_cb->cb_id == idx) { @@ -163,11 +180,13 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) kfree(intr_cb); spin_unlock_irqrestore( &mdev->irq_info.mic_intr_lock, flags); + spin_unlock(&mdev->irq_info.mic_thread_lock); return i; } } - spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); } + spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); + spin_unlock(&mdev->irq_info.mic_thread_lock); return MIC_NUM_OFFSETS; } @@ -242,6 +261,7 @@ static int mic_setup_callbacks(struct mic_device *mdev) INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]); ida_init(&mdev->irq_info.cb_ida); spin_lock_init(&mdev->irq_info.mic_intr_lock); + spin_lock_init(&mdev->irq_info.mic_thread_lock); return 0; } @@ -258,14 +278,12 @@ static void mic_release_callbacks(struct mic_device *mdev) struct mic_intr_cb *intr_cb; int i; + spin_lock(&mdev->irq_info.mic_thread_lock); + spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); for (i = 0; i < MIC_NUM_OFFSETS; i++) { - spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags); - if (list_empty(&mdev->irq_info.cb_list[i])) { - spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, - flags); + if (list_empty(&mdev->irq_info.cb_list[i])) break; - } list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { intr_cb = list_entry(pos, struct mic_intr_cb, list); @@ -274,8 +292,9 @@ static void mic_release_callbacks(struct mic_device *mdev) intr_cb->cb_id); kfree(intr_cb); } - spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); } + spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); + spin_unlock(&mdev->irq_info.mic_thread_lock); ida_destroy(&mdev->irq_info.cb_ida); kfree(mdev->irq_info.cb_list); } @@ -313,7 +332,8 @@ static int mic_setup_msi(struct mic_device *mdev, struct pci_dev *pdev) goto err_nomem2; } - rc = request_irq(pdev->irq, mic_interrupt, 0 , "mic-msi", mdev); + rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn, + 0, "mic-msi", mdev); if (rc) { dev_err(&pdev->dev, "Error allocating MSI interrupt\n"); goto err_irq_req_fail; @@ -353,8 +373,8 @@ static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev) goto err_nomem; } - rc = request_irq(pdev->irq, mic_interrupt, - IRQF_SHARED, "mic-intx", mdev); + rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn, + IRQF_SHARED, "mic-intx", mdev); if (rc) goto err; @@ -391,13 +411,14 @@ int mic_next_db(struct mic_device *mdev) #define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT) /** - * mic_request_irq - request an irq. mic_mutex needs + * mic_request_threaded_irq - request an irq. mic_mutex needs * to be held before calling this function. * * @mdev: pointer to mic_device instance - * @func: The callback function that handles the interrupt. + * @handler: The callback function that handles the interrupt. * The function needs to call ack_interrupts * (mdev->ops->ack_interrupt(mdev)) when handling the interrupts. + * @thread_fn: thread fn required by request_threaded_irq. * @name: The ASCII name of the callee requesting the irq. * @data: private data that is returned back when calling the * function handler. @@ -412,10 +433,11 @@ int mic_next_db(struct mic_device *mdev) * error code. * */ -struct mic_irq *mic_request_irq(struct mic_device *mdev, - irqreturn_t (*func)(int irq, void *dev), - const char *name, void *data, int intr_src, - enum mic_intr_type type) +struct mic_irq * +mic_request_threaded_irq(struct mic_device *mdev, + irq_handler_t handler, irq_handler_t thread_fn, + const char *name, void *data, int intr_src, + enum mic_intr_type type) { u16 offset; int rc = 0; @@ -444,7 +466,8 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev, goto err; } - rc = request_irq(msix->vector, func, 0, name, data); + rc = request_threaded_irq(msix->vector, handler, thread_fn, + 0, name, data); if (rc) { dev_dbg(mdev->sdev->parent, "request irq failed rc = %d\n", rc); @@ -458,8 +481,8 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev, dev_dbg(mdev->sdev->parent, "irq: %d assigned for src: %d\n", msix->vector, intr_src); } else { - intr_cb = mic_register_intr_callback(mdev, - offset, func, data); + intr_cb = mic_register_intr_callback(mdev, offset, handler, + thread_fn, data); if (IS_ERR(intr_cb)) { dev_err(mdev->sdev->parent, "No available callback entries for use\n"); @@ -487,9 +510,9 @@ err: * needs to be held before calling this function. * * @mdev: pointer to mic_device instance - * @cookie: cookie obtained during a successful call to mic_request_irq + * @cookie: cookie obtained during a successful call to mic_request_threaded_irq * @data: private data specified by the calling function during the - * mic_request_irq + * mic_request_threaded_irq * * returns: none. */ diff --git a/drivers/misc/mic/host/mic_intr.h b/drivers/misc/mic/host/mic_intr.h index 6091aa97e116..9f783d4ad7f1 100644 --- a/drivers/misc/mic/host/mic_intr.h +++ b/drivers/misc/mic/host/mic_intr.h @@ -21,12 +21,15 @@ #ifndef _MIC_INTR_H_ #define _MIC_INTR_H_ +#include <linux/bitops.h> +#include <linux/interrupt.h> /* * The minimum number of msix vectors required for normal operation. * 3 for virtio network, console and block devices. * 1 for card shutdown notifications. + * 4 for host owned DMA channels. */ -#define MIC_MIN_MSIX 4 +#define MIC_MIN_MSIX 8 #define MIC_NUM_OFFSETS 32 /** @@ -68,7 +71,11 @@ struct mic_intr_info { * @num_vectors: The number of MSI/MSI-x vectors that have been allocated. * @cb_ida: callback ID allocator to track the callbacks registered. * @mic_intr_lock: spinlock to protect the interrupt callback list. + * @mic_thread_lock: spinlock to protect the thread callback list. + * This lock is used to protect against thread_fn while + * mic_intr_lock is used to protect against interrupt handler. * @cb_list: Array of callback lists one for each source. + * @mask: Mask used by the main thread fn to call the underlying thread fns. */ struct mic_irq_info { int next_avail_src; @@ -77,19 +84,23 @@ struct mic_irq_info { u16 num_vectors; struct ida cb_ida; spinlock_t mic_intr_lock; + spinlock_t mic_thread_lock; struct list_head *cb_list; + unsigned long mask; }; /** * struct mic_intr_cb - Interrupt callback structure. * - * @func: The callback function + * @handler: The callback function + * @thread_fn: The thread_fn. * @data: Private data of the requester. * @cb_id: The callback id. Identifies this callback. * @list: list head pointing to the next callback structure. */ struct mic_intr_cb { - irqreturn_t (*func) (int irq, void *data); + irq_handler_t handler; + irq_handler_t thread_fn; void *data; int cb_id; struct list_head list; @@ -124,11 +135,11 @@ struct mic_hw_intr_ops { }; int mic_next_db(struct mic_device *mdev); -struct mic_irq *mic_request_irq(struct mic_device *mdev, - irqreturn_t (*func)(int irq, void *data), - const char *name, void *data, int intr_src, - enum mic_intr_type type); - +struct mic_irq * +mic_request_threaded_irq(struct mic_device *mdev, + irq_handler_t handler, irq_handler_t thread_fn, + const char *name, void *data, int intr_src, + enum mic_intr_type type); void mic_free_irq(struct mic_device *mdev, struct mic_irq *cookie, void *data); int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev); diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index c04a021e20c7..ab37a3117d23 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -38,7 +38,7 @@ static const char mic_driver_name[] = "mic"; -static DEFINE_PCI_DEVICE_TABLE(mic_pci_tbl) = { +static const struct pci_device_id mic_pci_tbl[] = { {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2250)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2251)}, {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2252)}, @@ -389,8 +389,9 @@ static int mic_probe(struct pci_dev *pdev, mutex_lock(&mdev->mic_mutex); mdev->shutdown_db = mic_next_db(mdev); - mdev->shutdown_cookie = mic_request_irq(mdev, mic_shutdown_db, - "shutdown-interrupt", mdev, mdev->shutdown_db, MIC_INTR_DB); + mdev->shutdown_cookie = mic_request_threaded_irq(mdev, mic_shutdown_db, + NULL, "shutdown-interrupt", mdev, + mdev->shutdown_db, MIC_INTR_DB); if (IS_ERR(mdev->shutdown_cookie)) { rc = PTR_ERR(mdev->shutdown_cookie); mutex_unlock(&mdev->mic_mutex); diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c index 7e1ef0ebbb80..a020e4eb435a 100644 --- a/drivers/misc/mic/host/mic_virtio.c +++ b/drivers/misc/mic/host/mic_virtio.c @@ -21,60 +21,157 @@ #include <linux/pci.h> #include <linux/sched.h> #include <linux/uaccess.h> - +#include <linux/dmaengine.h> #include <linux/mic_common.h> + #include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" #include "mic_virtio.h" /* - * Initiates the copies across the PCIe bus from card memory to - * a user space buffer. + * Size of the internal buffer used during DMA's as an intermediate buffer + * for copy to/from user. */ -static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, - void __user *ubuf, size_t len, u64 addr) +#define MIC_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL) + +static int mic_sync_dma(struct mic_device *mdev, dma_addr_t dst, + dma_addr_t src, size_t len) { - int err; - void __iomem *dbuf = mvdev->mdev->aper.va + addr; - /* - * We are copying from IO below an should ideally use something - * like copy_to_user_fromio(..) if it existed. - */ - if (copy_to_user(ubuf, (void __force *)dbuf, len)) { - err = -EFAULT; - dev_err(mic_dev(mvdev), "%s %d err %d\n", + int err = 0; + struct dma_async_tx_descriptor *tx; + struct dma_chan *mic_ch = mdev->dma_ch; + + if (!mic_ch) { + err = -EBUSY; + goto error; + } + + tx = mic_ch->device->device_prep_dma_memcpy(mic_ch, dst, src, len, + DMA_PREP_FENCE); + if (!tx) { + err = -ENOMEM; + goto error; + } else { + dma_cookie_t cookie = tx->tx_submit(tx); + + err = dma_submit_error(cookie); + if (err) + goto error; + err = dma_sync_wait(mic_ch, cookie); + } +error: + if (err) + dev_err(mdev->sdev->parent, "%s %d err %d\n", __func__, __LINE__, err); - goto err; + return err; +} + +/* + * Initiates the copies across the PCIe bus from card memory to a user + * space buffer. When transfers are done using DMA, source/destination + * addresses and transfer length must follow the alignment requirements of + * the MIC DMA engine. + */ +static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, void __user *ubuf, + size_t len, u64 daddr, size_t dlen, + int vr_idx) +{ + struct mic_device *mdev = mvdev->mdev; + void __iomem *dbuf = mdev->aper.va + daddr; + struct mic_vringh *mvr = &mvdev->mvr[vr_idx]; + size_t dma_alignment = 1 << mdev->dma_ch->device->copy_align; + size_t dma_offset; + size_t partlen; + int err; + + dma_offset = daddr - round_down(daddr, dma_alignment); + daddr -= dma_offset; + len += dma_offset; + + while (len) { + partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE); + + err = mic_sync_dma(mdev, mvr->buf_da, daddr, + ALIGN(partlen, dma_alignment)); + if (err) + goto err; + + if (copy_to_user(ubuf, mvr->buf + dma_offset, + partlen - dma_offset)) { + err = -EFAULT; + goto err; + } + daddr += partlen; + ubuf += partlen; + dbuf += partlen; + mvdev->in_bytes_dma += partlen; + mvdev->in_bytes += partlen; + len -= partlen; + dma_offset = 0; } - mvdev->in_bytes += len; - err = 0; + return 0; err: + dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err); return err; } /* - * Initiates copies across the PCIe bus from a user space - * buffer to card memory. + * Initiates copies across the PCIe bus from a user space buffer to card + * memory. When transfers are done using DMA, source/destination addresses + * and transfer length must follow the alignment requirements of the MIC + * DMA engine. */ -static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, - void __user *ubuf, size_t len, u64 addr) +static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, void __user *ubuf, + size_t len, u64 daddr, size_t dlen, + int vr_idx) { + struct mic_device *mdev = mvdev->mdev; + void __iomem *dbuf = mdev->aper.va + daddr; + struct mic_vringh *mvr = &mvdev->mvr[vr_idx]; + size_t dma_alignment = 1 << mdev->dma_ch->device->copy_align; + size_t partlen; int err; - void __iomem *dbuf = mvdev->mdev->aper.va + addr; + + if (daddr & (dma_alignment - 1)) { + mvdev->tx_dst_unaligned += len; + goto memcpy; + } else if (ALIGN(len, dma_alignment) > dlen) { + mvdev->tx_len_unaligned += len; + goto memcpy; + } + + while (len) { + partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE); + + if (copy_from_user(mvr->buf, ubuf, partlen)) { + err = -EFAULT; + goto err; + } + err = mic_sync_dma(mdev, daddr, mvr->buf_da, + ALIGN(partlen, dma_alignment)); + if (err) + goto err; + daddr += partlen; + ubuf += partlen; + dbuf += partlen; + mvdev->out_bytes_dma += partlen; + mvdev->out_bytes += partlen; + len -= partlen; + } +memcpy: /* * We are copying to IO below and should ideally use something * like copy_from_user_toio(..) if it existed. */ if (copy_from_user((void __force *)dbuf, ubuf, len)) { err = -EFAULT; - dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, err); goto err; } mvdev->out_bytes += len; - err = 0; + return 0; err: + dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err); return err; } @@ -110,7 +207,8 @@ static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov) * way to override the VRINGH xfer(..) routines as of v3.10. */ static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, - void __user *ubuf, size_t len, bool read, size_t *out_len) + void __user *ubuf, size_t len, bool read, int vr_idx, + size_t *out_len) { int ret = 0; size_t partlen, tot_len = 0; @@ -118,13 +216,15 @@ static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov, while (len && iov->i < iov->used) { partlen = min(iov->iov[iov->i].iov_len, len); if (read) - ret = mic_virtio_copy_to_user(mvdev, - ubuf, partlen, - (u64)iov->iov[iov->i].iov_base); + ret = mic_virtio_copy_to_user(mvdev, ubuf, partlen, + (u64)iov->iov[iov->i].iov_base, + iov->iov[iov->i].iov_len, + vr_idx); else - ret = mic_virtio_copy_from_user(mvdev, - ubuf, partlen, - (u64)iov->iov[iov->i].iov_base); + ret = mic_virtio_copy_from_user(mvdev, ubuf, partlen, + (u64)iov->iov[iov->i].iov_base, + iov->iov[iov->i].iov_len, + vr_idx); if (ret) { dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, ret); @@ -192,8 +292,8 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, ubuf = iov.iov_base; } /* Issue all the read descriptors first */ - ret = mic_vringh_copy(mvdev, riov, ubuf, len, - MIC_VRINGH_READ, &out_len); + ret = mic_vringh_copy(mvdev, riov, ubuf, len, MIC_VRINGH_READ, + copy->vr_idx, &out_len); if (ret) { dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, ret); @@ -203,8 +303,8 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, ubuf += out_len; copy->out_len += out_len; /* Issue the write descriptors next */ - ret = mic_vringh_copy(mvdev, wiov, ubuf, len, - !MIC_VRINGH_READ, &out_len); + ret = mic_vringh_copy(mvdev, wiov, ubuf, len, !MIC_VRINGH_READ, + copy->vr_idx, &out_len); if (ret) { dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, ret); @@ -589,13 +689,19 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, dev_dbg(mdev->sdev->parent, "%s %d index %d va %p info %p vr_size 0x%x\n", __func__, __LINE__, i, vr->va, vr->info, vr_size); + mvr->buf = (void *)__get_free_pages(GFP_KERNEL, + get_order(MIC_INT_DMA_BUF_SIZE)); + mvr->buf_da = mic_map_single(mvdev->mdev, mvr->buf, + MIC_INT_DMA_BUF_SIZE); } snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id, mvdev->virtio_id); mvdev->virtio_db = mic_next_db(mdev); - mvdev->virtio_cookie = mic_request_irq(mdev, mic_virtio_intr_handler, - irqname, mvdev, mvdev->virtio_db, MIC_INTR_DB); + mvdev->virtio_cookie = mic_request_threaded_irq(mdev, + mic_virtio_intr_handler, + NULL, irqname, mvdev, + mvdev->virtio_db, MIC_INTR_DB); if (IS_ERR(mvdev->virtio_cookie)) { ret = PTR_ERR(mvdev->virtio_cookie); dev_dbg(mdev->sdev->parent, "request irq failed\n"); @@ -671,6 +777,11 @@ skip_hot_remove: vqconfig = mic_vq_config(mvdev->dd); for (i = 0; i < mvdev->dd->num_vq; i++) { struct mic_vringh *mvr = &mvdev->mvr[i]; + + mic_unmap_single(mvdev->mdev, mvr->buf_da, + MIC_INT_DMA_BUF_SIZE); + free_pages((unsigned long)mvr->buf, + get_order(MIC_INT_DMA_BUF_SIZE)); vringh_kiov_cleanup(&mvr->riov); vringh_kiov_cleanup(&mvr->wiov); mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), diff --git a/drivers/misc/mic/host/mic_virtio.h b/drivers/misc/mic/host/mic_virtio.h index 184f3c84805b..d574efb853d9 100644 --- a/drivers/misc/mic/host/mic_virtio.h +++ b/drivers/misc/mic/host/mic_virtio.h @@ -46,18 +46,23 @@ * @vrh: The host VRINGH used for accessing the card vrings. * @riov: The VRINGH read kernel IOV. * @wiov: The VRINGH write kernel IOV. - * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). * @vr_mutex: Mutex for synchronizing access to the VRING. + * @buf: Temporary kernel buffer used to copy in/out data + * from/to the card via DMA. + * @buf_da: dma address of buf. * @mvdev: Back pointer to MIC virtio device for vringh_notify(..). + * @head: The VRINGH head index address passed to vringh_getdesc_kern(..). */ struct mic_vringh { struct mic_vring vring; struct vringh vrh; struct vringh_kiov riov; struct vringh_kiov wiov; - u16 head; struct mutex vr_mutex; + void *buf; + dma_addr_t buf_da; struct mic_vdev *mvdev; + u16 head; }; /** @@ -69,6 +74,14 @@ struct mic_vringh { * @poll_wake - Used for waking up threads blocked in poll. * @out_bytes - Debug stats for number of bytes copied from host to card. * @in_bytes - Debug stats for number of bytes copied from card to host. + * @out_bytes_dma - Debug stats for number of bytes copied from host to card + * using DMA. + * @in_bytes_dma - Debug stats for number of bytes copied from card to host + * using DMA. + * @tx_len_unaligned - Debug stats for number of bytes copied to the card where + * the transfer length did not have the required DMA alignment. + * @tx_dst_unaligned - Debug stats for number of bytes copied where the + * destination address on the card did not have the required DMA alignment. * @mvr - Store per VRING data structures. * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. * @dd - Virtio device descriptor. @@ -84,6 +97,10 @@ struct mic_vdev { int poll_wake; unsigned long out_bytes; unsigned long in_bytes; + unsigned long out_bytes_dma; + unsigned long in_bytes_dma; + unsigned long tx_len_unaligned; + unsigned long tx_dst_unaligned; struct mic_vringh mvr[MIC_MAX_VRINGS]; struct work_struct virtio_bh_work; struct mic_device_desc *dd; diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index 5562fdd3ef4e..b7a21e11dcdf 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -549,6 +549,13 @@ struct mic_smpt_ops mic_x100_smpt_ops = { .set = mic_x100_smpt_set, }; +static bool mic_x100_dma_filter(struct dma_chan *chan, void *param) +{ + if (chan->device->dev->parent == (struct device *)param) + return true; + return false; +} + struct mic_hw_ops mic_x100_ops = { .aper_bar = MIC_X100_APER_BAR, .mmio_bar = MIC_X100_MMIO_BAR, @@ -563,6 +570,7 @@ struct mic_hw_ops mic_x100_ops = { .send_firmware_intr = mic_x100_send_firmware_intr, .load_mic_fw = mic_x100_load_firmware, .get_postcode = mic_x100_get_postcode, + .dma_filter = mic_x100_dma_filter, }; struct mic_hw_intr_ops mic_x100_intr_ops = { |