From b170d8ce3f81bd97e85756e9184779a56a5f55a7 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Thu, 5 Sep 2013 16:41:31 -0700 Subject: Intel MIC Host Driver for X100 family. This patch enables the following: a) Initializes the Intel MIC X100 PCIe devices. b) Provides sysfs entries for family and stepping information. Co-author: Dasaratharaman Chandramouli Signed-off-by: Ashutosh Dixit Signed-off-by: Caz Yokoyama Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Harshavardhan R Kharche Signed-off-by: Nikhil Rao Signed-off-by: Sudeep Dutt Acked-by: Yaozu (Eddie) Dong Reviewed-by: Peter P Waskiewicz Jr Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/Makefile | 8 + drivers/misc/mic/host/mic_device.h | 109 +++++++++++++ drivers/misc/mic/host/mic_main.c | 309 +++++++++++++++++++++++++++++++++++++ drivers/misc/mic/host/mic_sysfs.c | 97 ++++++++++++ drivers/misc/mic/host/mic_x100.c | 75 +++++++++ drivers/misc/mic/host/mic_x100.h | 47 ++++++ 6 files changed, 645 insertions(+) create mode 100644 drivers/misc/mic/host/Makefile create mode 100644 drivers/misc/mic/host/mic_device.h create mode 100644 drivers/misc/mic/host/mic_main.c create mode 100644 drivers/misc/mic/host/mic_sysfs.c create mode 100644 drivers/misc/mic/host/mic_x100.c create mode 100644 drivers/misc/mic/host/mic_x100.h (limited to 'drivers/misc/mic/host') diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile new file mode 100644 index 000000000000..93b9d25d2c88 --- /dev/null +++ b/drivers/misc/mic/host/Makefile @@ -0,0 +1,8 @@ +# +# Makefile - Intel MIC Linux driver. +# Copyright(c) 2013, Intel Corporation. +# +obj-$(CONFIG_INTEL_MIC_HOST) += mic_host.o +mic_host-objs := mic_main.o +mic_host-objs += mic_x100.o +mic_host-objs += mic_sysfs.o diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h new file mode 100644 index 000000000000..6cd904cdfdf9 --- /dev/null +++ b/drivers/misc/mic/host/mic_device.h @@ -0,0 +1,109 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#ifndef _MIC_DEVICE_H_ +#define _MIC_DEVICE_H_ + +/* The maximum number of MIC devices supported in a single host system. */ +#define MIC_MAX_NUM_DEVS 256 + +/** + * enum mic_hw_family - The hardware family to which a device belongs. + */ +enum mic_hw_family { + MIC_FAMILY_X100 = 0, + MIC_FAMILY_UNKNOWN +}; + +/** + * enum mic_stepping - MIC stepping ids. + */ +enum mic_stepping { + MIC_A0_STEP = 0x0, + MIC_B0_STEP = 0x10, + MIC_B1_STEP = 0x11, + MIC_C0_STEP = 0x20, +}; + +/** + * struct mic_device - MIC device information for each card. + * + * @mmio: MMIO bar information. + * @aper: Aperture bar information. + * @family: The MIC family to which this device belongs. + * @ops: MIC HW specific operations. + * @id: The unique device id for this MIC device. + * @stepping: Stepping ID. + * @attr_group: Pointer to list of sysfs attribute groups. + * @sdev: Device for sysfs entries. + */ +struct mic_device { + struct mic_mw mmio; + struct mic_mw aper; + enum mic_hw_family family; + struct mic_hw_ops *ops; + int id; + enum mic_stepping stepping; + const struct attribute_group **attr_group; + struct device *sdev; +}; + +/** + * struct mic_hw_ops - MIC HW specific operations. + * @aper_bar: Aperture bar resource number. + * @mmio_bar: MMIO bar resource number. + * @read_spad: Read from scratch pad register. + * @write_spad: Write to scratch pad register. + */ +struct mic_hw_ops { + u8 aper_bar; + u8 mmio_bar; + u32 (*read_spad)(struct mic_device *mdev, unsigned int idx); + void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val); +}; + +/** + * mic_mmio_read - read from an MMIO register. + * @mw: MMIO register base virtual address. + * @offset: register offset. + * + * RETURNS: register value. + */ +static inline u32 mic_mmio_read(struct mic_mw *mw, u32 offset) +{ + return ioread32(mw->va + offset); +} + +/** + * mic_mmio_write - write to an MMIO register. + * @mw: MMIO register base virtual address. + * @val: the data value to put into the register + * @offset: register offset. + * + * RETURNS: none. + */ +static inline void +mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) +{ + iowrite32(val, mw->va + offset); +} + +void mic_sysfs_init(struct mic_device *mdev); +#endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c new file mode 100644 index 000000000000..228c96c04a03 --- /dev/null +++ b/drivers/misc/mic/host/mic_main.c @@ -0,0 +1,309 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + * Global TODO's across the driver to be added after initial base + * patches are accepted upstream: + * 1) Enable DMA support. + * 2) Enable per vring interrupt support. + */ +#include +#include +#include +#include + +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_x100.h" + +static const char mic_driver_name[] = "mic"; + +static DEFINE_PCI_DEVICE_TABLE(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)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2253)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2254)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2255)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2256)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2257)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2258)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_2259)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225a)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225b)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225c)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225d)}, + {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MIC_X100_PCI_DEVICE_225e)}, + + /* required last entry */ + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, mic_pci_tbl); + +/* ID allocator for MIC devices */ +static struct ida g_mic_ida; +/* Class of MIC devices for sysfs accessibility. */ +static struct class *g_mic_class; +/* Base device node number for MIC devices */ +static dev_t g_mic_devno; + +/** + * mic_ops_init: Initialize HW specific operation tables. + * + * @mdev: pointer to mic_device instance + * + * returns none. + */ +static void mic_ops_init(struct mic_device *mdev) +{ + switch (mdev->family) { + case MIC_FAMILY_X100: + mdev->ops = &mic_x100_ops; + break; + default: + break; + } +} + +/** + * mic_get_family - Determine hardware family to which this MIC belongs. + * + * @pdev: The pci device structure + * + * returns family. + */ +static enum mic_hw_family mic_get_family(struct pci_dev *pdev) +{ + enum mic_hw_family family; + + switch (pdev->device) { + case MIC_X100_PCI_DEVICE_2250: + case MIC_X100_PCI_DEVICE_2251: + case MIC_X100_PCI_DEVICE_2252: + case MIC_X100_PCI_DEVICE_2253: + case MIC_X100_PCI_DEVICE_2254: + case MIC_X100_PCI_DEVICE_2255: + case MIC_X100_PCI_DEVICE_2256: + case MIC_X100_PCI_DEVICE_2257: + case MIC_X100_PCI_DEVICE_2258: + case MIC_X100_PCI_DEVICE_2259: + case MIC_X100_PCI_DEVICE_225a: + case MIC_X100_PCI_DEVICE_225b: + case MIC_X100_PCI_DEVICE_225c: + case MIC_X100_PCI_DEVICE_225d: + case MIC_X100_PCI_DEVICE_225e: + family = MIC_FAMILY_X100; + break; + default: + family = MIC_FAMILY_UNKNOWN; + break; + } + return family; +} + +/** + * mic_device_init - Allocates and initializes the MIC device structure + * + * @mdev: pointer to mic_device instance + * @pdev: The pci device structure + * + * returns none. + */ +static void +mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) +{ + mdev->family = mic_get_family(pdev); + mdev->stepping = pdev->revision; + mic_ops_init(mdev); + mic_sysfs_init(mdev); +} + +/** + * mic_probe - Device Initialization Routine + * + * @pdev: PCI device structure + * @ent: entry in mic_pci_tbl + * + * returns 0 on success, < 0 on failure. + */ +static int mic_probe(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int rc; + struct mic_device *mdev; + + mdev = kzalloc(sizeof(*mdev), GFP_KERNEL); + if (!mdev) { + rc = -ENOMEM; + dev_err(&pdev->dev, "mdev kmalloc failed rc %d\n", rc); + goto mdev_alloc_fail; + } + mdev->id = ida_simple_get(&g_mic_ida, 0, MIC_MAX_NUM_DEVS, GFP_KERNEL); + if (mdev->id < 0) { + rc = mdev->id; + dev_err(&pdev->dev, "ida_simple_get failed rc %d\n", rc); + goto ida_fail; + } + + mic_device_init(mdev, pdev); + + rc = pci_enable_device(pdev); + if (rc) { + dev_err(&pdev->dev, "failed to enable pci device.\n"); + goto ida_remove; + } + + pci_set_master(pdev); + + rc = pci_request_regions(pdev, mic_driver_name); + if (rc) { + dev_err(&pdev->dev, "failed to get pci regions.\n"); + goto disable_device; + } + + rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); + if (rc) { + dev_err(&pdev->dev, "Cannot set DMA mask\n"); + goto release_regions; + } + + mdev->mmio.pa = pci_resource_start(pdev, mdev->ops->mmio_bar); + mdev->mmio.len = pci_resource_len(pdev, mdev->ops->mmio_bar); + mdev->mmio.va = pci_ioremap_bar(pdev, mdev->ops->mmio_bar); + if (!mdev->mmio.va) { + dev_err(&pdev->dev, "Cannot remap MMIO BAR\n"); + rc = -EIO; + goto release_regions; + } + + mdev->aper.pa = pci_resource_start(pdev, mdev->ops->aper_bar); + mdev->aper.len = pci_resource_len(pdev, mdev->ops->aper_bar); + mdev->aper.va = ioremap_wc(mdev->aper.pa, mdev->aper.len); + if (!mdev->aper.va) { + dev_err(&pdev->dev, "Cannot remap Aperture BAR\n"); + rc = -EIO; + goto unmap_mmio; + } + + pci_set_drvdata(pdev, mdev); + + mdev->sdev = device_create_with_groups(g_mic_class, &pdev->dev, + MKDEV(MAJOR(g_mic_devno), mdev->id), NULL, + mdev->attr_group, "mic%d", mdev->id); + if (IS_ERR(mdev->sdev)) { + rc = PTR_ERR(mdev->sdev); + dev_err(&pdev->dev, + "device_create_with_groups failed rc %d\n", rc); + goto unmap_aper; + } + return 0; +unmap_aper: + iounmap(mdev->aper.va); +unmap_mmio: + iounmap(mdev->mmio.va); +release_regions: + pci_release_regions(pdev); +disable_device: + pci_disable_device(pdev); +ida_remove: + ida_simple_remove(&g_mic_ida, mdev->id); +ida_fail: + kfree(mdev); +mdev_alloc_fail: + dev_err(&pdev->dev, "Probe failed rc %d\n", rc); + return rc; +} + +/** + * mic_remove - Device Removal Routine + * mic_remove is called by the PCI subsystem to alert the driver + * that it should release a PCI device. + * + * @pdev: PCI device structure + */ +static void mic_remove(struct pci_dev *pdev) +{ + struct mic_device *mdev; + + mdev = pci_get_drvdata(pdev); + if (!mdev) + return; + + device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id)); + iounmap(mdev->mmio.va); + iounmap(mdev->aper.va); + pci_release_regions(pdev); + pci_disable_device(pdev); + ida_simple_remove(&g_mic_ida, mdev->id); + kfree(mdev); +} +static struct pci_driver mic_driver = { + .name = mic_driver_name, + .id_table = mic_pci_tbl, + .probe = mic_probe, + .remove = mic_remove +}; + +static int __init mic_init(void) +{ + int ret; + + ret = alloc_chrdev_region(&g_mic_devno, 0, + MIC_MAX_NUM_DEVS, mic_driver_name); + if (ret) { + pr_err("alloc_chrdev_region failed ret %d\n", ret); + goto error; + } + + g_mic_class = class_create(THIS_MODULE, mic_driver_name); + if (IS_ERR(g_mic_class)) { + ret = PTR_ERR(g_mic_class); + pr_err("class_create failed ret %d\n", ret); + goto cleanup_chrdev; + } + + ida_init(&g_mic_ida); + ret = pci_register_driver(&mic_driver); + if (ret) { + pr_err("pci_register_driver failed ret %d\n", ret); + goto class_destroy; + } + return ret; +class_destroy: + class_destroy(g_mic_class); +cleanup_chrdev: + unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); +error: + return ret; +} + +static void __exit mic_exit(void) +{ + pci_unregister_driver(&mic_driver); + ida_destroy(&g_mic_ida); + class_destroy(g_mic_class); + unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); +} + +module_init(mic_init); +module_exit(mic_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_DESCRIPTION("Intel(R) MIC X100 Host driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c new file mode 100644 index 000000000000..972c18255263 --- /dev/null +++ b/drivers/misc/mic/host/mic_sysfs.c @@ -0,0 +1,97 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#include + +#include "../common/mic_device.h" +#include "mic_device.h" + +static ssize_t +mic_show_family(struct device *dev, struct device_attribute *attr, char *buf) +{ + static const char x100[] = "x100"; + static const char unknown[] = "Unknown"; + const char *card = NULL; + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + switch (mdev->family) { + case MIC_FAMILY_X100: + card = x100; + break; + default: + card = unknown; + break; + } + return scnprintf(buf, PAGE_SIZE, "%s\n", card); +} +static DEVICE_ATTR(family, S_IRUGO, mic_show_family, NULL); + +static ssize_t +mic_show_stepping(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + char *string = "??"; + + if (!mdev) + return -EINVAL; + + switch (mdev->stepping) { + case MIC_A0_STEP: + string = "A0"; + break; + case MIC_B0_STEP: + string = "B0"; + break; + case MIC_B1_STEP: + string = "B1"; + break; + case MIC_C0_STEP: + string = "C0"; + break; + default: + break; + } + return scnprintf(buf, PAGE_SIZE, "%s\n", string); +} +static DEVICE_ATTR(stepping, S_IRUGO, mic_show_stepping, NULL); + +static struct attribute *mic_default_attrs[] = { + &dev_attr_family.attr, + &dev_attr_stepping.attr, + + NULL +}; + +static struct attribute_group mic_attr_group = { + .attrs = mic_default_attrs, +}; + +static const struct attribute_group *__mic_attr_group[] = { + &mic_attr_group, + NULL +}; + +void mic_sysfs_init(struct mic_device *mdev) +{ + mdev->attr_group = __mic_attr_group; +} diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c new file mode 100644 index 000000000000..da481b173af9 --- /dev/null +++ b/drivers/misc/mic/host/mic_x100.c @@ -0,0 +1,75 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#include +#include + +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_x100.h" + +/** + * mic_x100_write_spad - write to the scratchpad register + * @mdev: pointer to mic_device instance + * @idx: index to the scratchpad register, 0 based + * @val: the data value to put into the register + * + * This function allows writing of a 32bit value to the indexed scratchpad + * register. + * + * RETURNS: none. + */ +static void +mic_x100_write_spad(struct mic_device *mdev, unsigned int idx, u32 val) +{ + dev_dbg(mdev->sdev->parent, "Writing 0x%x to scratch pad index %d\n", + val, idx); + mic_mmio_write(&mdev->mmio, val, + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_SPAD0 + idx * 4); +} + +/** + * mic_x100_read_spad - read from the scratchpad register + * @mdev: pointer to mic_device instance + * @idx: index to scratchpad register, 0 based + * + * This function allows reading of the 32bit scratchpad register. + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static u32 +mic_x100_read_spad(struct mic_device *mdev, unsigned int idx) +{ + u32 val = mic_mmio_read(&mdev->mmio, + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_SPAD0 + idx * 4); + + dev_dbg(mdev->sdev->parent, + "Reading 0x%x from scratch pad index %d\n", val, idx); + return val; +} + +struct mic_hw_ops mic_x100_ops = { + .aper_bar = MIC_X100_APER_BAR, + .mmio_bar = MIC_X100_MMIO_BAR, + .read_spad = mic_x100_read_spad, + .write_spad = mic_x100_write_spad, +}; diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h new file mode 100644 index 000000000000..1f4e6305dcf5 --- /dev/null +++ b/drivers/misc/mic/host/mic_x100.h @@ -0,0 +1,47 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#ifndef _MIC_X100_HW_H_ +#define _MIC_X100_HW_H_ + +#define MIC_X100_PCI_DEVICE_2250 0x2250 +#define MIC_X100_PCI_DEVICE_2251 0x2251 +#define MIC_X100_PCI_DEVICE_2252 0x2252 +#define MIC_X100_PCI_DEVICE_2253 0x2253 +#define MIC_X100_PCI_DEVICE_2254 0x2254 +#define MIC_X100_PCI_DEVICE_2255 0x2255 +#define MIC_X100_PCI_DEVICE_2256 0x2256 +#define MIC_X100_PCI_DEVICE_2257 0x2257 +#define MIC_X100_PCI_DEVICE_2258 0x2258 +#define MIC_X100_PCI_DEVICE_2259 0x2259 +#define MIC_X100_PCI_DEVICE_225a 0x225a +#define MIC_X100_PCI_DEVICE_225b 0x225b +#define MIC_X100_PCI_DEVICE_225c 0x225c +#define MIC_X100_PCI_DEVICE_225d 0x225d +#define MIC_X100_PCI_DEVICE_225e 0x225e + +#define MIC_X100_APER_BAR 0 +#define MIC_X100_MMIO_BAR 4 + +#define MIC_X100_SBOX_BASE_ADDRESS 0x00010000 +#define MIC_X100_SBOX_SPAD0 0x0000AB20 +extern struct mic_hw_ops mic_x100_ops; + +#endif -- cgit v1.2.3 From a01e28f692088e9789ebb0c374fdac83de59899b Mon Sep 17 00:00:00 2001 From: Dasaratharaman Chandramouli Date: Thu, 5 Sep 2013 16:41:41 -0700 Subject: Intel MIC Host Driver Interrupt/SMPT support. This patch enables the following features: a) MSIx, MSI and legacy interrupt support. b) System Memory Page Table(SMPT) support. SMPT enables system memory access from the card. On X100 devices the host can program 32 SMPT registers each capable of accessing 16GB of system memory address space from X100 devices. The registers can thereby be used to access a cumulative 512GB of system memory address space from X100 devices at any point in time. Co-author: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Caz Yokoyama Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Acked-by: Yaozu (Eddie) Dong Reviewed-by: Peter P Waskiewicz Jr Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/Makefile | 2 + drivers/misc/mic/host/mic_device.h | 21 ++ drivers/misc/mic/host/mic_intr.c | 628 +++++++++++++++++++++++++++++++++++++ drivers/misc/mic/host/mic_intr.h | 137 ++++++++ drivers/misc/mic/host/mic_main.c | 26 +- drivers/misc/mic/host/mic_smpt.c | 442 ++++++++++++++++++++++++++ drivers/misc/mic/host/mic_smpt.h | 98 ++++++ drivers/misc/mic/host/mic_x100.c | 247 +++++++++++++++ drivers/misc/mic/host/mic_x100.h | 39 +++ 9 files changed, 1638 insertions(+), 2 deletions(-) create mode 100644 drivers/misc/mic/host/mic_intr.c create mode 100644 drivers/misc/mic/host/mic_intr.h create mode 100644 drivers/misc/mic/host/mic_smpt.c create mode 100644 drivers/misc/mic/host/mic_smpt.h (limited to 'drivers/misc/mic/host') diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile index 93b9d25d2c88..6ff5550f10ac 100644 --- a/drivers/misc/mic/host/Makefile +++ b/drivers/misc/mic/host/Makefile @@ -6,3 +6,5 @@ obj-$(CONFIG_INTEL_MIC_HOST) += mic_host.o mic_host-objs := mic_main.o mic_host-objs += mic_x100.o mic_host-objs += mic_sysfs.o +mic_host-objs += mic_smpt.o +mic_host-objs += mic_intr.o diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 6cd904cdfdf9..dd34b6532a01 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -21,6 +21,10 @@ #ifndef _MIC_DEVICE_H_ #define _MIC_DEVICE_H_ +#include + +#include "mic_intr.h" + /* The maximum number of MIC devices supported in a single host system. */ #define MIC_MAX_NUM_DEVS 256 @@ -53,6 +57,12 @@ enum mic_stepping { * @stepping: Stepping ID. * @attr_group: Pointer to list of sysfs attribute groups. * @sdev: Device for sysfs entries. + * @mic_mutex: Mutex for synchronizing access to mic_device. + * @intr_ops: HW specific interrupt operations. + * @smpt_ops: Hardware specific SMPT operations. + * @smpt: MIC SMPT information. + * @intr_info: H/W specific interrupt information. + * @irq_info: The OS specific irq information */ struct mic_device { struct mic_mw mmio; @@ -63,6 +73,12 @@ struct mic_device { enum mic_stepping stepping; const struct attribute_group **attr_group; struct device *sdev; + struct mutex mic_mutex; + struct mic_hw_intr_ops *intr_ops; + struct mic_smpt_ops *smpt_ops; + struct mic_smpt_info *smpt; + struct mic_intr_info *intr_info; + struct mic_irq_info irq_info; }; /** @@ -71,12 +87,17 @@ struct mic_device { * @mmio_bar: MMIO bar resource number. * @read_spad: Read from scratch pad register. * @write_spad: Write to scratch pad register. + * @send_intr: Send an interrupt for a particular doorbell on the card. + * @ack_interrupt: Hardware specific operations to ack the h/w on + * receipt of an interrupt. */ struct mic_hw_ops { u8 aper_bar; u8 mmio_bar; u32 (*read_spad)(struct mic_device *mdev, unsigned int idx); void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val); + void (*send_intr)(struct mic_device *mdev, int doorbell); + u32 (*ack_interrupt)(struct mic_device *mdev); }; /** diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c new file mode 100644 index 000000000000..cdae3141dbb9 --- /dev/null +++ b/drivers/misc/mic/host/mic_intr.c @@ -0,0 +1,628 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#include +#include + +#include "../common/mic_device.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) +{ + struct mic_intr_cb *intr_cb; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + + 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); +} + +/** + * mic_interrupt - Generic interrupt handler for + * MSI and INTx based interrupts. + */ +static irqreturn_t mic_interrupt(int irq, void *dev) +{ + struct mic_device *mdev = dev; + struct mic_intr_info *info = mdev->intr_info; + u32 mask; + int i; + + mask = mdev->ops->ack_interrupt(mdev); + 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; +} + +/* Return the interrupt offset from the index. Index is 0 based. */ +static u16 mic_map_src_to_offset(struct mic_device *mdev, + int intr_src, enum mic_intr_type type) { + + if (type >= MIC_NUM_INTR_TYPES) + return MIC_NUM_OFFSETS; + if (intr_src >= mdev->intr_info->intr_len[type]) + return MIC_NUM_OFFSETS; + + return mdev->intr_info->intr_start_idx[type] + intr_src; +} + +/* Return next available msix_entry. */ +static struct msix_entry *mic_get_available_vector(struct mic_device *mdev) +{ + int i; + struct mic_irq_info *info = &mdev->irq_info; + + for (i = 0; i < info->num_vectors; i++) + if (!info->mic_msi_map[i]) + return &info->msix_entries[i]; + return NULL; +} + +/** + * mic_register_intr_callback - Register a callback handler for the + * given source id. + * + * @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 + * the interrupt. + * @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), + void *data) +{ + struct mic_intr_cb *intr_cb; + unsigned long flags; + int rc; + intr_cb = kmalloc(sizeof(struct mic_intr_cb), GFP_KERNEL); + + if (!intr_cb) + return ERR_PTR(-ENOMEM); + + intr_cb->func = func; + intr_cb->data = data; + intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida, + 0, 0, GFP_KERNEL); + if (intr_cb->cb_id < 0) { + rc = intr_cb->cb_id; + goto ida_fail; + } + + 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); + + return intr_cb; +ida_fail: + kfree(intr_cb); + return ERR_PTR(rc); +} + +/** + * mic_unregister_intr_callback - Unregister the callback handler + * identified by its callback id. + * + * @mdev: pointer to the mic_device instance + * @idx: The callback structure id to be unregistered. + * Return the source id that was unregistered or MIC_NUM_OFFSETS if no + * such callback handler was found. + */ +static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) +{ + struct list_head *pos, *tmp; + struct mic_intr_cb *intr_cb; + unsigned long flags; + int i; + + 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) { + list_del(pos); + ida_simple_remove(&mdev->irq_info.cb_ida, + intr_cb->cb_id); + kfree(intr_cb); + spin_unlock_irqrestore( + &mdev->irq_info.mic_intr_lock, flags); + return i; + } + } + spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); + } + return MIC_NUM_OFFSETS; +} + +/** + * mic_setup_msix - Initializes MSIx interrupts. + * + * @mdev: pointer to mic_device instance + * + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int mic_setup_msix(struct mic_device *mdev, struct pci_dev *pdev) +{ + int rc, i; + + mdev->irq_info.msix_entries = kmalloc(sizeof(struct msix_entry) * + MIC_MIN_MSIX, GFP_KERNEL); + if (!mdev->irq_info.msix_entries) { + rc = -ENOMEM; + goto err_nomem1; + } + + for (i = 0; i < MIC_MIN_MSIX; i++) + mdev->irq_info.msix_entries[i].entry = i; + + rc = pci_enable_msix(pdev, mdev->irq_info.msix_entries, + MIC_MIN_MSIX); + if (rc) { + dev_dbg(&pdev->dev, "Error enabling MSIx. rc = %d\n", rc); + goto err_enable_msix; + } + + mdev->irq_info.num_vectors = MIC_MIN_MSIX; + mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) * + mdev->irq_info.num_vectors), GFP_KERNEL); + + if (!mdev->irq_info.mic_msi_map) { + rc = -ENOMEM; + goto err_nomem2; + } + + dev_dbg(mdev->sdev->parent, + "%d MSIx irqs setup\n", mdev->irq_info.num_vectors); + return 0; +err_nomem2: + pci_disable_msix(pdev); +err_enable_msix: + kfree(mdev->irq_info.msix_entries); +err_nomem1: + mdev->irq_info.num_vectors = 0; + return rc; +} + +/** + * mic_setup_callbacks - Initialize data structures needed + * to handle callbacks. + * + * @mdev: pointer to mic_device instance + */ +static int mic_setup_callbacks(struct mic_device *mdev) +{ + int i; + + mdev->irq_info.cb_list = kmalloc(sizeof(struct list_head) * + MIC_NUM_OFFSETS, GFP_KERNEL); + if (!mdev->irq_info.cb_list) + return -ENOMEM; + + for (i = 0; i < MIC_NUM_OFFSETS; i++) + 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); + return 0; +} + +/** + * mic_release_callbacks - Uninitialize data structures needed + * to handle callbacks. + * + * @mdev: pointer to mic_device instance + */ +static void mic_release_callbacks(struct mic_device *mdev) +{ + unsigned long flags; + struct list_head *pos, *tmp; + struct mic_intr_cb *intr_cb; + int i; + + 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); + break; + } + + list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) { + intr_cb = list_entry(pos, struct mic_intr_cb, list); + list_del(pos); + ida_simple_remove(&mdev->irq_info.cb_ida, + intr_cb->cb_id); + kfree(intr_cb); + } + spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); + } + ida_destroy(&mdev->irq_info.cb_ida); + kfree(mdev->irq_info.cb_list); +} + +/** + * mic_setup_msi - Initializes MSI interrupts. + * + * @mdev: pointer to mic_device instance + * @pdev: PCI device structure + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int mic_setup_msi(struct mic_device *mdev, struct pci_dev *pdev) +{ + int rc; + + rc = pci_enable_msi(pdev); + if (rc) { + dev_dbg(&pdev->dev, "Error enabling MSI. rc = %d\n", rc); + return rc; + } + + mdev->irq_info.num_vectors = 1; + mdev->irq_info.mic_msi_map = kzalloc((sizeof(u32) * + mdev->irq_info.num_vectors), GFP_KERNEL); + + if (!mdev->irq_info.mic_msi_map) { + rc = -ENOMEM; + goto err_nomem1; + } + + rc = mic_setup_callbacks(mdev); + if (rc) { + dev_err(&pdev->dev, "Error setting up callbacks\n"); + goto err_nomem2; + } + + rc = request_irq(pdev->irq, mic_interrupt, 0 , "mic-msi", mdev); + if (rc) { + dev_err(&pdev->dev, "Error allocating MSI interrupt\n"); + goto err_irq_req_fail; + } + + dev_dbg(&pdev->dev, "%d MSI irqs setup\n", mdev->irq_info.num_vectors); + return 0; +err_irq_req_fail: + mic_release_callbacks(mdev); +err_nomem2: + kfree(mdev->irq_info.mic_msi_map); +err_nomem1: + pci_disable_msi(pdev); + mdev->irq_info.num_vectors = 0; + return rc; +} + +/** + * mic_setup_intx - Initializes legacy interrupts. + * + * @mdev: pointer to mic_device instance + * @pdev: PCI device structure + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev) +{ + int rc; + + pci_msi_off(pdev); + + /* Enable intx */ + pci_intx(pdev, 1); + rc = mic_setup_callbacks(mdev); + if (rc) { + dev_err(&pdev->dev, "Error setting up callbacks\n"); + goto err_nomem; + } + + rc = request_irq(pdev->irq, mic_interrupt, + IRQF_SHARED, "mic-intx", mdev); + if (rc) + goto err; + + dev_dbg(&pdev->dev, "intx irq setup\n"); + return 0; +err: + mic_release_callbacks(mdev); +err_nomem: + return rc; +} + +/** + * mic_next_db - Retrieve the next doorbell interrupt source id. + * The id is picked sequentially from the available pool of + * doorlbell ids. + * + * @mdev: pointer to the mic_device instance. + * + * Returns the next doorbell interrupt source. + */ +int mic_next_db(struct mic_device *mdev) +{ + int next_db; + + next_db = mdev->irq_info.next_avail_src % + mdev->intr_info->intr_len[MIC_INTR_DB]; + mdev->irq_info.next_avail_src++; + return next_db; +} + +#define COOKIE_ID_SHIFT 16 +#define GET_ENTRY(cookie) ((cookie) & 0xFFFF) +#define GET_OFFSET(cookie) ((cookie) >> COOKIE_ID_SHIFT) +#define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT) + +/** + * mic_request_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. + * The function needs to call ack_interrupts + * (mdev->ops->ack_interrupt(mdev)) when handling the interrupts. + * @name: The ASCII name of the callee requesting the irq. + * @data: private data that is returned back when calling the + * function handler. + * @intr_src: The source id of the requester. Its the doorbell id + * for Doorbell interrupts and DMA channel id for DMA interrupts. + * @type: The type of interrupt. Values defined in mic_intr_type + * + * returns: The cookie that is transparent to the caller. Passed + * back when calling mic_free_irq. An appropriate error code + * is returned on failure. Caller needs to use IS_ERR(return_val) + * to check for failure and PTR_ERR(return_val) to obtained the + * 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) +{ + u16 offset; + int rc = 0; + struct msix_entry *msix = NULL; + unsigned long cookie = 0; + u16 entry; + struct mic_intr_cb *intr_cb; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + + offset = mic_map_src_to_offset(mdev, intr_src, type); + if (offset >= MIC_NUM_OFFSETS) { + dev_err(mdev->sdev->parent, + "Error mapping index %d to a valid source id.\n", + intr_src); + rc = -EINVAL; + goto err; + } + + if (mdev->irq_info.num_vectors > 1) { + msix = mic_get_available_vector(mdev); + if (!msix) { + dev_err(mdev->sdev->parent, + "No MSIx vectors available for use.\n"); + rc = -ENOSPC; + goto err; + } + + rc = request_irq(msix->vector, func, 0, name, data); + if (rc) { + dev_dbg(mdev->sdev->parent, + "request irq failed rc = %d\n", rc); + goto err; + } + entry = msix->entry; + mdev->irq_info.mic_msi_map[entry] |= BIT(offset); + mdev->intr_ops->program_msi_to_src_map(mdev, + entry, offset, true); + cookie = MK_COOKIE(entry, offset); + 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); + if (IS_ERR(intr_cb)) { + dev_err(mdev->sdev->parent, + "No available callback entries for use\n"); + rc = PTR_ERR(intr_cb); + goto err; + } + + entry = 0; + if (pci_dev_msi_enabled(pdev)) { + mdev->irq_info.mic_msi_map[entry] |= (1 << offset); + mdev->intr_ops->program_msi_to_src_map(mdev, + entry, offset, true); + } + cookie = MK_COOKIE(entry, intr_cb->cb_id); + dev_dbg(mdev->sdev->parent, "callback %d registered for src: %d\n", + intr_cb->cb_id, intr_src); + } + return (struct mic_irq *)cookie; +err: + return ERR_PTR(rc); +} + +/** + * mic_free_irq - free irq. mic_mutex + * 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 + * @data: private data specified by the calling function during the + * mic_request_irq + * + * returns: none. + */ +void mic_free_irq(struct mic_device *mdev, + struct mic_irq *cookie, void *data) +{ + u32 offset; + u32 entry; + u8 src_id; + unsigned int irq; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + + entry = GET_ENTRY((unsigned long)cookie); + offset = GET_OFFSET((unsigned long)cookie); + if (mdev->irq_info.num_vectors > 1) { + if (entry >= mdev->irq_info.num_vectors) { + dev_warn(mdev->sdev->parent, + "entry %d should be < num_irq %d\n", + entry, mdev->irq_info.num_vectors); + return; + } + irq = mdev->irq_info.msix_entries[entry].vector; + free_irq(irq, data); + mdev->irq_info.mic_msi_map[entry] &= ~(BIT(offset)); + mdev->intr_ops->program_msi_to_src_map(mdev, + entry, offset, false); + + dev_dbg(mdev->sdev->parent, "irq: %d freed\n", irq); + } else { + irq = pdev->irq; + src_id = mic_unregister_intr_callback(mdev, offset); + if (src_id >= MIC_NUM_OFFSETS) { + dev_warn(mdev->sdev->parent, "Error unregistering callback\n"); + return; + } + if (pci_dev_msi_enabled(pdev)) { + mdev->irq_info.mic_msi_map[entry] &= ~(BIT(src_id)); + mdev->intr_ops->program_msi_to_src_map(mdev, + entry, src_id, false); + } + dev_dbg(mdev->sdev->parent, "callback %d unregistered for src: %d\n", + offset, src_id); + } +} + +/** + * mic_setup_interrupts - Initializes interrupts. + * + * @mdev: pointer to mic_device instance + * @pdev: PCI device structure + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev) +{ + int rc; + + rc = mic_setup_msix(mdev, pdev); + if (!rc) + goto done; + + rc = mic_setup_msi(mdev, pdev); + if (!rc) + goto done; + + rc = mic_setup_intx(mdev, pdev); + if (rc) { + dev_err(mdev->sdev->parent, "no usable interrupts\n"); + return rc; + } +done: + mdev->intr_ops->enable_interrupts(mdev); + return 0; +} + +/** + * mic_free_interrupts - Frees interrupts setup by mic_setup_interrupts + * + * @mdev: pointer to mic_device instance + * @pdev: PCI device structure + * + * returns none. + */ +void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev) +{ + int i; + + mdev->intr_ops->disable_interrupts(mdev); + if (mdev->irq_info.num_vectors > 1) { + for (i = 0; i < mdev->irq_info.num_vectors; i++) { + if (mdev->irq_info.mic_msi_map[i]) + dev_warn(&pdev->dev, "irq %d may still be in use.\n", + mdev->irq_info.msix_entries[i].vector); + } + kfree(mdev->irq_info.mic_msi_map); + kfree(mdev->irq_info.msix_entries); + pci_disable_msix(pdev); + } else { + if (pci_dev_msi_enabled(pdev)) { + free_irq(pdev->irq, mdev); + kfree(mdev->irq_info.mic_msi_map); + pci_disable_msi(pdev); + } else { + free_irq(pdev->irq, mdev); + } + mic_release_callbacks(mdev); + } +} + +/** + * mic_intr_restore - Restore MIC interrupt registers. + * + * @mdev: pointer to mic_device instance. + * + * Restore the interrupt registers to values previously + * stored in the SW data structures. mic_mutex needs to + * be held before calling this function. + * + * returns None. + */ +void mic_intr_restore(struct mic_device *mdev) +{ + int entry, offset; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + + if (!pci_dev_msi_enabled(pdev)) + return; + + for (entry = 0; entry < mdev->irq_info.num_vectors; entry++) { + for (offset = 0; offset < MIC_NUM_OFFSETS; offset++) { + if (mdev->irq_info.mic_msi_map[entry] & BIT(offset)) + mdev->intr_ops->program_msi_to_src_map(mdev, + entry, offset, true); + } + } +} diff --git a/drivers/misc/mic/host/mic_intr.h b/drivers/misc/mic/host/mic_intr.h new file mode 100644 index 000000000000..6091aa97e116 --- /dev/null +++ b/drivers/misc/mic/host/mic_intr.h @@ -0,0 +1,137 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#ifndef _MIC_INTR_H_ +#define _MIC_INTR_H_ + +/* + * The minimum number of msix vectors required for normal operation. + * 3 for virtio network, console and block devices. + * 1 for card shutdown notifications. + */ +#define MIC_MIN_MSIX 4 +#define MIC_NUM_OFFSETS 32 + +/** + * mic_intr_source - The type of source that will generate + * the interrupt.The number of types needs to be in sync with + * MIC_NUM_INTR_TYPES + * + * MIC_INTR_DB: The source is a doorbell + * MIC_INTR_DMA: The source is a DMA channel + * MIC_INTR_ERR: The source is an error interrupt e.g. SBOX ERR + * MIC_NUM_INTR_TYPES: Total number of interrupt sources. + */ +enum mic_intr_type { + MIC_INTR_DB = 0, + MIC_INTR_DMA, + MIC_INTR_ERR, + MIC_NUM_INTR_TYPES +}; + +/** + * struct mic_intr_info - Contains h/w specific interrupt sources + * information. + * + * @intr_start_idx: Contains the starting indexes of the + * interrupt types. + * @intr_len: Contains the length of the interrupt types. + */ +struct mic_intr_info { + u16 intr_start_idx[MIC_NUM_INTR_TYPES]; + u16 intr_len[MIC_NUM_INTR_TYPES]; +}; + +/** + * struct mic_irq_info - OS specific irq information + * + * @next_avail_src: next available doorbell that can be assigned. + * @msix_entries: msix entries allocated while setting up MSI-x + * @mic_msi_map: The MSI/MSI-x mapping information. + * @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. + * @cb_list: Array of callback lists one for each source. + */ +struct mic_irq_info { + int next_avail_src; + struct msix_entry *msix_entries; + u32 *mic_msi_map; + u16 num_vectors; + struct ida cb_ida; + spinlock_t mic_intr_lock; + struct list_head *cb_list; +}; + +/** + * struct mic_intr_cb - Interrupt callback structure. + * + * @func: The callback function + * @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); + void *data; + int cb_id; + struct list_head list; +}; + +/** + * struct mic_irq - opaque pointer used as cookie + */ +struct mic_irq; + +/* Forward declaration */ +struct mic_device; + +/** + * struct mic_hw_intr_ops: MIC HW specific interrupt operations + * @intr_init: Initialize H/W specific interrupt information. + * @enable_interrupts: Enable interrupts from the hardware. + * @disable_interrupts: Disable interrupts from the hardware. + * @program_msi_to_src_map: Update MSI mapping registers with + * irq information. + * @read_msi_to_src_map: Read MSI mapping registers containing + * irq information. + */ +struct mic_hw_intr_ops { + void (*intr_init)(struct mic_device *mdev); + void (*enable_interrupts)(struct mic_device *mdev); + void (*disable_interrupts)(struct mic_device *mdev); + void (*program_msi_to_src_map) (struct mic_device *mdev, + int idx, int intr_src, bool set); + u32 (*read_msi_to_src_map) (struct mic_device *mdev, + int idx); +}; + +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); + +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); +void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev); +void mic_intr_restore(struct mic_device *mdev); +#endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index 228c96c04a03..332a15e4215b 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -23,13 +23,13 @@ * 2) Enable per vring interrupt support. */ #include -#include #include #include #include "../common/mic_device.h" #include "mic_device.h" #include "mic_x100.h" +#include "mic_smpt.h" static const char mic_driver_name[] = "mic"; @@ -75,6 +75,8 @@ static void mic_ops_init(struct mic_device *mdev) switch (mdev->family) { case MIC_FAMILY_X100: mdev->ops = &mic_x100_ops; + mdev->intr_ops = &mic_x100_intr_ops; + mdev->smpt_ops = &mic_x100_smpt_ops; break; default: break; @@ -132,6 +134,8 @@ mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) mdev->stepping = pdev->revision; mic_ops_init(mdev); mic_sysfs_init(mdev); + mutex_init(&mdev->mic_mutex); + mdev->irq_info.next_avail_src = 0; } /** @@ -201,6 +205,18 @@ static int mic_probe(struct pci_dev *pdev, goto unmap_mmio; } + mdev->intr_ops->intr_init(mdev); + rc = mic_setup_interrupts(mdev, pdev); + if (rc) { + dev_err(&pdev->dev, "mic_setup_interrupts failed %d\n", rc); + goto unmap_aper; + } + rc = mic_smpt_init(mdev); + if (rc) { + dev_err(&pdev->dev, "smpt_init failed %d\n", rc); + goto free_interrupts; + } + pci_set_drvdata(pdev, mdev); mdev->sdev = device_create_with_groups(g_mic_class, &pdev->dev, @@ -210,9 +226,13 @@ static int mic_probe(struct pci_dev *pdev, rc = PTR_ERR(mdev->sdev); dev_err(&pdev->dev, "device_create_with_groups failed rc %d\n", rc); - goto unmap_aper; + goto smpt_uninit; } return 0; +smpt_uninit: + mic_smpt_uninit(mdev); +free_interrupts: + mic_free_interrupts(mdev, pdev); unmap_aper: iounmap(mdev->aper.va); unmap_mmio: @@ -246,6 +266,8 @@ static void mic_remove(struct pci_dev *pdev) return; device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id)); + mic_smpt_uninit(mdev); + mic_free_interrupts(mdev, pdev); iounmap(mdev->mmio.va); iounmap(mdev->aper.va); pci_release_regions(pdev); diff --git a/drivers/misc/mic/host/mic_smpt.c b/drivers/misc/mic/host/mic_smpt.c new file mode 100644 index 000000000000..a3462076bc57 --- /dev/null +++ b/drivers/misc/mic/host/mic_smpt.c @@ -0,0 +1,442 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#include + +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_smpt.h" + +static inline u64 mic_system_page_mask(struct mic_device *mdev) +{ + return (1ULL << mdev->smpt->info.page_shift) - 1ULL; +} + +static inline u8 mic_sys_addr_to_smpt(struct mic_device *mdev, dma_addr_t pa) +{ + return (pa - mdev->smpt->info.base) >> mdev->smpt->info.page_shift; +} + +static inline u64 mic_smpt_to_pa(struct mic_device *mdev, u8 index) +{ + return mdev->smpt->info.base + (index * mdev->smpt->info.page_size); +} + +static inline u64 mic_smpt_offset(struct mic_device *mdev, dma_addr_t pa) +{ + return pa & mic_system_page_mask(mdev); +} + +static inline u64 mic_smpt_align_low(struct mic_device *mdev, dma_addr_t pa) +{ + return ALIGN(pa - mic_system_page_mask(mdev), + mdev->smpt->info.page_size); +} + +static inline u64 mic_smpt_align_high(struct mic_device *mdev, dma_addr_t pa) +{ + return ALIGN(pa, mdev->smpt->info.page_size); +} + +/* Total Cumulative system memory accessible by MIC across all SMPT entries */ +static inline u64 mic_max_system_memory(struct mic_device *mdev) +{ + return mdev->smpt->info.num_reg * mdev->smpt->info.page_size; +} + +/* Maximum system memory address accessible by MIC */ +static inline u64 mic_max_system_addr(struct mic_device *mdev) +{ + return mdev->smpt->info.base + mic_max_system_memory(mdev) - 1ULL; +} + +/* Check if the DMA address is a MIC system memory address */ +static inline bool +mic_is_system_addr(struct mic_device *mdev, dma_addr_t pa) +{ + return pa >= mdev->smpt->info.base && pa <= mic_max_system_addr(mdev); +} + +/* Populate an SMPT entry and update the reference counts. */ +static void mic_add_smpt_entry(int spt, s64 *ref, u64 addr, + int entries, struct mic_device *mdev) +{ + struct mic_smpt_info *smpt_info = mdev->smpt; + int i; + + for (i = spt; i < spt + entries; i++, + addr += smpt_info->info.page_size) { + if (!smpt_info->entry[i].ref_count && + (smpt_info->entry[i].dma_addr != addr)) { + mdev->smpt_ops->set(mdev, addr, i); + smpt_info->entry[i].dma_addr = addr; + } + smpt_info->entry[i].ref_count += ref[i - spt]; + } +} + +/* + * Find an available MIC address in MIC SMPT address space + * for a given DMA address and size. + */ +static dma_addr_t mic_smpt_op(struct mic_device *mdev, u64 dma_addr, + int entries, s64 *ref, size_t size) +{ + int spt; + int ae = 0; + int i; + unsigned long flags; + dma_addr_t mic_addr = 0; + dma_addr_t addr = dma_addr; + struct mic_smpt_info *smpt_info = mdev->smpt; + + spin_lock_irqsave(&smpt_info->smpt_lock, flags); + + /* find existing entries */ + for (i = 0; i < smpt_info->info.num_reg; i++) { + if (smpt_info->entry[i].dma_addr == addr) { + ae++; + addr += smpt_info->info.page_size; + } else if (ae) /* cannot find contiguous entries */ + goto not_found; + + if (ae == entries) + goto found; + } + + /* find free entry */ + for (ae = 0, i = 0; i < smpt_info->info.num_reg; i++) { + ae = (smpt_info->entry[i].ref_count == 0) ? ae + 1 : 0; + if (ae == entries) + goto found; + } + +not_found: + spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); + return mic_addr; + +found: + spt = i - entries + 1; + mic_addr = mic_smpt_to_pa(mdev, spt); + mic_add_smpt_entry(spt, ref, dma_addr, entries, mdev); + smpt_info->map_count++; + smpt_info->ref_count += (s64)size; + spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); + return mic_addr; +} + +/* + * Returns number of smpt entries needed for dma_addr to dma_addr + size + * also returns the reference count array for each of those entries + * and the starting smpt address + */ +static int mic_get_smpt_ref_count(struct mic_device *mdev, dma_addr_t dma_addr, + size_t size, s64 *ref, u64 *smpt_start) +{ + u64 start = dma_addr; + u64 end = dma_addr + size; + int i = 0; + + while (start < end) { + ref[i++] = min(mic_smpt_align_high(mdev, start + 1), + end) - start; + start = mic_smpt_align_high(mdev, start + 1); + } + + if (smpt_start) + *smpt_start = mic_smpt_align_low(mdev, dma_addr); + + return i; +} + +/* + * mic_to_dma_addr - Converts a MIC address to a DMA address. + * + * @mdev: pointer to mic_device instance. + * @mic_addr: MIC address. + * + * returns a DMA address. + */ +static dma_addr_t +mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr) +{ + struct mic_smpt_info *smpt_info = mdev->smpt; + int spt; + dma_addr_t dma_addr; + + if (!mic_is_system_addr(mdev, mic_addr)) { + dev_err(mdev->sdev->parent, + "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr); + return -EINVAL; + } + spt = mic_sys_addr_to_smpt(mdev, mic_addr); + dma_addr = smpt_info->entry[spt].dma_addr + + mic_smpt_offset(mdev, mic_addr); + return dma_addr; +} + +/** + * mic_map - Maps a DMA address to a MIC physical address. + * + * @mdev: pointer to mic_device instance. + * @dma_addr: DMA address. + * @size: Size of the region to be mapped. + * + * This API converts the DMA address provided to a DMA address understood + * by MIC. Caller should check for errors by calling mic_map_error(..). + * + * returns DMA address as required by MIC. + */ +dma_addr_t mic_map(struct mic_device *mdev, dma_addr_t dma_addr, size_t size) +{ + dma_addr_t mic_addr = 0; + int num_entries; + s64 *ref; + u64 smpt_start; + + if (!size || size > mic_max_system_memory(mdev)) + return mic_addr; + + ref = kmalloc(mdev->smpt->info.num_reg * sizeof(s64), GFP_KERNEL); + if (!ref) + return mic_addr; + + num_entries = mic_get_smpt_ref_count(mdev, dma_addr, size, + ref, &smpt_start); + + /* Set the smpt table appropriately and get 16G aligned mic address */ + mic_addr = mic_smpt_op(mdev, smpt_start, num_entries, ref, size); + + kfree(ref); + + /* + * If mic_addr is zero then its an error case + * since mic_addr can never be zero. + * else generate mic_addr by adding the 16G offset in dma_addr + */ + if (!mic_addr && MIC_FAMILY_X100 == mdev->family) { + dev_err(mdev->sdev->parent, + "mic_map failed dma_addr 0x%llx size 0x%lx\n", + dma_addr, size); + return mic_addr; + } else { + return mic_addr + mic_smpt_offset(mdev, dma_addr); + } +} + +/** + * mic_unmap - Unmaps a MIC physical address. + * + * @mdev: pointer to mic_device instance. + * @mic_addr: MIC physical address. + * @size: Size of the region to be unmapped. + * + * This API unmaps the mappings created by mic_map(..). + * + * returns None. + */ +void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) +{ + struct mic_smpt_info *smpt_info = mdev->smpt; + s64 *ref; + int num_smpt; + int spt; + int i; + unsigned long flags; + + if (!size) + return; + + if (!mic_is_system_addr(mdev, mic_addr)) { + dev_err(mdev->sdev->parent, + "invalid address: 0x%llx\n", mic_addr); + return; + } + + spt = mic_sys_addr_to_smpt(mdev, mic_addr); + ref = kmalloc(mdev->smpt->info.num_reg * sizeof(s64), GFP_KERNEL); + if (!ref) + return; + + /* Get number of smpt entries to be mapped, ref count array */ + num_smpt = mic_get_smpt_ref_count(mdev, mic_addr, size, ref, NULL); + + spin_lock_irqsave(&smpt_info->smpt_lock, flags); + smpt_info->unmap_count++; + smpt_info->ref_count -= (s64)size; + + for (i = spt; i < spt + num_smpt; i++) { + smpt_info->entry[i].ref_count -= ref[i - spt]; + if (smpt_info->entry[i].ref_count < 0) + dev_warn(mdev->sdev->parent, + "ref count for entry %d is negative\n", i); + } + spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); + kfree(ref); +} + +/** + * mic_map_single - Maps a virtual address to a MIC physical address. + * + * @mdev: pointer to mic_device instance. + * @va: Kernel direct mapped virtual address. + * @size: Size of the region to be mapped. + * + * This API calls pci_map_single(..) for the direct mapped virtual address + * and then converts the DMA address provided to a DMA address understood + * by MIC. Caller should check for errors by calling mic_map_error(..). + * + * returns DMA address as required by MIC. + */ +dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size) +{ + dma_addr_t mic_addr = 0; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + dma_addr_t dma_addr = + pci_map_single(pdev, va, size, PCI_DMA_BIDIRECTIONAL); + + if (!pci_dma_mapping_error(pdev, dma_addr)) { + mic_addr = mic_map(mdev, dma_addr, size); + if (!mic_addr) { + dev_err(mdev->sdev->parent, + "mic_map failed dma_addr 0x%llx size 0x%lx\n", + dma_addr, size); + pci_unmap_single(pdev, dma_addr, + size, PCI_DMA_BIDIRECTIONAL); + } + } + return mic_addr; +} + +/** + * mic_unmap_single - Unmaps a MIC physical address. + * + * @mdev: pointer to mic_device instance. + * @mic_addr: MIC physical address. + * @size: Size of the region to be unmapped. + * + * This API unmaps the mappings created by mic_map_single(..). + * + * returns None. + */ +void +mic_unmap_single(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) +{ + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + dma_addr_t dma_addr = mic_to_dma_addr(mdev, mic_addr); + mic_unmap(mdev, mic_addr, size); + pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL); +} + +/** + * mic_smpt_init - Initialize MIC System Memory Page Tables. + * + * @mdev: pointer to mic_device instance. + * + * returns 0 for success and -errno for error. + */ +int mic_smpt_init(struct mic_device *mdev) +{ + int i, err = 0; + dma_addr_t dma_addr; + struct mic_smpt_info *smpt_info; + + mdev->smpt = kmalloc(sizeof(*mdev->smpt), GFP_KERNEL); + if (!mdev->smpt) + return -ENOMEM; + + smpt_info = mdev->smpt; + mdev->smpt_ops->init(mdev); + smpt_info->entry = kmalloc(sizeof(struct mic_smpt) + * smpt_info->info.num_reg, GFP_KERNEL); + if (!smpt_info->entry) { + err = -ENOMEM; + goto free_smpt; + } + spin_lock_init(&smpt_info->smpt_lock); + for (i = 0; i < smpt_info->info.num_reg; i++) { + dma_addr = i * smpt_info->info.page_size; + smpt_info->entry[i].dma_addr = dma_addr; + smpt_info->entry[i].ref_count = 0; + mdev->smpt_ops->set(mdev, dma_addr, i); + } + smpt_info->ref_count = 0; + smpt_info->map_count = 0; + smpt_info->unmap_count = 0; + return 0; +free_smpt: + kfree(smpt_info); + return err; +} + +/** + * mic_smpt_uninit - UnInitialize MIC System Memory Page Tables. + * + * @mdev: pointer to mic_device instance. + * + * returns None. + */ +void mic_smpt_uninit(struct mic_device *mdev) +{ + struct mic_smpt_info *smpt_info = mdev->smpt; + int i; + + dev_dbg(mdev->sdev->parent, + "nodeid %d SMPT ref count %lld map %lld unmap %lld\n", + mdev->id, smpt_info->ref_count, + smpt_info->map_count, smpt_info->unmap_count); + + for (i = 0; i < smpt_info->info.num_reg; i++) { + dev_dbg(mdev->sdev->parent, + "SMPT entry[%d] dma_addr = 0x%llx ref_count = %lld\n", + i, smpt_info->entry[i].dma_addr, + smpt_info->entry[i].ref_count); + if (smpt_info->entry[i].ref_count) + dev_warn(mdev->sdev->parent, + "ref count for entry %d is not zero\n", i); + } + kfree(smpt_info->entry); + kfree(smpt_info); +} + +/** + * mic_smpt_restore - Restore MIC System Memory Page Tables. + * + * @mdev: pointer to mic_device instance. + * + * Restore the SMPT registers to values previously stored in the + * SW data structures. Some MIC steppings lose register state + * across resets and this API should be called for performing + * a restore operation if required. + * + * returns None. + */ +void mic_smpt_restore(struct mic_device *mdev) +{ + int i; + dma_addr_t dma_addr; + + for (i = 0; i < mdev->smpt->info.num_reg; i++) { + dma_addr = mdev->smpt->entry[i].dma_addr; + mdev->smpt_ops->set(mdev, dma_addr, i); + } +} diff --git a/drivers/misc/mic/host/mic_smpt.h b/drivers/misc/mic/host/mic_smpt.h new file mode 100644 index 000000000000..51970abfe7df --- /dev/null +++ b/drivers/misc/mic/host/mic_smpt.h @@ -0,0 +1,98 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#ifndef MIC_SMPT_H +#define MIC_SMPT_H +/** + * struct mic_smpt_ops - MIC HW specific SMPT operations. + * @init: Initialize hardware specific SMPT information in mic_smpt_hw_info. + * @set: Set the value for a particular SMPT entry. + */ +struct mic_smpt_ops { + void (*init)(struct mic_device *mdev); + void (*set)(struct mic_device *mdev, dma_addr_t dma_addr, u8 index); +}; + +/** + * struct mic_smpt - MIC SMPT entry information. + * @dma_addr: Base DMA address for this SMPT entry. + * @ref_count: Number of active mappings for this SMPT entry in bytes. + */ +struct mic_smpt { + dma_addr_t dma_addr; + s64 ref_count; +}; + +/** + * struct mic_smpt_hw_info - MIC SMPT hardware specific information. + * @num_reg: Number of SMPT registers. + * @page_shift: System memory page shift. + * @page_size: System memory page size. + * @base: System address base. + */ +struct mic_smpt_hw_info { + u8 num_reg; + u8 page_shift; + u64 page_size; + u64 base; +}; + +/** + * struct mic_smpt_info - MIC SMPT information. + * @entry: Array of SMPT entries. + * @smpt_lock: Spin lock protecting access to SMPT data structures. + * @info: Hardware specific SMPT information. + * @ref_count: Number of active SMPT mappings (for debug). + * @map_count: Number of SMPT mappings created (for debug). + * @unmap_count: Number of SMPT mappings destroyed (for debug). + */ +struct mic_smpt_info { + struct mic_smpt *entry; + spinlock_t smpt_lock; + struct mic_smpt_hw_info info; + s64 ref_count; + s64 map_count; + s64 unmap_count; +}; + +dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size); +void mic_unmap_single(struct mic_device *mdev, + dma_addr_t mic_addr, size_t size); +dma_addr_t mic_map(struct mic_device *mdev, + dma_addr_t dma_addr, size_t size); +void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size); + +/** + * mic_map_error - Check a MIC address for errors. + * + * @mdev: pointer to mic_device instance. + * + * returns Whether there was an error during mic_map..(..) APIs. + */ +static inline bool mic_map_error(dma_addr_t mic_addr) +{ + return !mic_addr; +} + +int mic_smpt_init(struct mic_device *mdev); +void mic_smpt_uninit(struct mic_device *mdev); +void mic_smpt_restore(struct mic_device *mdev); + +#endif diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index da481b173af9..b63731691c73 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -24,6 +24,7 @@ #include "../common/mic_device.h" #include "mic_device.h" #include "mic_x100.h" +#include "mic_smpt.h" /** * mic_x100_write_spad - write to the scratchpad register @@ -67,9 +68,255 @@ mic_x100_read_spad(struct mic_device *mdev, unsigned int idx) return val; } +/** + * mic_x100_enable_interrupts - Enable interrupts. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_enable_interrupts(struct mic_device *mdev) +{ + u32 reg; + struct mic_mw *mw = &mdev->mmio; + u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; + u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; + + reg = mic_mmio_read(mw, sice0); + reg |= MIC_X100_SBOX_DBR_BITS(0xf) | MIC_X100_SBOX_DMA_BITS(0xff); + mic_mmio_write(mw, reg, sice0); + + /* + * Enable auto-clear when enabling interrupts. Applicable only for + * MSI-x. Legacy and MSI mode cannot have auto-clear enabled. + */ + if (mdev->irq_info.num_vectors > 1) { + reg = mic_mmio_read(mw, siac0); + reg |= MIC_X100_SBOX_DBR_BITS(0xf) | + MIC_X100_SBOX_DMA_BITS(0xff); + mic_mmio_write(mw, reg, siac0); + } +} + +/** + * mic_x100_disable_interrupts - Disable interrupts. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_disable_interrupts(struct mic_device *mdev) +{ + u32 reg; + struct mic_mw *mw = &mdev->mmio; + u32 sice0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICE0; + u32 siac0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SIAC0; + u32 sicc0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICC0; + + reg = mic_mmio_read(mw, sice0); + mic_mmio_write(mw, reg, sicc0); + + if (mdev->irq_info.num_vectors > 1) { + reg = mic_mmio_read(mw, siac0); + reg &= ~(MIC_X100_SBOX_DBR_BITS(0xf) | + MIC_X100_SBOX_DMA_BITS(0xff)); + mic_mmio_write(mw, reg, siac0); + } +} + +/** + * mic_x100_send_sbox_intr - Send an MIC_X100_SBOX interrupt to MIC. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_send_sbox_intr(struct mic_device *mdev, + int doorbell) +{ + struct mic_mw *mw = &mdev->mmio; + u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8; + u32 apicicr_low = mic_mmio_read(mw, + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); + + /* for MIC we need to make sure we "hit" the send_icr bit (13) */ + apicicr_low = (apicicr_low | (1 << 13)); + + /* Ensure that the interrupt is ordered w.r.t. previous stores. */ + wmb(); + mic_mmio_write(mw, apicicr_low, + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); +} + +/** + * mic_x100_send_rdmasr_intr - Send an RDMASR interrupt to MIC. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_send_rdmasr_intr(struct mic_device *mdev, + int doorbell) +{ + int rdmasr_offset = MIC_X100_SBOX_RDMASR0 + (doorbell << 2); + /* Ensure that the interrupt is ordered w.r.t. previous stores. */ + wmb(); + mic_mmio_write(&mdev->mmio, 0, + MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); +} + +/** + * __mic_x100_send_intr - Send interrupt to MIC. + * @mdev: pointer to mic_device instance + * @doorbell: doorbell number. + */ +static void mic_x100_send_intr(struct mic_device *mdev, int doorbell) +{ + int rdmasr_db; + if (doorbell < MIC_X100_NUM_SBOX_IRQ) { + mic_x100_send_sbox_intr(mdev, doorbell); + } else { + rdmasr_db = doorbell - MIC_X100_NUM_SBOX_IRQ + + MIC_X100_RDMASR_IRQ_BASE; + mic_x100_send_rdmasr_intr(mdev, rdmasr_db); + } +} + +/** + * mic_ack_interrupt - Device specific interrupt handling. + * @mdev: pointer to mic_device instance + * + * Returns: bitmask of doorbell events triggered. + */ +static u32 mic_x100_ack_interrupt(struct mic_device *mdev) +{ + u32 reg = 0; + struct mic_mw *mw = &mdev->mmio; + u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0; + + /* Clear pending bit array. */ + if (MIC_A0_STEP == mdev->stepping) + mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_MSIXPBACR); + + if (mdev->irq_info.num_vectors <= 1) { + reg = mic_mmio_read(mw, sicr0); + + if (unlikely(!reg)) + goto done; + + mic_mmio_write(mw, reg, sicr0); + } + + if (mdev->stepping >= MIC_B0_STEP) + mdev->intr_ops->enable_interrupts(mdev); +done: + return reg; +} + +/** + * mic_x100_hw_intr_init - Initialize h/w specific interrupt + * information. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_hw_intr_init(struct mic_device *mdev) +{ + mdev->intr_info = (struct mic_intr_info *) mic_x100_intr_init; +} + +/** + * mic_x100_read_msi_to_src_map - read from the MSI mapping registers + * @mdev: pointer to mic_device instance + * @idx: index to the mapping register, 0 based + * + * This function allows reading of the 32bit MSI mapping register. + * + * RETURNS: The value in the register. + */ +static u32 +mic_x100_read_msi_to_src_map(struct mic_device *mdev, int idx) +{ + return mic_mmio_read(&mdev->mmio, + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_MXAR0 + idx * 4); +} + +/** + * mic_x100_program_msi_to_src_map - program the MSI mapping registers + * @mdev: pointer to mic_device instance + * @idx: index to the mapping register, 0 based + * @offset: The bit offset in the register that needs to be updated. + * @set: boolean specifying if the bit in the specified offset needs + * to be set or cleared. + * + * RETURNS: None. + */ +static void +mic_x100_program_msi_to_src_map(struct mic_device *mdev, + int idx, int offset, bool set) +{ + unsigned long reg; + struct mic_mw *mw = &mdev->mmio; + u32 mxar = MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_MXAR0 + idx * 4; + + reg = mic_mmio_read(mw, mxar); + if (set) + __set_bit(offset, ®); + else + __clear_bit(offset, ®); + mic_mmio_write(mw, reg, mxar); +} + +/** + * mic_x100_smpt_set - Update an SMPT entry with a DMA address. + * @mdev: pointer to mic_device instance + * + * RETURNS: none. + */ +static void +mic_x100_smpt_set(struct mic_device *mdev, dma_addr_t dma_addr, u8 index) +{ +#define SNOOP_ON (0 << 0) +#define SNOOP_OFF (1 << 0) +/* + * Sbox Smpt Reg Bits: + * Bits 31:2 Host address + * Bits 1 RSVD + * Bits 0 No snoop + */ +#define BUILD_SMPT(NO_SNOOP, HOST_ADDR) \ + (u32)(((HOST_ADDR) << 2) | ((NO_SNOOP) & 0x01)) + + uint32_t smpt_reg_val = BUILD_SMPT(SNOOP_ON, + dma_addr >> mdev->smpt->info.page_shift); + mic_mmio_write(&mdev->mmio, smpt_reg_val, + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_SMPT00 + (4 * index)); +} + +/** + * mic_x100_smpt_hw_init - Initialize SMPT X100 specific fields. + * @mdev: pointer to mic_device instance + * + * RETURNS: none. + */ +static void mic_x100_smpt_hw_init(struct mic_device *mdev) +{ + struct mic_smpt_hw_info *info = &mdev->smpt->info; + + info->num_reg = 32; + info->page_shift = 34; + info->page_size = (1ULL << info->page_shift); + info->base = 0x8000000000ULL; +} + +struct mic_smpt_ops mic_x100_smpt_ops = { + .init = mic_x100_smpt_hw_init, + .set = mic_x100_smpt_set, +}; + struct mic_hw_ops mic_x100_ops = { .aper_bar = MIC_X100_APER_BAR, .mmio_bar = MIC_X100_MMIO_BAR, .read_spad = mic_x100_read_spad, .write_spad = mic_x100_write_spad, + .send_intr = mic_x100_send_intr, + .ack_interrupt = mic_x100_ack_interrupt, +}; + +struct mic_hw_intr_ops mic_x100_intr_ops = { + .intr_init = mic_x100_hw_intr_init, + .enable_interrupts = mic_x100_enable_interrupts, + .disable_interrupts = mic_x100_disable_interrupts, + .program_msi_to_src_map = mic_x100_program_msi_to_src_map, + .read_msi_to_src_map = mic_x100_read_msi_to_src_map, }; diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h index 1f4e6305dcf5..642cae9a0041 100644 --- a/drivers/misc/mic/host/mic_x100.h +++ b/drivers/misc/mic/host/mic_x100.h @@ -42,6 +42,45 @@ #define MIC_X100_SBOX_BASE_ADDRESS 0x00010000 #define MIC_X100_SBOX_SPAD0 0x0000AB20 +#define MIC_X100_SBOX_SICR0_DBR(x) ((x) & 0xf) +#define MIC_X100_SBOX_SICR0_DMA(x) (((x) >> 8) & 0xff) +#define MIC_X100_SBOX_SICE0_DBR(x) ((x) & 0xf) +#define MIC_X100_SBOX_DBR_BITS(x) ((x) & 0xf) +#define MIC_X100_SBOX_SICE0_DMA(x) (((x) >> 8) & 0xff) +#define MIC_X100_SBOX_DMA_BITS(x) (((x) & 0xff) << 8) + +#define MIC_X100_SBOX_APICICR0 0x0000A9D0 +#define MIC_X100_SBOX_SICR0 0x00009004 +#define MIC_X100_SBOX_SICE0 0x0000900C +#define MIC_X100_SBOX_SICC0 0x00009010 +#define MIC_X100_SBOX_SIAC0 0x00009014 +#define MIC_X100_SBOX_MSIXPBACR 0x00009084 +#define MIC_X100_SBOX_MXAR0 0x00009044 +#define MIC_X100_SBOX_SMPT00 0x00003100 +#define MIC_X100_SBOX_RDMASR0 0x0000B180 + +#define MIC_X100_DOORBELL_IDX_START 0 +#define MIC_X100_NUM_DOORBELL 4 +#define MIC_X100_DMA_IDX_START 8 +#define MIC_X100_NUM_DMA 8 +#define MIC_X100_ERR_IDX_START 30 +#define MIC_X100_NUM_ERR 1 + +#define MIC_X100_NUM_SBOX_IRQ 8 +#define MIC_X100_NUM_RDMASR_IRQ 8 +#define MIC_X100_RDMASR_IRQ_BASE 17 + +static const u16 mic_x100_intr_init[] = { + MIC_X100_DOORBELL_IDX_START, + MIC_X100_DMA_IDX_START, + MIC_X100_ERR_IDX_START, + MIC_X100_NUM_DOORBELL, + MIC_X100_NUM_DMA, + MIC_X100_NUM_ERR, +}; + extern struct mic_hw_ops mic_x100_ops; +extern struct mic_smpt_ops mic_x100_smpt_ops; +extern struct mic_hw_intr_ops mic_x100_intr_ops; #endif -- cgit v1.2.3 From 3a6a9201897c6482573ad07ee880574147761006 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Thu, 5 Sep 2013 16:41:55 -0700 Subject: Intel MIC Host Driver, card OS state management. This patch enables the following features: a) Boots and shuts down the card via sysfs entries. b) Allocates and maps a device page for communication with the card driver and updates the device page address via scratchpad registers. c) Provides sysfs entries for shutdown status, kernel command line, ramdisk and log buffer information. Co-author: Dasaratharaman Chandramouli Signed-off-by: Ashutosh Dixit Signed-off-by: Caz Yokoyama Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Harshavardhan R Kharche Signed-off-by: Nikhil Rao Signed-off-by: Sudeep Dutt Acked-by: Yaozu (Eddie) Dong Reviewed-by: Peter P Waskiewicz Jr Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-mic.txt | 113 ++++++++ drivers/misc/mic/common/mic_device.h | 7 + drivers/misc/mic/host/Makefile | 2 + drivers/misc/mic/host/mic_boot.c | 184 +++++++++++++ drivers/misc/mic/host/mic_debugfs.c | 355 +++++++++++++++++++++++++ drivers/misc/mic/host/mic_device.h | 60 +++++ drivers/misc/mic/host/mic_main.c | 129 ++++++++- drivers/misc/mic/host/mic_sysfs.c | 369 ++++++++++++++++++++++++++ drivers/misc/mic/host/mic_x100.c | 251 ++++++++++++++++++ drivers/misc/mic/host/mic_x100.h | 12 + include/uapi/linux/Kbuild | 1 + include/uapi/linux/mic_common.h | 74 ++++++ 12 files changed, 1553 insertions(+), 4 deletions(-) create mode 100644 drivers/misc/mic/host/mic_boot.c create mode 100644 drivers/misc/mic/host/mic_debugfs.c create mode 100644 include/uapi/linux/mic_common.h (limited to 'drivers/misc/mic/host') diff --git a/Documentation/ABI/testing/sysfs-class-mic.txt b/Documentation/ABI/testing/sysfs-class-mic.txt index 09eb3c6d0237..82cdad3b614a 100644 --- a/Documentation/ABI/testing/sysfs-class-mic.txt +++ b/Documentation/ABI/testing/sysfs-class-mic.txt @@ -32,3 +32,116 @@ Contact: Sudeep Dutt Description: Provides information about the silicon stepping for an Intel MIC device. For example - "A0" or "B0" + +What: /sys/class/mic/mic(x)/state +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + When read, this entry provides the current state of an Intel + MIC device in the context of the card OS. Possible values that + will be read are: + "offline" - The MIC device is ready to boot the card OS. + "online" - The MIC device has initiated booting a card OS. + "shutting_down" - The card OS is shutting down. + "reset_failed" - The MIC device has failed to reset. + + When written, this sysfs entry triggers different state change + operations depending upon the current state of the card OS. + Acceptable values are: + "boot" - Boot the card OS image specified by the combination + of firmware, ramdisk, cmdline and bootmode + sysfs entries. + "reset" - Initiates device reset. + "shutdown" - Initiates card OS shutdown. + +What: /sys/class/mic/mic(x)/shutdown_status +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + An Intel MIC device runs a Linux OS during its operation. This + OS can shutdown because of various reasons. When read, this + entry provides the status on why the card OS was shutdown. + Possible values are: + "nop" - shutdown status is not applicable, when the card OS is + "online" + "crashed" - Shutdown because of a HW or SW crash. + "halted" - Shutdown because of a halt command. + "poweroff" - Shutdown because of a poweroff command. + "restart" - Shutdown because of a restart command. + +What: /sys/class/mic/mic(x)/cmdline +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + An Intel MIC device runs a Linux OS during its operation. Before + booting this card OS, it is possible to pass kernel command line + options to configure various features in it, similar to + self-bootable machines. When read, this entry provides + information about the current kernel command line options set to + boot the card OS. This entry can be written to change the + existing kernel command line options. Typically, the user would + want to read the current command line options, append new ones + or modify existing ones and then write the whole kernel command + line back to this entry. + +What: /sys/class/mic/mic(x)/firmware +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + When read, this sysfs entry provides the path name under + /lib/firmware/ where the firmware image to be booted on the + card can be found. The entry can be written to change the + firmware image location under /lib/firmware/. + +What: /sys/class/mic/mic(x)/ramdisk +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + When read, this sysfs entry provides the path name under + /lib/firmware/ where the ramdisk image to be used during card + OS boot can be found. The entry can be written to change + the ramdisk image location under /lib/firmware/. + +What: /sys/class/mic/mic(x)/bootmode +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + When read, this sysfs entry provides the current bootmode for + the card. This sysfs entry can be written with the following + valid strings: + a) linux - Boot a Linux image. + b) elf - Boot an elf image for flash updates. + +What: /sys/class/mic/mic(x)/log_buf_addr +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + An Intel MIC device runs a Linux OS during its operation. For + debugging purpose and early kernel boot messages, the user can + access the card OS log buffer via debugfs. When read, this entry + provides the kernel virtual address of the buffer where the card + OS log buffer can be read. This entry is written by the host + configuration daemon to set the log buffer address. The correct + log buffer address to be written can be found in the System.map + file of the card OS. + +What: /sys/class/mic/mic(x)/log_buf_len +Date: August 2013 +KernelVersion: 3.11 +Contact: Sudeep Dutt +Description: + An Intel MIC device runs a Linux OS during its operation. For + debugging purpose and early kernel boot messages, the user can + access the card OS log buffer via debugfs. When read, this entry + provides the kernel virtual address where the card OS log buffer + length can be read. This entry is written by host configuration + daemon to set the log buffer length address. The correct log + buffer length address to be written can be found in the + System.map file of the card OS. diff --git a/drivers/misc/mic/common/mic_device.h b/drivers/misc/mic/common/mic_device.h index f02262e1c9d3..6440e9d58d3a 100644 --- a/drivers/misc/mic/common/mic_device.h +++ b/drivers/misc/mic/common/mic_device.h @@ -34,4 +34,11 @@ struct mic_mw { resource_size_t len; }; +/* + * Scratch pad register offsets used by the host to communicate + * device page DMA address to the card. + */ +#define MIC_DPLO_SPAD 14 +#define MIC_DPHI_SPAD 15 + #endif diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile index 6ff5550f10ac..a375dd32c3e2 100644 --- a/drivers/misc/mic/host/Makefile +++ b/drivers/misc/mic/host/Makefile @@ -8,3 +8,5 @@ mic_host-objs += mic_x100.o mic_host-objs += mic_sysfs.o mic_host-objs += mic_smpt.o mic_host-objs += mic_intr.o +mic_host-objs += mic_boot.o +mic_host-objs += mic_debugfs.o diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c new file mode 100644 index 000000000000..936fc58084f3 --- /dev/null +++ b/drivers/misc/mic/host/mic_boot.c @@ -0,0 +1,184 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#include +#include +#include + +#include +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_smpt.h" + +/** + * mic_reset - Reset the MIC device. + * @mdev: pointer to mic_device instance + */ +static void mic_reset(struct mic_device *mdev) +{ + int i; + +#define MIC_RESET_TO (45) + + mdev->ops->reset_fw_ready(mdev); + mdev->ops->reset(mdev); + + for (i = 0; i < MIC_RESET_TO; i++) { + if (mdev->ops->is_fw_ready(mdev)) + return; + /* + * Resets typically take 10s of seconds to complete. + * Since an MMIO read is required to check if the + * firmware is ready or not, a 1 second delay works nicely. + */ + msleep(1000); + } + mic_set_state(mdev, MIC_RESET_FAILED); +} + +/* Initialize the MIC bootparams */ +void mic_bootparam_init(struct mic_device *mdev) +{ + struct mic_bootparam *bootparam = mdev->dp; + + bootparam->magic = MIC_MAGIC; + bootparam->c2h_shutdown_db = mdev->shutdown_db; + bootparam->h2c_shutdown_db = -1; + bootparam->h2c_config_db = -1; + bootparam->shutdown_status = 0; + bootparam->shutdown_card = 0; +} + +/** + * mic_start - Start the MIC. + * @mdev: pointer to mic_device instance + * @buf: buffer containing boot string including firmware/ramdisk path. + * + * This function prepares an MIC for boot and initiates boot. + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +int mic_start(struct mic_device *mdev, const char *buf) +{ + int rc; + mutex_lock(&mdev->mic_mutex); +retry: + if (MIC_OFFLINE != mdev->state) { + rc = -EINVAL; + goto unlock_ret; + } + if (!mdev->ops->is_fw_ready(mdev)) { + mic_reset(mdev); + /* + * The state will either be MIC_OFFLINE if the reset succeeded + * or MIC_RESET_FAILED if the firmware reset failed. + */ + goto retry; + } + rc = mdev->ops->load_mic_fw(mdev, buf); + if (rc) + goto unlock_ret; + mic_smpt_restore(mdev); + mic_intr_restore(mdev); + mdev->intr_ops->enable_interrupts(mdev); + mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr); + 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); +unlock_ret: + mutex_unlock(&mdev->mic_mutex); + return rc; +} + +/** + * mic_stop - Prepare the MIC for reset and trigger reset. + * @mdev: pointer to mic_device instance + * @force: force a MIC to reset even if it is already offline. + * + * RETURNS: None. + */ +void mic_stop(struct mic_device *mdev, bool force) +{ + mutex_lock(&mdev->mic_mutex); + if (MIC_OFFLINE != mdev->state || force) { + mic_bootparam_init(mdev); + mic_reset(mdev); + if (MIC_RESET_FAILED == mdev->state) + goto unlock; + mic_set_shutdown_status(mdev, MIC_NOP); + mic_set_state(mdev, MIC_OFFLINE); + } +unlock: + mutex_unlock(&mdev->mic_mutex); +} + +/** + * mic_shutdown - Initiate MIC shutdown. + * @mdev: pointer to mic_device instance + * + * RETURNS: None. + */ +void mic_shutdown(struct mic_device *mdev) +{ + struct mic_bootparam *bootparam = mdev->dp; + s8 db = bootparam->h2c_shutdown_db; + + mutex_lock(&mdev->mic_mutex); + if (MIC_ONLINE == mdev->state && db != -1) { + bootparam->shutdown_card = 1; + mdev->ops->send_intr(mdev, db); + mic_set_state(mdev, MIC_SHUTTING_DOWN); + } + mutex_unlock(&mdev->mic_mutex); +} + +/** + * mic_shutdown_work - Handle shutdown interrupt from MIC. + * @work: The work structure. + * + * This work is scheduled whenever the host has received a shutdown + * interrupt from the MIC. + */ +void mic_shutdown_work(struct work_struct *work) +{ + struct mic_device *mdev = container_of(work, struct mic_device, + shutdown_work); + struct mic_bootparam *bootparam = mdev->dp; + + mutex_lock(&mdev->mic_mutex); + mic_set_shutdown_status(mdev, bootparam->shutdown_status); + bootparam->shutdown_status = 0; + if (MIC_SHUTTING_DOWN != mdev->state) + mic_set_state(mdev, MIC_SHUTTING_DOWN); + mutex_unlock(&mdev->mic_mutex); +} + +/** + * mic_reset_trigger_work - Trigger MIC reset. + * @work: The work structure. + * + * This work is scheduled whenever the host wants to reset the MIC. + */ +void mic_reset_trigger_work(struct work_struct *work) +{ + struct mic_device *mdev = container_of(work, struct mic_device, + reset_trigger_work); + + mic_stop(mdev, false); +} diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c new file mode 100644 index 000000000000..78541d42aaf9 --- /dev/null +++ b/drivers/misc/mic/host/mic_debugfs.c @@ -0,0 +1,355 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#include +#include +#include + +#include +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_smpt.h" + +/* Debugfs parent dir */ +static struct dentry *mic_dbg; + +/** + * mic_log_buf_show - Display MIC kernel log buffer. + * + * log_buf addr/len is read from System.map by user space + * and populated in sysfs entries. + */ +static int mic_log_buf_show(struct seq_file *s, void *unused) +{ + void __iomem *log_buf_va; + int __iomem *log_buf_len_va; + struct mic_device *mdev = s->private; + void *kva; + int size; + unsigned long aper_offset; + + if (!mdev || !mdev->log_buf_addr || !mdev->log_buf_len) + goto done; + /* + * Card kernel will never be relocated and any kernel text/data mapping + * can be translated to phys address by subtracting __START_KERNEL_map. + */ + aper_offset = (unsigned long)mdev->log_buf_len - __START_KERNEL_map; + log_buf_len_va = mdev->aper.va + aper_offset; + aper_offset = (unsigned long)mdev->log_buf_addr - __START_KERNEL_map; + log_buf_va = mdev->aper.va + aper_offset; + size = ioread32(log_buf_len_va); + + kva = kmalloc(size, GFP_KERNEL); + if (!kva) + goto done; + mutex_lock(&mdev->mic_mutex); + memcpy_fromio(kva, log_buf_va, size); + switch (mdev->state) { + case MIC_ONLINE: + /* Fall through */ + case MIC_SHUTTING_DOWN: + seq_write(s, kva, size); + break; + default: + break; + } + mutex_unlock(&mdev->mic_mutex); + kfree(kva); +done: + return 0; +} + +static int mic_log_buf_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_log_buf_show, inode->i_private); +} + +static int mic_log_buf_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations log_buf_ops = { + .owner = THIS_MODULE, + .open = mic_log_buf_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_log_buf_release +}; + +static int mic_smpt_show(struct seq_file *s, void *pos) +{ + int i; + struct mic_device *mdev = s->private; + unsigned long flags; + + seq_printf(s, "MIC %-2d |%-10s| %-14s %-10s\n", + mdev->id, "SMPT entry", "SW DMA addr", "RefCount"); + seq_puts(s, "====================================================\n"); + + if (mdev->smpt) { + struct mic_smpt_info *smpt_info = mdev->smpt; + spin_lock_irqsave(&smpt_info->smpt_lock, flags); + for (i = 0; i < smpt_info->info.num_reg; i++) { + seq_printf(s, "%9s|%-10d| %-#14llx %-10lld\n", + " ", i, smpt_info->entry[i].dma_addr, + smpt_info->entry[i].ref_count); + } + spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); + } + seq_puts(s, "====================================================\n"); + return 0; +} + +static int mic_smpt_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_smpt_show, inode->i_private); +} + +static int mic_smpt_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations smpt_file_ops = { + .owner = THIS_MODULE, + .open = mic_smpt_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_smpt_debug_release +}; + +static int mic_soft_reset_show(struct seq_file *s, void *pos) +{ + struct mic_device *mdev = s->private; + + mic_stop(mdev, true); + return 0; +} + +static int mic_soft_reset_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_soft_reset_show, inode->i_private); +} + +static int mic_soft_reset_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations soft_reset_ops = { + .owner = THIS_MODULE, + .open = mic_soft_reset_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_soft_reset_debug_release +}; + +static int mic_post_code_show(struct seq_file *s, void *pos) +{ + struct mic_device *mdev = s->private; + u32 reg = mdev->ops->get_postcode(mdev); + + seq_printf(s, "%c%c", reg & 0xff, (reg >> 8) & 0xff); + return 0; +} + +static int mic_post_code_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_post_code_show, inode->i_private); +} + +static int mic_post_code_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations post_code_ops = { + .owner = THIS_MODULE, + .open = mic_post_code_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_post_code_debug_release +}; + +static int mic_dp_show(struct seq_file *s, void *pos) +{ + struct mic_device *mdev = s->private; + struct mic_bootparam *bootparam = mdev->dp; + + seq_printf(s, "Bootparam: magic 0x%x\n", + bootparam->magic); + seq_printf(s, "Bootparam: h2c_shutdown_db %d\n", + bootparam->h2c_shutdown_db); + seq_printf(s, "Bootparam: h2c_config_db %d\n", + bootparam->h2c_config_db); + seq_printf(s, "Bootparam: c2h_shutdown_db %d\n", + bootparam->c2h_shutdown_db); + seq_printf(s, "Bootparam: shutdown_status %d\n", + bootparam->shutdown_status); + seq_printf(s, "Bootparam: shutdown_card %d\n", + bootparam->shutdown_card); + + return 0; +} + +static int mic_dp_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_dp_show, inode->i_private); +} + +static int mic_dp_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations dp_ops = { + .owner = THIS_MODULE, + .open = mic_dp_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_dp_debug_release +}; + +static int mic_msi_irq_info_show(struct seq_file *s, void *pos) +{ + struct mic_device *mdev = s->private; + int reg; + int i, j; + u16 entry; + u16 vector; + struct pci_dev *pdev = container_of(mdev->sdev->parent, + struct pci_dev, dev); + + if (pci_dev_msi_enabled(pdev)) { + for (i = 0; i < mdev->irq_info.num_vectors; i++) { + if (pdev->msix_enabled) { + entry = mdev->irq_info.msix_entries[i].entry; + vector = mdev->irq_info.msix_entries[i].vector; + } else { + entry = 0; + vector = pdev->irq; + } + + reg = mdev->intr_ops->read_msi_to_src_map(mdev, entry); + + seq_printf(s, "%s %-10d %s %-10d MXAR[%d]: %08X\n", + "IRQ:", vector, "Entry:", entry, i, reg); + + seq_printf(s, "%-10s", "offset:"); + for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) + seq_printf(s, "%4d ", j); + seq_puts(s, "\n"); + + + seq_printf(s, "%-10s", "count:"); + for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) + seq_printf(s, "%4d ", + (mdev->irq_info.mic_msi_map[i] & BIT(j)) ? + 1 : 0); + seq_puts(s, "\n\n"); + } + } else { + seq_puts(s, "MSI/MSIx interrupts not enabled\n"); + } + + return 0; + +} + +static int mic_msi_irq_info_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_msi_irq_info_show, inode->i_private); +} + +static int +mic_msi_irq_info_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations msi_irq_info_ops = { + .owner = THIS_MODULE, + .open = mic_msi_irq_info_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_msi_irq_info_debug_release +}; + +/** + * mic_create_debug_dir - Initialize MIC debugfs entries. + */ +void mic_create_debug_dir(struct mic_device *mdev) +{ + if (!mic_dbg) + return; + + mdev->dbg_dir = debugfs_create_dir(dev_name(mdev->sdev), mic_dbg); + if (!mdev->dbg_dir) + return; + + debugfs_create_file("log_buf", 0444, mdev->dbg_dir, + mdev, &log_buf_ops); + + debugfs_create_file("smpt", 0444, mdev->dbg_dir, + mdev, &smpt_file_ops); + + debugfs_create_file("soft_reset", 0444, mdev->dbg_dir, + mdev, &soft_reset_ops); + + debugfs_create_file("post_code", 0444, mdev->dbg_dir, + mdev, &post_code_ops); + + debugfs_create_file("dp", 0444, mdev->dbg_dir, + mdev, &dp_ops); + + debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, + mdev, &msi_irq_info_ops); +} + +/** + * mic_delete_debug_dir - Uninitialize MIC debugfs entries. + */ +void mic_delete_debug_dir(struct mic_device *mdev) +{ + if (!mdev->dbg_dir) + return; + + debugfs_remove_recursive(mdev->dbg_dir); +} + +/** + * mic_init_debugfs - Initialize global debugfs entry. + */ +void __init mic_init_debugfs(void) +{ + mic_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL); + if (!mic_dbg) + pr_err("can't create debugfs dir\n"); +} + +/** + * mic_exit_debugfs - Uninitialize global debugfs entry + */ +void mic_exit_debugfs(void) +{ + debugfs_remove(mic_dbg); +} diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index dd34b6532a01..50b8b88d70d3 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -63,6 +63,23 @@ enum mic_stepping { * @smpt: MIC SMPT information. * @intr_info: H/W specific interrupt information. * @irq_info: The OS specific irq information + * @dbg_dir: debugfs directory of this MIC device. + * @cmdline: Kernel command line. + * @firmware: Firmware file name. + * @ramdisk: Ramdisk file name. + * @bootmode: Boot mode i.e. "linux" or "elf" for flash updates. + * @bootaddr: MIC boot address. + * @reset_trigger_work: Work for triggering reset requests. + * @shutdown_work: Work for handling shutdown interrupts. + * @state: MIC state. + * @shutdown_status: MIC status reported by card for shutdown/crashes. + * @state_sysfs: Sysfs dirent for notifying ring 3 about MIC state changes. + * @log_buf_addr: Log buffer address for MIC. + * @log_buf_len: Log buffer length address for MIC. + * @dp: virtio device page + * @dp_dma_addr: virtio device page DMA address. + * @shutdown_db: shutdown doorbell. + * @shutdown_cookie: shutdown cookie. */ struct mic_device { struct mic_mw mmio; @@ -79,6 +96,23 @@ struct mic_device { struct mic_smpt_info *smpt; struct mic_intr_info *intr_info; struct mic_irq_info irq_info; + struct dentry *dbg_dir; + char *cmdline; + char *firmware; + char *ramdisk; + char *bootmode; + u32 bootaddr; + struct work_struct reset_trigger_work; + struct work_struct shutdown_work; + u8 state; + u8 shutdown_status; + struct sysfs_dirent *state_sysfs; + void *log_buf_addr; + int *log_buf_len; + void *dp; + dma_addr_t dp_dma_addr; + int shutdown_db; + struct mic_irq *shutdown_cookie; }; /** @@ -90,6 +124,13 @@ struct mic_device { * @send_intr: Send an interrupt for a particular doorbell on the card. * @ack_interrupt: Hardware specific operations to ack the h/w on * receipt of an interrupt. + * @reset: Reset the remote processor. + * @reset_fw_ready: Reset firmware ready field. + * @is_fw_ready: Check if firmware is ready for OS download. + * @send_firmware_intr: Send an interrupt to the card firmware. + * @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. */ struct mic_hw_ops { u8 aper_bar; @@ -98,6 +139,12 @@ struct mic_hw_ops { void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val); void (*send_intr)(struct mic_device *mdev, int doorbell); u32 (*ack_interrupt)(struct mic_device *mdev); + void (*reset)(struct mic_device *mdev); + void (*reset_fw_ready)(struct mic_device *mdev); + bool (*is_fw_ready)(struct mic_device *mdev); + 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); }; /** @@ -127,4 +174,17 @@ mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset) } 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); +void mic_shutdown(struct mic_device *mdev); +void mic_reset_delayed_work(struct work_struct *work); +void mic_reset_trigger_work(struct work_struct *work); +void mic_shutdown_work(struct work_struct *work); +void mic_bootparam_init(struct mic_device *mdev); +void mic_set_state(struct mic_device *mdev, u8 state); +void mic_set_shutdown_status(struct mic_device *mdev, u8 status); +void mic_create_debug_dir(struct mic_device *dev); +void mic_delete_debug_dir(struct mic_device *dev); +void __init mic_init_debugfs(void); +void mic_exit_debugfs(void); #endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index 332a15e4215b..998a20ab7e96 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -26,6 +26,7 @@ #include #include +#include #include "../common/mic_device.h" #include "mic_device.h" #include "mic_x100.h" @@ -63,6 +64,60 @@ static struct class *g_mic_class; /* Base device node number for MIC devices */ static dev_t g_mic_devno; +/* Initialize the device page */ +static int mic_dp_init(struct mic_device *mdev) +{ + mdev->dp = kzalloc(MIC_DP_SIZE, GFP_KERNEL); + if (!mdev->dp) { + dev_err(mdev->sdev->parent, "%s %d err %d\n", + __func__, __LINE__, -ENOMEM); + return -ENOMEM; + } + + mdev->dp_dma_addr = mic_map_single(mdev, + mdev->dp, MIC_DP_SIZE); + if (mic_map_error(mdev->dp_dma_addr)) { + kfree(mdev->dp); + dev_err(mdev->sdev->parent, "%s %d err %d\n", + __func__, __LINE__, -ENOMEM); + return -ENOMEM; + } + mdev->ops->write_spad(mdev, MIC_DPLO_SPAD, mdev->dp_dma_addr); + mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32); + return 0; +} + +/* Uninitialize the device page */ +static void mic_dp_uninit(struct mic_device *mdev) +{ + mic_unmap_single(mdev, mdev->dp_dma_addr, MIC_DP_SIZE); + kfree(mdev->dp); +} + +/** + * mic_shutdown_db - Shutdown doorbell interrupt handler. + */ +static irqreturn_t mic_shutdown_db(int irq, void *data) +{ + struct mic_device *mdev = data; + struct mic_bootparam *bootparam = mdev->dp; + + mdev->ops->ack_interrupt(mdev); + + switch (bootparam->shutdown_status) { + case MIC_HALTED: + case MIC_POWER_OFF: + case MIC_RESTART: + /* Fall through */ + case MIC_CRASHED: + schedule_work(&mdev->shutdown_work); + break; + default: + break; + }; + return IRQ_HANDLED; +} + /** * mic_ops_init: Initialize HW specific operation tables. * @@ -136,6 +191,26 @@ mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) mic_sysfs_init(mdev); mutex_init(&mdev->mic_mutex); mdev->irq_info.next_avail_src = 0; + INIT_WORK(&mdev->reset_trigger_work, mic_reset_trigger_work); + INIT_WORK(&mdev->shutdown_work, mic_shutdown_work); +} + +/** + * mic_device_uninit - Frees resources allocated during mic_device_init(..) + * + * @mdev: pointer to mic_device instance + * + * returns none + */ +static void mic_device_uninit(struct mic_device *mdev) +{ + /* The cmdline sysfs entry might have allocated cmdline */ + kfree(mdev->cmdline); + kfree(mdev->firmware); + kfree(mdev->ramdisk); + kfree(mdev->bootmode); + flush_work(&mdev->reset_trigger_work); + flush_work(&mdev->shutdown_work); } /** @@ -170,7 +245,7 @@ static int mic_probe(struct pci_dev *pdev, rc = pci_enable_device(pdev); if (rc) { dev_err(&pdev->dev, "failed to enable pci device.\n"); - goto ida_remove; + goto uninit_device; } pci_set_master(pdev); @@ -228,7 +303,40 @@ static int mic_probe(struct pci_dev *pdev, "device_create_with_groups failed rc %d\n", rc); goto smpt_uninit; } + mdev->state_sysfs = sysfs_get_dirent(mdev->sdev->kobj.sd, + NULL, "state"); + if (!mdev->state_sysfs) { + rc = -ENODEV; + dev_err(&pdev->dev, "sysfs_get_dirent failed rc %d\n", rc); + goto destroy_device; + } + + rc = mic_dp_init(mdev); + if (rc) { + dev_err(&pdev->dev, "mic_dp_init failed rc %d\n", rc); + goto sysfs_put; + } + 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); + if (IS_ERR(mdev->shutdown_cookie)) { + rc = PTR_ERR(mdev->shutdown_cookie); + mutex_unlock(&mdev->mic_mutex); + goto dp_uninit; + } + mutex_unlock(&mdev->mic_mutex); + mic_bootparam_init(mdev); + + mic_create_debug_dir(mdev); return 0; +dp_uninit: + mic_dp_uninit(mdev); +sysfs_put: + sysfs_put(mdev->state_sysfs); +destroy_device: + device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id)); smpt_uninit: mic_smpt_uninit(mdev); free_interrupts: @@ -241,7 +349,8 @@ release_regions: pci_release_regions(pdev); disable_device: pci_disable_device(pdev); -ida_remove: +uninit_device: + mic_device_uninit(mdev); ida_simple_remove(&g_mic_ida, mdev->id); ida_fail: kfree(mdev); @@ -265,11 +374,20 @@ static void mic_remove(struct pci_dev *pdev) if (!mdev) return; + mic_stop(mdev, false); + mic_delete_debug_dir(mdev); + mutex_lock(&mdev->mic_mutex); + mic_free_irq(mdev, mdev->shutdown_cookie, mdev); + mutex_unlock(&mdev->mic_mutex); + flush_work(&mdev->shutdown_work); + mic_dp_uninit(mdev); + sysfs_put(mdev->state_sysfs); device_destroy(g_mic_class, MKDEV(MAJOR(g_mic_devno), mdev->id)); mic_smpt_uninit(mdev); mic_free_interrupts(mdev, pdev); iounmap(mdev->mmio.va); iounmap(mdev->aper.va); + mic_device_uninit(mdev); pci_release_regions(pdev); pci_disable_device(pdev); ida_simple_remove(&g_mic_ida, mdev->id); @@ -300,14 +418,16 @@ static int __init mic_init(void) goto cleanup_chrdev; } + mic_init_debugfs(); ida_init(&g_mic_ida); ret = pci_register_driver(&mic_driver); if (ret) { pr_err("pci_register_driver failed ret %d\n", ret); - goto class_destroy; + goto cleanup_debugfs; } return ret; -class_destroy: +cleanup_debugfs: + mic_exit_debugfs(); class_destroy(g_mic_class); cleanup_chrdev: unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); @@ -319,6 +439,7 @@ static void __exit mic_exit(void) { pci_unregister_driver(&mic_driver); ida_destroy(&g_mic_ida); + mic_exit_debugfs(); class_destroy(g_mic_class); unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS); } diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c index 972c18255263..aaf849945111 100644 --- a/drivers/misc/mic/host/mic_sysfs.c +++ b/drivers/misc/mic/host/mic_sysfs.c @@ -20,9 +20,50 @@ */ #include +#include #include "../common/mic_device.h" #include "mic_device.h" +/* + * A state-to-string lookup table, for exposing a human readable state + * via sysfs. Always keep in sync with enum mic_states + */ +static const char * const mic_state_string[] = { + [MIC_OFFLINE] = "offline", + [MIC_ONLINE] = "online", + [MIC_SHUTTING_DOWN] = "shutting_down", + [MIC_RESET_FAILED] = "reset_failed", +}; + +/* + * A shutdown-status-to-string lookup table, for exposing a human + * readable state via sysfs. Always keep in sync with enum mic_shutdown_status + */ +static const char * const mic_shutdown_status_string[] = { + [MIC_NOP] = "nop", + [MIC_CRASHED] = "crashed", + [MIC_HALTED] = "halted", + [MIC_POWER_OFF] = "poweroff", + [MIC_RESTART] = "restart", +}; + +void mic_set_shutdown_status(struct mic_device *mdev, u8 shutdown_status) +{ + dev_dbg(mdev->sdev->parent, "Shutdown Status %s -> %s\n", + mic_shutdown_status_string[mdev->shutdown_status], + mic_shutdown_status_string[shutdown_status]); + mdev->shutdown_status = shutdown_status; +} + +void mic_set_state(struct mic_device *mdev, u8 state) +{ + dev_dbg(mdev->sdev->parent, "State %s -> %s\n", + mic_state_string[mdev->state], + mic_state_string[state]); + mdev->state = state; + sysfs_notify_dirent(mdev->state_sysfs); +} + static ssize_t mic_show_family(struct device *dev, struct device_attribute *attr, char *buf) { @@ -75,9 +116,337 @@ mic_show_stepping(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(stepping, S_IRUGO, mic_show_stepping, NULL); +static ssize_t +mic_show_state(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev || mdev->state >= MIC_LAST) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%s\n", + mic_state_string[mdev->state]); +} + +static ssize_t +mic_store_state(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = 0; + struct mic_device *mdev = dev_get_drvdata(dev->parent); + if (!mdev) + return -EINVAL; + if (sysfs_streq(buf, "boot")) { + rc = mic_start(mdev, buf); + if (rc) { + dev_err(mdev->sdev->parent, + "mic_boot failed rc %d\n", rc); + count = rc; + } + goto done; + } + + if (sysfs_streq(buf, "reset")) { + schedule_work(&mdev->reset_trigger_work); + goto done; + } + + if (sysfs_streq(buf, "shutdown")) { + mic_shutdown(mdev); + goto done; + } + + count = -EINVAL; +done: + return count; +} +static DEVICE_ATTR(state, S_IRUGO|S_IWUSR, mic_show_state, mic_store_state); + +static ssize_t mic_show_shutdown_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev || mdev->shutdown_status >= MIC_STATUS_LAST) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%s\n", + mic_shutdown_status_string[mdev->shutdown_status]); +} +static DEVICE_ATTR(shutdown_status, S_IRUGO|S_IWUSR, + mic_show_shutdown_status, NULL); + +static ssize_t +mic_show_cmdline(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + char *cmdline; + + if (!mdev) + return -EINVAL; + + cmdline = mdev->cmdline; + + if (cmdline) + return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline); + return 0; +} + +static ssize_t +mic_store_cmdline(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + mutex_lock(&mdev->mic_mutex); + kfree(mdev->cmdline); + + mdev->cmdline = kmalloc(count + 1, GFP_KERNEL); + if (!mdev->cmdline) { + count = -ENOMEM; + goto unlock; + } + + strncpy(mdev->cmdline, buf, count); + + if (mdev->cmdline[count - 1] == '\n') + mdev->cmdline[count - 1] = '\0'; + else + mdev->cmdline[count] = '\0'; +unlock: + mutex_unlock(&mdev->mic_mutex); + return count; +} +static DEVICE_ATTR(cmdline, S_IRUGO | S_IWUSR, + mic_show_cmdline, mic_store_cmdline); + +static ssize_t +mic_show_firmware(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + char *firmware; + + if (!mdev) + return -EINVAL; + + firmware = mdev->firmware; + + if (firmware) + return scnprintf(buf, PAGE_SIZE, "%s\n", firmware); + return 0; +} + +static ssize_t +mic_store_firmware(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + mutex_lock(&mdev->mic_mutex); + kfree(mdev->firmware); + + mdev->firmware = kmalloc(count + 1, GFP_KERNEL); + if (!mdev->firmware) { + count = -ENOMEM; + goto unlock; + } + strncpy(mdev->firmware, buf, count); + + if (mdev->firmware[count - 1] == '\n') + mdev->firmware[count - 1] = '\0'; + else + mdev->firmware[count] = '\0'; +unlock: + mutex_unlock(&mdev->mic_mutex); + return count; +} +static DEVICE_ATTR(firmware, S_IRUGO | S_IWUSR, + mic_show_firmware, mic_store_firmware); + +static ssize_t +mic_show_ramdisk(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + char *ramdisk; + + if (!mdev) + return -EINVAL; + + ramdisk = mdev->ramdisk; + + if (ramdisk) + return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk); + return 0; +} + +static ssize_t +mic_store_ramdisk(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + mutex_lock(&mdev->mic_mutex); + kfree(mdev->ramdisk); + + mdev->ramdisk = kmalloc(count + 1, GFP_KERNEL); + if (!mdev->ramdisk) { + count = -ENOMEM; + goto unlock; + } + + strncpy(mdev->ramdisk, buf, count); + + if (mdev->ramdisk[count - 1] == '\n') + mdev->ramdisk[count - 1] = '\0'; + else + mdev->ramdisk[count] = '\0'; +unlock: + mutex_unlock(&mdev->mic_mutex); + return count; +} +static DEVICE_ATTR(ramdisk, S_IRUGO | S_IWUSR, + mic_show_ramdisk, mic_store_ramdisk); + +static ssize_t +mic_show_bootmode(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + char *bootmode; + + if (!mdev) + return -EINVAL; + + bootmode = mdev->bootmode; + + if (bootmode) + return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode); + return 0; +} + +static ssize_t +mic_store_bootmode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "elf")) + return -EINVAL; + + mutex_lock(&mdev->mic_mutex); + kfree(mdev->bootmode); + + mdev->bootmode = kmalloc(count + 1, GFP_KERNEL); + if (!mdev->bootmode) { + count = -ENOMEM; + goto unlock; + } + + strncpy(mdev->bootmode, buf, count); + + if (mdev->bootmode[count - 1] == '\n') + mdev->bootmode[count - 1] = '\0'; + else + mdev->bootmode[count] = '\0'; +unlock: + mutex_unlock(&mdev->mic_mutex); + return count; +} +static DEVICE_ATTR(bootmode, S_IRUGO | S_IWUSR, + mic_show_bootmode, mic_store_bootmode); + +static ssize_t +mic_show_log_buf_addr(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_addr); +} + +static ssize_t +mic_store_log_buf_addr(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + int ret; + unsigned long addr; + + if (!mdev) + return -EINVAL; + + ret = kstrtoul(buf, 16, &addr); + if (ret) + goto exit; + + mdev->log_buf_addr = (void *)addr; + ret = count; +exit: + return ret; +} +static DEVICE_ATTR(log_buf_addr, S_IRUGO | S_IWUSR, + mic_show_log_buf_addr, mic_store_log_buf_addr); + +static ssize_t +mic_show_log_buf_len(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + + if (!mdev) + return -EINVAL; + + return scnprintf(buf, PAGE_SIZE, "%p\n", mdev->log_buf_len); +} + +static ssize_t +mic_store_log_buf_len(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mic_device *mdev = dev_get_drvdata(dev->parent); + int ret; + unsigned long addr; + + if (!mdev) + return -EINVAL; + + ret = kstrtoul(buf, 16, &addr); + if (ret) + goto exit; + + mdev->log_buf_len = (int *)addr; + ret = count; +exit: + return ret; +} +static DEVICE_ATTR(log_buf_len, S_IRUGO | S_IWUSR, + mic_show_log_buf_len, mic_store_log_buf_len); + static struct attribute *mic_default_attrs[] = { &dev_attr_family.attr, &dev_attr_stepping.attr, + &dev_attr_state.attr, + &dev_attr_shutdown_status.attr, + &dev_attr_cmdline.attr, + &dev_attr_firmware.attr, + &dev_attr_ramdisk.attr, + &dev_attr_bootmode.attr, + &dev_attr_log_buf_addr.attr, + &dev_attr_log_buf_len.attr, NULL }; diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index b63731691c73..a12ae5c8844d 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -20,6 +20,9 @@ */ #include #include +#include +#include +#include #include "../common/mic_device.h" #include "mic_device.h" @@ -256,6 +259,248 @@ mic_x100_program_msi_to_src_map(struct mic_device *mdev, mic_mmio_write(mw, reg, mxar); } +/* + * mic_x100_reset_fw_ready - Reset Firmware ready status field. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_reset_fw_ready(struct mic_device *mdev) +{ + mdev->ops->write_spad(mdev, MIC_X100_DOWNLOAD_INFO, 0); +} + +/* + * mic_x100_is_fw_ready - Check if firmware is ready. + * @mdev: pointer to mic_device instance + */ +static bool mic_x100_is_fw_ready(struct mic_device *mdev) +{ + u32 scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); + return MIC_X100_SPAD2_DOWNLOAD_STATUS(scratch2) ? true : false; +} + +/** + * mic_x100_get_apic_id - Get bootstrap APIC ID. + * @mdev: pointer to mic_device instance + */ +static u32 mic_x100_get_apic_id(struct mic_device *mdev) +{ + u32 scratch2 = 0; + + scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); + return MIC_X100_SPAD2_APIC_ID(scratch2); +} + +/** + * mic_x100_send_firmware_intr - Send an interrupt to the firmware on MIC. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_send_firmware_intr(struct mic_device *mdev) +{ + u32 apicicr_low; + u64 apic_icr_offset = MIC_X100_SBOX_APICICR7; + int vector = MIC_X100_BSP_INTERRUPT_VECTOR; + struct mic_mw *mw = &mdev->mmio; + + /* + * For MIC we need to make sure we "hit" + * the send_icr bit (13). + */ + apicicr_low = (vector | (1 << 13)); + + mic_mmio_write(mw, mic_x100_get_apic_id(mdev), + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4); + + /* Ensure that the interrupt is ordered w.r.t. previous stores. */ + wmb(); + mic_mmio_write(mw, apicicr_low, + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); +} + +/** + * mic_x100_hw_reset - Reset the MIC device. + * @mdev: pointer to mic_device instance + */ +static void mic_x100_hw_reset(struct mic_device *mdev) +{ + u32 reset_reg; + u32 rgcr = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_RGCR; + struct mic_mw *mw = &mdev->mmio; + + /* Ensure that the reset is ordered w.r.t. previous loads and stores */ + mb(); + /* Trigger reset */ + reset_reg = mic_mmio_read(mw, rgcr); + reset_reg |= 0x1; + mic_mmio_write(mw, reset_reg, rgcr); + /* + * It seems we really want to delay at least 1 second + * after touching reset to prevent a lot of problems. + */ + msleep(1000); +} + +/** + * mic_x100_load_command_line - Load command line to MIC. + * @mdev: pointer to mic_device instance + * @fw: the firmware image + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int +mic_x100_load_command_line(struct mic_device *mdev, const struct firmware *fw) +{ + u32 len = 0; + u32 boot_mem; + char *buf; + void __iomem *cmd_line_va = mdev->aper.va + mdev->bootaddr + fw->size; +#define CMDLINE_SIZE 2048 + + boot_mem = mdev->aper.len >> 20; + buf = kzalloc(CMDLINE_SIZE, GFP_KERNEL); + if (!buf) { + dev_err(mdev->sdev->parent, + "%s %d allocation failed\n", __func__, __LINE__); + return -ENOMEM; + } + len += snprintf(buf, CMDLINE_SIZE - len, + " mem=%dM", boot_mem); + if (mdev->cmdline) + snprintf(buf + len, CMDLINE_SIZE - len, + " %s", mdev->cmdline); + memcpy_toio(cmd_line_va, buf, strlen(buf) + 1); + kfree(buf); + return 0; +} + +/** + * mic_x100_load_ramdisk - Load ramdisk to MIC. + * @mdev: pointer to mic_device instance + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int +mic_x100_load_ramdisk(struct mic_device *mdev) +{ + const struct firmware *fw; + int rc; + struct boot_params __iomem *bp = mdev->aper.va + mdev->bootaddr; + + rc = request_firmware(&fw, + mdev->ramdisk, mdev->sdev->parent); + if (rc < 0) { + dev_err(mdev->sdev->parent, + "ramdisk request_firmware failed: %d %s\n", + rc, mdev->ramdisk); + goto error; + } + /* + * Typically the bootaddr for card OS is 64M + * so copy over the ramdisk @ 128M. + */ + memcpy_toio(mdev->aper.va + (mdev->bootaddr << 1), + fw->data, fw->size); + iowrite32(cpu_to_le32(mdev->bootaddr << 1), &bp->hdr.ramdisk_image); + iowrite32(cpu_to_le32(fw->size), &bp->hdr.ramdisk_size); + release_firmware(fw); +error: + return rc; +} + +/** + * mic_x100_get_boot_addr - Get MIC boot address. + * @mdev: pointer to mic_device instance + * + * This function is called during firmware load to determine + * the address at which the OS should be downloaded in card + * memory i.e. GDDR. + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int +mic_x100_get_boot_addr(struct mic_device *mdev) +{ + u32 scratch2, boot_addr; + int rc = 0; + + scratch2 = mdev->ops->read_spad(mdev, MIC_X100_DOWNLOAD_INFO); + boot_addr = MIC_X100_SPAD2_DOWNLOAD_ADDR(scratch2); + dev_dbg(mdev->sdev->parent, "%s %d boot_addr 0x%x\n", + __func__, __LINE__, boot_addr); + if (boot_addr > (1 << 31)) { + dev_err(mdev->sdev->parent, + "incorrect bootaddr 0x%x\n", + boot_addr); + rc = -EINVAL; + goto error; + } + mdev->bootaddr = boot_addr; +error: + return rc; +} + +/** + * mic_x100_load_firmware - Load firmware to MIC. + * @mdev: pointer to mic_device instance + * @buf: buffer containing boot string including firmware/ramdisk path. + * + * RETURNS: An appropriate -ERRNO error value on error, or zero for success. + */ +static int +mic_x100_load_firmware(struct mic_device *mdev, const char *buf) +{ + int rc; + const struct firmware *fw; + + rc = mic_x100_get_boot_addr(mdev); + if (rc) + goto error; + /* load OS */ + rc = request_firmware(&fw, mdev->firmware, mdev->sdev->parent); + if (rc < 0) { + dev_err(mdev->sdev->parent, + "ramdisk request_firmware failed: %d %s\n", + rc, mdev->firmware); + goto error; + } + if (mdev->bootaddr > mdev->aper.len - fw->size) { + rc = -EINVAL; + dev_err(mdev->sdev->parent, "%s %d rc %d bootaddr 0x%x\n", + __func__, __LINE__, rc, mdev->bootaddr); + release_firmware(fw); + goto error; + } + memcpy_toio(mdev->aper.va + mdev->bootaddr, fw->data, fw->size); + mdev->ops->write_spad(mdev, MIC_X100_FW_SIZE, fw->size); + if (!strcmp(mdev->bootmode, "elf")) + goto done; + /* load command line */ + rc = mic_x100_load_command_line(mdev, fw); + if (rc) { + dev_err(mdev->sdev->parent, "%s %d rc %d\n", + __func__, __LINE__, rc); + goto error; + } + release_firmware(fw); + /* load ramdisk */ + if (mdev->ramdisk) + rc = mic_x100_load_ramdisk(mdev); +error: + dev_dbg(mdev->sdev->parent, "%s %d rc %d\n", + __func__, __LINE__, rc); +done: + return rc; +} + +/** + * mic_x100_get_postcode - Get postcode status from firmware. + * @mdev: pointer to mic_device instance + * + * RETURNS: postcode. + */ +static u32 mic_x100_get_postcode(struct mic_device *mdev) +{ + return mic_mmio_read(&mdev->mmio, MIC_X100_POSTCODE); +} + /** * mic_x100_smpt_set - Update an SMPT entry with a DMA address. * @mdev: pointer to mic_device instance @@ -311,6 +556,12 @@ struct mic_hw_ops mic_x100_ops = { .write_spad = mic_x100_write_spad, .send_intr = mic_x100_send_intr, .ack_interrupt = mic_x100_ack_interrupt, + .reset = mic_x100_hw_reset, + .reset_fw_ready = mic_x100_reset_fw_ready, + .is_fw_ready = mic_x100_is_fw_ready, + .send_firmware_intr = mic_x100_send_firmware_intr, + .load_mic_fw = mic_x100_load_firmware, + .get_postcode = mic_x100_get_postcode, }; struct mic_hw_intr_ops mic_x100_intr_ops = { diff --git a/drivers/misc/mic/host/mic_x100.h b/drivers/misc/mic/host/mic_x100.h index 642cae9a0041..8b7daa182e54 100644 --- a/drivers/misc/mic/host/mic_x100.h +++ b/drivers/misc/mic/host/mic_x100.h @@ -69,6 +69,15 @@ #define MIC_X100_NUM_SBOX_IRQ 8 #define MIC_X100_NUM_RDMASR_IRQ 8 #define MIC_X100_RDMASR_IRQ_BASE 17 +#define MIC_X100_SPAD2_DOWNLOAD_STATUS(x) ((x) & 0x1) +#define MIC_X100_SPAD2_APIC_ID(x) (((x) >> 1) & 0x1ff) +#define MIC_X100_SPAD2_DOWNLOAD_ADDR(x) ((x) & 0xfffff000) +#define MIC_X100_SBOX_APICICR7 0x0000AA08 +#define MIC_X100_SBOX_RGCR 0x00004010 +#define MIC_X100_SBOX_SDBIC0 0x0000CC90 +#define MIC_X100_DOWNLOAD_INFO 2 +#define MIC_X100_FW_SIZE 5 +#define MIC_X100_POSTCODE 0x242c static const u16 mic_x100_intr_init[] = { MIC_X100_DOORBELL_IDX_START, @@ -79,6 +88,9 @@ static const u16 mic_x100_intr_init[] = { MIC_X100_NUM_ERR, }; +/* Host->Card(bootstrap) Interrupt Vector */ +#define MIC_X100_BSP_INTERRUPT_VECTOR 229 + extern struct mic_hw_ops mic_x100_ops; extern struct mic_smpt_ops mic_x100_smpt_ops; extern struct mic_hw_intr_ops mic_x100_intr_ops; diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 115add2515aa..d37263fbdca7 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -241,6 +241,7 @@ header-y += media.h header-y += mei.h header-y += mempolicy.h header-y += meye.h +header-y += mic_common.h header-y += mii.h header-y += minix_fs.h header-y += mman.h diff --git a/include/uapi/linux/mic_common.h b/include/uapi/linux/mic_common.h new file mode 100644 index 000000000000..a9091e529183 --- /dev/null +++ b/include/uapi/linux/mic_common.h @@ -0,0 +1,74 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 driver. + * + */ +#ifndef __MIC_COMMON_H_ +#define __MIC_COMMON_H_ + +#include + +/** + * struct mic_bootparam: Virtio device independent information in device page + * + * @magic: A magic value used by the card to ensure it can see the host + * @c2h_shutdown_db: Card to Host shutdown doorbell set by host + * @h2c_shutdown_db: Host to Card shutdown doorbell set by card + * @h2c_config_db: Host to Card Virtio config doorbell set by card + * @shutdown_status: Card shutdown status set by card + * @shutdown_card: Set to 1 by the host when a card shutdown is initiated + */ +struct mic_bootparam { + __u32 magic; + __s8 c2h_shutdown_db; + __s8 h2c_shutdown_db; + __s8 h2c_config_db; + __u8 shutdown_status; + __u8 shutdown_card; +} __aligned(8); + +/* Device page size */ +#define MIC_DP_SIZE 4096 + +#define MIC_MAGIC 0xc0ffee00 + +/** + * enum mic_states - MIC states. + */ +enum mic_states { + MIC_OFFLINE = 0, + MIC_ONLINE, + MIC_SHUTTING_DOWN, + MIC_RESET_FAILED, + MIC_LAST +}; + +/** + * enum mic_status - MIC status reported by card after + * a host or card initiated shutdown or a card crash. + */ +enum mic_status { + MIC_NOP = 0, + MIC_CRASHED, + MIC_HALTED, + MIC_POWER_OFF, + MIC_RESTART, + MIC_STATUS_LAST +}; + +#endif -- cgit v1.2.3 From f69bcbf3b4c4b333dcd7a48eaf868bf0c88edab5 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Thu, 5 Sep 2013 16:42:18 -0700 Subject: Intel MIC Host Driver Changes for Virtio Devices. This patch introduces the host "Virtio over PCIe" interface for Intel MIC. It allows creating user space backends on the host and instantiating virtio devices for them on the Intel MIC card. It uses the existing VRINGH infrastructure in the kernel to access virtio rings from the host. A character device per MIC is exposed with IOCTL, mmap and poll callbacks. This allows the user space backend to: (a) add/remove a virtio device via a device page. (b) map (R/O) virtio rings and device page to user space. (c) poll for availability of data. (d) copy a descriptor or entire descriptor chain to/from the card. (e) modify virtio configuration. (f) handle virtio device reset. The buffers are copied over using CPU copies for this initial patch and host initiated MIC DMA support is planned for future patches. The avail and desc virtio rings are in host memory and the used ring is in card memory to maximize writes across PCIe for performance. Co-author: Sudeep Dutt Signed-off-by: Ashutosh Dixit Signed-off-by: Caz Yokoyama Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Acked-by: Yaozu (Eddie) Dong Reviewed-by: Peter P Waskiewicz Jr Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/Kconfig | 1 + drivers/misc/mic/common/mic_device.h | 7 + drivers/misc/mic/host/Makefile | 2 + drivers/misc/mic/host/mic_boot.c | 3 +- drivers/misc/mic/host/mic_debugfs.c | 140 +++++++ drivers/misc/mic/host/mic_device.h | 5 + drivers/misc/mic/host/mic_fops.c | 221 +++++++++++ drivers/misc/mic/host/mic_fops.h | 32 ++ drivers/misc/mic/host/mic_main.c | 26 ++ drivers/misc/mic/host/mic_virtio.c | 703 +++++++++++++++++++++++++++++++++++ drivers/misc/mic/host/mic_virtio.h | 138 +++++++ include/uapi/linux/Kbuild | 1 + include/uapi/linux/mic_common.h | 166 ++++++++- include/uapi/linux/mic_ioctl.h | 74 ++++ 14 files changed, 1517 insertions(+), 2 deletions(-) create mode 100644 drivers/misc/mic/host/mic_fops.c create mode 100644 drivers/misc/mic/host/mic_fops.h create mode 100644 drivers/misc/mic/host/mic_virtio.c create mode 100644 drivers/misc/mic/host/mic_virtio.h create mode 100644 include/uapi/linux/mic_ioctl.h (limited to 'drivers/misc/mic/host') diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig index 279a2e649059..01f1a4a84c63 100644 --- a/drivers/misc/mic/Kconfig +++ b/drivers/misc/mic/Kconfig @@ -3,6 +3,7 @@ comment "Intel MIC Host Driver" config INTEL_MIC_HOST tristate "Intel MIC Host Driver" depends on 64BIT && PCI + select VHOST_RING default N help This enables Host Driver support for the Intel Many Integrated diff --git a/drivers/misc/mic/common/mic_device.h b/drivers/misc/mic/common/mic_device.h index 6440e9d58d3a..01eb74faae6b 100644 --- a/drivers/misc/mic/common/mic_device.h +++ b/drivers/misc/mic/common/mic_device.h @@ -41,4 +41,11 @@ struct mic_mw { #define MIC_DPLO_SPAD 14 #define MIC_DPHI_SPAD 15 +/* + * These values are supposed to be in the config_change field of the + * device page when the host sends a config change interrupt to the card. + */ +#define MIC_VIRTIO_PARAM_DEV_REMOVE 0x1 +#define MIC_VIRTIO_PARAM_CONFIG_CHANGED 0x2 + #endif diff --git a/drivers/misc/mic/host/Makefile b/drivers/misc/mic/host/Makefile index a375dd32c3e2..c2197f999394 100644 --- a/drivers/misc/mic/host/Makefile +++ b/drivers/misc/mic/host/Makefile @@ -10,3 +10,5 @@ mic_host-objs += mic_smpt.o mic_host-objs += mic_intr.o mic_host-objs += mic_boot.o mic_host-objs += mic_debugfs.o +mic_host-objs += mic_fops.o +mic_host-objs += mic_virtio.o diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index 936fc58084f3..fd9ff6d3784e 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -20,12 +20,12 @@ */ #include #include -#include #include #include "../common/mic_device.h" #include "mic_device.h" #include "mic_smpt.h" +#include "mic_virtio.h" /** * mic_reset - Reset the MIC device. @@ -117,6 +117,7 @@ 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); mic_bootparam_init(mdev); mic_reset(mdev); if (MIC_RESET_FAILED == mdev->state) diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c index 78541d42aaf9..e22fb7bbbb98 100644 --- a/drivers/misc/mic/host/mic_debugfs.c +++ b/drivers/misc/mic/host/mic_debugfs.c @@ -26,6 +26,7 @@ #include "../common/mic_device.h" #include "mic_device.h" #include "mic_smpt.h" +#include "mic_virtio.h" /* Debugfs parent dir */ static struct dentry *mic_dbg; @@ -193,7 +194,13 @@ static const struct file_operations post_code_ops = { static int mic_dp_show(struct seq_file *s, void *pos) { struct mic_device *mdev = s->private; + struct mic_device_desc *d; + struct mic_device_ctrl *dc; + struct mic_vqconfig *vqconfig; + __u32 *features; + __u8 *config; struct mic_bootparam *bootparam = mdev->dp; + int i, j; seq_printf(s, "Bootparam: magic 0x%x\n", bootparam->magic); @@ -208,6 +215,53 @@ static int mic_dp_show(struct seq_file *s, void *pos) seq_printf(s, "Bootparam: shutdown_card %d\n", bootparam->shutdown_card); + for (i = sizeof(*bootparam); i < MIC_DP_SIZE; + i += mic_total_desc_size(d)) { + d = mdev->dp + i; + dc = (void *)d + mic_aligned_desc_size(d); + + /* end of list */ + if (d->type == 0) + break; + + if (d->type == -1) + continue; + + seq_printf(s, "Type %d ", d->type); + seq_printf(s, "Num VQ %d ", d->num_vq); + seq_printf(s, "Feature Len %d\n", d->feature_len); + seq_printf(s, "Config Len %d ", d->config_len); + seq_printf(s, "Shutdown Status %d\n", d->status); + + for (j = 0; j < d->num_vq; j++) { + vqconfig = mic_vq_config(d) + j; + seq_printf(s, "vqconfig[%d]: ", j); + seq_printf(s, "address 0x%llx ", vqconfig->address); + seq_printf(s, "num %d ", vqconfig->num); + seq_printf(s, "used address 0x%llx\n", + vqconfig->used_address); + } + + features = (__u32 *) mic_vq_features(d); + seq_printf(s, "Features: Host 0x%x ", features[0]); + seq_printf(s, "Guest 0x%x\n", features[1]); + + config = mic_vq_configspace(d); + for (j = 0; j < d->config_len; j++) + seq_printf(s, "config[%d]=%d\n", j, config[j]); + + seq_puts(s, "Device control:\n"); + seq_printf(s, "Config Change %d ", dc->config_change); + seq_printf(s, "Vdev reset %d\n", dc->vdev_reset); + seq_printf(s, "Guest Ack %d ", dc->guest_ack); + seq_printf(s, "Host ack %d\n", dc->host_ack); + seq_printf(s, "Used address updated %d ", + dc->used_address_updated); + seq_printf(s, "Vdev 0x%llx\n", dc->vdev); + seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); + seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); + } + return 0; } @@ -229,6 +283,89 @@ static const struct file_operations dp_ops = { .release = mic_dp_debug_release }; +static int mic_vdev_info_show(struct seq_file *s, void *unused) +{ + struct mic_device *mdev = s->private; + struct list_head *pos, *tmp; + struct mic_vdev *mvdev; + int i, j; + + mutex_lock(&mdev->mic_mutex); + list_for_each_safe(pos, tmp, &mdev->vdev_list) { + mvdev = list_entry(pos, struct mic_vdev, list); + seq_printf(s, "VDEV type %d state %s in %ld out %ld\n", + mvdev->virtio_id, + mic_vdevup(mvdev) ? "UP" : "DOWN", + mvdev->in_bytes, + mvdev->out_bytes); + for (i = 0; i < MIC_MAX_VRINGS; i++) { + struct vring_desc *desc; + struct vring_avail *avail; + struct vring_used *used; + struct mic_vringh *mvr = &mvdev->mvr[i]; + struct vringh *vrh = &mvr->vrh; + int num = vrh->vring.num; + if (!num) + continue; + desc = vrh->vring.desc; + seq_printf(s, "vring i %d avail_idx %d", + i, mvr->vring.info->avail_idx & (num - 1)); + seq_printf(s, " vring i %d avail_idx %d\n", + i, mvr->vring.info->avail_idx); + seq_printf(s, "vrh i %d weak_barriers %d", + i, vrh->weak_barriers); + seq_printf(s, " last_avail_idx %d last_used_idx %d", + vrh->last_avail_idx, vrh->last_used_idx); + seq_printf(s, " completed %d\n", vrh->completed); + for (j = 0; j < num; j++) { + seq_printf(s, "desc[%d] addr 0x%llx len %d", + j, desc->addr, desc->len); + seq_printf(s, " flags 0x%x next %d\n", + desc->flags, + desc->next); + desc++; + } + avail = vrh->vring.avail; + seq_printf(s, "avail flags 0x%x idx %d\n", + avail->flags, avail->idx & (num - 1)); + seq_printf(s, "avail flags 0x%x idx %d\n", + avail->flags, avail->idx); + for (j = 0; j < num; j++) + seq_printf(s, "avail ring[%d] %d\n", + j, avail->ring[j]); + used = vrh->vring.used; + seq_printf(s, "used flags 0x%x idx %d\n", + used->flags, used->idx & (num - 1)); + seq_printf(s, "used flags 0x%x idx %d\n", + used->flags, used->idx); + for (j = 0; j < num; j++) + seq_printf(s, "used ring[%d] id %d len %d\n", + j, used->ring[j].id, used->ring[j].len); + } + } + mutex_unlock(&mdev->mic_mutex); + + return 0; +} + +static int mic_vdev_info_debug_open(struct inode *inode, struct file *file) +{ + return single_open(file, mic_vdev_info_show, inode->i_private); +} + +static int mic_vdev_info_debug_release(struct inode *inode, struct file *file) +{ + return single_release(inode, file); +} + +static const struct file_operations vdev_info_ops = { + .owner = THIS_MODULE, + .open = mic_vdev_info_debug_open, + .read = seq_read, + .llseek = seq_lseek, + .release = mic_vdev_info_debug_release +}; + static int mic_msi_irq_info_show(struct seq_file *s, void *pos) { struct mic_device *mdev = s->private; @@ -321,6 +458,9 @@ void mic_create_debug_dir(struct mic_device *mdev) debugfs_create_file("dp", 0444, mdev->dbg_dir, mdev, &dp_ops); + debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, + mdev, &vdev_info_ops); + debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev, &msi_irq_info_ops); } diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 50b8b88d70d3..dcba2a59e77f 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -21,6 +21,7 @@ #ifndef _MIC_DEVICE_H_ #define _MIC_DEVICE_H_ +#include #include #include "mic_intr.h" @@ -80,6 +81,8 @@ enum mic_stepping { * @dp_dma_addr: virtio device page DMA address. * @shutdown_db: shutdown doorbell. * @shutdown_cookie: shutdown cookie. + * @cdev: Character device for MIC. + * @vdev_list: list of virtio devices. */ struct mic_device { struct mic_mw mmio; @@ -113,6 +116,8 @@ struct mic_device { dma_addr_t dp_dma_addr; int shutdown_db; struct mic_irq *shutdown_cookie; + struct cdev cdev; + struct list_head vdev_list; }; /** diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c new file mode 100644 index 000000000000..661469ad339d --- /dev/null +++ b/drivers/misc/mic/host/mic_fops.c @@ -0,0 +1,221 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#include + +#include +#include "../common/mic_device.h" +#include "mic_device.h" +#include "mic_fops.h" +#include "mic_virtio.h" + +int mic_open(struct inode *inode, struct file *f) +{ + struct mic_vdev *mvdev; + struct mic_device *mdev = container_of(inode->i_cdev, + struct mic_device, cdev); + + mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL); + if (!mvdev) + return -ENOMEM; + + init_waitqueue_head(&mvdev->waitq); + INIT_LIST_HEAD(&mvdev->list); + mvdev->mdev = mdev; + mvdev->virtio_id = -1; + + f->private_data = mvdev; + return 0; +} + +int mic_release(struct inode *inode, struct file *f) +{ + struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; + + if (-1 != mvdev->virtio_id) + mic_virtio_del_device(mvdev); + f->private_data = NULL; + kfree(mvdev); + return 0; +} + +long mic_ioctl(struct file *f, unsigned int cmd, unsigned long arg) +{ + struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; + void __user *argp = (void __user *)arg; + int ret; + + switch (cmd) { + case MIC_VIRTIO_ADD_DEVICE: + { + ret = mic_virtio_add_device(mvdev, argp); + if (ret < 0) { + dev_err(mic_dev(mvdev), + "%s %d errno ret %d\n", + __func__, __LINE__, ret); + return ret; + } + break; + } + case MIC_VIRTIO_COPY_DESC: + { + struct mic_copy_desc copy; + + ret = mic_vdev_inited(mvdev); + if (ret) + return ret; + + if (copy_from_user(©, argp, sizeof(copy))) + return -EFAULT; + + dev_dbg(mic_dev(mvdev), + "%s %d === iovcnt 0x%x vr_idx 0x%x update_used %d\n", + __func__, __LINE__, copy.iovcnt, copy.vr_idx, + copy.update_used); + + ret = mic_virtio_copy_desc(mvdev, ©); + if (ret < 0) { + dev_err(mic_dev(mvdev), + "%s %d errno ret %d\n", + __func__, __LINE__, ret); + return ret; + } + if (copy_to_user( + &((struct mic_copy_desc __user *)argp)->out_len, + ©.out_len, sizeof(copy.out_len))) { + dev_err(mic_dev(mvdev), "%s %d errno ret %d\n", + __func__, __LINE__, -EFAULT); + return -EFAULT; + } + break; + } + case MIC_VIRTIO_CONFIG_CHANGE: + { + ret = mic_vdev_inited(mvdev); + if (ret) + return ret; + + ret = mic_virtio_config_change(mvdev, argp); + if (ret < 0) { + dev_err(mic_dev(mvdev), + "%s %d errno ret %d\n", + __func__, __LINE__, ret); + return ret; + } + break; + } + default: + return -ENOIOCTLCMD; + }; + return 0; +} + +/* + * We return POLLIN | POLLOUT from poll when new buffers are enqueued, and + * not when previously enqueued buffers may be available. This means that + * in the card->host (TX) path, when userspace is unblocked by poll it + * must drain all available descriptors or it can stall. + */ +unsigned int mic_poll(struct file *f, poll_table *wait) +{ + struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; + int mask = 0; + + poll_wait(f, &mvdev->waitq, wait); + + if (mic_vdev_inited(mvdev)) + mask = POLLERR; + else if (mvdev->poll_wake) { + mvdev->poll_wake = 0; + mask = POLLIN | POLLOUT; + } + + return mask; +} + +static inline int +mic_query_offset(struct mic_vdev *mvdev, unsigned long offset, + unsigned long *size, unsigned long *pa) +{ + struct mic_device *mdev = mvdev->mdev; + unsigned long start = MIC_DP_SIZE; + int i; + + /* + * MMAP interface is as follows: + * offset region + * 0x0 virtio device_page + * 0x1000 first vring + * 0x1000 + size of 1st vring second vring + * .... + */ + if (!offset) { + *pa = virt_to_phys(mdev->dp); + *size = MIC_DP_SIZE; + return 0; + } + + for (i = 0; i < mvdev->dd->num_vq; i++) { + struct mic_vringh *mvr = &mvdev->mvr[i]; + if (offset == start) { + *pa = virt_to_phys(mvr->vring.va); + *size = mvr->vring.len; + return 0; + } + start += mvr->vring.len; + } + return -1; +} + +/* + * Maps the device page and virtio rings to user space for readonly access. + */ +int +mic_mmap(struct file *f, struct vm_area_struct *vma) +{ + struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size; + int i, err; + + err = mic_vdev_inited(mvdev); + if (err) + return err; + + if (vma->vm_flags & VM_WRITE) + return -EACCES; + + while (size_rem) { + i = mic_query_offset(mvdev, offset, &size, &pa); + if (i < 0) + return -EINVAL; + err = remap_pfn_range(vma, vma->vm_start + offset, + pa >> PAGE_SHIFT, size, vma->vm_page_prot); + if (err) + return err; + dev_dbg(mic_dev(mvdev), + "%s %d type %d size 0x%lx off 0x%lx pa 0x%lx vma 0x%lx\n", + __func__, __LINE__, mvdev->virtio_id, size, offset, + pa, vma->vm_start + offset); + size_rem -= size; + offset += size; + } + return 0; +} diff --git a/drivers/misc/mic/host/mic_fops.h b/drivers/misc/mic/host/mic_fops.h new file mode 100644 index 000000000000..dc3893dff667 --- /dev/null +++ b/drivers/misc/mic/host/mic_fops.h @@ -0,0 +1,32 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#ifndef _MIC_FOPS_H_ +#define _MIC_FOPS_H_ + +int mic_open(struct inode *inode, struct file *filp); +int mic_release(struct inode *inode, struct file *filp); +ssize_t mic_read(struct file *filp, char __user *buf, + size_t count, loff_t *pos); +long mic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); +int mic_mmap(struct file *f, struct vm_area_struct *vma); +unsigned int mic_poll(struct file *f, poll_table *wait); + +#endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index 998a20ab7e96..a8965d496e84 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -25,12 +25,15 @@ #include #include #include +#include #include #include "../common/mic_device.h" #include "mic_device.h" #include "mic_x100.h" #include "mic_smpt.h" +#include "mic_fops.h" +#include "mic_virtio.h" static const char mic_driver_name[] = "mic"; @@ -64,6 +67,15 @@ static struct class *g_mic_class; /* Base device node number for MIC devices */ static dev_t g_mic_devno; +static const struct file_operations mic_fops = { + .open = mic_open, + .release = mic_release, + .unlocked_ioctl = mic_ioctl, + .poll = mic_poll, + .mmap = mic_mmap, + .owner = THIS_MODULE, +}; + /* Initialize the device page */ static int mic_dp_init(struct mic_device *mdev) { @@ -193,6 +205,7 @@ mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) mdev->irq_info.next_avail_src = 0; INIT_WORK(&mdev->reset_trigger_work, mic_reset_trigger_work); INIT_WORK(&mdev->shutdown_work, mic_shutdown_work); + INIT_LIST_HEAD(&mdev->vdev_list); } /** @@ -330,7 +343,19 @@ static int mic_probe(struct pci_dev *pdev, mic_bootparam_init(mdev); mic_create_debug_dir(mdev); + cdev_init(&mdev->cdev, &mic_fops); + mdev->cdev.owner = THIS_MODULE; + rc = cdev_add(&mdev->cdev, MKDEV(MAJOR(g_mic_devno), mdev->id), 1); + if (rc) { + dev_err(&pdev->dev, "cdev_add err id %d rc %d\n", mdev->id, rc); + goto cleanup_debug_dir; + } return 0; +cleanup_debug_dir: + mic_delete_debug_dir(mdev); + mutex_lock(&mdev->mic_mutex); + mic_free_irq(mdev, mdev->shutdown_cookie, mdev); + mutex_unlock(&mdev->mic_mutex); dp_uninit: mic_dp_uninit(mdev); sysfs_put: @@ -375,6 +400,7 @@ static void mic_remove(struct pci_dev *pdev) return; mic_stop(mdev, false); + cdev_del(&mdev->cdev); mic_delete_debug_dir(mdev); mutex_lock(&mdev->mic_mutex); mic_free_irq(mdev, mdev->shutdown_cookie, mdev); diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c new file mode 100644 index 000000000000..be2a1f06c4ca --- /dev/null +++ b/drivers/misc/mic/host/mic_virtio.c @@ -0,0 +1,703 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#include +#include +#include + +#include +#include "../common/mic_device.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. + */ +static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, + void __user *ubuf, size_t len, u64 addr) +{ + 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, dbuf, len)) { + err = -EFAULT; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + mvdev->in_bytes += len; + err = 0; +err: + return err; +} + +/* + * Initiates copies across the PCIe bus from a user space + * buffer to card memory. + */ +static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, + void __user *ubuf, size_t len, u64 addr) +{ + int err; + void __iomem *dbuf = mvdev->mdev->aper.va + addr; + /* + * We are copying to IO below and should ideally use something + * like copy_from_user_toio(..) if it existed. + */ + if (copy_from_user(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; +err: + return err; +} + +#define MIC_VRINGH_READ true + +/* The function to call to notify the card about added buffers */ +static void mic_notify(struct vringh *vrh) +{ + struct mic_vringh *mvrh = container_of(vrh, struct mic_vringh, vrh); + struct mic_vdev *mvdev = mvrh->mvdev; + s8 db = mvdev->dc->h2c_vdev_db; + + if (db != -1) + mvdev->mdev->ops->send_intr(mvdev->mdev, db); +} + +/* Determine the total number of bytes consumed in a VRINGH KIOV */ +static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov) +{ + int i; + u32 total = iov->consumed; + + for (i = 0; i < iov->i; i++) + total += iov->iov[i].iov_len; + return total; +} + +/* + * Traverse the VRINGH KIOV and issue the APIs to trigger the copies. + * This API is heavily based on the vringh_iov_xfer(..) implementation + * in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..) + * and vringh_iov_push_kern(..) directly is because there is no + * 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) +{ + int ret = 0; + size_t partlen, tot_len = 0; + + 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); + else + ret = mic_virtio_copy_from_user(mvdev, + ubuf, partlen, + (u64)iov->iov[iov->i].iov_base); + if (ret) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len -= partlen; + ubuf += partlen; + tot_len += partlen; + iov->consumed += partlen; + iov->iov[iov->i].iov_len -= partlen; + iov->iov[iov->i].iov_base += partlen; + if (!iov->iov[iov->i].iov_len) { + /* Fix up old iov element then increment. */ + iov->iov[iov->i].iov_len = iov->consumed; + iov->iov[iov->i].iov_base -= iov->consumed; + + iov->consumed = 0; + iov->i++; + } + } + *out_len = tot_len; + return ret; +} + +/* + * Use the standard VRINGH infrastructure in the kernel to fetch new + * descriptors, initiate the copies and update the used ring. + */ +static int _mic_virtio_copy(struct mic_vdev *mvdev, + struct mic_copy_desc *copy) +{ + int ret = 0, iovcnt = copy->iovcnt; + struct iovec iov; + struct iovec __user *u_iov = copy->iov; + void __user *ubuf = NULL; + struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx]; + struct vringh_kiov *riov = &mvr->riov; + struct vringh_kiov *wiov = &mvr->wiov; + struct vringh *vrh = &mvr->vrh; + u16 *head = &mvr->head; + struct mic_vring *vr = &mvr->vring; + size_t len = 0, out_len; + + copy->out_len = 0; + /* Fetch a new IOVEC if all previous elements have been processed */ + if (riov->i == riov->used && wiov->i == wiov->used) { + ret = vringh_getdesc_kern(vrh, riov, wiov, + head, GFP_KERNEL); + /* Check if there are available descriptors */ + if (ret <= 0) + return ret; + } + while (iovcnt) { + if (!len) { + /* Copy over a new iovec from user space. */ + ret = copy_from_user(&iov, u_iov, sizeof(*u_iov)); + if (ret) { + ret = -EINVAL; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len = iov.iov_len; + ubuf = iov.iov_base; + } + /* Issue all the read descriptors first */ + ret = mic_vringh_copy(mvdev, riov, ubuf, len, + MIC_VRINGH_READ, &out_len); + if (ret) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len -= out_len; + 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); + if (ret) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + break; + } + len -= out_len; + ubuf += out_len; + copy->out_len += out_len; + if (!len) { + /* One user space iovec is now completed */ + iovcnt--; + u_iov++; + } + /* Exit loop if all elements in KIOVs have been processed. */ + if (riov->i == riov->used && wiov->i == wiov->used) + break; + } + /* + * Update the used ring if a descriptor was available and some data was + * copied in/out and the user asked for a used ring update. + */ + if (*head != USHRT_MAX && copy->out_len && + copy->update_used) { + u32 total = 0; + + /* Determine the total data consumed */ + total += mic_vringh_iov_consumed(riov); + total += mic_vringh_iov_consumed(wiov); + vringh_complete_kern(vrh, *head, total); + *head = USHRT_MAX; + if (vringh_need_notify_kern(vrh) > 0) + vringh_notify(vrh); + vringh_kiov_cleanup(riov); + vringh_kiov_cleanup(wiov); + /* Update avail idx for user space */ + vr->info->avail_idx = vrh->last_avail_idx; + } + return ret; +} + +static inline int mic_verify_copy_args(struct mic_vdev *mvdev, + struct mic_copy_desc *copy) +{ + if (copy->vr_idx >= mvdev->dd->num_vq) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -EINVAL); + return -EINVAL; + } + return 0; +} + +/* Copy a specified number of virtio descriptors in a chain */ +int mic_virtio_copy_desc(struct mic_vdev *mvdev, + struct mic_copy_desc *copy) +{ + int err; + struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx]; + + err = mic_verify_copy_args(mvdev, copy); + if (err) + return err; + + mutex_lock(&mvr->vr_mutex); + if (!mic_vdevup(mvdev)) { + err = -ENODEV; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, err); + goto err; + } + err = _mic_virtio_copy(mvdev, copy); + if (err) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, err); + } +err: + mutex_unlock(&mvr->vr_mutex); + return err; +} + +static void mic_virtio_init_post(struct mic_vdev *mvdev) +{ + struct mic_vqconfig *vqconfig = mic_vq_config(mvdev->dd); + int i; + + for (i = 0; i < mvdev->dd->num_vq; i++) { + if (!le64_to_cpu(vqconfig[i].used_address)) { + dev_warn(mic_dev(mvdev), "used_address zero??\n"); + continue; + } + mvdev->mvr[i].vrh.vring.used = + mvdev->mdev->aper.va + + le64_to_cpu(vqconfig[i].used_address); + } + + mvdev->dc->used_address_updated = 0; + + dev_dbg(mic_dev(mvdev), "%s: device type %d LINKUP\n", + __func__, mvdev->virtio_id); +} + +static inline void mic_virtio_device_reset(struct mic_vdev *mvdev) +{ + int i; + + dev_dbg(mic_dev(mvdev), "%s: status %d device type %d RESET\n", + __func__, mvdev->dd->status, mvdev->virtio_id); + + for (i = 0; i < mvdev->dd->num_vq; i++) + /* + * Avoid lockdep false positive. The + 1 is for the mic + * mutex which is held in the reset devices code path. + */ + mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1); + + /* 0 status means "reset" */ + mvdev->dd->status = 0; + mvdev->dc->vdev_reset = 0; + mvdev->dc->host_ack = 1; + + for (i = 0; i < mvdev->dd->num_vq; i++) { + struct vringh *vrh = &mvdev->mvr[i].vrh; + mvdev->mvr[i].vring.info->avail_idx = 0; + vrh->completed = 0; + vrh->last_avail_idx = 0; + vrh->last_used_idx = 0; + } + + for (i = 0; i < mvdev->dd->num_vq; i++) + mutex_unlock(&mvdev->mvr[i].vr_mutex); +} + +void mic_virtio_reset_devices(struct mic_device *mdev) +{ + struct list_head *pos, *tmp; + struct mic_vdev *mvdev; + + dev_dbg(mdev->sdev->parent, "%s\n", __func__); + + list_for_each_safe(pos, tmp, &mdev->vdev_list) { + mvdev = list_entry(pos, struct mic_vdev, list); + mic_virtio_device_reset(mvdev); + mvdev->poll_wake = 1; + wake_up(&mvdev->waitq); + } +} + +void mic_bh_handler(struct work_struct *work) +{ + struct mic_vdev *mvdev = container_of(work, struct mic_vdev, + virtio_bh_work); + + if (mvdev->dc->used_address_updated) + mic_virtio_init_post(mvdev); + + if (mvdev->dc->vdev_reset) + mic_virtio_device_reset(mvdev); + + mvdev->poll_wake = 1; + wake_up(&mvdev->waitq); +} + +static irqreturn_t mic_virtio_intr_handler(int irq, void *data) +{ + + struct mic_vdev *mvdev = data; + struct mic_device *mdev = mvdev->mdev; + + mdev->ops->ack_interrupt(mdev); + schedule_work(&mvdev->virtio_bh_work); + return IRQ_HANDLED; +} + +int mic_virtio_config_change(struct mic_vdev *mvdev, + void __user *argp) +{ + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); + int ret = 0, retry = 100, i; + struct mic_bootparam *bootparam = mvdev->mdev->dp; + s8 db = bootparam->h2c_config_db; + + mutex_lock(&mvdev->mdev->mic_mutex); + for (i = 0; i < mvdev->dd->num_vq; i++) + mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1); + + if (db == -1 || mvdev->dd->type == -1) { + ret = -EIO; + goto exit; + } + + if (copy_from_user(mic_vq_configspace(mvdev->dd), + argp, mvdev->dd->config_len)) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -EFAULT); + ret = -EFAULT; + goto exit; + } + mvdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED; + mvdev->mdev->ops->send_intr(mvdev->mdev, db); + + for (i = retry; i--;) { + ret = wait_event_timeout(wake, + mvdev->dc->guest_ack, msecs_to_jiffies(100)); + if (ret) + break; + } + + dev_dbg(mic_dev(mvdev), + "%s %d retry: %d\n", __func__, __LINE__, retry); + mvdev->dc->config_change = 0; + mvdev->dc->guest_ack = 0; +exit: + for (i = 0; i < mvdev->dd->num_vq; i++) + mutex_unlock(&mvdev->mvr[i].vr_mutex); + mutex_unlock(&mvdev->mdev->mic_mutex); + return ret; +} + +static int mic_copy_dp_entry(struct mic_vdev *mvdev, + void __user *argp, + __u8 *type, + struct mic_device_desc **devpage) +{ + struct mic_device *mdev = mvdev->mdev; + struct mic_device_desc dd, *dd_config, *devp; + struct mic_vqconfig *vqconfig; + int ret = 0, i; + bool slot_found = false; + + if (copy_from_user(&dd, argp, sizeof(dd))) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -EFAULT); + return -EFAULT; + } + + if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE + || dd.num_vq > MIC_MAX_VRINGS) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -EINVAL); + return -EINVAL; + } + + dd_config = kmalloc(mic_desc_size(&dd), GFP_KERNEL); + if (dd_config == NULL) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -ENOMEM); + return -ENOMEM; + } + if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) { + ret = -EFAULT; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto exit; + } + + vqconfig = mic_vq_config(dd_config); + for (i = 0; i < dd.num_vq; i++) { + if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) { + ret = -EINVAL; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto exit; + } + } + + /* Find the first free device page entry */ + for (i = mic_aligned_size(struct mic_bootparam); + i < MIC_DP_SIZE - mic_total_desc_size(dd_config); + i += mic_total_desc_size(devp)) { + devp = mdev->dp + i; + if (devp->type == 0 || devp->type == -1) { + slot_found = true; + break; + } + } + if (!slot_found) { + ret = -EINVAL; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto exit; + } + /* + * Save off the type before doing the memcpy. Type will be set in the + * end after completing all initialization for the new device. + */ + *type = dd_config->type; + dd_config->type = 0; + memcpy(devp, dd_config, mic_desc_size(dd_config)); + + *devpage = devp; +exit: + kfree(dd_config); + return ret; +} + +static void mic_init_device_ctrl(struct mic_vdev *mvdev, + struct mic_device_desc *devpage) +{ + struct mic_device_ctrl *dc; + + dc = mvdev->dc = (void *)devpage + mic_aligned_desc_size(devpage); + + dc->config_change = 0; + dc->guest_ack = 0; + dc->vdev_reset = 0; + dc->host_ack = 0; + dc->used_address_updated = 0; + dc->c2h_vdev_db = -1; + dc->h2c_vdev_db = -1; +} + +int mic_virtio_add_device(struct mic_vdev *mvdev, + void __user *argp) +{ + struct mic_device *mdev = mvdev->mdev; + struct mic_device_desc *dd; + struct mic_vqconfig *vqconfig; + int vr_size, i, j, ret; + u8 type; + s8 db; + char irqname[10]; + struct mic_bootparam *bootparam = mdev->dp; + u16 num; + + mutex_lock(&mdev->mic_mutex); + + ret = mic_copy_dp_entry(mvdev, argp, &type, &dd); + if (ret) { + mutex_unlock(&mdev->mic_mutex); + return ret; + } + + mic_init_device_ctrl(mvdev, dd); + + mvdev->dd = dd; + mvdev->virtio_id = type; + vqconfig = mic_vq_config(dd); + INIT_WORK(&mvdev->virtio_bh_work, mic_bh_handler); + + for (i = 0; i < dd->num_vq; i++) { + struct mic_vringh *mvr = &mvdev->mvr[i]; + struct mic_vring *vr = &mvdev->mvr[i].vring; + num = le16_to_cpu(vqconfig[i].num); + mutex_init(&mvr->vr_mutex); + vr_size = PAGE_ALIGN(vring_size(num, MIC_VIRTIO_RING_ALIGN) + + sizeof(struct _mic_vring_info)); + vr->va = (void *) + __get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(vr_size)); + if (!vr->va) { + ret = -ENOMEM; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto err; + } + vr->len = vr_size; + vr->info = vr->va + vring_size(num, MIC_VIRTIO_RING_ALIGN); + vr->info->magic = MIC_MAGIC + mvdev->virtio_id + i; + vqconfig[i].address = mic_map_single(mdev, + vr->va, vr_size); + if (mic_map_error(vqconfig[i].address)) { + free_pages((unsigned long)vr->va, + get_order(vr_size)); + ret = -ENOMEM; + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto err; + } + vqconfig[i].address = cpu_to_le64(vqconfig[i].address); + + vring_init(&vr->vr, num, + vr->va, MIC_VIRTIO_RING_ALIGN); + ret = vringh_init_kern(&mvr->vrh, + *(u32 *)mic_vq_features(mvdev->dd), num, false, + vr->vr.desc, vr->vr.avail, vr->vr.used); + if (ret) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, ret); + goto err; + } + vringh_kiov_init(&mvr->riov, NULL, 0); + vringh_kiov_init(&mvr->wiov, NULL, 0); + mvr->head = USHRT_MAX; + mvr->mvdev = mvdev; + mvr->vrh.notify = mic_notify; + 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); + } + + 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); + if (IS_ERR(mvdev->virtio_cookie)) { + ret = PTR_ERR(mvdev->virtio_cookie); + dev_dbg(mdev->sdev->parent, "request irq failed\n"); + goto err; + } + + mvdev->dc->c2h_vdev_db = mvdev->virtio_db; + + list_add_tail(&mvdev->list, &mdev->vdev_list); + /* + * Order the type update with previous stores. This write barrier + * is paired with the corresponding read barrier before the uncached + * system memory read of the type, on the card while scanning the + * device page. + */ + smp_wmb(); + dd->type = type; + + dev_dbg(mdev->sdev->parent, "Added virtio device id %d\n", dd->type); + + db = bootparam->h2c_config_db; + if (db != -1) + mdev->ops->send_intr(mdev, db); + mutex_unlock(&mdev->mic_mutex); + return 0; +err: + vqconfig = mic_vq_config(dd); + for (j = 0; j < i; j++) { + struct mic_vringh *mvr = &mvdev->mvr[j]; + mic_unmap_single(mdev, le64_to_cpu(vqconfig[j].address), + mvr->vring.len); + free_pages((unsigned long)mvr->vring.va, + get_order(mvr->vring.len)); + } + mutex_unlock(&mdev->mic_mutex); + return ret; +} + +void mic_virtio_del_device(struct mic_vdev *mvdev) +{ + struct list_head *pos, *tmp; + struct mic_vdev *tmp_mvdev; + struct mic_device *mdev = mvdev->mdev; + DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake); + int i, ret, retry = 100; + struct mic_vqconfig *vqconfig; + struct mic_bootparam *bootparam = mdev->dp; + s8 db; + + mutex_lock(&mdev->mic_mutex); + db = bootparam->h2c_config_db; + if (db == -1) + goto skip_hot_remove; + dev_dbg(mdev->sdev->parent, + "Requesting hot remove id %d\n", mvdev->virtio_id); + mvdev->dc->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE; + mdev->ops->send_intr(mdev, db); + for (i = retry; i--;) { + ret = wait_event_timeout(wake, + mvdev->dc->guest_ack, msecs_to_jiffies(100)); + if (ret) + break; + } + dev_dbg(mdev->sdev->parent, + "Device id %d config_change %d guest_ack %d\n", + mvdev->virtio_id, mvdev->dc->config_change, + mvdev->dc->guest_ack); + mvdev->dc->config_change = 0; + mvdev->dc->guest_ack = 0; +skip_hot_remove: + mic_free_irq(mdev, mvdev->virtio_cookie, mvdev); + flush_work(&mvdev->virtio_bh_work); + vqconfig = mic_vq_config(mvdev->dd); + for (i = 0; i < mvdev->dd->num_vq; i++) { + struct mic_vringh *mvr = &mvdev->mvr[i]; + vringh_kiov_cleanup(&mvr->riov); + vringh_kiov_cleanup(&mvr->wiov); + mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), + mvr->vring.len); + free_pages((unsigned long)mvr->vring.va, + get_order(mvr->vring.len)); + } + + list_for_each_safe(pos, tmp, &mdev->vdev_list) { + tmp_mvdev = list_entry(pos, struct mic_vdev, list); + if (tmp_mvdev == mvdev) { + list_del(pos); + dev_dbg(mdev->sdev->parent, + "Removing virtio device id %d\n", + mvdev->virtio_id); + break; + } + } + /* + * Order the type update with previous stores. This write barrier + * is paired with the corresponding read barrier before the uncached + * system memory read of the type, on the card while scanning the + * device page. + */ + smp_wmb(); + mvdev->dd->type = -1; + mutex_unlock(&mdev->mic_mutex); +} diff --git a/drivers/misc/mic/host/mic_virtio.h b/drivers/misc/mic/host/mic_virtio.h new file mode 100644 index 000000000000..184f3c84805b --- /dev/null +++ b/drivers/misc/mic/host/mic_virtio.h @@ -0,0 +1,138 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#ifndef MIC_VIRTIO_H +#define MIC_VIRTIO_H + +#include +#include + +/* + * Note on endianness. + * 1. Host can be both BE or LE + * 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail + * rings and ioreadXX/iowriteXX to access used ring. + * 3. Device page exposed by host to guest contains LE values. Guest + * accesses these using ioreadXX/iowriteXX etc. This way in general we + * obey the virtio spec according to which guest works with native + * endianness and host is aware of guest endianness and does all + * required endianness conversion. + * 4. Data provided from user space to guest (in ADD_DEVICE and + * CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be + * in guest endianness. + */ + +/** + * struct mic_vringh - Virtio ring host information. + * + * @vring: The MIC vring used for setting up user space mappings. + * @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. + * @mvdev: Back pointer to MIC virtio device for vringh_notify(..). + */ +struct mic_vringh { + struct mic_vring vring; + struct vringh vrh; + struct vringh_kiov riov; + struct vringh_kiov wiov; + u16 head; + struct mutex vr_mutex; + struct mic_vdev *mvdev; +}; + +/** + * struct mic_vdev - Host information for a card Virtio device. + * + * @virtio_id - Virtio device id. + * @waitq - Waitqueue to allow ring3 apps to poll. + * @mdev - Back pointer to host MIC device. + * @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. + * @mvr - Store per VRING data structures. + * @virtio_bh_work - Work struct used to schedule virtio bottom half handling. + * @dd - Virtio device descriptor. + * @dc - Virtio device control fields. + * @list - List of Virtio devices. + * @virtio_db - The doorbell used by the card to interrupt the host. + * @virtio_cookie - The cookie returned while requesting interrupts. + */ +struct mic_vdev { + int virtio_id; + wait_queue_head_t waitq; + struct mic_device *mdev; + int poll_wake; + unsigned long out_bytes; + unsigned long in_bytes; + struct mic_vringh mvr[MIC_MAX_VRINGS]; + struct work_struct virtio_bh_work; + struct mic_device_desc *dd; + struct mic_device_ctrl *dc; + struct list_head list; + int virtio_db; + struct mic_irq *virtio_cookie; +}; + +void mic_virtio_uninit(struct mic_device *mdev); +int mic_virtio_add_device(struct mic_vdev *mvdev, + void __user *argp); +void mic_virtio_del_device(struct mic_vdev *mvdev); +int mic_virtio_config_change(struct mic_vdev *mvdev, + void __user *argp); +int mic_virtio_copy_desc(struct mic_vdev *mvdev, + struct mic_copy_desc *request); +void mic_virtio_reset_devices(struct mic_device *mdev); +void mic_bh_handler(struct work_struct *work); + +/* Helper API to obtain the MIC PCIe device */ +static inline struct device *mic_dev(struct mic_vdev *mvdev) +{ + return mvdev->mdev->sdev->parent; +} + +/* Helper API to check if a virtio device is initialized */ +static inline int mic_vdev_inited(struct mic_vdev *mvdev) +{ + /* Device has not been created yet */ + if (!mvdev->dd || !mvdev->dd->type) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -EINVAL); + return -EINVAL; + } + + /* Device has been removed/deleted */ + if (mvdev->dd->type == -1) { + dev_err(mic_dev(mvdev), "%s %d err %d\n", + __func__, __LINE__, -ENODEV); + return -ENODEV; + } + + return 0; +} + +/* Helper API to check if a virtio device is running */ +static inline bool mic_vdevup(struct mic_vdev *mvdev) +{ + return !!mvdev->dd->status; +} +#endif diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index d37263fbdca7..33d2b8fe166d 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -242,6 +242,7 @@ header-y += mei.h header-y += mempolicy.h header-y += meye.h header-y += mic_common.h +header-y += mic_ioctl.h header-y += mii.h header-y += minix_fs.h header-y += mman.h diff --git a/include/uapi/linux/mic_common.h b/include/uapi/linux/mic_common.h index a9091e529183..364ac751bd04 100644 --- a/include/uapi/linux/mic_common.h +++ b/include/uapi/linux/mic_common.h @@ -21,7 +21,60 @@ #ifndef __MIC_COMMON_H_ #define __MIC_COMMON_H_ -#include +#include + +#ifndef __KERNEL__ +#define ALIGN(a, x) (((a) + (x) - 1) & ~((x) - 1)) +#define __aligned(x) __attribute__ ((aligned(x))) +#endif + +#define mic_aligned_size(x) ALIGN(sizeof(x), 8) + +/** + * struct mic_device_desc: Virtio device information shared between the + * virtio driver and userspace backend + * + * @type: Device type: console/network/disk etc. Type 0/-1 terminates. + * @num_vq: Number of virtqueues. + * @feature_len: Number of bytes of feature bits. Multiply by 2: one for + host features and one for guest acknowledgements. + * @config_len: Number of bytes of the config array after virtqueues. + * @status: A status byte, written by the Guest. + * @config: Start of the following variable length config. + */ +struct mic_device_desc { + __s8 type; + __u8 num_vq; + __u8 feature_len; + __u8 config_len; + __u8 status; + __u64 config[0]; +} __aligned(8); + +/** + * struct mic_device_ctrl: Per virtio device information in the device page + * used internally by the host and card side drivers. + * + * @vdev: Used for storing MIC vdev information by the guest. + * @config_change: Set to 1 by host when a config change is requested. + * @vdev_reset: Set to 1 by guest to indicate virtio device has been reset. + * @guest_ack: Set to 1 by guest to ack a command. + * @host_ack: Set to 1 by host to ack a command. + * @used_address_updated: Set to 1 by guest when the used address should be + * updated. + * @c2h_vdev_db: The doorbell number to be used by guest. Set by host. + * @h2c_vdev_db: The doorbell number to be used by host. Set by guest. + */ +struct mic_device_ctrl { + __u64 vdev; + __u8 config_change; + __u8 vdev_reset; + __u8 guest_ack; + __u8 host_ack; + __u8 used_address_updated; + __s8 c2h_vdev_db; + __s8 h2c_vdev_db; +} __aligned(8); /** * struct mic_bootparam: Virtio device independent information in device page @@ -42,6 +95,117 @@ struct mic_bootparam { __u8 shutdown_card; } __aligned(8); +/** + * struct mic_device_page: High level representation of the device page + * + * @bootparam: The bootparam structure is used for sharing information and + * status updates between MIC host and card drivers. + * @desc: Array of MIC virtio device descriptors. + */ +struct mic_device_page { + struct mic_bootparam bootparam; + struct mic_device_desc desc[0]; +}; +/** + * struct mic_vqconfig: This is how we expect the device configuration field + * for a virtqueue to be laid out in config space. + * + * @address: Guest/MIC physical address of the virtio ring + * (avail and desc rings) + * @used_address: Guest/MIC physical address of the used ring + * @num: The number of entries in the virtio_ring + */ +struct mic_vqconfig { + __u64 address; + __u64 used_address; + __u16 num; +} __aligned(8); + +/* + * The alignment to use between consumer and producer parts of vring. + * This is pagesize for historical reasons. + */ +#define MIC_VIRTIO_RING_ALIGN 4096 + +#define MIC_MAX_VRINGS 4 +#define MIC_VRING_ENTRIES 128 + +/* + * Max vring entries (power of 2) to ensure desc and avail rings + * fit in a single page + */ +#define MIC_MAX_VRING_ENTRIES 128 + +/** + * Max size of the desc block in bytes: includes: + * - struct mic_device_desc + * - struct mic_vqconfig (num_vq of these) + * - host and guest features + * - virtio device config space + */ +#define MIC_MAX_DESC_BLK_SIZE 256 + +/** + * struct _mic_vring_info - Host vring info exposed to userspace backend + * for the avail index and magic for the card. + * + * @avail_idx: host avail idx + * @magic: A magic debug cookie. + */ +struct _mic_vring_info { + __u16 avail_idx; + int magic; +}; + +/** + * struct mic_vring - Vring information. + * + * @vr: The virtio ring. + * @info: Host vring information exposed to the userspace backend for the + * avail index and magic for the card. + * @va: The va for the buffer allocated for vr and info. + * @len: The length of the buffer required for allocating vr and info. + */ +struct mic_vring { + struct vring vr; + struct _mic_vring_info *info; + void *va; + int len; +}; + +#define mic_aligned_desc_size(d) ALIGN(mic_desc_size(d), 8) + +#ifndef INTEL_MIC_CARD +static inline unsigned mic_desc_size(const struct mic_device_desc *desc) +{ + return mic_aligned_size(*desc) + + desc->num_vq * mic_aligned_size(struct mic_vqconfig) + + desc->feature_len * 2 + + desc->config_len; +} + +static inline struct mic_vqconfig * +mic_vq_config(const struct mic_device_desc *desc) +{ + return (struct mic_vqconfig *)(desc + 1); +} + +static inline __u8 *mic_vq_features(const struct mic_device_desc *desc) +{ + return (__u8 *)(mic_vq_config(desc) + desc->num_vq); +} + +static inline __u8 *mic_vq_configspace(const struct mic_device_desc *desc) +{ + return mic_vq_features(desc) + desc->feature_len * 2; +} +static inline unsigned mic_total_desc_size(struct mic_device_desc *desc) +{ + return mic_aligned_desc_size(desc) + + mic_aligned_size(struct mic_device_ctrl); +} +#endif + /* Device page size */ #define MIC_DP_SIZE 4096 diff --git a/include/uapi/linux/mic_ioctl.h b/include/uapi/linux/mic_ioctl.h new file mode 100644 index 000000000000..0e6cbf3e5292 --- /dev/null +++ b/include/uapi/linux/mic_ioctl.h @@ -0,0 +1,74 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 Host driver. + * + */ +#ifndef _MIC_IOCTL_H_ +#define _MIC_IOCTL_H_ + +/* + * mic_copy - MIC virtio descriptor copy. + * + * @iov: An array of IOVEC structures containing user space buffers. + * @iovcnt: Number of IOVEC structures in iov. + * @vr_idx: The vring index. + * @update_used: A non zero value results in used index being updated. + * @out_len: The aggregate of the total length written to or read from + * the virtio device. + */ +struct mic_copy_desc { +#ifdef __KERNEL__ + struct iovec __user *iov; +#else + struct iovec *iov; +#endif + int iovcnt; + __u8 vr_idx; + __u8 update_used; + __u32 out_len; +}; + +/* + * Add a new virtio device + * The (struct mic_device_desc *) pointer points to a device page entry + * for the virtio device consisting of: + * - struct mic_device_desc + * - struct mic_vqconfig (num_vq of these) + * - host and guest features + * - virtio device config space + * The total size referenced by the pointer should equal the size returned + * by desc_size() in mic_common.h + */ +#define MIC_VIRTIO_ADD_DEVICE _IOWR('s', 1, struct mic_device_desc *) + +/* + * Copy the number of entries in the iovec and update the used index + * if requested by the user. + */ +#define MIC_VIRTIO_COPY_DESC _IOWR('s', 2, struct mic_copy_desc *) + +/* + * Notify virtio device of a config change + * The (__u8 *) pointer points to config space values for the device + * as they should be written into the device page. The total size + * referenced by the pointer should equal the config_len field of struct + * mic_device_desc. + */ +#define MIC_VIRTIO_CONFIG_CHANGE _IOWR('s', 5, __u8 *) + +#endif -- cgit v1.2.3 From 2ce585286d217c1c753bec29ea6f52080a05ced5 Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Fri, 27 Sep 2013 09:49:31 -0700 Subject: misc: mic: host driver sysfs cleanups. + Use DEVICE_ATTR_RO/RW instead of DEVICE_ATTR + Use ATTRIBUTE_GROUPS These changes were suggested by Greg Kroah-Hartman during the code review @ https://lkml.org/lkml/2013/9/6/13 Reported-by: Greg Kroah-Hartman Signed-off-by: Ashutosh Dixit Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_sysfs.c | 72 ++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 43 deletions(-) (limited to 'drivers/misc/mic/host') diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c index aaf849945111..bebbae2fa9f8 100644 --- a/drivers/misc/mic/host/mic_sysfs.c +++ b/drivers/misc/mic/host/mic_sysfs.c @@ -65,7 +65,7 @@ void mic_set_state(struct mic_device *mdev, u8 state) } static ssize_t -mic_show_family(struct device *dev, struct device_attribute *attr, char *buf) +family_show(struct device *dev, struct device_attribute *attr, char *buf) { static const char x100[] = "x100"; static const char unknown[] = "Unknown"; @@ -85,10 +85,10 @@ mic_show_family(struct device *dev, struct device_attribute *attr, char *buf) } return scnprintf(buf, PAGE_SIZE, "%s\n", card); } -static DEVICE_ATTR(family, S_IRUGO, mic_show_family, NULL); +static DEVICE_ATTR_RO(family); static ssize_t -mic_show_stepping(struct device *dev, struct device_attribute *attr, char *buf) +stepping_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); char *string = "??"; @@ -114,10 +114,10 @@ mic_show_stepping(struct device *dev, struct device_attribute *attr, char *buf) } return scnprintf(buf, PAGE_SIZE, "%s\n", string); } -static DEVICE_ATTR(stepping, S_IRUGO, mic_show_stepping, NULL); +static DEVICE_ATTR_RO(stepping); static ssize_t -mic_show_state(struct device *dev, struct device_attribute *attr, char *buf) +state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -129,7 +129,7 @@ mic_show_state(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -mic_store_state(struct device *dev, struct device_attribute *attr, +state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { int rc = 0; @@ -160,9 +160,9 @@ mic_store_state(struct device *dev, struct device_attribute *attr, done: return count; } -static DEVICE_ATTR(state, S_IRUGO|S_IWUSR, mic_show_state, mic_store_state); +static DEVICE_ATTR_RW(state); -static ssize_t mic_show_shutdown_status(struct device *dev, +static ssize_t shutdown_status_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -173,11 +173,10 @@ static ssize_t mic_show_shutdown_status(struct device *dev, return scnprintf(buf, PAGE_SIZE, "%s\n", mic_shutdown_status_string[mdev->shutdown_status]); } -static DEVICE_ATTR(shutdown_status, S_IRUGO|S_IWUSR, - mic_show_shutdown_status, NULL); +static DEVICE_ATTR_RO(shutdown_status); static ssize_t -mic_show_cmdline(struct device *dev, struct device_attribute *attr, char *buf) +cmdline_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); char *cmdline; @@ -193,7 +192,7 @@ mic_show_cmdline(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -mic_store_cmdline(struct device *dev, struct device_attribute *attr, +cmdline_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -220,11 +219,10 @@ unlock: mutex_unlock(&mdev->mic_mutex); return count; } -static DEVICE_ATTR(cmdline, S_IRUGO | S_IWUSR, - mic_show_cmdline, mic_store_cmdline); +static DEVICE_ATTR_RW(cmdline); static ssize_t -mic_show_firmware(struct device *dev, struct device_attribute *attr, char *buf) +firmware_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); char *firmware; @@ -240,7 +238,7 @@ mic_show_firmware(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -mic_store_firmware(struct device *dev, struct device_attribute *attr, +firmware_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -266,11 +264,10 @@ unlock: mutex_unlock(&mdev->mic_mutex); return count; } -static DEVICE_ATTR(firmware, S_IRUGO | S_IWUSR, - mic_show_firmware, mic_store_firmware); +static DEVICE_ATTR_RW(firmware); static ssize_t -mic_show_ramdisk(struct device *dev, struct device_attribute *attr, char *buf) +ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); char *ramdisk; @@ -286,7 +283,7 @@ mic_show_ramdisk(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -mic_store_ramdisk(struct device *dev, struct device_attribute *attr, +ramdisk_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -313,11 +310,10 @@ unlock: mutex_unlock(&mdev->mic_mutex); return count; } -static DEVICE_ATTR(ramdisk, S_IRUGO | S_IWUSR, - mic_show_ramdisk, mic_store_ramdisk); +static DEVICE_ATTR_RW(ramdisk); static ssize_t -mic_show_bootmode(struct device *dev, struct device_attribute *attr, char *buf) +bootmode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); char *bootmode; @@ -333,7 +329,7 @@ mic_show_bootmode(struct device *dev, struct device_attribute *attr, char *buf) } static ssize_t -mic_store_bootmode(struct device *dev, struct device_attribute *attr, +bootmode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -363,11 +359,10 @@ unlock: mutex_unlock(&mdev->mic_mutex); return count; } -static DEVICE_ATTR(bootmode, S_IRUGO | S_IWUSR, - mic_show_bootmode, mic_store_bootmode); +static DEVICE_ATTR_RW(bootmode); static ssize_t -mic_show_log_buf_addr(struct device *dev, struct device_attribute *attr, +log_buf_addr_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -379,7 +374,7 @@ mic_show_log_buf_addr(struct device *dev, struct device_attribute *attr, } static ssize_t -mic_store_log_buf_addr(struct device *dev, struct device_attribute *attr, +log_buf_addr_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -398,11 +393,10 @@ mic_store_log_buf_addr(struct device *dev, struct device_attribute *attr, exit: return ret; } -static DEVICE_ATTR(log_buf_addr, S_IRUGO | S_IWUSR, - mic_show_log_buf_addr, mic_store_log_buf_addr); +static DEVICE_ATTR_RW(log_buf_addr); static ssize_t -mic_show_log_buf_len(struct device *dev, struct device_attribute *attr, +log_buf_len_show(struct device *dev, struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -414,7 +408,7 @@ mic_show_log_buf_len(struct device *dev, struct device_attribute *attr, } static ssize_t -mic_store_log_buf_len(struct device *dev, struct device_attribute *attr, +log_buf_len_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -433,8 +427,7 @@ mic_store_log_buf_len(struct device *dev, struct device_attribute *attr, exit: return ret; } -static DEVICE_ATTR(log_buf_len, S_IRUGO | S_IWUSR, - mic_show_log_buf_len, mic_store_log_buf_len); +static DEVICE_ATTR_RW(log_buf_len); static struct attribute *mic_default_attrs[] = { &dev_attr_family.attr, @@ -451,16 +444,9 @@ static struct attribute *mic_default_attrs[] = { NULL }; -static struct attribute_group mic_attr_group = { - .attrs = mic_default_attrs, -}; - -static const struct attribute_group *__mic_attr_group[] = { - &mic_attr_group, - NULL -}; +ATTRIBUTE_GROUPS(mic_default); void mic_sysfs_init(struct mic_device *mdev) { - mdev->attr_group = __mic_attr_group; + mdev->attr_group = mic_default_groups; } -- cgit v1.2.3 From 4aa79961a50119d6112a160e97d5e6a77656b68c Mon Sep 17 00:00:00 2001 From: Sudeep Dutt Date: Fri, 27 Sep 2013 09:49:42 -0700 Subject: misc: mic: header file cleanups. Dont use same name for header files in different folders. These changes were suggested by Greg Kroah-Hartman during the code review @ https://lkml.org/lkml/2013/9/6/18 Reported-by: Greg Kroah-Hartman Signed-off-by: Ashutosh Dixit Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/card/mic_debugfs.c | 2 +- drivers/misc/mic/card/mic_device.c | 2 +- drivers/misc/mic/card/mic_virtio.c | 2 +- drivers/misc/mic/card/mic_x100.c | 2 +- drivers/misc/mic/common/mic_dev.h | 51 ++++++++++++++++++++++++++++++++++++ drivers/misc/mic/common/mic_device.h | 51 ------------------------------------ drivers/misc/mic/host/mic_boot.c | 2 +- drivers/misc/mic/host/mic_debugfs.c | 2 +- drivers/misc/mic/host/mic_fops.c | 2 +- drivers/misc/mic/host/mic_intr.c | 2 +- drivers/misc/mic/host/mic_main.c | 2 +- drivers/misc/mic/host/mic_smpt.c | 2 +- drivers/misc/mic/host/mic_sysfs.c | 2 +- drivers/misc/mic/host/mic_virtio.c | 2 +- drivers/misc/mic/host/mic_x100.c | 2 +- 15 files changed, 64 insertions(+), 64 deletions(-) create mode 100644 drivers/misc/mic/common/mic_dev.h delete mode 100644 drivers/misc/mic/common/mic_device.h (limited to 'drivers/misc/mic/host') diff --git a/drivers/misc/mic/card/mic_debugfs.c b/drivers/misc/mic/card/mic_debugfs.c index 95cf186ff73a..421b3d7911df 100644 --- a/drivers/misc/mic/card/mic_debugfs.c +++ b/drivers/misc/mic/card/mic_debugfs.c @@ -30,7 +30,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" /* Debugfs parent dir */ diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c index 4125217579af..175d11425636 100644 --- a/drivers/misc/mic/card/mic_device.c +++ b/drivers/misc/mic/card/mic_device.c @@ -30,7 +30,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_virtio.h" diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c index 38275c1b9e03..7420c33d8f65 100644 --- a/drivers/misc/mic/card/mic_virtio.c +++ b/drivers/misc/mic/card/mic_virtio.c @@ -40,7 +40,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_virtio.h" #define VIRTIO_SUBCODE_64 0x0D00 diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c index 7cb3469cf684..f491c9163bb2 100644 --- a/drivers/misc/mic/card/mic_x100.c +++ b/drivers/misc/mic/card/mic_x100.c @@ -28,7 +28,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_x100.h" diff --git a/drivers/misc/mic/common/mic_dev.h b/drivers/misc/mic/common/mic_dev.h new file mode 100644 index 000000000000..92999c2bbf82 --- /dev/null +++ b/drivers/misc/mic/common/mic_dev.h @@ -0,0 +1,51 @@ +/* + * Intel MIC Platform Software Stack (MPSS) + * + * Copyright(c) 2013 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 driver. + * + */ +#ifndef __MIC_DEV_H__ +#define __MIC_DEV_H__ + +/** + * struct mic_mw - MIC memory window + * + * @pa: Base physical address. + * @va: Base ioremap'd virtual address. + * @len: Size of the memory window. + */ +struct mic_mw { + phys_addr_t pa; + void __iomem *va; + resource_size_t len; +}; + +/* + * Scratch pad register offsets used by the host to communicate + * device page DMA address to the card. + */ +#define MIC_DPLO_SPAD 14 +#define MIC_DPHI_SPAD 15 + +/* + * These values are supposed to be in the config_change field of the + * device page when the host sends a config change interrupt to the card. + */ +#define MIC_VIRTIO_PARAM_DEV_REMOVE 0x1 +#define MIC_VIRTIO_PARAM_CONFIG_CHANGED 0x2 + +#endif diff --git a/drivers/misc/mic/common/mic_device.h b/drivers/misc/mic/common/mic_device.h deleted file mode 100644 index 01eb74faae6b..000000000000 --- a/drivers/misc/mic/common/mic_device.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Intel MIC Platform Software Stack (MPSS) - * - * Copyright(c) 2013 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 driver. - * - */ -#ifndef __MIC_COMMON_DEVICE_H_ -#define __MIC_COMMON_DEVICE_H_ - -/** - * struct mic_mw - MIC memory window - * - * @pa: Base physical address. - * @va: Base ioremap'd virtual address. - * @len: Size of the memory window. - */ -struct mic_mw { - phys_addr_t pa; - void __iomem *va; - resource_size_t len; -}; - -/* - * Scratch pad register offsets used by the host to communicate - * device page DMA address to the card. - */ -#define MIC_DPLO_SPAD 14 -#define MIC_DPHI_SPAD 15 - -/* - * These values are supposed to be in the config_change field of the - * device page when the host sends a config change interrupt to the card. - */ -#define MIC_VIRTIO_PARAM_DEV_REMOVE 0x1 -#define MIC_VIRTIO_PARAM_CONFIG_CHANGED 0x2 - -#endif diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index fd9ff6d3784e..60c54d5c43c2 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -22,7 +22,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" #include "mic_virtio.h" diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c index e22fb7bbbb98..98a61b4319f4 100644 --- a/drivers/misc/mic/host/mic_debugfs.c +++ b/drivers/misc/mic/host/mic_debugfs.c @@ -23,7 +23,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" #include "mic_virtio.h" diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c index 661469ad339d..e699c80a8c0a 100644 --- a/drivers/misc/mic/host/mic_fops.c +++ b/drivers/misc/mic/host/mic_fops.c @@ -21,7 +21,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_fops.h" #include "mic_virtio.h" diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c index cdae3141dbb9..71a7521cf1a4 100644 --- a/drivers/misc/mic/host/mic_intr.c +++ b/drivers/misc/mic/host/mic_intr.c @@ -21,7 +21,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" /* diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index a8965d496e84..ca06aa9b7114 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -28,7 +28,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_x100.h" #include "mic_smpt.h" diff --git a/drivers/misc/mic/host/mic_smpt.c b/drivers/misc/mic/host/mic_smpt.c index a3462076bc57..003d02b212be 100644 --- a/drivers/misc/mic/host/mic_smpt.c +++ b/drivers/misc/mic/host/mic_smpt.c @@ -20,7 +20,7 @@ */ #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c index bebbae2fa9f8..029a4f31bee7 100644 --- a/drivers/misc/mic/host/mic_sysfs.c +++ b/drivers/misc/mic/host/mic_sysfs.c @@ -21,7 +21,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" /* diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c index be2a1f06c4ca..9e0456fb1ea8 100644 --- a/drivers/misc/mic/host/mic_virtio.c +++ b/drivers/misc/mic/host/mic_virtio.c @@ -23,7 +23,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_smpt.h" #include "mic_virtio.h" diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index a12ae5c8844d..3a0d660bad4a 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -24,7 +24,7 @@ #include #include -#include "../common/mic_device.h" +#include "../common/mic_dev.h" #include "mic_device.h" #include "mic_x100.h" #include "mic_smpt.h" -- cgit v1.2.3 From ced2c60fb5024a5cf5c33cb573b3d6a66d738f36 Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Fri, 27 Sep 2013 09:49:53 -0700 Subject: misc: mic: cleanups for "--strict" checkpatch. These changes were mostly authored by Joe Perches @ https://lkml.org/lkml/2013/9/5/602 Reported-by: Joe Perches Signed-off-by: Ashutosh Dixit Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Signed-off-by: Greg Kroah-Hartman --- Documentation/mic/mpssd/mpssd.c | 65 ++++++++++++++------------- Documentation/mic/mpssd/sysfs.c | 4 +- drivers/misc/mic/card/mic_device.c | 1 - drivers/misc/mic/card/mic_virtio.c | 17 ++++--- drivers/misc/mic/card/mic_x100.c | 4 +- drivers/misc/mic/host/mic_debugfs.c | 90 ++++++++++++++++++------------------- drivers/misc/mic/host/mic_fops.c | 6 +-- drivers/misc/mic/host/mic_intr.c | 34 +++++++------- drivers/misc/mic/host/mic_smpt.c | 14 +++--- drivers/misc/mic/host/mic_sysfs.c | 20 ++++----- drivers/misc/mic/host/mic_virtio.c | 37 +++++++-------- drivers/misc/mic/host/mic_x100.c | 33 +++++++------- 12 files changed, 158 insertions(+), 167 deletions(-) (limited to 'drivers/misc/mic/host') diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c index 8064804cdac3..f9327a29ae4c 100644 --- a/Documentation/mic/mpssd/mpssd.c +++ b/Documentation/mic/mpssd/mpssd.c @@ -246,7 +246,7 @@ static int tun_alloc(struct mic_info *mic, char *dev) if (*dev) strncpy(ifr.ifr_name, dev, IFNAMSIZ); - err = ioctl(fd, TUNSETIFF, (void *) &ifr); + err = ioctl(fd, TUNSETIFF, (void *)&ifr); if (err < 0) { mpsslog("%s %s %d TUNSETIFF failed %s\n", mic->name, __func__, __LINE__, strerror(errno)); @@ -363,7 +363,7 @@ static inline void verify_out_len(struct mic_info *mic, { if (copy->out_len != sum_iovec_len(copy)) { mpsslog("%s %s %d BUG copy->out_len 0x%x len 0x%x\n", - mic->name, __func__, __LINE__, + mic->name, __func__, __LINE__, copy->out_len, sum_iovec_len(copy)); assert(copy->out_len == sum_iovec_len(copy)); } @@ -372,7 +372,7 @@ static inline void verify_out_len(struct mic_info *mic, /* Display an iovec */ static void disp_iovec(struct mic_info *mic, struct mic_copy_desc *copy, - const char *s, int line) + const char *s, int line) { int i; @@ -401,7 +401,7 @@ static inline void txrx_prepare(int type, bool tx, struct mic_vring *vr, /* Central API which triggers the copies */ static int mic_virtio_copy(struct mic_info *mic, int fd, - struct mic_vring *vr, struct mic_copy_desc *copy) + struct mic_vring *vr, struct mic_copy_desc *copy) { int ret; @@ -440,7 +440,7 @@ init_vr(struct mic_info *mic, int fd, int type, vr0->info = vr0->va + vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN); vring_init(&vr0->vr, - MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN); + MIC_VRING_ENTRIES, vr0->va, MIC_VIRTIO_RING_ALIGN); mpsslog("%s %s vr0 %p vr0->info %p vr_size 0x%x vring 0x%x ", __func__, mic->name, vr0->va, vr0->info, vr_size, vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); @@ -453,7 +453,7 @@ init_vr(struct mic_info *mic, int fd, int type, vr1->info = vr1->va + vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN); vring_init(&vr1->vr, - MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN); + MIC_VRING_ENTRIES, vr1->va, MIC_VIRTIO_RING_ALIGN); mpsslog("%s %s vr1 %p vr1->info %p vr_size 0x%x vring 0x%x ", __func__, mic->name, vr1->va, vr1->info, vr_size, vring_size(MIC_VRING_ENTRIES, MIC_VIRTIO_RING_ALIGN)); @@ -551,7 +551,7 @@ virtio_net(void *arg) net_poll[NET_FD_TUN].events = POLLIN; if (MAP_FAILED == init_vr(mic, mic->mic_net.virtio_net_fd, - VIRTIO_ID_NET, &tx_vr, &rx_vr, + VIRTIO_ID_NET, &tx_vr, &rx_vr, virtnet_dev_page.dd.num_vq)) { mpsslog("%s init_vr failed %s\n", mic->name, strerror(errno)); @@ -576,7 +576,7 @@ virtio_net(void *arg) } if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) wait_for_card_driver(mic, mic->mic_net.virtio_net_fd, - VIRTIO_ID_NET); + VIRTIO_ID_NET); /* * Check if there is data to be read from TUN and write to * virtio net fd if there is. @@ -587,7 +587,7 @@ virtio_net(void *arg) copy.iov, copy.iovcnt); if (len > 0) { struct virtio_net_hdr *hdr - = (struct virtio_net_hdr *) vnet_hdr[0]; + = (struct virtio_net_hdr *)vnet_hdr[0]; /* Disable checksums on the card since we are on a reliable PCIe link */ @@ -606,7 +606,7 @@ virtio_net(void *arg) #endif spin_for_descriptors(mic, &tx_vr); txrx_prepare(VIRTIO_ID_NET, 1, &tx_vr, ©, - len); + len); err = mic_virtio_copy(mic, mic->mic_net.virtio_net_fd, &tx_vr, @@ -644,7 +644,7 @@ virtio_net(void *arg) le16toh(rx_vr.vr.avail->idx)) { copy.iov = iov1; txrx_prepare(VIRTIO_ID_NET, 0, &rx_vr, ©, - MAX_NET_PKT_SIZE + MAX_NET_PKT_SIZE + sizeof(struct virtio_net_hdr)); err = mic_virtio_copy(mic, @@ -669,7 +669,7 @@ virtio_net(void *arg) verify_out_len(mic, ©); #ifdef DEBUG disp_iovec(mic, copy, __func__, - __LINE__); + __LINE__); mpsslog("%s %s %d ", mic->name, __func__, __LINE__); mpsslog("read from net 0x%lx\n", @@ -686,7 +686,7 @@ virtio_net(void *arg) } else { #ifdef DEBUG disp_iovec(mic, ©, __func__, - __LINE__); + __LINE__); mpsslog("%s %s %d ", mic->name, __func__, __LINE__); @@ -750,13 +750,13 @@ virtio_console(void *arg) err = grantpt(pty_fd); if (err < 0) { mpsslog("can't grant access: %s %s\n", - pts_name, strerror(errno)); + pts_name, strerror(errno)); goto _close_pty; } err = unlockpt(pty_fd); if (err < 0) { mpsslog("can't unlock a pseudoterminal: %s %s\n", - pts_name, strerror(errno)); + pts_name, strerror(errno)); goto _close_pty; } console_poll[MONITOR_FD].fd = pty_fd; @@ -766,7 +766,7 @@ virtio_console(void *arg) console_poll[VIRTIO_CONSOLE_FD].events = POLLIN; if (MAP_FAILED == init_vr(mic, mic->mic_console.virtio_console_fd, - VIRTIO_ID_CONSOLE, &tx_vr, &rx_vr, + VIRTIO_ID_CONSOLE, &tx_vr, &rx_vr, virtcons_dev_page.dd.num_vq)) { mpsslog("%s init_vr failed %s\n", mic->name, strerror(errno)); @@ -787,7 +787,7 @@ virtio_console(void *arg) } if (!(desc->status & VIRTIO_CONFIG_S_DRIVER_OK)) wait_for_card_driver(mic, - mic->mic_console.virtio_console_fd, + mic->mic_console.virtio_console_fd, VIRTIO_ID_CONSOLE); if (console_poll[MONITOR_FD].revents & POLLIN) { @@ -802,7 +802,7 @@ virtio_console(void *arg) #endif spin_for_descriptors(mic, &tx_vr); txrx_prepare(VIRTIO_ID_CONSOLE, 1, &tx_vr, - ©, len); + ©, len); err = mic_virtio_copy(mic, mic->mic_console.virtio_console_fd, @@ -837,7 +837,7 @@ virtio_console(void *arg) le16toh(rx_vr.vr.avail->idx)) { copy.iov = iov1; txrx_prepare(VIRTIO_ID_CONSOLE, 0, &rx_vr, - ©, PAGE_SIZE); + ©, PAGE_SIZE); err = mic_virtio_copy(mic, mic->mic_console.virtio_console_fd, @@ -848,7 +848,7 @@ virtio_console(void *arg) verify_out_len(mic, ©); #ifdef DEBUG disp_iovec(mic, copy, __func__, - __LINE__); + __LINE__); mpsslog("%s %s %d ", mic->name, __func__, __LINE__); mpsslog("read from net 0x%lx\n", @@ -865,7 +865,7 @@ virtio_console(void *arg) } else { #ifdef DEBUG disp_iovec(mic, copy, __func__, - __LINE__); + __LINE__); mpsslog("%s %s %d ", mic->name, __func__, __LINE__); @@ -1033,7 +1033,8 @@ start_virtblk(struct mic_info *mic, struct mic_vring *vring) } add_virtio_device(mic, &virtblk_dev_page.dd); if (MAP_FAILED == init_vr(mic, mic->mic_virtblk.virtio_block_fd, - VIRTIO_ID_BLOCK, vring, NULL, virtblk_dev_page.dd.num_vq)) { + VIRTIO_ID_BLOCK, vring, NULL, + virtblk_dev_page.dd.num_vq)) { mpsslog("%s init_vr failed %s\n", mic->name, strerror(errno)); return false; @@ -1060,7 +1061,7 @@ header_error_check(struct vring_desc *desc) { if (le32toh(desc->len) != sizeof(struct virtio_blk_outhdr)) { mpsslog("%s() %d: length is not sizeof(virtio_blk_outhd)\n", - __func__, __LINE__); + __func__, __LINE__); return -EIO; } if (!(le16toh(desc->flags) & VRING_DESC_F_NEXT)) { @@ -1132,7 +1133,7 @@ write_status(int fd, __u8 *status) static void * virtio_block(void *arg) { - struct mic_info *mic = (struct mic_info *) arg; + struct mic_info *mic = (struct mic_info *)arg; int ret; struct pollfd block_poll; struct mic_vring vring; @@ -1219,8 +1220,8 @@ virtio_block(void *arg) status = 0; fos = mic->mic_virtblk.backend_addr + (hdr.sector * SECTOR_SIZE); - buffer_desc_idx = desc_idx = - next_desc(desc); + buffer_desc_idx = next_desc(desc); + desc_idx = buffer_desc_idx; for (desc = &vring.vr.desc[buffer_desc_idx]; desc->flags & VRING_DESC_F_NEXT; desc_idx = next_desc(desc), @@ -1255,7 +1256,7 @@ virtio_block(void *arg) iovec, piov - iovec); if (ret < 0 && - status != 0) + status != 0) status = ret; } /* write status and update used pointer */ @@ -1391,7 +1392,7 @@ mic_config(void *arg) char value[4096]; snprintf(pathname, PATH_MAX - 1, "%s/%s/%s", - MICSYSFSDIR, mic->name, "state"); + MICSYSFSDIR, mic->name, "state"); fd = open(pathname, O_RDONLY); if (fd < 0) { @@ -1560,22 +1561,22 @@ init_mic(struct mic_info *mic) virtio_console, mic); if (err) mpsslog("%s virtcons pthread_create failed %s\n", - mic->name, strerror(err)); + mic->name, strerror(err)); err = pthread_create(&mic->mic_net.net_thread, NULL, virtio_net, mic); if (err) mpsslog("%s virtnet pthread_create failed %s\n", - mic->name, strerror(err)); + mic->name, strerror(err)); err = pthread_create(&mic->mic_virtblk.block_thread, NULL, virtio_block, mic); if (err) mpsslog("%s virtblk pthread_create failed %s\n", - mic->name, strerror(err)); + mic->name, strerror(err)); sigemptyset(&act.sa_mask); err = sigaction(SIGUSR1, &act, NULL); if (err) mpsslog("%s sigaction SIGUSR1 failed %s\n", - mic->name, strerror(errno)); + mic->name, strerror(errno)); while (1) sleep(60); case -1: diff --git a/Documentation/mic/mpssd/sysfs.c b/Documentation/mic/mpssd/sysfs.c index 11de72b63386..8dd326936083 100644 --- a/Documentation/mic/mpssd/sysfs.c +++ b/Documentation/mic/mpssd/sysfs.c @@ -35,7 +35,7 @@ readsysfs(char *dir, char *entry) snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry); else snprintf(filename, PATH_MAX, - "%s/%s/%s", MICSYSFSDIR, dir, entry); + "%s/%s/%s", MICSYSFSDIR, dir, entry); fd = open(filename, O_RDONLY); if (fd < 0) { @@ -75,7 +75,7 @@ setsysfs(char *dir, char *entry, char *value) snprintf(filename, PATH_MAX, "%s/%s", MICSYSFSDIR, entry); else snprintf(filename, PATH_MAX, "%s/%s/%s", - MICSYSFSDIR, dir, entry); + MICSYSFSDIR, dir, entry); oldvalue = readsysfs(dir, entry); diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c index 175d11425636..d0980ff96833 100644 --- a/drivers/misc/mic/card/mic_device.c +++ b/drivers/misc/mic/card/mic_device.c @@ -167,7 +167,6 @@ struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data), return (struct mic_irq *)cookie; err: return ERR_PTR(rc); - } /** diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c index 7420c33d8f65..914cc9b2caad 100644 --- a/drivers/misc/mic/card/mic_virtio.c +++ b/drivers/misc/mic/card/mic_virtio.c @@ -103,7 +103,7 @@ static void mic_finalize_features(struct virtio_device *vdev) for (i = 0; i < bits; i++) { if (test_bit(i, vdev->features)) iowrite8(ioread8(&out_features[i / 8]) | (1 << (i % 8)), - &out_features[i / 8]); + &out_features[i / 8]); } } @@ -197,10 +197,9 @@ static void mic_notify(struct virtqueue *vq) static void mic_del_vq(struct virtqueue *vq, int n) { struct mic_vdev *mvdev = to_micvdev(vq->vdev); - struct vring *vr = (struct vring *) (vq + 1); + struct vring *vr = (struct vring *)(vq + 1); - free_pages((unsigned long) vr->used, - get_order(mvdev->used_size[n])); + free_pages((unsigned long) vr->used, get_order(mvdev->used_size[n])); vring_del_virtqueue(vq); mic_card_unmap(mvdev->mdev, mvdev->vr[n]); mvdev->vr[n] = NULL; @@ -274,8 +273,8 @@ static struct virtqueue *mic_find_vq(struct virtio_device *vdev, /* Allocate and reassign used ring now */ mvdev->used_size[index] = PAGE_ALIGN(sizeof(__u16) * 3 + sizeof(struct vring_used_elem) * config.num); - used = (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(mvdev->used_size[index])); + used = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, + get_order(mvdev->used_size[index])); if (!used) { err = -ENOMEM; dev_err(mic_dev(mvdev), "%s %d err %d\n", @@ -291,7 +290,7 @@ static struct virtqueue *mic_find_vq(struct virtio_device *vdev, * vring_new_virtqueue() would ensure that * (&vq->vring == (struct vring *) (&vq->vq + 1)); */ - vr = (struct vring *) (vq + 1); + vr = (struct vring *)(vq + 1); vr->used = used; vq->priv = mvdev; @@ -544,7 +543,7 @@ static void mic_scan_devices(struct mic_driver *mdrv, bool remove) if (dev) { if (remove) iowrite8(MIC_VIRTIO_PARAM_DEV_REMOVE, - &dc->config_change); + &dc->config_change); put_device(dev); mic_handle_config_change(d, i, mdrv); ret = mic_remove_device(d, i, mdrv); @@ -559,7 +558,7 @@ static void mic_scan_devices(struct mic_driver *mdrv, bool remove) /* new device */ dev_dbg(mdrv->dev, "%s %d Adding new virtio device %p\n", - __func__, __LINE__, d); + __func__, __LINE__, d); if (!remove) mic_add_device(d, i, mdrv); } diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c index f491c9163bb2..2868945c9a4d 100644 --- a/drivers/misc/mic/card/mic_x100.c +++ b/drivers/misc/mic/card/mic_x100.c @@ -66,8 +66,8 @@ void mic_send_intr(struct mic_device *mdev, int doorbell) /* Ensure that the interrupt is ordered w.r.t previous stores. */ wmb(); mic_mmio_write(mw, MIC_X100_SBOX_SDBIC0_DBREQ_BIT, - MIC_X100_SBOX_BASE_ADDRESS + - (MIC_X100_SBOX_SDBIC0 + (4 * doorbell))); + MIC_X100_SBOX_BASE_ADDRESS + + (MIC_X100_SBOX_SDBIC0 + (4 * doorbell))); } /** diff --git a/drivers/misc/mic/host/mic_debugfs.c b/drivers/misc/mic/host/mic_debugfs.c index 98a61b4319f4..028ba5d6fd1c 100644 --- a/drivers/misc/mic/host/mic_debugfs.c +++ b/drivers/misc/mic/host/mic_debugfs.c @@ -103,7 +103,7 @@ static int mic_smpt_show(struct seq_file *s, void *pos) unsigned long flags; seq_printf(s, "MIC %-2d |%-10s| %-14s %-10s\n", - mdev->id, "SMPT entry", "SW DMA addr", "RefCount"); + mdev->id, "SMPT entry", "SW DMA addr", "RefCount"); seq_puts(s, "====================================================\n"); if (mdev->smpt) { @@ -111,8 +111,8 @@ static int mic_smpt_show(struct seq_file *s, void *pos) spin_lock_irqsave(&smpt_info->smpt_lock, flags); for (i = 0; i < smpt_info->info.num_reg; i++) { seq_printf(s, "%9s|%-10d| %-#14llx %-10lld\n", - " ", i, smpt_info->entry[i].dma_addr, - smpt_info->entry[i].ref_count); + " ", i, smpt_info->entry[i].dma_addr, + smpt_info->entry[i].ref_count); } spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); } @@ -203,17 +203,17 @@ static int mic_dp_show(struct seq_file *s, void *pos) int i, j; seq_printf(s, "Bootparam: magic 0x%x\n", - bootparam->magic); + bootparam->magic); seq_printf(s, "Bootparam: h2c_shutdown_db %d\n", - bootparam->h2c_shutdown_db); + bootparam->h2c_shutdown_db); seq_printf(s, "Bootparam: h2c_config_db %d\n", - bootparam->h2c_config_db); + bootparam->h2c_config_db); seq_printf(s, "Bootparam: c2h_shutdown_db %d\n", - bootparam->c2h_shutdown_db); + bootparam->c2h_shutdown_db); seq_printf(s, "Bootparam: shutdown_status %d\n", - bootparam->shutdown_status); + bootparam->shutdown_status); seq_printf(s, "Bootparam: shutdown_card %d\n", - bootparam->shutdown_card); + bootparam->shutdown_card); for (i = sizeof(*bootparam); i < MIC_DP_SIZE; i += mic_total_desc_size(d)) { @@ -239,10 +239,10 @@ static int mic_dp_show(struct seq_file *s, void *pos) seq_printf(s, "address 0x%llx ", vqconfig->address); seq_printf(s, "num %d ", vqconfig->num); seq_printf(s, "used address 0x%llx\n", - vqconfig->used_address); + vqconfig->used_address); } - features = (__u32 *) mic_vq_features(d); + features = (__u32 *)mic_vq_features(d); seq_printf(s, "Features: Host 0x%x ", features[0]); seq_printf(s, "Guest 0x%x\n", features[1]); @@ -256,7 +256,7 @@ static int mic_dp_show(struct seq_file *s, void *pos) seq_printf(s, "Guest Ack %d ", dc->guest_ack); seq_printf(s, "Host ack %d\n", dc->host_ack); seq_printf(s, "Used address updated %d ", - dc->used_address_updated); + dc->used_address_updated); seq_printf(s, "Vdev 0x%llx\n", dc->vdev); seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db); seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db); @@ -294,10 +294,10 @@ static int mic_vdev_info_show(struct seq_file *s, void *unused) list_for_each_safe(pos, tmp, &mdev->vdev_list) { mvdev = list_entry(pos, struct mic_vdev, list); seq_printf(s, "VDEV type %d state %s in %ld out %ld\n", - mvdev->virtio_id, - mic_vdevup(mvdev) ? "UP" : "DOWN", - mvdev->in_bytes, - mvdev->out_bytes); + mvdev->virtio_id, + mic_vdevup(mvdev) ? "UP" : "DOWN", + mvdev->in_bytes, + mvdev->out_bytes); for (i = 0; i < MIC_MAX_VRINGS; i++) { struct vring_desc *desc; struct vring_avail *avail; @@ -309,38 +309,38 @@ static int mic_vdev_info_show(struct seq_file *s, void *unused) continue; desc = vrh->vring.desc; seq_printf(s, "vring i %d avail_idx %d", - i, mvr->vring.info->avail_idx & (num - 1)); + i, mvr->vring.info->avail_idx & (num - 1)); seq_printf(s, " vring i %d avail_idx %d\n", - i, mvr->vring.info->avail_idx); + i, mvr->vring.info->avail_idx); seq_printf(s, "vrh i %d weak_barriers %d", - i, vrh->weak_barriers); + i, vrh->weak_barriers); seq_printf(s, " last_avail_idx %d last_used_idx %d", - vrh->last_avail_idx, vrh->last_used_idx); + vrh->last_avail_idx, vrh->last_used_idx); seq_printf(s, " completed %d\n", vrh->completed); for (j = 0; j < num; j++) { seq_printf(s, "desc[%d] addr 0x%llx len %d", - j, desc->addr, desc->len); + j, desc->addr, desc->len); seq_printf(s, " flags 0x%x next %d\n", - desc->flags, - desc->next); + desc->flags, desc->next); desc++; } avail = vrh->vring.avail; seq_printf(s, "avail flags 0x%x idx %d\n", - avail->flags, avail->idx & (num - 1)); + avail->flags, avail->idx & (num - 1)); seq_printf(s, "avail flags 0x%x idx %d\n", - avail->flags, avail->idx); + avail->flags, avail->idx); for (j = 0; j < num; j++) seq_printf(s, "avail ring[%d] %d\n", - j, avail->ring[j]); + j, avail->ring[j]); used = vrh->vring.used; seq_printf(s, "used flags 0x%x idx %d\n", - used->flags, used->idx & (num - 1)); + used->flags, used->idx & (num - 1)); seq_printf(s, "used flags 0x%x idx %d\n", - used->flags, used->idx); + used->flags, used->idx); for (j = 0; j < num; j++) seq_printf(s, "used ring[%d] id %d len %d\n", - j, used->ring[j].id, used->ring[j].len); + j, used->ring[j].id, + used->ring[j].len); } } mutex_unlock(&mdev->mic_mutex); @@ -389,7 +389,7 @@ static int mic_msi_irq_info_show(struct seq_file *s, void *pos) reg = mdev->intr_ops->read_msi_to_src_map(mdev, entry); seq_printf(s, "%s %-10d %s %-10d MXAR[%d]: %08X\n", - "IRQ:", vector, "Entry:", entry, i, reg); + "IRQ:", vector, "Entry:", entry, i, reg); seq_printf(s, "%-10s", "offset:"); for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) @@ -400,8 +400,8 @@ static int mic_msi_irq_info_show(struct seq_file *s, void *pos) seq_printf(s, "%-10s", "count:"); for (j = (MIC_NUM_OFFSETS - 1); j >= 0; j--) seq_printf(s, "%4d ", - (mdev->irq_info.mic_msi_map[i] & BIT(j)) ? - 1 : 0); + (mdev->irq_info.mic_msi_map[i] & + BIT(j)) ? 1 : 0); seq_puts(s, "\n\n"); } } else { @@ -409,7 +409,6 @@ static int mic_msi_irq_info_show(struct seq_file *s, void *pos) } return 0; - } static int mic_msi_irq_info_debug_open(struct inode *inode, struct file *file) @@ -443,26 +442,23 @@ void mic_create_debug_dir(struct mic_device *mdev) if (!mdev->dbg_dir) return; - debugfs_create_file("log_buf", 0444, mdev->dbg_dir, - mdev, &log_buf_ops); + debugfs_create_file("log_buf", 0444, mdev->dbg_dir, mdev, &log_buf_ops); - debugfs_create_file("smpt", 0444, mdev->dbg_dir, - mdev, &smpt_file_ops); + debugfs_create_file("smpt", 0444, mdev->dbg_dir, mdev, &smpt_file_ops); - debugfs_create_file("soft_reset", 0444, mdev->dbg_dir, - mdev, &soft_reset_ops); + debugfs_create_file("soft_reset", 0444, mdev->dbg_dir, mdev, + &soft_reset_ops); - debugfs_create_file("post_code", 0444, mdev->dbg_dir, - mdev, &post_code_ops); + debugfs_create_file("post_code", 0444, mdev->dbg_dir, mdev, + &post_code_ops); - debugfs_create_file("dp", 0444, mdev->dbg_dir, - mdev, &dp_ops); + debugfs_create_file("dp", 0444, mdev->dbg_dir, mdev, &dp_ops); - debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, - mdev, &vdev_info_ops); + debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, mdev, + &vdev_info_ops); - debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, - mdev, &msi_irq_info_ops); + debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev, + &msi_irq_info_ops); } /** diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c index e699c80a8c0a..8dc6ff16845a 100644 --- a/drivers/misc/mic/host/mic_fops.c +++ b/drivers/misc/mic/host/mic_fops.c @@ -140,9 +140,9 @@ unsigned int mic_poll(struct file *f, poll_table *wait) poll_wait(f, &mvdev->waitq, wait); - if (mic_vdev_inited(mvdev)) + if (mic_vdev_inited(mvdev)) { mask = POLLERR; - else if (mvdev->poll_wake) { + } else if (mvdev->poll_wake) { mvdev->poll_wake = 0; mask = POLLIN | POLLOUT; } @@ -152,7 +152,7 @@ unsigned int mic_poll(struct file *f, poll_table *wait) static inline int mic_query_offset(struct mic_vdev *mvdev, unsigned long offset, - unsigned long *size, unsigned long *pa) + unsigned long *size, unsigned long *pa) { struct mic_device *mdev = mvdev->mdev; unsigned long start = MIC_DP_SIZE; diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c index 71a7521cf1a4..f9c29bc918bc 100644 --- a/drivers/misc/mic/host/mic_intr.c +++ b/drivers/misc/mic/host/mic_intr.c @@ -71,8 +71,8 @@ static irqreturn_t mic_interrupt(int irq, void *dev) /* Return the interrupt offset from the index. Index is 0 based. */ static u16 mic_map_src_to_offset(struct mic_device *mdev, - int intr_src, enum mic_intr_type type) { - + int intr_src, enum mic_intr_type type) +{ if (type >= MIC_NUM_INTR_TYPES) return MIC_NUM_OFFSETS; if (intr_src >= mdev->intr_info->intr_len[type]) @@ -112,7 +112,7 @@ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev, struct mic_intr_cb *intr_cb; unsigned long flags; int rc; - intr_cb = kmalloc(sizeof(struct mic_intr_cb), GFP_KERNEL); + intr_cb = kmalloc(sizeof(*intr_cb), GFP_KERNEL); if (!intr_cb) return ERR_PTR(-ENOMEM); @@ -159,7 +159,7 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) if (intr_cb->cb_id == idx) { list_del(pos); ida_simple_remove(&mdev->irq_info.cb_ida, - intr_cb->cb_id); + intr_cb->cb_id); kfree(intr_cb); spin_unlock_irqrestore( &mdev->irq_info.mic_intr_lock, flags); @@ -182,9 +182,10 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx) static int mic_setup_msix(struct mic_device *mdev, struct pci_dev *pdev) { int rc, i; + int entry_size = sizeof(*mdev->irq_info.msix_entries); - mdev->irq_info.msix_entries = kmalloc(sizeof(struct msix_entry) * - MIC_MIN_MSIX, GFP_KERNEL); + mdev->irq_info.msix_entries = kmalloc_array(MIC_MIN_MSIX, + entry_size, GFP_KERNEL); if (!mdev->irq_info.msix_entries) { rc = -ENOMEM; goto err_nomem1; @@ -231,8 +232,9 @@ static int mic_setup_callbacks(struct mic_device *mdev) { int i; - mdev->irq_info.cb_list = kmalloc(sizeof(struct list_head) * - MIC_NUM_OFFSETS, GFP_KERNEL); + mdev->irq_info.cb_list = kmalloc_array(MIC_NUM_OFFSETS, + sizeof(*mdev->irq_info.cb_list), + GFP_KERNEL); if (!mdev->irq_info.cb_list) return -ENOMEM; @@ -261,7 +263,7 @@ static void mic_release_callbacks(struct mic_device *mdev) if (list_empty(&mdev->irq_info.cb_list[i])) { spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, - flags); + flags); break; } @@ -269,7 +271,7 @@ static void mic_release_callbacks(struct mic_device *mdev) intr_cb = list_entry(pos, struct mic_intr_cb, list); list_del(pos); ida_simple_remove(&mdev->irq_info.cb_ida, - intr_cb->cb_id); + intr_cb->cb_id); kfree(intr_cb); } spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags); @@ -427,8 +429,8 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev, offset = mic_map_src_to_offset(mdev, intr_src, type); if (offset >= MIC_NUM_OFFSETS) { dev_err(mdev->sdev->parent, - "Error mapping index %d to a valid source id.\n", - intr_src); + "Error mapping index %d to a valid source id.\n", + intr_src); rc = -EINVAL; goto err; } @@ -437,7 +439,7 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev, msix = mic_get_available_vector(mdev); if (!msix) { dev_err(mdev->sdev->parent, - "No MSIx vectors available for use.\n"); + "No MSIx vectors available for use.\n"); rc = -ENOSPC; goto err; } @@ -460,7 +462,7 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev, offset, func, data); if (IS_ERR(intr_cb)) { dev_err(mdev->sdev->parent, - "No available callback entries for use\n"); + "No available callback entries for use\n"); rc = PTR_ERR(intr_cb); goto err; } @@ -506,7 +508,7 @@ void mic_free_irq(struct mic_device *mdev, if (mdev->irq_info.num_vectors > 1) { if (entry >= mdev->irq_info.num_vectors) { dev_warn(mdev->sdev->parent, - "entry %d should be < num_irq %d\n", + "entry %d should be < num_irq %d\n", entry, mdev->irq_info.num_vectors); return; } @@ -581,7 +583,7 @@ void mic_free_interrupts(struct mic_device *mdev, struct pci_dev *pdev) for (i = 0; i < mdev->irq_info.num_vectors; i++) { if (mdev->irq_info.mic_msi_map[i]) dev_warn(&pdev->dev, "irq %d may still be in use.\n", - mdev->irq_info.msix_entries[i].vector); + mdev->irq_info.msix_entries[i].vector); } kfree(mdev->irq_info.mic_msi_map); kfree(mdev->irq_info.msix_entries); diff --git a/drivers/misc/mic/host/mic_smpt.c b/drivers/misc/mic/host/mic_smpt.c index 003d02b212be..fae474c4899e 100644 --- a/drivers/misc/mic/host/mic_smpt.c +++ b/drivers/misc/mic/host/mic_smpt.c @@ -84,7 +84,7 @@ static void mic_add_smpt_entry(int spt, s64 *ref, u64 addr, for (i = spt; i < spt + entries; i++, addr += smpt_info->info.page_size) { if (!smpt_info->entry[i].ref_count && - (smpt_info->entry[i].dma_addr != addr)) { + (smpt_info->entry[i].dma_addr != addr)) { mdev->smpt_ops->set(mdev, addr, i); smpt_info->entry[i].dma_addr = addr; } @@ -183,7 +183,7 @@ mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr) if (!mic_is_system_addr(mdev, mic_addr)) { dev_err(mdev->sdev->parent, - "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr); + "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr); return -EINVAL; } spt = mic_sys_addr_to_smpt(mdev, mic_addr); @@ -286,7 +286,7 @@ void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size) smpt_info->entry[i].ref_count -= ref[i - spt]; if (smpt_info->entry[i].ref_count < 0) dev_warn(mdev->sdev->parent, - "ref count for entry %d is negative\n", i); + "ref count for entry %d is negative\n", i); } spin_unlock_irqrestore(&smpt_info->smpt_lock, flags); kfree(ref); @@ -320,7 +320,7 @@ dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size) "mic_map failed dma_addr 0x%llx size 0x%lx\n", dma_addr, size); pci_unmap_single(pdev, dma_addr, - size, PCI_DMA_BIDIRECTIONAL); + size, PCI_DMA_BIDIRECTIONAL); } } return mic_addr; @@ -366,8 +366,8 @@ int mic_smpt_init(struct mic_device *mdev) smpt_info = mdev->smpt; mdev->smpt_ops->init(mdev); - smpt_info->entry = kmalloc(sizeof(struct mic_smpt) - * smpt_info->info.num_reg, GFP_KERNEL); + smpt_info->entry = kmalloc_array(smpt_info->info.num_reg, + sizeof(*smpt_info->entry), GFP_KERNEL); if (!smpt_info->entry) { err = -ENOMEM; goto free_smpt; @@ -412,7 +412,7 @@ void mic_smpt_uninit(struct mic_device *mdev) smpt_info->entry[i].ref_count); if (smpt_info->entry[i].ref_count) dev_warn(mdev->sdev->parent, - "ref count for entry %d is not zero\n", i); + "ref count for entry %d is not zero\n", i); } kfree(smpt_info->entry); kfree(smpt_info); diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c index 029a4f31bee7..75746adfb155 100644 --- a/drivers/misc/mic/host/mic_sysfs.c +++ b/drivers/misc/mic/host/mic_sysfs.c @@ -130,7 +130,7 @@ state_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t state_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { int rc = 0; struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -163,7 +163,7 @@ done: static DEVICE_ATTR_RW(state); static ssize_t shutdown_status_show(struct device *dev, - struct device_attribute *attr, char *buf) + struct device_attribute *attr, char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -193,7 +193,7 @@ cmdline_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t cmdline_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -239,7 +239,7 @@ firmware_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t firmware_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -284,7 +284,7 @@ ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t ramdisk_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -330,7 +330,7 @@ bootmode_show(struct device *dev, struct device_attribute *attr, char *buf) static ssize_t bootmode_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -363,7 +363,7 @@ static DEVICE_ATTR_RW(bootmode); static ssize_t log_buf_addr_show(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -375,7 +375,7 @@ log_buf_addr_show(struct device *dev, struct device_attribute *attr, static ssize_t log_buf_addr_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); int ret; @@ -397,7 +397,7 @@ static DEVICE_ATTR_RW(log_buf_addr); static ssize_t log_buf_len_show(struct device *dev, struct device_attribute *attr, - char *buf) + char *buf) { struct mic_device *mdev = dev_get_drvdata(dev->parent); @@ -409,7 +409,7 @@ log_buf_len_show(struct device *dev, struct device_attribute *attr, static ssize_t log_buf_len_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) + const char *buf, size_t count) { struct mic_device *mdev = dev_get_drvdata(dev->parent); int ret; diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c index 9e0456fb1ea8..0c883cd4f1d1 100644 --- a/drivers/misc/mic/host/mic_virtio.c +++ b/drivers/misc/mic/host/mic_virtio.c @@ -195,7 +195,7 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, MIC_VRINGH_READ, &out_len); if (ret) { dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); + __func__, __LINE__, ret); break; } len -= out_len; @@ -206,7 +206,7 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, !MIC_VRINGH_READ, &out_len); if (ret) { dev_err(mic_dev(mvdev), "%s %d err %d\n", - __func__, __LINE__, ret); + __func__, __LINE__, ret); break; } len -= out_len; @@ -225,8 +225,7 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev, * Update the used ring if a descriptor was available and some data was * copied in/out and the user asked for a used ring update. */ - if (*head != USHRT_MAX && copy->out_len && - copy->update_used) { + if (*head != USHRT_MAX && copy->out_len && copy->update_used) { u32 total = 0; /* Determine the total data consumed */ @@ -367,7 +366,6 @@ void mic_bh_handler(struct work_struct *work) static irqreturn_t mic_virtio_intr_handler(int irq, void *data) { - struct mic_vdev *mvdev = data; struct mic_device *mdev = mvdev->mdev; @@ -394,7 +392,7 @@ int mic_virtio_config_change(struct mic_vdev *mvdev, } if (copy_from_user(mic_vq_configspace(mvdev->dd), - argp, mvdev->dd->config_len)) { + argp, mvdev->dd->config_len)) { dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, -EFAULT); ret = -EFAULT; @@ -438,8 +436,8 @@ static int mic_copy_dp_entry(struct mic_vdev *mvdev, return -EFAULT; } - if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE - || dd.num_vq > MIC_MAX_VRINGS) { + if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE || + dd.num_vq > MIC_MAX_VRINGS) { dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, -EINVAL); return -EINVAL; @@ -503,7 +501,7 @@ static void mic_init_device_ctrl(struct mic_vdev *mvdev, { struct mic_device_ctrl *dc; - dc = mvdev->dc = (void *)devpage + mic_aligned_desc_size(devpage); + dc = (void *)devpage + mic_aligned_desc_size(devpage); dc->config_change = 0; dc->guest_ack = 0; @@ -512,6 +510,7 @@ static void mic_init_device_ctrl(struct mic_vdev *mvdev, dc->used_address_updated = 0; dc->c2h_vdev_db = -1; dc->h2c_vdev_db = -1; + mvdev->dc = dc; } int mic_virtio_add_device(struct mic_vdev *mvdev, @@ -551,7 +550,7 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, sizeof(struct _mic_vring_info)); vr->va = (void *) __get_free_pages(GFP_KERNEL | __GFP_ZERO, - get_order(vr_size)); + get_order(vr_size)); if (!vr->va) { ret = -ENOMEM; dev_err(mic_dev(mvdev), "%s %d err %d\n", @@ -564,8 +563,7 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, vqconfig[i].address = mic_map_single(mdev, vr->va, vr_size); if (mic_map_error(vqconfig[i].address)) { - free_pages((unsigned long)vr->va, - get_order(vr_size)); + free_pages((unsigned long)vr->va, get_order(vr_size)); ret = -ENOMEM; dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, ret); @@ -573,8 +571,7 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, } vqconfig[i].address = cpu_to_le64(vqconfig[i].address); - vring_init(&vr->vr, num, - vr->va, MIC_VIRTIO_RING_ALIGN); + vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN); ret = vringh_init_kern(&mvr->vrh, *(u32 *)mic_vq_features(mvdev->dd), num, false, vr->vr.desc, vr->vr.avail, vr->vr.used); @@ -593,8 +590,8 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, __func__, __LINE__, i, vr->va, vr->info, vr_size); } - snprintf(irqname, sizeof(irqname), - "mic%dvirtio%d", mdev->id, mvdev->virtio_id); + 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); @@ -628,9 +625,9 @@ err: for (j = 0; j < i; j++) { struct mic_vringh *mvr = &mvdev->mvr[j]; mic_unmap_single(mdev, le64_to_cpu(vqconfig[j].address), - mvr->vring.len); + mvr->vring.len); free_pages((unsigned long)mvr->vring.va, - get_order(mvr->vring.len)); + get_order(mvr->vring.len)); } mutex_unlock(&mdev->mic_mutex); return ret; @@ -676,9 +673,9 @@ skip_hot_remove: vringh_kiov_cleanup(&mvr->riov); vringh_kiov_cleanup(&mvr->wiov); mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address), - mvr->vring.len); + mvr->vring.len); free_pages((unsigned long)mvr->vring.va, - get_order(mvr->vring.len)); + get_order(mvr->vring.len)); } list_for_each_safe(pos, tmp, &mdev->vdev_list) { diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c index 3a0d660bad4a..81e9541b784c 100644 --- a/drivers/misc/mic/host/mic_x100.c +++ b/drivers/misc/mic/host/mic_x100.c @@ -46,8 +46,8 @@ mic_x100_write_spad(struct mic_device *mdev, unsigned int idx, u32 val) dev_dbg(mdev->sdev->parent, "Writing 0x%x to scratch pad index %d\n", val, idx); mic_mmio_write(&mdev->mmio, val, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_SPAD0 + idx * 4); + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_SPAD0 + idx * 4); } /** @@ -130,8 +130,8 @@ static void mic_x100_send_sbox_intr(struct mic_device *mdev, { struct mic_mw *mw = &mdev->mmio; u64 apic_icr_offset = MIC_X100_SBOX_APICICR0 + doorbell * 8; - u32 apicicr_low = mic_mmio_read(mw, - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); + u32 apicicr_low = mic_mmio_read(mw, MIC_X100_SBOX_BASE_ADDRESS + + apic_icr_offset); /* for MIC we need to make sure we "hit" the send_icr bit (13) */ apicicr_low = (apicicr_low | (1 << 13)); @@ -139,7 +139,7 @@ static void mic_x100_send_sbox_intr(struct mic_device *mdev, /* Ensure that the interrupt is ordered w.r.t. previous stores. */ wmb(); mic_mmio_write(mw, apicicr_low, - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); } /** @@ -153,7 +153,7 @@ static void mic_x100_send_rdmasr_intr(struct mic_device *mdev, /* Ensure that the interrupt is ordered w.r.t. previous stores. */ wmb(); mic_mmio_write(&mdev->mmio, 0, - MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); + MIC_X100_SBOX_BASE_ADDRESS + rdmasr_offset); } /** @@ -212,7 +212,7 @@ done: */ static void mic_x100_hw_intr_init(struct mic_device *mdev) { - mdev->intr_info = (struct mic_intr_info *) mic_x100_intr_init; + mdev->intr_info = (struct mic_intr_info *)mic_x100_intr_init; } /** @@ -244,7 +244,7 @@ mic_x100_read_msi_to_src_map(struct mic_device *mdev, int idx) */ static void mic_x100_program_msi_to_src_map(struct mic_device *mdev, - int idx, int offset, bool set) + int idx, int offset, bool set) { unsigned long reg; struct mic_mw *mw = &mdev->mmio; @@ -308,12 +308,12 @@ static void mic_x100_send_firmware_intr(struct mic_device *mdev) apicicr_low = (vector | (1 << 13)); mic_mmio_write(mw, mic_x100_get_apic_id(mdev), - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4); + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset + 4); /* Ensure that the interrupt is ordered w.r.t. previous stores. */ wmb(); mic_mmio_write(mw, apicicr_low, - MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); + MIC_X100_SBOX_BASE_ADDRESS + apic_icr_offset); } /** @@ -365,8 +365,7 @@ mic_x100_load_command_line(struct mic_device *mdev, const struct firmware *fw) len += snprintf(buf, CMDLINE_SIZE - len, " mem=%dM", boot_mem); if (mdev->cmdline) - snprintf(buf + len, CMDLINE_SIZE - len, - " %s", mdev->cmdline); + snprintf(buf + len, CMDLINE_SIZE - len, " %s", mdev->cmdline); memcpy_toio(cmd_line_va, buf, strlen(buf) + 1); kfree(buf); return 0; @@ -397,8 +396,7 @@ mic_x100_load_ramdisk(struct mic_device *mdev) * Typically the bootaddr for card OS is 64M * so copy over the ramdisk @ 128M. */ - memcpy_toio(mdev->aper.va + (mdev->bootaddr << 1), - fw->data, fw->size); + memcpy_toio(mdev->aper.va + (mdev->bootaddr << 1), fw->data, fw->size); iowrite32(cpu_to_le32(mdev->bootaddr << 1), &bp->hdr.ramdisk_image); iowrite32(cpu_to_le32(fw->size), &bp->hdr.ramdisk_size); release_firmware(fw); @@ -484,8 +482,7 @@ mic_x100_load_firmware(struct mic_device *mdev, const char *buf) if (mdev->ramdisk) rc = mic_x100_load_ramdisk(mdev); error: - dev_dbg(mdev->sdev->parent, "%s %d rc %d\n", - __func__, __LINE__, rc); + dev_dbg(mdev->sdev->parent, "%s %d rc %d\n", __func__, __LINE__, rc); done: return rc; } @@ -524,8 +521,8 @@ mic_x100_smpt_set(struct mic_device *mdev, dma_addr_t dma_addr, u8 index) uint32_t smpt_reg_val = BUILD_SMPT(SNOOP_ON, dma_addr >> mdev->smpt->info.page_shift); mic_mmio_write(&mdev->mmio, smpt_reg_val, - MIC_X100_SBOX_BASE_ADDRESS + - MIC_X100_SBOX_SMPT00 + (4 * index)); + MIC_X100_SBOX_BASE_ADDRESS + + MIC_X100_SBOX_SMPT00 + (4 * index)); } /** -- cgit v1.2.3 From af190494f9b2e1fb6e1c039e9626c3c334717da1 Mon Sep 17 00:00:00 2001 From: Dasaratharaman Chandramouli Date: Thu, 3 Oct 2013 18:06:23 -0700 Subject: misc: mic: Enable OSPM suspend and resume support. This patch enables support for OSPM suspend and resume in the MIC driver. During a host suspend event, the driver performs an orderly shutdown of the cards if they are online. Upon resume, any cards that were previously online before suspend are rebooted. The driver performs an orderly shutdown of the card primarily to ensure that applications in the card are terminated and mounted devices are safely un-mounted before the card is powered down in the event of an OSPM suspend. The driver makes use of the MIC daemon to accomplish OSPM suspend and resume. The driver registers a PM notifier per MIC device. The devices get notified synchronously during PM_SUSPEND_PREPARE and PM_POST_SUSPEND phases. During the PM_SUSPEND_PREPARE phase, the driver performs one of the following three tasks. 1) If the card is 'offline', the driver sets the card to a 'suspended' state and returns. 2) If the card is 'online', the driver initiates card shutdown by setting the card state to suspending. This notifies the MIC daemon which invokes shutdown and sets card state to 'suspended'. The driver returns after the shutdown is complete. 3) If the card is already being shutdown, possibly by a host user space application, the driver sets the card state to 'suspended' and returns after the shutdown is complete. During the PM_POST_SUSPEND phase, the driver simply notifies the daemon and returns. The daemon boots those cards that were previously online during the suspend phase. Signed-off-by: Ashutosh Dixit Signed-off-by: Nikhil Rao Signed-off-by: Harshavardhan R Kharche Signed-off-by: Sudeep Dutt Signed-off-by: Dasaratharaman Chandramouli Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-mic.txt | 60 +++++++------ Documentation/mic/mic_overview.txt | 4 +- Documentation/mic/mpssd/mpssd.c | 27 +++++- Documentation/mic/mpssd/mpssd.h | 1 + drivers/misc/mic/host/mic_boot.c | 120 +++++++++++++++++++++++++- drivers/misc/mic/host/mic_device.h | 8 ++ drivers/misc/mic/host/mic_main.c | 63 +++++++++++++- drivers/misc/mic/host/mic_sysfs.c | 7 ++ include/uapi/linux/mic_common.h | 2 + 9 files changed, 257 insertions(+), 35 deletions(-) (limited to 'drivers/misc/mic/host') diff --git a/Documentation/ABI/testing/sysfs-class-mic.txt b/Documentation/ABI/testing/sysfs-class-mic.txt index 82cdad3b614a..13f48afc534f 100644 --- a/Documentation/ABI/testing/sysfs-class-mic.txt +++ b/Documentation/ABI/testing/sysfs-class-mic.txt @@ -1,6 +1,6 @@ What: /sys/class/mic/ -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: The mic class directory belongs to Intel MIC devices and @@ -9,8 +9,8 @@ Description: Integrated Core (MIC) architecture that runs a Linux OS. What: /sys/class/mic/mic(x) -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: The directories /sys/class/mic/mic0, /sys/class/mic/mic1 etc., @@ -18,33 +18,41 @@ Description: information specific to that MIC device. What: /sys/class/mic/mic(x)/family -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: Provides information about the Coprocessor family for an Intel MIC device. For example - "x100" What: /sys/class/mic/mic(x)/stepping -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: Provides information about the silicon stepping for an Intel MIC device. For example - "A0" or "B0" What: /sys/class/mic/mic(x)/state -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: When read, this entry provides the current state of an Intel MIC device in the context of the card OS. Possible values that will be read are: - "offline" - The MIC device is ready to boot the card OS. + "offline" - The MIC device is ready to boot the card OS. On + reading this entry after an OSPM resume, a "boot" has to be + written to this entry if the card was previously shutdown + during OSPM suspend. "online" - The MIC device has initiated booting a card OS. "shutting_down" - The card OS is shutting down. "reset_failed" - The MIC device has failed to reset. + "suspending" - The MIC device is currently being prepared for + suspend. On reading this entry, a "suspend" has to be written + to the state sysfs entry to ensure the card is shutdown during + OSPM suspend. + "suspended" - The MIC device has been suspended. When written, this sysfs entry triggers different state change operations depending upon the current state of the card OS. @@ -54,10 +62,12 @@ Description: sysfs entries. "reset" - Initiates device reset. "shutdown" - Initiates card OS shutdown. + "suspend" - Initiates card OS shutdown and also marks the card + as suspended. What: /sys/class/mic/mic(x)/shutdown_status -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: An Intel MIC device runs a Linux OS during its operation. This @@ -72,8 +82,8 @@ Description: "restart" - Shutdown because of a restart command. What: /sys/class/mic/mic(x)/cmdline -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: An Intel MIC device runs a Linux OS during its operation. Before @@ -88,8 +98,8 @@ Description: line back to this entry. What: /sys/class/mic/mic(x)/firmware -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: When read, this sysfs entry provides the path name under @@ -98,8 +108,8 @@ Description: firmware image location under /lib/firmware/. What: /sys/class/mic/mic(x)/ramdisk -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: When read, this sysfs entry provides the path name under @@ -108,8 +118,8 @@ Description: the ramdisk image location under /lib/firmware/. What: /sys/class/mic/mic(x)/bootmode -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: When read, this sysfs entry provides the current bootmode for @@ -119,8 +129,8 @@ Description: b) elf - Boot an elf image for flash updates. What: /sys/class/mic/mic(x)/log_buf_addr -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: An Intel MIC device runs a Linux OS during its operation. For @@ -133,8 +143,8 @@ Description: file of the card OS. What: /sys/class/mic/mic(x)/log_buf_len -Date: August 2013 -KernelVersion: 3.11 +Date: October 2013 +KernelVersion: 3.13 Contact: Sudeep Dutt Description: An Intel MIC device runs a Linux OS during its operation. For diff --git a/Documentation/mic/mic_overview.txt b/Documentation/mic/mic_overview.txt index c4424ed1b746..b41929224804 100644 --- a/Documentation/mic/mic_overview.txt +++ b/Documentation/mic/mic_overview.txt @@ -4,7 +4,9 @@ that runs a Linux OS. It is a PCIe endpoint in a platform and therefore implements the three required standard address spaces i.e. configuration, memory and I/O. The host OS loads a device driver as is typical for PCIe devices. The card itself runs a bootstrap after reset that -transfers control to the card OS downloaded from the host driver. +transfers control to the card OS downloaded from the host driver. The +host driver supports OSPM suspend and resume operations. It shuts down +the card during suspend and reboots the card OS during resume. The card OS as shipped by Intel is a Linux kernel with modifications for the X100 devices. diff --git a/Documentation/mic/mpssd/mpssd.c b/Documentation/mic/mpssd/mpssd.c index 82c6bc2e3cb6..0c980ad40b17 100644 --- a/Documentation/mic/mpssd/mpssd.c +++ b/Documentation/mic/mpssd/mpssd.c @@ -1295,7 +1295,13 @@ reset(struct mic_info *mic) goto retry; mpsslog("%s: %s %d state %s\n", mic->name, __func__, __LINE__, state); - if ((!strcmp(state, "offline"))) { + + /* + * If the shutdown was initiated by OSPM, the state stays + * in "suspended" which is also a valid condition for reset. + */ + if ((!strcmp(state, "offline")) || + (!strcmp(state, "suspended"))) { free(state); break; } @@ -1334,6 +1340,10 @@ static int get_mic_state(struct mic_info *mic, char *state) return MIC_SHUTTING_DOWN; if (!strcmp(state, "reset_failed")) return MIC_RESET_FAILED; + if (!strcmp(state, "suspending")) + return MIC_SUSPENDING; + if (!strcmp(state, "suspended")) + return MIC_SUSPENDED; mpsslog("%s: BUG invalid state %s\n", mic->name, state); /* Invalid state */ assert(0); @@ -1418,6 +1428,17 @@ retry: case MIC_SHUTTING_DOWN: mic_handle_shutdown(mic); goto close_error; + case MIC_SUSPENDING: + mic->boot_on_resume = 1; + setsysfs(mic->name, "state", "suspend"); + mic_handle_shutdown(mic); + goto close_error; + case MIC_OFFLINE: + if (mic->boot_on_resume) { + setsysfs(mic->name, "state", "boot"); + mic->boot_on_resume = 0; + } + break; default: break; } @@ -1621,11 +1642,9 @@ init_mic_list(void) while ((file = readdir(dp)) != NULL) { if (!strncmp(file->d_name, "mic", 3)) { - mic->next = malloc(sizeof(struct mic_info)); + mic->next = calloc(1, sizeof(struct mic_info)); if (mic->next) { mic = mic->next; - mic->next = NULL; - memset(mic, 0, sizeof(struct mic_info)); mic->id = atoi(&file->d_name[3]); mic->name = malloc(strlen(file->d_name) + 16); if (mic->name) diff --git a/Documentation/mic/mpssd/mpssd.h b/Documentation/mic/mpssd/mpssd.h index ccd589ff9146..f5f18b15d9a0 100644 --- a/Documentation/mic/mpssd/mpssd.h +++ b/Documentation/mic/mpssd/mpssd.h @@ -91,6 +91,7 @@ struct mic_info { struct mic_net_info mic_net; struct mic_virtblk_info mic_virtblk; int restart; + int boot_on_resume; struct mic_info *next; }; diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index 60c54d5c43c2..d56b8bf4eaf6 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -37,12 +37,13 @@ static void mic_reset(struct mic_device *mdev) #define MIC_RESET_TO (45) + INIT_COMPLETION(mdev->reset_wait); mdev->ops->reset_fw_ready(mdev); mdev->ops->reset(mdev); for (i = 0; i < MIC_RESET_TO; i++) { if (mdev->ops->is_fw_ready(mdev)) - return; + goto done; /* * Resets typically take 10s of seconds to complete. * Since an MMIO read is required to check if the @@ -51,6 +52,8 @@ static void mic_reset(struct mic_device *mdev) msleep(1000); } mic_set_state(mdev, MIC_RESET_FAILED); +done: + complete_all(&mdev->reset_wait); } /* Initialize the MIC bootparams */ @@ -123,7 +126,8 @@ void mic_stop(struct mic_device *mdev, bool force) if (MIC_RESET_FAILED == mdev->state) goto unlock; mic_set_shutdown_status(mdev, MIC_NOP); - mic_set_state(mdev, MIC_OFFLINE); + if (MIC_SUSPENDED != mdev->state) + mic_set_state(mdev, MIC_OFFLINE); } unlock: mutex_unlock(&mdev->mic_mutex); @@ -165,7 +169,14 @@ void mic_shutdown_work(struct work_struct *work) mutex_lock(&mdev->mic_mutex); mic_set_shutdown_status(mdev, bootparam->shutdown_status); bootparam->shutdown_status = 0; - if (MIC_SHUTTING_DOWN != mdev->state) + + /* + * if state is MIC_SUSPENDED, OSPM suspend is in progress. We do not + * change the state here so as to prevent users from booting the card + * during and after the suspend operation. + */ + if (MIC_SHUTTING_DOWN != mdev->state && + MIC_SUSPENDED != mdev->state) mic_set_state(mdev, MIC_SHUTTING_DOWN); mutex_unlock(&mdev->mic_mutex); } @@ -183,3 +194,106 @@ void mic_reset_trigger_work(struct work_struct *work) mic_stop(mdev, false); } + +/** + * mic_complete_resume - Complete MIC Resume after an OSPM suspend/hibernate + * event. + * @mdev: pointer to mic_device instance + * + * RETURNS: None. + */ +void mic_complete_resume(struct mic_device *mdev) +{ + if (mdev->state != MIC_SUSPENDED) { + dev_warn(mdev->sdev->parent, "state %d should be %d\n", + mdev->state, MIC_SUSPENDED); + return; + } + + /* Make sure firmware is ready */ + if (!mdev->ops->is_fw_ready(mdev)) + mic_stop(mdev, true); + + mutex_lock(&mdev->mic_mutex); + mic_set_state(mdev, MIC_OFFLINE); + mutex_unlock(&mdev->mic_mutex); +} + +/** + * mic_prepare_suspend - Handle suspend notification for the MIC device. + * @mdev: pointer to mic_device instance + * + * RETURNS: None. + */ +void mic_prepare_suspend(struct mic_device *mdev) +{ + int rc; + +#define MIC_SUSPEND_TIMEOUT (60 * HZ) + + mutex_lock(&mdev->mic_mutex); + switch (mdev->state) { + case MIC_OFFLINE: + /* + * Card is already offline. Set state to MIC_SUSPENDED + * to prevent users from booting the card. + */ + mic_set_state(mdev, MIC_SUSPENDED); + mutex_unlock(&mdev->mic_mutex); + break; + case MIC_ONLINE: + /* + * Card is online. Set state to MIC_SUSPENDING and notify + * MIC user space daemon which will issue card + * shutdown and reset. + */ + mic_set_state(mdev, MIC_SUSPENDING); + mutex_unlock(&mdev->mic_mutex); + rc = wait_for_completion_timeout(&mdev->reset_wait, + MIC_SUSPEND_TIMEOUT); + /* Force reset the card if the shutdown completion timed out */ + if (!rc) { + mutex_lock(&mdev->mic_mutex); + mic_set_state(mdev, MIC_SUSPENDED); + mutex_unlock(&mdev->mic_mutex); + mic_stop(mdev, true); + } + break; + case MIC_SHUTTING_DOWN: + /* + * Card is shutting down. Set state to MIC_SUSPENDED + * to prevent further boot of the card. + */ + mic_set_state(mdev, MIC_SUSPENDED); + mutex_unlock(&mdev->mic_mutex); + rc = wait_for_completion_timeout(&mdev->reset_wait, + MIC_SUSPEND_TIMEOUT); + /* Force reset the card if the shutdown completion timed out */ + if (!rc) + mic_stop(mdev, true); + break; + default: + mutex_unlock(&mdev->mic_mutex); + break; + } +} + +/** + * mic_suspend - Initiate MIC suspend. Suspend merely issues card shutdown. + * @mdev: pointer to mic_device instance + * + * RETURNS: None. + */ +void mic_suspend(struct mic_device *mdev) +{ + struct mic_bootparam *bootparam = mdev->dp; + s8 db = bootparam->h2c_shutdown_db; + + mutex_lock(&mdev->mic_mutex); + if (MIC_SUSPENDING == mdev->state && db != -1) { + bootparam->shutdown_card = 1; + mdev->ops->send_intr(mdev, db); + mic_set_state(mdev, MIC_SUSPENDED); + } + mutex_unlock(&mdev->mic_mutex); +} diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index dcba2a59e77f..3574cc375bb9 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -23,6 +23,7 @@ #include #include +#include #include "mic_intr.h" @@ -75,6 +76,7 @@ enum mic_stepping { * @state: MIC state. * @shutdown_status: MIC status reported by card for shutdown/crashes. * @state_sysfs: Sysfs dirent for notifying ring 3 about MIC state changes. + * @reset_wait: Waitqueue for sleeping while reset completes. * @log_buf_addr: Log buffer address for MIC. * @log_buf_len: Log buffer length address for MIC. * @dp: virtio device page @@ -83,6 +85,7 @@ enum mic_stepping { * @shutdown_cookie: shutdown cookie. * @cdev: Character device for MIC. * @vdev_list: list of virtio devices. + * @pm_notifier: Handles PM notifications from the OS. */ struct mic_device { struct mic_mw mmio; @@ -110,6 +113,7 @@ struct mic_device { u8 state; u8 shutdown_status; struct sysfs_dirent *state_sysfs; + struct completion reset_wait; void *log_buf_addr; int *log_buf_len; void *dp; @@ -118,6 +122,7 @@ struct mic_device { struct mic_irq *shutdown_cookie; struct cdev cdev; struct list_head vdev_list; + struct notifier_block pm_notifier; }; /** @@ -192,4 +197,7 @@ void mic_create_debug_dir(struct mic_device *dev); void mic_delete_debug_dir(struct mic_device *dev); void __init mic_init_debugfs(void); void mic_exit_debugfs(void); +void mic_prepare_suspend(struct mic_device *mdev); +void mic_complete_resume(struct mic_device *mdev); +void mic_suspend(struct mic_device *mdev); #endif diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c index ca06aa9b7114..b3520859abd3 100644 --- a/drivers/misc/mic/host/mic_main.c +++ b/drivers/misc/mic/host/mic_main.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "../common/mic_dev.h" @@ -186,6 +187,43 @@ static enum mic_hw_family mic_get_family(struct pci_dev *pdev) return family; } +/** +* mic_pm_notifier: Notifier callback function that handles +* PM notifications. +* +* @notifier_block: The notifier structure. +* @pm_event: The event for which the driver was notified. +* @unused: Meaningless. Always NULL. +* +* returns NOTIFY_DONE +*/ +static int mic_pm_notifier(struct notifier_block *notifier, + unsigned long pm_event, void *unused) +{ + struct mic_device *mdev = container_of(notifier, + struct mic_device, pm_notifier); + + switch (pm_event) { + case PM_HIBERNATION_PREPARE: + /* Fall through */ + case PM_SUSPEND_PREPARE: + mic_prepare_suspend(mdev); + break; + case PM_POST_HIBERNATION: + /* Fall through */ + case PM_POST_SUSPEND: + /* Fall through */ + case PM_POST_RESTORE: + mic_complete_resume(mdev); + break; + case PM_RESTORE_PREPARE: + break; + default: + break; + } + return NOTIFY_DONE; +} + /** * mic_device_init - Allocates and initializes the MIC device structure * @@ -194,9 +232,11 @@ static enum mic_hw_family mic_get_family(struct pci_dev *pdev) * * returns none. */ -static void +static int mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) { + int rc; + mdev->family = mic_get_family(pdev); mdev->stepping = pdev->revision; mic_ops_init(mdev); @@ -205,7 +245,20 @@ mic_device_init(struct mic_device *mdev, struct pci_dev *pdev) mdev->irq_info.next_avail_src = 0; INIT_WORK(&mdev->reset_trigger_work, mic_reset_trigger_work); INIT_WORK(&mdev->shutdown_work, mic_shutdown_work); + init_completion(&mdev->reset_wait); INIT_LIST_HEAD(&mdev->vdev_list); + mdev->pm_notifier.notifier_call = mic_pm_notifier; + rc = register_pm_notifier(&mdev->pm_notifier); + if (rc) { + dev_err(&pdev->dev, "register_pm_notifier failed rc %d\n", + rc); + goto register_pm_notifier_fail; + } + return 0; +register_pm_notifier_fail: + flush_work(&mdev->shutdown_work); + flush_work(&mdev->reset_trigger_work); + return rc; } /** @@ -224,6 +277,7 @@ static void mic_device_uninit(struct mic_device *mdev) kfree(mdev->bootmode); flush_work(&mdev->reset_trigger_work); flush_work(&mdev->shutdown_work); + unregister_pm_notifier(&mdev->pm_notifier); } /** @@ -253,7 +307,11 @@ static int mic_probe(struct pci_dev *pdev, goto ida_fail; } - mic_device_init(mdev, pdev); + rc = mic_device_init(mdev, pdev); + if (rc) { + dev_err(&pdev->dev, "mic_device_init failed rc %d\n", rc); + goto device_init_fail; + } rc = pci_enable_device(pdev); if (rc) { @@ -376,6 +434,7 @@ disable_device: pci_disable_device(pdev); uninit_device: mic_device_uninit(mdev); +device_init_fail: ida_simple_remove(&g_mic_ida, mdev->id); ida_fail: kfree(mdev); diff --git a/drivers/misc/mic/host/mic_sysfs.c b/drivers/misc/mic/host/mic_sysfs.c index 75746adfb155..6dd864e4a617 100644 --- a/drivers/misc/mic/host/mic_sysfs.c +++ b/drivers/misc/mic/host/mic_sysfs.c @@ -33,6 +33,8 @@ static const char * const mic_state_string[] = { [MIC_ONLINE] = "online", [MIC_SHUTTING_DOWN] = "shutting_down", [MIC_RESET_FAILED] = "reset_failed", + [MIC_SUSPENDING] = "suspending", + [MIC_SUSPENDED] = "suspended", }; /* @@ -156,6 +158,11 @@ state_store(struct device *dev, struct device_attribute *attr, goto done; } + if (sysfs_streq(buf, "suspend")) { + mic_suspend(mdev); + goto done; + } + count = -EINVAL; done: return count; diff --git a/include/uapi/linux/mic_common.h b/include/uapi/linux/mic_common.h index 364ac751bd04..17e7d95e4f53 100644 --- a/include/uapi/linux/mic_common.h +++ b/include/uapi/linux/mic_common.h @@ -219,6 +219,8 @@ enum mic_states { MIC_ONLINE, MIC_SHUTTING_DOWN, MIC_RESET_FAILED, + MIC_SUSPENDING, + MIC_SUSPENDED, MIC_LAST }; -- cgit v1.2.3 From 425792266a40189e0b3fec02cb59a69935d8c58c Mon Sep 17 00:00:00 2001 From: Ashutosh Dixit Date: Tue, 29 Oct 2013 17:09:59 -0700 Subject: misc: mic: Fixes for randconfig build errors and warnings. This patch fixes the build errors and warnings reported at https://lkml.org/lkml/2013/10/29/421. Co-author: Dasaratharaman Chandramouli Reviewed-by: Sudeep Dutt Reported-by: Jim Davis Signed-off-by: Ashutosh Dixit Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mic/host/mic_boot.c | 1 + drivers/misc/mic/host/mic_fops.c | 1 + drivers/misc/mic/host/mic_virtio.c | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/misc/mic/host') diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c index d56b8bf4eaf6..b079c65eed6d 100644 --- a/drivers/misc/mic/host/mic_boot.c +++ b/drivers/misc/mic/host/mic_boot.c @@ -20,6 +20,7 @@ */ #include #include +#include #include #include "../common/mic_dev.h" diff --git a/drivers/misc/mic/host/mic_fops.c b/drivers/misc/mic/host/mic_fops.c index 8dc6ff16845a..85776d7327f3 100644 --- a/drivers/misc/mic/host/mic_fops.c +++ b/drivers/misc/mic/host/mic_fops.c @@ -19,6 +19,7 @@ * */ #include +#include #include #include "../common/mic_dev.h" diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c index 0c883cd4f1d1..5b8494bd1e00 100644 --- a/drivers/misc/mic/host/mic_virtio.c +++ b/drivers/misc/mic/host/mic_virtio.c @@ -517,10 +517,10 @@ int mic_virtio_add_device(struct mic_vdev *mvdev, void __user *argp) { struct mic_device *mdev = mvdev->mdev; - struct mic_device_desc *dd; + struct mic_device_desc *dd = NULL; struct mic_vqconfig *vqconfig; int vr_size, i, j, ret; - u8 type; + u8 type = 0; s8 db; char irqname[10]; struct mic_bootparam *bootparam = mdev->dp; -- cgit v1.2.3