summaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/carma/carma-fpga.c5
-rw-r--r--drivers/misc/dummy-irq.c1
-rw-r--r--drivers/misc/genwqe/Kconfig6
-rw-r--r--drivers/misc/genwqe/card_base.c215
-rw-r--r--drivers/misc/genwqe/card_base.h2
-rw-r--r--drivers/misc/genwqe/card_ddcb.c28
-rw-r--r--drivers/misc/genwqe/card_debugfs.c7
-rw-r--r--drivers/misc/genwqe/card_dev.c5
-rw-r--r--drivers/misc/genwqe/card_sysfs.c25
-rw-r--r--drivers/misc/genwqe/card_utils.c20
-rw-r--r--drivers/misc/genwqe/genwqe_driver.h2
-rw-r--r--drivers/misc/lkdtm.c1
-rw-r--r--drivers/misc/mei/hw-me-regs.h1
-rw-r--r--drivers/misc/mei/main.c148
-rw-r--r--drivers/misc/mei/mei_dev.h9
-rw-r--r--drivers/misc/mei/pci-me.c4
-rw-r--r--drivers/misc/mei/pci-txe.c2
-rw-r--r--drivers/misc/mic/Kconfig21
-rw-r--r--drivers/misc/mic/Makefile1
-rw-r--r--drivers/misc/mic/bus/Makefile5
-rw-r--r--drivers/misc/mic/bus/mic_bus.c218
-rw-r--r--drivers/misc/mic/card/mic_device.c23
-rw-r--r--drivers/misc/mic/card/mic_device.h9
-rw-r--r--drivers/misc/mic/card/mic_virtio.c7
-rw-r--r--drivers/misc/mic/card/mic_x100.c55
-rw-r--r--drivers/misc/mic/host/mic_boot.c83
-rw-r--r--drivers/misc/mic/host/mic_device.h24
-rw-r--r--drivers/misc/mic/host/mic_intr.c121
-rw-r--r--drivers/misc/mic/host/mic_intr.h27
-rw-r--r--drivers/misc/mic/host/mic_main.c5
-rw-r--r--drivers/misc/mic/host/mic_virtio.c187
-rw-r--r--drivers/misc/mic/host/mic_virtio.h21
-rw-r--r--drivers/misc/mic/host/mic_x100.c8
-rw-r--r--drivers/misc/ti-st/st_kim.c3
34 files changed, 1117 insertions, 182 deletions
diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c
index 14d90eae605b..55e913b7eb11 100644
--- a/drivers/misc/carma/carma-fpga.c
+++ b/drivers/misc/carma/carma-fpga.c
@@ -954,10 +954,7 @@ static int data_debugfs_init(struct fpga_device *priv)
{
priv->dbg_entry = debugfs_create_file(drv_name, S_IRUGO, NULL, priv,
&data_debug_fops);
- if (IS_ERR(priv->dbg_entry))
- return PTR_ERR(priv->dbg_entry);
-
- return 0;
+ return PTR_ERR_OR_ZERO(priv->dbg_entry);
}
static void data_debugfs_exit(struct fpga_device *priv)
diff --git a/drivers/misc/dummy-irq.c b/drivers/misc/dummy-irq.c
index 4d0db15df115..acbbe0390be4 100644
--- a/drivers/misc/dummy-irq.c
+++ b/drivers/misc/dummy-irq.c
@@ -61,3 +61,4 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Jiri Kosina");
module_param(irq, uint, 0444);
MODULE_PARM_DESC(irq, "The IRQ to register for");
+MODULE_DESCRIPTION("Dummy IRQ handler driver");
diff --git a/drivers/misc/genwqe/Kconfig b/drivers/misc/genwqe/Kconfig
index 6069d8cd79d7..4c0a033cbfdb 100644
--- a/drivers/misc/genwqe/Kconfig
+++ b/drivers/misc/genwqe/Kconfig
@@ -11,3 +11,9 @@ menuconfig GENWQE
Enables PCIe card driver for IBM GenWQE accelerators.
The user-space interface is described in
include/linux/genwqe/genwqe_card.h.
+
+config GENWQE_PLATFORM_ERROR_RECOVERY
+ int "Use platform recovery procedures (0=off, 1=on)"
+ depends on GENWQE
+ default 1 if PPC64
+ default 0
diff --git a/drivers/misc/genwqe/card_base.c b/drivers/misc/genwqe/card_base.c
index 74d51c9bb858..dc8c04ae5113 100644
--- a/drivers/misc/genwqe/card_base.c
+++ b/drivers/misc/genwqe/card_base.c
@@ -38,7 +38,6 @@
#include <linux/notifier.h>
#include <linux/device.h>
#include <linux/log2.h>
-#include <linux/genwqe/genwqe_card.h>
#include "card_base.h"
#include "card_ddcb.h"
@@ -140,6 +139,12 @@ static struct genwqe_dev *genwqe_dev_alloc(void)
cd->class_genwqe = class_genwqe;
cd->debugfs_genwqe = debugfs_genwqe;
+ /*
+ * This comes from kernel config option and can be overritten via
+ * debugfs.
+ */
+ cd->use_platform_recovery = CONFIG_GENWQE_PLATFORM_ERROR_RECOVERY;
+
init_waitqueue_head(&cd->queue_waitq);
spin_lock_init(&cd->file_lock);
@@ -761,6 +766,124 @@ static u64 genwqe_fir_checking(struct genwqe_dev *cd)
}
/**
+ * genwqe_pci_fundamental_reset() - trigger a PCIe fundamental reset on the slot
+ *
+ * Note: pci_set_pcie_reset_state() is not implemented on all archs, so this
+ * reset method will not work in all cases.
+ *
+ * Return: 0 on success or error code from pci_set_pcie_reset_state()
+ */
+static int genwqe_pci_fundamental_reset(struct pci_dev *pci_dev)
+{
+ int rc;
+
+ /*
+ * lock pci config space access from userspace,
+ * save state and issue PCIe fundamental reset
+ */
+ pci_cfg_access_lock(pci_dev);
+ pci_save_state(pci_dev);
+ rc = pci_set_pcie_reset_state(pci_dev, pcie_warm_reset);
+ if (!rc) {
+ /* keep PCIe reset asserted for 250ms */
+ msleep(250);
+ pci_set_pcie_reset_state(pci_dev, pcie_deassert_reset);
+ /* Wait for 2s to reload flash and train the link */
+ msleep(2000);
+ }
+ pci_restore_state(pci_dev);
+ pci_cfg_access_unlock(pci_dev);
+ return rc;
+}
+
+
+static int genwqe_platform_recovery(struct genwqe_dev *cd)
+{
+ struct pci_dev *pci_dev = cd->pci_dev;
+ int rc;
+
+ dev_info(&pci_dev->dev,
+ "[%s] resetting card for error recovery\n", __func__);
+
+ /* Clear out error injection flags */
+ cd->err_inject &= ~(GENWQE_INJECT_HARDWARE_FAILURE |
+ GENWQE_INJECT_GFIR_FATAL |
+ GENWQE_INJECT_GFIR_INFO);
+
+ genwqe_stop(cd);
+
+ /* Try recoverying the card with fundamental reset */
+ rc = genwqe_pci_fundamental_reset(pci_dev);
+ if (!rc) {
+ rc = genwqe_start(cd);
+ if (!rc)
+ dev_info(&pci_dev->dev,
+ "[%s] card recovered\n", __func__);
+ else
+ dev_err(&pci_dev->dev,
+ "[%s] err: cannot start card services! (err=%d)\n",
+ __func__, rc);
+ } else {
+ dev_err(&pci_dev->dev,
+ "[%s] card reset failed\n", __func__);
+ }
+
+ return rc;
+}
+
+/*
+ * genwqe_reload_bistream() - reload card bitstream
+ *
+ * Set the appropriate register and call fundamental reset to reaload the card
+ * bitstream.
+ *
+ * Return: 0 on success, error code otherwise
+ */
+static int genwqe_reload_bistream(struct genwqe_dev *cd)
+{
+ struct pci_dev *pci_dev = cd->pci_dev;
+ int rc;
+
+ dev_info(&pci_dev->dev,
+ "[%s] resetting card for bitstream reload\n",
+ __func__);
+
+ genwqe_stop(cd);
+
+ /*
+ * Cause a CPLD reprogram with the 'next_bitstream'
+ * partition on PCIe hot or fundamental reset
+ */
+ __genwqe_writeq(cd, IO_SLC_CFGREG_SOFTRESET,
+ (cd->softreset & 0xcull) | 0x70ull);
+
+ rc = genwqe_pci_fundamental_reset(pci_dev);
+ if (rc) {
+ /*
+ * A fundamental reset failure can be caused
+ * by lack of support on the arch, so we just
+ * log the error and try to start the card
+ * again.
+ */
+ dev_err(&pci_dev->dev,
+ "[%s] err: failed to reset card for bitstream reload\n",
+ __func__);
+ }
+
+ rc = genwqe_start(cd);
+ if (rc) {
+ dev_err(&pci_dev->dev,
+ "[%s] err: cannot start card services! (err=%d)\n",
+ __func__, rc);
+ return rc;
+ }
+ dev_info(&pci_dev->dev,
+ "[%s] card reloaded\n", __func__);
+ return 0;
+}
+
+
+/**
* genwqe_health_thread() - Health checking thread
*
* This thread is only started for the PF of the card.
@@ -786,6 +909,7 @@ static int genwqe_health_thread(void *data)
struct pci_dev *pci_dev = cd->pci_dev;
u64 gfir, gfir_masked, slu_unitcfg, app_unitcfg;
+ health_thread_begin:
while (!kthread_should_stop()) {
rc = wait_event_interruptible_timeout(cd->health_waitq,
(genwqe_health_check_cond(cd, &gfir) ||
@@ -846,6 +970,13 @@ static int genwqe_health_thread(void *data)
}
}
+ if (cd->card_state == GENWQE_CARD_RELOAD_BITSTREAM) {
+ /* Userspace requested card bitstream reload */
+ rc = genwqe_reload_bistream(cd);
+ if (rc)
+ goto fatal_error;
+ }
+
cd->last_gfir = gfir;
cond_resched();
}
@@ -853,6 +984,28 @@ static int genwqe_health_thread(void *data)
return 0;
fatal_error:
+ if (cd->use_platform_recovery) {
+ /*
+ * Since we use raw accessors, EEH errors won't be detected
+ * by the platform until we do a non-raw MMIO or config space
+ * read
+ */
+ readq(cd->mmio + IO_SLC_CFGREG_GFIR);
+
+ /* We do nothing if the card is going over PCI recovery */
+ if (pci_channel_offline(pci_dev))
+ return -EIO;
+
+ /*
+ * If it's supported by the platform, we try a fundamental reset
+ * to recover from a fatal error. Otherwise, we continue to wait
+ * for an external recovery procedure to take care of it.
+ */
+ rc = genwqe_platform_recovery(cd);
+ if (!rc)
+ goto health_thread_begin;
+ }
+
dev_err(&pci_dev->dev,
"[%s] card unusable. Please trigger unbind!\n", __func__);
@@ -958,6 +1111,9 @@ static int genwqe_pci_setup(struct genwqe_dev *cd)
pci_set_master(pci_dev);
pci_enable_pcie_error_reporting(pci_dev);
+ /* EEH recovery requires PCIe fundamental reset */
+ pci_dev->needs_freset = 1;
+
/* request complete BAR-0 space (length = 0) */
cd->mmio_len = pci_resource_len(pci_dev, 0);
cd->mmio = pci_iomap(pci_dev, 0, 0);
@@ -1096,23 +1252,40 @@ static pci_ers_result_t genwqe_err_error_detected(struct pci_dev *pci_dev,
dev_err(&pci_dev->dev, "[%s] state=%d\n", __func__, state);
- if (pci_dev == NULL)
- return PCI_ERS_RESULT_NEED_RESET;
-
cd = dev_get_drvdata(&pci_dev->dev);
if (cd == NULL)
- return PCI_ERS_RESULT_NEED_RESET;
+ return PCI_ERS_RESULT_DISCONNECT;
- switch (state) {
- case pci_channel_io_normal:
- return PCI_ERS_RESULT_CAN_RECOVER;
- case pci_channel_io_frozen:
- return PCI_ERS_RESULT_NEED_RESET;
- case pci_channel_io_perm_failure:
+ /* Stop the card */
+ genwqe_health_check_stop(cd);
+ genwqe_stop(cd);
+
+ /*
+ * On permanent failure, the PCI code will call device remove
+ * after the return of this function.
+ * genwqe_stop() can be called twice.
+ */
+ if (state == pci_channel_io_perm_failure) {
return PCI_ERS_RESULT_DISCONNECT;
+ } else {
+ genwqe_pci_remove(cd);
+ return PCI_ERS_RESULT_NEED_RESET;
}
+}
+
+static pci_ers_result_t genwqe_err_slot_reset(struct pci_dev *pci_dev)
+{
+ int rc;
+ struct genwqe_dev *cd = dev_get_drvdata(&pci_dev->dev);
- return PCI_ERS_RESULT_NEED_RESET;
+ rc = genwqe_pci_setup(cd);
+ if (!rc) {
+ return PCI_ERS_RESULT_RECOVERED;
+ } else {
+ dev_err(&pci_dev->dev,
+ "err: problems with PCI setup (err=%d)\n", rc);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
}
static pci_ers_result_t genwqe_err_result_none(struct pci_dev *dev)
@@ -1120,8 +1293,22 @@ static pci_ers_result_t genwqe_err_result_none(struct pci_dev *dev)
return PCI_ERS_RESULT_NONE;
}
-static void genwqe_err_resume(struct pci_dev *dev)
+static void genwqe_err_resume(struct pci_dev *pci_dev)
{
+ int rc;
+ struct genwqe_dev *cd = dev_get_drvdata(&pci_dev->dev);
+
+ rc = genwqe_start(cd);
+ if (!rc) {
+ rc = genwqe_health_check_start(cd);
+ if (rc)
+ dev_err(&pci_dev->dev,
+ "err: cannot start health checking! (err=%d)\n",
+ rc);
+ } else {
+ dev_err(&pci_dev->dev,
+ "err: cannot start card services! (err=%d)\n", rc);
+ }
}
static int genwqe_sriov_configure(struct pci_dev *dev, int numvfs)
@@ -1144,7 +1331,7 @@ static struct pci_error_handlers genwqe_err_handler = {
.error_detected = genwqe_err_error_detected,
.mmio_enabled = genwqe_err_result_none,
.link_reset = genwqe_err_result_none,
- .slot_reset = genwqe_err_result_none,
+ .slot_reset = genwqe_err_slot_reset,
.resume = genwqe_err_resume,
};
diff --git a/drivers/misc/genwqe/card_base.h b/drivers/misc/genwqe/card_base.h
index 0e608a288603..67abd8cb2247 100644
--- a/drivers/misc/genwqe/card_base.h
+++ b/drivers/misc/genwqe/card_base.h
@@ -291,6 +291,8 @@ struct genwqe_dev {
struct task_struct *health_thread;
wait_queue_head_t health_waitq;
+ int use_platform_recovery; /* use platform recovery mechanisms */
+
/* char device */
dev_t devnum_genwqe; /* major/minor num card */
struct class *class_genwqe; /* reference to class object */
diff --git a/drivers/misc/genwqe/card_ddcb.c b/drivers/misc/genwqe/card_ddcb.c
index c8046db2d5a2..dc9851a5540e 100644
--- a/drivers/misc/genwqe/card_ddcb.c
+++ b/drivers/misc/genwqe/card_ddcb.c
@@ -1118,7 +1118,21 @@ static irqreturn_t genwqe_pf_isr(int irq, void *dev_id)
* safer, but slower for the good-case ... See above.
*/
gfir = __genwqe_readq(cd, IO_SLC_CFGREG_GFIR);
- if ((gfir & GFIR_ERR_TRIGGER) != 0x0) {
+ if (((gfir & GFIR_ERR_TRIGGER) != 0x0) &&
+ !pci_channel_offline(pci_dev)) {
+
+ if (cd->use_platform_recovery) {
+ /*
+ * Since we use raw accessors, EEH errors won't be
+ * detected by the platform until we do a non-raw
+ * MMIO or config space read
+ */
+ readq(cd->mmio + IO_SLC_CFGREG_GFIR);
+
+ /* Don't do anything if the PCI channel is frozen */
+ if (pci_channel_offline(pci_dev))
+ goto exit;
+ }
wake_up_interruptible(&cd->health_waitq);
@@ -1126,12 +1140,12 @@ static irqreturn_t genwqe_pf_isr(int irq, void *dev_id)
* By default GFIRs causes recovery actions. This
* count is just for debug when recovery is masked.
*/
- printk_ratelimited(KERN_ERR
- "%s %s: [%s] GFIR=%016llx\n",
- GENWQE_DEVNAME, dev_name(&pci_dev->dev),
- __func__, gfir);
+ dev_err_ratelimited(&pci_dev->dev,
+ "[%s] GFIR=%016llx\n",
+ __func__, gfir);
}
+ exit:
return IRQ_HANDLED;
}
@@ -1237,9 +1251,7 @@ int genwqe_setup_service_layer(struct genwqe_dev *cd)
}
rc = genwqe_set_interrupt_capability(cd, GENWQE_MSI_IRQS);
- if (rc > 0)
- rc = genwqe_set_interrupt_capability(cd, rc);
- if (rc != 0) {
+ if (rc) {
rc = -ENODEV;
goto stop_kthread;
}
diff --git a/drivers/misc/genwqe/card_debugfs.c b/drivers/misc/genwqe/card_debugfs.c
index 0a33ade64109..c9b4d6d0eb99 100644
--- a/drivers/misc/genwqe/card_debugfs.c
+++ b/drivers/misc/genwqe/card_debugfs.c
@@ -485,6 +485,13 @@ int genwqe_init_debugfs(struct genwqe_dev *cd)
goto err1;
}
+ file = debugfs_create_u32("use_platform_recovery", 0666, root,
+ &cd->use_platform_recovery);
+ if (!file) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
cd->debugfs_root = root;
return 0;
err1:
diff --git a/drivers/misc/genwqe/card_dev.c b/drivers/misc/genwqe/card_dev.c
index 1d2f163a1906..aae42555e2ca 100644
--- a/drivers/misc/genwqe/card_dev.c
+++ b/drivers/misc/genwqe/card_dev.c
@@ -1048,10 +1048,15 @@ static long genwqe_ioctl(struct file *filp, unsigned int cmd,
int rc = 0;
struct genwqe_file *cfile = (struct genwqe_file *)filp->private_data;
struct genwqe_dev *cd = cfile->cd;
+ struct pci_dev *pci_dev = cd->pci_dev;
struct genwqe_reg_io __user *io;
u64 val;
u32 reg_offs;
+ /* Return -EIO if card hit EEH */
+ if (pci_channel_offline(pci_dev))
+ return -EIO;
+
if (_IOC_TYPE(cmd) != GENWQE_IOC_CODE)
return -EINVAL;
diff --git a/drivers/misc/genwqe/card_sysfs.c b/drivers/misc/genwqe/card_sysfs.c
index a72a99266c3c..7232e40a3ad9 100644
--- a/drivers/misc/genwqe/card_sysfs.c
+++ b/drivers/misc/genwqe/card_sysfs.c
@@ -223,6 +223,30 @@ static ssize_t next_bitstream_store(struct device *dev,
}
static DEVICE_ATTR_RW(next_bitstream);
+static ssize_t reload_bitstream_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int reload;
+ struct genwqe_dev *cd = dev_get_drvdata(dev);
+
+ if (kstrtoint(buf, 0, &reload) < 0)
+ return -EINVAL;
+
+ if (reload == 0x1) {
+ if (cd->card_state == GENWQE_CARD_UNUSED ||
+ cd->card_state == GENWQE_CARD_USED)
+ cd->card_state = GENWQE_CARD_RELOAD_BITSTREAM;
+ else
+ return -EIO;
+ } else {
+ return -EINVAL;
+ }
+
+ return count;
+}
+static DEVICE_ATTR_WO(reload_bitstream);
+
/*
* Create device_attribute structures / params: name, mode, show, store
* additional flag if valid in VF
@@ -239,6 +263,7 @@ static struct attribute *genwqe_attributes[] = {
&dev_attr_status.attr,
&dev_attr_freerunning_timer.attr,
&dev_attr_queue_working_time.attr,
+ &dev_attr_reload_bitstream.attr,
NULL,
};
diff --git a/drivers/misc/genwqe/card_utils.c b/drivers/misc/genwqe/card_utils.c
index 62cc6bb3f62e..a6400f09229c 100644
--- a/drivers/misc/genwqe/card_utils.c
+++ b/drivers/misc/genwqe/card_utils.c
@@ -53,12 +53,17 @@
*/
int __genwqe_writeq(struct genwqe_dev *cd, u64 byte_offs, u64 val)
{
+ struct pci_dev *pci_dev = cd->pci_dev;
+
if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE)
return -EIO;
if (cd->mmio == NULL)
return -EIO;
+ if (pci_channel_offline(pci_dev))
+ return -EIO;
+
__raw_writeq((__force u64)cpu_to_be64(val), cd->mmio + byte_offs);
return 0;
}
@@ -99,12 +104,17 @@ u64 __genwqe_readq(struct genwqe_dev *cd, u64 byte_offs)
*/
int __genwqe_writel(struct genwqe_dev *cd, u64 byte_offs, u32 val)
{
+ struct pci_dev *pci_dev = cd->pci_dev;
+
if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE)
return -EIO;
if (cd->mmio == NULL)
return -EIO;
+ if (pci_channel_offline(pci_dev))
+ return -EIO;
+
__raw_writel((__force u32)cpu_to_be32(val), cd->mmio + byte_offs);
return 0;
}
@@ -718,10 +728,12 @@ int genwqe_set_interrupt_capability(struct genwqe_dev *cd, int count)
int rc;
struct pci_dev *pci_dev = cd->pci_dev;
- rc = pci_enable_msi_exact(pci_dev, count);
- if (rc == 0)
- cd->flags |= GENWQE_FLAG_MSI_ENABLED;
- return rc;
+ rc = pci_enable_msi_range(pci_dev, 1, count);
+ if (rc < 0)
+ return rc;
+
+ cd->flags |= GENWQE_FLAG_MSI_ENABLED;
+ return 0;
}
/**
diff --git a/drivers/misc/genwqe/genwqe_driver.h b/drivers/misc/genwqe/genwqe_driver.h
index cd5263163a6e..a506e9aa2d57 100644
--- a/drivers/misc/genwqe/genwqe_driver.h
+++ b/drivers/misc/genwqe/genwqe_driver.h
@@ -36,7 +36,7 @@
#include <asm/byteorder.h>
#include <linux/genwqe/genwqe_card.h>
-#define DRV_VERS_STRING "2.0.15"
+#define DRV_VERS_STRING "2.0.21"
/*
* Static minor number assignement, until we decide/implement
diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c
index d66a2f24f6b3..b5abe34120b8 100644
--- a/drivers/misc/lkdtm.c
+++ b/drivers/misc/lkdtm.c
@@ -870,3 +870,4 @@ module_init(lkdtm_module_init);
module_exit(lkdtm_module_exit);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Kprobe module for testing crash dumps");
diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h
index a7856c0ac576..c5feafdd58a8 100644
--- a/drivers/misc/mei/hw-me-regs.h
+++ b/drivers/misc/mei/hw-me-regs.h
@@ -115,6 +115,7 @@
#define MEI_DEV_ID_LPT_HR 0x8CBA /* Lynx Point H Refresh */
#define MEI_DEV_ID_WPT_LP 0x9CBA /* Wildcat Point LP */
+#define MEI_DEV_ID_WPT_LP_2 0x9CBB /* Wildcat Point LP 2 */
/* Host Firmware Status Registers in PCI Config Space */
#define PCI_CFG_HFS_1 0x40
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 66f0a1a06451..401a3d526cd0 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -32,7 +32,6 @@
#include <linux/compat.h>
#include <linux/jiffies.h>
#include <linux/interrupt.h>
-#include <linux/miscdevice.h>
#include <linux/mei.h>
@@ -49,19 +48,12 @@
*/
static int mei_open(struct inode *inode, struct file *file)
{
- struct miscdevice *misc = file->private_data;
- struct pci_dev *pdev;
- struct mei_cl *cl;
struct mei_device *dev;
+ struct mei_cl *cl;
int err;
- if (!misc->parent)
- return -ENODEV;
-
- pdev = container_of(misc->parent, struct pci_dev, dev);
-
- dev = pci_get_drvdata(pdev);
+ dev = container_of(inode->i_cdev, struct mei_device, cdev);
if (!dev)
return -ENODEV;
@@ -667,46 +659,148 @@ static const struct file_operations mei_fops = {
.llseek = no_llseek
};
-/*
- * Misc Device Struct
+static struct class *mei_class;
+static dev_t mei_devt;
+#define MEI_MAX_DEVS MINORMASK
+static DEFINE_MUTEX(mei_minor_lock);
+static DEFINE_IDR(mei_idr);
+
+/**
+ * mei_minor_get - obtain next free device minor number
+ *
+ * @dev: device pointer
+ *
+ * returns allocated minor, or -ENOSPC if no free minor left
*/
-static struct miscdevice mei_misc_device = {
- .name = "mei",
- .fops = &mei_fops,
- .minor = MISC_DYNAMIC_MINOR,
-};
+static int mei_minor_get(struct mei_device *dev)
+{
+ int ret;
+
+ mutex_lock(&mei_minor_lock);
+ ret = idr_alloc(&mei_idr, dev, 0, MEI_MAX_DEVS, GFP_KERNEL);
+ if (ret >= 0)
+ dev->minor = ret;
+ else if (ret == -ENOSPC)
+ dev_err(&dev->pdev->dev, "too many mei devices\n");
+ mutex_unlock(&mei_minor_lock);
+ return ret;
+}
-int mei_register(struct mei_device *dev)
+/**
+ * mei_minor_free - mark device minor number as free
+ *
+ * @dev: device pointer
+ */
+static void mei_minor_free(struct mei_device *dev)
{
- int ret;
- mei_misc_device.parent = &dev->pdev->dev;
- ret = misc_register(&mei_misc_device);
- if (ret)
+ mutex_lock(&mei_minor_lock);
+ idr_remove(&mei_idr, dev->minor);
+ mutex_unlock(&mei_minor_lock);
+}
+
+int mei_register(struct mei_device *dev, struct device *parent)
+{
+ struct device *clsdev; /* class device */
+ int ret, devno;
+
+ ret = mei_minor_get(dev);
+ if (ret < 0)
return ret;
- if (mei_dbgfs_register(dev, mei_misc_device.name))
- dev_err(&dev->pdev->dev, "cannot register debugfs\n");
+ /* Fill in the data structures */
+ devno = MKDEV(MAJOR(mei_devt), dev->minor);
+ cdev_init(&dev->cdev, &mei_fops);
+ dev->cdev.owner = mei_fops.owner;
+
+ /* Add the device */
+ ret = cdev_add(&dev->cdev, devno, 1);
+ if (ret) {
+ dev_err(parent, "unable to add device %d:%d\n",
+ MAJOR(mei_devt), dev->minor);
+ goto err_dev_add;
+ }
+
+ clsdev = device_create(mei_class, parent, devno,
+ NULL, "mei%d", dev->minor);
+
+ if (IS_ERR(clsdev)) {
+ dev_err(parent, "unable to create device %d:%d\n",
+ MAJOR(mei_devt), dev->minor);
+ ret = PTR_ERR(clsdev);
+ goto err_dev_create;
+ }
+
+ ret = mei_dbgfs_register(dev, dev_name(clsdev));
+ if (ret) {
+ dev_err(clsdev, "cannot register debugfs ret = %d\n", ret);
+ goto err_dev_dbgfs;
+ }
return 0;
+
+err_dev_dbgfs:
+ device_destroy(mei_class, devno);
+err_dev_create:
+ cdev_del(&dev->cdev);
+err_dev_add:
+ mei_minor_free(dev);
+ return ret;
}
EXPORT_SYMBOL_GPL(mei_register);
void mei_deregister(struct mei_device *dev)
{
+ int devno;
+
+ devno = dev->cdev.dev;
+ cdev_del(&dev->cdev);
+
mei_dbgfs_deregister(dev);
- misc_deregister(&mei_misc_device);
- mei_misc_device.parent = NULL;
+
+ device_destroy(mei_class, devno);
+
+ mei_minor_free(dev);
}
EXPORT_SYMBOL_GPL(mei_deregister);
static int __init mei_init(void)
{
- return mei_cl_bus_init();
+ int ret;
+
+ mei_class = class_create(THIS_MODULE, "mei");
+ if (IS_ERR(mei_class)) {
+ pr_err("couldn't create class\n");
+ ret = PTR_ERR(mei_class);
+ goto err;
+ }
+
+ ret = alloc_chrdev_region(&mei_devt, 0, MEI_MAX_DEVS, "mei");
+ if (ret < 0) {
+ pr_err("unable to allocate char dev region\n");
+ goto err_class;
+ }
+
+ ret = mei_cl_bus_init();
+ if (ret < 0) {
+ pr_err("unable to initialize bus\n");
+ goto err_chrdev;
+ }
+
+ return 0;
+
+err_chrdev:
+ unregister_chrdev_region(mei_devt, MEI_MAX_DEVS);
+err_class:
+ class_destroy(mei_class);
+err:
+ return ret;
}
static void __exit mei_exit(void)
{
+ unregister_chrdev_region(mei_devt, MEI_MAX_DEVS);
+ class_destroy(mei_class);
mei_cl_bus_exit();
}
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index 5c7e990e2f22..2afa3d69107d 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -400,6 +400,10 @@ struct mei_cfg {
/**
* struct mei_device - MEI private device struct
+ * @pdev - pointer to pci device struct
+ * @cdev - character device
+ * @minor - minor number allocated for device
+ *
* @reset_count - limits the number of consecutive resets
* @hbm_state - state of host bus message protocol
* @pg_event - power gating event
@@ -412,6 +416,9 @@ struct mei_cfg {
*/
struct mei_device {
struct pci_dev *pdev; /* pointer to pci device struct */
+ struct cdev cdev;
+ int minor;
+
/*
* lists of queues
*/
@@ -741,7 +748,7 @@ static inline int mei_dbgfs_register(struct mei_device *dev, const char *name)
static inline void mei_dbgfs_deregister(struct mei_device *dev) {}
#endif /* CONFIG_DEBUG_FS */
-int mei_register(struct mei_device *dev);
+int mei_register(struct mei_device *dev, struct device *parent);
void mei_deregister(struct mei_device *dev);
#define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d internal=%1d comp=%1d"
diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c
index 1b46c64a649f..f0da3c91f0a8 100644
--- a/drivers/misc/mei/pci-me.c
+++ b/drivers/misc/mei/pci-me.c
@@ -31,7 +31,6 @@
#include <linux/compat.h>
#include <linux/jiffies.h>
#include <linux/interrupt.h>
-#include <linux/miscdevice.h>
#include <linux/pm_runtime.h>
@@ -82,6 +81,7 @@ static const struct pci_device_id mei_me_pci_tbl[] = {
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_LP, mei_me_pch_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_LPT_HR, mei_me_lpt_cfg)},
{MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, mei_me_pch_cfg)},
+ {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP_2, mei_me_pch_cfg)},
/* required last entry */
{0, }
@@ -207,7 +207,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_ME_RPM_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
- err = mei_register(dev);
+ err = mei_register(dev, &pdev->dev);
if (err)
goto release_irq;
diff --git a/drivers/misc/mei/pci-txe.c b/drivers/misc/mei/pci-txe.c
index 2343c6236df9..d7480fe8994d 100644
--- a/drivers/misc/mei/pci-txe.c
+++ b/drivers/misc/mei/pci-txe.c
@@ -149,7 +149,7 @@ static int mei_txe_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
pm_runtime_set_autosuspend_delay(&pdev->dev, MEI_TXI_RPM_TIMEOUT);
pm_runtime_use_autosuspend(&pdev->dev);
- err = mei_register(dev);
+ err = mei_register(dev, &pdev->dev);
if (err)
goto release_irq;
diff --git a/drivers/misc/mic/Kconfig b/drivers/misc/mic/Kconfig
index 462a5b1d8651..cc4eef040c14 100644
--- a/drivers/misc/mic/Kconfig
+++ b/drivers/misc/mic/Kconfig
@@ -1,8 +1,25 @@
+comment "Intel MIC Bus Driver"
+
+config INTEL_MIC_BUS
+ tristate "Intel MIC Bus Driver"
+ depends on 64BIT && PCI && X86 && X86_DEV_DMA_OPS
+ help
+ This option is selected by any driver which registers a
+ device or driver on the MIC Bus, such as CONFIG_INTEL_MIC_HOST,
+ CONFIG_INTEL_MIC_CARD, CONFIG_INTEL_MIC_X100_DMA etc.
+
+ If you are building a host/card kernel with an Intel MIC device
+ then say M (recommended) or Y, else say N. If unsure say N.
+
+ More information about the Intel MIC family as well as the Linux
+ OS and tools for MIC to use with this driver are available from
+ <http://software.intel.com/en-us/mic-developer>.
+
comment "Intel MIC Host Driver"
config INTEL_MIC_HOST
tristate "Intel MIC Host Driver"
- depends on 64BIT && PCI && X86
+ depends on 64BIT && PCI && X86 && INTEL_MIC_BUS
select VHOST_RING
help
This enables Host Driver support for the Intel Many Integrated
@@ -22,7 +39,7 @@ comment "Intel MIC Card Driver"
config INTEL_MIC_CARD
tristate "Intel MIC Card Driver"
- depends on 64BIT && X86
+ depends on 64BIT && X86 && INTEL_MIC_BUS
select VIRTIO
help
This enables card driver support for the Intel Many Integrated
diff --git a/drivers/misc/mic/Makefile b/drivers/misc/mic/Makefile
index 05b34d683a58..e9bf148755e2 100644
--- a/drivers/misc/mic/Makefile
+++ b/drivers/misc/mic/Makefile
@@ -4,3 +4,4 @@
#
obj-$(CONFIG_INTEL_MIC_HOST) += host/
obj-$(CONFIG_INTEL_MIC_CARD) += card/
+obj-$(CONFIG_INTEL_MIC_BUS) += bus/
diff --git a/drivers/misc/mic/bus/Makefile b/drivers/misc/mic/bus/Makefile
new file mode 100644
index 000000000000..d85c7f2a0af4
--- /dev/null
+++ b/drivers/misc/mic/bus/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile - Intel MIC Linux driver.
+# Copyright(c) 2014, Intel Corporation.
+#
+obj-$(CONFIG_INTEL_MIC_BUS) += mic_bus.o
diff --git a/drivers/misc/mic/bus/mic_bus.c b/drivers/misc/mic/bus/mic_bus.c
new file mode 100644
index 000000000000..961ae90aae47
--- /dev/null
+++ b/drivers/misc/mic/bus/mic_bus.c
@@ -0,0 +1,218 @@
+/*
+ * Intel MIC Platform Software Stack (MPSS)
+ *
+ * Copyright(c) 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * The full GNU General Public License is included in this distribution in
+ * the file called "COPYING".
+ *
+ * Intel MIC Bus driver.
+ *
+ * This implementation is very similar to the the virtio bus driver
+ * implementation @ drivers/virtio/virtio.c
+ */
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/idr.h>
+#include <linux/mic_bus.h>
+
+/* Unique numbering for mbus devices. */
+static DEFINE_IDA(mbus_index_ida);
+
+static ssize_t device_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct mbus_device *dev = dev_to_mbus(d);
+ return sprintf(buf, "0x%04x\n", dev->id.device);
+}
+static DEVICE_ATTR_RO(device);
+
+static ssize_t vendor_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct mbus_device *dev = dev_to_mbus(d);
+ return sprintf(buf, "0x%04x\n", dev->id.vendor);
+}
+static DEVICE_ATTR_RO(vendor);
+
+static ssize_t modalias_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct mbus_device *dev = dev_to_mbus(d);
+ return sprintf(buf, "mbus:d%08Xv%08X\n",
+ dev->id.device, dev->id.vendor);
+}
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *mbus_dev_attrs[] = {
+ &dev_attr_device.attr,
+ &dev_attr_vendor.attr,
+ &dev_attr_modalias.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(mbus_dev);
+
+static inline int mbus_id_match(const struct mbus_device *dev,
+ const struct mbus_device_id *id)
+{
+ if (id->device != dev->id.device && id->device != MBUS_DEV_ANY_ID)
+ return 0;
+
+ return id->vendor == MBUS_DEV_ANY_ID || id->vendor == dev->id.vendor;
+}
+
+/*
+ * This looks through all the IDs a driver claims to support. If any of them
+ * match, we return 1 and the kernel will call mbus_dev_probe().
+ */
+static int mbus_dev_match(struct device *dv, struct device_driver *dr)
+{
+ unsigned int i;
+ struct mbus_device *dev = dev_to_mbus(dv);
+ const struct mbus_device_id *ids;
+
+ ids = drv_to_mbus(dr)->id_table;
+ for (i = 0; ids[i].device; i++)
+ if (mbus_id_match(dev, &ids[i]))
+ return 1;
+ return 0;
+}
+
+static int mbus_uevent(struct device *dv, struct kobj_uevent_env *env)
+{
+ struct mbus_device *dev = dev_to_mbus(dv);
+
+ return add_uevent_var(env, "MODALIAS=mbus:d%08Xv%08X",
+ dev->id.device, dev->id.vendor);
+}
+
+static int mbus_dev_probe(struct device *d)
+{
+ int err;
+ struct mbus_device *dev = dev_to_mbus(d);
+ struct mbus_driver *drv = drv_to_mbus(dev->dev.driver);
+
+ err = drv->probe(dev);
+ if (!err)
+ if (drv->scan)
+ drv->scan(dev);
+ return err;
+}
+
+static int mbus_dev_remove(struct device *d)
+{
+ struct mbus_device *dev = dev_to_mbus(d);
+ struct mbus_driver *drv = drv_to_mbus(dev->dev.driver);
+
+ drv->remove(dev);
+ return 0;
+}
+
+static struct bus_type mic_bus = {
+ .name = "mic_bus",
+ .match = mbus_dev_match,
+ .dev_groups = mbus_dev_groups,
+ .uevent = mbus_uevent,
+ .probe = mbus_dev_probe,
+ .remove = mbus_dev_remove,
+};
+
+int mbus_register_driver(struct mbus_driver *driver)
+{
+ driver->driver.bus = &mic_bus;
+ return driver_register(&driver->driver);
+}
+EXPORT_SYMBOL_GPL(mbus_register_driver);
+
+void mbus_unregister_driver(struct mbus_driver *driver)
+{
+ driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL_GPL(mbus_unregister_driver);
+
+static void mbus_release_dev(struct device *d)
+{
+ struct mbus_device *mbdev = dev_to_mbus(d);
+ kfree(mbdev);
+}
+
+struct mbus_device *
+mbus_register_device(struct device *pdev, int id, struct dma_map_ops *dma_ops,
+ struct mbus_hw_ops *hw_ops, void __iomem *mmio_va)
+{
+ int ret;
+ struct mbus_device *mbdev;
+
+ mbdev = kzalloc(sizeof(*mbdev), GFP_KERNEL);
+ if (!mbdev)
+ return ERR_PTR(-ENOMEM);
+
+ mbdev->mmio_va = mmio_va;
+ mbdev->dev.parent = pdev;
+ mbdev->id.device = id;
+ mbdev->id.vendor = MBUS_DEV_ANY_ID;
+ mbdev->dev.archdata.dma_ops = dma_ops;
+ mbdev->dev.dma_mask = &mbdev->dev.coherent_dma_mask;
+ dma_set_mask(&mbdev->dev, DMA_BIT_MASK(64));
+ mbdev->dev.release = mbus_release_dev;
+ mbdev->hw_ops = hw_ops;
+ mbdev->dev.bus = &mic_bus;
+
+ /* Assign a unique device index and hence name. */
+ ret = ida_simple_get(&mbus_index_ida, 0, 0, GFP_KERNEL);
+ if (ret < 0)
+ goto free_mbdev;
+
+ mbdev->index = ret;
+ dev_set_name(&mbdev->dev, "mbus-dev%u", mbdev->index);
+ /*
+ * device_register() causes the bus infrastructure to look for a
+ * matching driver.
+ */
+ ret = device_register(&mbdev->dev);
+ if (ret)
+ goto ida_remove;
+ return mbdev;
+ida_remove:
+ ida_simple_remove(&mbus_index_ida, mbdev->index);
+free_mbdev:
+ kfree(mbdev);
+ return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(mbus_register_device);
+
+void mbus_unregister_device(struct mbus_device *mbdev)
+{
+ int index = mbdev->index; /* save for after device release */
+
+ device_unregister(&mbdev->dev);
+ ida_simple_remove(&mbus_index_ida, index);
+}
+EXPORT_SYMBOL_GPL(mbus_unregister_device);
+
+static int __init mbus_init(void)
+{
+ return bus_register(&mic_bus);
+}
+
+static void __exit mbus_exit(void)
+{
+ bus_unregister(&mic_bus);
+ ida_destroy(&mbus_index_ida);
+}
+
+core_initcall(mbus_init);
+module_exit(mbus_exit);
+
+MODULE_AUTHOR("Intel Corporation");
+MODULE_DESCRIPTION("Intel(R) MIC Bus driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/mic/card/mic_device.c b/drivers/misc/mic/card/mic_device.c
index d0980ff96833..83819eee553b 100644
--- a/drivers/misc/mic/card/mic_device.c
+++ b/drivers/misc/mic/card/mic_device.c
@@ -83,8 +83,8 @@ static int mic_shutdown_init(void)
int shutdown_db;
shutdown_db = mic_next_card_db();
- shutdown_cookie = mic_request_card_irq(mic_shutdown_isr,
- "Shutdown", mdrv, shutdown_db);
+ shutdown_cookie = mic_request_card_irq(mic_shutdown_isr, NULL,
+ "Shutdown", mdrv, shutdown_db);
if (IS_ERR(shutdown_cookie))
rc = PTR_ERR(shutdown_cookie);
else
@@ -136,7 +136,8 @@ static void mic_dp_uninit(void)
/**
* mic_request_card_irq - request an irq.
*
- * @func: The callback function that handles the interrupt.
+ * @handler: interrupt handler passed to request_threaded_irq.
+ * @thread_fn: thread fn. passed to request_threaded_irq.
* @name: The ASCII name of the callee requesting the irq.
* @data: private data that is returned back when calling the
* function handler.
@@ -149,17 +150,19 @@ static void mic_dp_uninit(void)
* error code.
*
*/
-struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data),
- const char *name, void *data, int index)
+struct mic_irq *
+mic_request_card_irq(irq_handler_t handler,
+ irq_handler_t thread_fn, const char *name,
+ void *data, int index)
{
int rc = 0;
unsigned long cookie;
struct mic_driver *mdrv = g_drv;
- rc = request_irq(mic_db_to_irq(mdrv, index), func,
- 0, name, data);
+ rc = request_threaded_irq(mic_db_to_irq(mdrv, index), handler,
+ thread_fn, 0, name, data);
if (rc) {
- dev_err(mdrv->dev, "request_irq failed rc = %d\n", rc);
+ dev_err(mdrv->dev, "request_threaded_irq failed rc = %d\n", rc);
goto err;
}
mdrv->irq_info.irq_usage_count[index]++;
@@ -172,9 +175,9 @@ err:
/**
* mic_free_card_irq - free irq.
*
- * @cookie: cookie obtained during a successful call to mic_request_irq
+ * @cookie: cookie obtained during a successful call to mic_request_threaded_irq
* @data: private data specified by the calling function during the
- * mic_request_irq
+ * mic_request_threaded_irq
*
* returns: none.
*/
diff --git a/drivers/misc/mic/card/mic_device.h b/drivers/misc/mic/card/mic_device.h
index 306f502be95e..844be8fc9b22 100644
--- a/drivers/misc/mic/card/mic_device.h
+++ b/drivers/misc/mic/card/mic_device.h
@@ -30,6 +30,8 @@
#include <linux/workqueue.h>
#include <linux/io.h>
#include <linux/irqreturn.h>
+#include <linux/interrupt.h>
+#include <linux/mic_bus.h>
/**
* struct mic_intr_info - Contains h/w specific interrupt sources info
@@ -70,6 +72,7 @@ struct mic_device {
* @hotplug_work: Hot plug work for adding/removing virtio devices.
* @irq_info: The OS specific irq information
* @intr_info: H/W specific interrupt information.
+ * @dma_mbdev: dma device on the MIC virtual bus.
*/
struct mic_driver {
char name[20];
@@ -80,6 +83,7 @@ struct mic_driver {
struct work_struct hotplug_work;
struct mic_irq_info irq_info;
struct mic_intr_info intr_info;
+ struct mbus_device *dma_mbdev;
};
/**
@@ -116,8 +120,9 @@ mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset)
int mic_driver_init(struct mic_driver *mdrv);
void mic_driver_uninit(struct mic_driver *mdrv);
int mic_next_card_db(void);
-struct mic_irq *mic_request_card_irq(irqreturn_t (*func)(int irq, void *data),
- const char *name, void *data, int intr_src);
+struct mic_irq *
+mic_request_card_irq(irq_handler_t handler, irq_handler_t thread_fn,
+ const char *name, void *data, int intr_src);
void mic_free_card_irq(struct mic_irq *cookie, void *data);
u32 mic_read_spad(struct mic_device *mdev, unsigned int idx);
void mic_send_intr(struct mic_device *mdev, int doorbell);
diff --git a/drivers/misc/mic/card/mic_virtio.c b/drivers/misc/mic/card/mic_virtio.c
index 653799b96bfa..f14b60080c21 100644
--- a/drivers/misc/mic/card/mic_virtio.c
+++ b/drivers/misc/mic/card/mic_virtio.c
@@ -417,7 +417,7 @@ static int mic_add_device(struct mic_device_desc __iomem *d,
virtio_db = mic_next_card_db();
mvdev->virtio_cookie = mic_request_card_irq(mic_virtio_intr_handler,
- "virtio intr", mvdev, virtio_db);
+ NULL, "virtio intr", mvdev, virtio_db);
if (IS_ERR(mvdev->virtio_cookie)) {
ret = PTR_ERR(mvdev->virtio_cookie);
goto kfree;
@@ -606,8 +606,9 @@ int mic_devices_init(struct mic_driver *mdrv)
mic_scan_devices(mdrv, !REMOVE_DEVICES);
config_db = mic_next_card_db();
- virtio_config_cookie = mic_request_card_irq(mic_extint_handler,
- "virtio_config_intr", mdrv, config_db);
+ virtio_config_cookie = mic_request_card_irq(mic_extint_handler, NULL,
+ "virtio_config_intr", mdrv,
+ config_db);
if (IS_ERR(virtio_config_cookie)) {
rc = PTR_ERR(virtio_config_cookie);
goto exit;
diff --git a/drivers/misc/mic/card/mic_x100.c b/drivers/misc/mic/card/mic_x100.c
index 2868945c9a4d..55c9465bd260 100644
--- a/drivers/misc/mic/card/mic_x100.c
+++ b/drivers/misc/mic/card/mic_x100.c
@@ -148,6 +148,47 @@ void mic_card_unmap(struct mic_device *mdev, void __iomem *addr)
iounmap(addr);
}
+static inline struct mic_driver *mbdev_to_mdrv(struct mbus_device *mbdev)
+{
+ return dev_get_drvdata(mbdev->dev.parent);
+}
+
+static struct mic_irq *
+_mic_request_threaded_irq(struct mbus_device *mbdev,
+ irq_handler_t handler, irq_handler_t thread_fn,
+ const char *name, void *data, int intr_src)
+{
+ int rc = 0;
+ unsigned int irq = intr_src;
+ unsigned long cookie = irq;
+
+ rc = request_threaded_irq(irq, handler, thread_fn, 0, name, data);
+ if (rc) {
+ dev_err(mbdev_to_mdrv(mbdev)->dev,
+ "request_threaded_irq failed rc = %d\n", rc);
+ return ERR_PTR(rc);
+ }
+ return (struct mic_irq *)cookie;
+}
+
+static void _mic_free_irq(struct mbus_device *mbdev,
+ struct mic_irq *cookie, void *data)
+{
+ unsigned long irq = (unsigned long)cookie;
+ free_irq(irq, data);
+}
+
+static void _mic_ack_interrupt(struct mbus_device *mbdev, int num)
+{
+ mic_ack_interrupt(&mbdev_to_mdrv(mbdev)->mdev);
+}
+
+static struct mbus_hw_ops mbus_hw_ops = {
+ .request_threaded_irq = _mic_request_threaded_irq,
+ .free_irq = _mic_free_irq,
+ .ack_interrupt = _mic_ack_interrupt,
+};
+
static int __init mic_probe(struct platform_device *pdev)
{
struct mic_driver *mdrv = &g_drv;
@@ -166,13 +207,24 @@ static int __init mic_probe(struct platform_device *pdev)
goto done;
}
mic_hw_intr_init(mdrv);
+ platform_set_drvdata(pdev, mdrv);
+ mdrv->dma_mbdev = mbus_register_device(mdrv->dev, MBUS_DEV_DMA_MIC,
+ NULL, &mbus_hw_ops,
+ mdrv->mdev.mmio.va);
+ if (IS_ERR(mdrv->dma_mbdev)) {
+ rc = PTR_ERR(mdrv->dma_mbdev);
+ dev_err(&pdev->dev, "mbus_add_device failed rc %d\n", rc);
+ goto iounmap;
+ }
rc = mic_driver_init(mdrv);
if (rc) {
dev_err(&pdev->dev, "mic_driver_init failed rc %d\n", rc);
- goto iounmap;
+ goto remove_dma;
}
done:
return rc;
+remove_dma:
+ mbus_unregister_device(mdrv->dma_mbdev);
iounmap:
iounmap(mdev->mmio.va);
return rc;
@@ -184,6 +236,7 @@ static int mic_remove(struct platform_device *pdev)
struct mic_device *mdev = &mdrv->mdev;
mic_driver_uninit(mdrv);
+ mbus_unregister_device(mdrv->dma_mbdev);
iounmap(mdev->mmio.va);
return 0;
}
diff --git a/drivers/misc/mic/host/mic_boot.c b/drivers/misc/mic/host/mic_boot.c
index b75c6b5cc20f..ff2b0fb1a6be 100644
--- a/drivers/misc/mic/host/mic_boot.c
+++ b/drivers/misc/mic/host/mic_boot.c
@@ -23,11 +23,70 @@
#include <linux/pci.h>
#include <linux/mic_common.h>
+#include <linux/mic_bus.h>
#include "../common/mic_dev.h"
#include "mic_device.h"
#include "mic_smpt.h"
#include "mic_virtio.h"
+static inline struct mic_device *mbdev_to_mdev(struct mbus_device *mbdev)
+{
+ return dev_get_drvdata(mbdev->dev.parent);
+}
+
+static dma_addr_t
+mic_dma_map_page(struct device *dev, struct page *page,
+ unsigned long offset, size_t size, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ void *va = phys_to_virt(page_to_phys(page)) + offset;
+ struct mic_device *mdev = dev_get_drvdata(dev->parent);
+
+ return mic_map_single(mdev, va, size);
+}
+
+static void
+mic_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
+ size_t size, enum dma_data_direction dir,
+ struct dma_attrs *attrs)
+{
+ struct mic_device *mdev = dev_get_drvdata(dev->parent);
+ mic_unmap_single(mdev, dma_addr, size);
+}
+
+static struct dma_map_ops mic_dma_ops = {
+ .map_page = mic_dma_map_page,
+ .unmap_page = mic_dma_unmap_page,
+};
+
+static struct mic_irq *
+_mic_request_threaded_irq(struct mbus_device *mbdev,
+ irq_handler_t handler, irq_handler_t thread_fn,
+ const char *name, void *data, int intr_src)
+{
+ return mic_request_threaded_irq(mbdev_to_mdev(mbdev), handler,
+ thread_fn, name, data,
+ intr_src, MIC_INTR_DMA);
+}
+
+static void _mic_free_irq(struct mbus_device *mbdev,
+ struct mic_irq *cookie, void *data)
+{
+ return mic_free_irq(mbdev_to_mdev(mbdev), cookie, data);
+}
+
+static void _mic_ack_interrupt(struct mbus_device *mbdev, int num)
+{
+ struct mic_device *mdev = mbdev_to_mdev(mbdev);
+ mdev->ops->intr_workarounds(mdev);
+}
+
+static struct mbus_hw_ops mbus_hw_ops = {
+ .request_threaded_irq = _mic_request_threaded_irq,
+ .free_irq = _mic_free_irq,
+ .ack_interrupt = _mic_ack_interrupt,
+};
+
/**
* mic_reset - Reset the MIC device.
* @mdev: pointer to mic_device instance
@@ -95,9 +154,21 @@ retry:
*/
goto retry;
}
+ mdev->dma_mbdev = mbus_register_device(mdev->sdev->parent,
+ MBUS_DEV_DMA_HOST, &mic_dma_ops,
+ &mbus_hw_ops, mdev->mmio.va);
+ if (IS_ERR(mdev->dma_mbdev)) {
+ rc = PTR_ERR(mdev->dma_mbdev);
+ goto unlock_ret;
+ }
+ mdev->dma_ch = mic_request_dma_chan(mdev);
+ if (!mdev->dma_ch) {
+ rc = -ENXIO;
+ goto dma_remove;
+ }
rc = mdev->ops->load_mic_fw(mdev, buf);
if (rc)
- goto unlock_ret;
+ goto dma_release;
mic_smpt_restore(mdev);
mic_intr_restore(mdev);
mdev->intr_ops->enable_interrupts(mdev);
@@ -105,6 +176,11 @@ retry:
mdev->ops->write_spad(mdev, MIC_DPHI_SPAD, mdev->dp_dma_addr >> 32);
mdev->ops->send_firmware_intr(mdev);
mic_set_state(mdev, MIC_ONLINE);
+ goto unlock_ret;
+dma_release:
+ dma_release_channel(mdev->dma_ch);
+dma_remove:
+ mbus_unregister_device(mdev->dma_mbdev);
unlock_ret:
mutex_unlock(&mdev->mic_mutex);
return rc;
@@ -122,6 +198,11 @@ void mic_stop(struct mic_device *mdev, bool force)
mutex_lock(&mdev->mic_mutex);
if (MIC_OFFLINE != mdev->state || force) {
mic_virtio_reset_devices(mdev);
+ if (mdev->dma_ch) {
+ dma_release_channel(mdev->dma_ch);
+ mdev->dma_ch = NULL;
+ }
+ mbus_unregister_device(mdev->dma_mbdev);
mic_bootparam_init(mdev);
mic_reset(mdev);
if (MIC_RESET_FAILED == mdev->state)
diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h
index 0398c696d257..016bd15a7bd1 100644
--- a/drivers/misc/mic/host/mic_device.h
+++ b/drivers/misc/mic/host/mic_device.h
@@ -25,6 +25,8 @@
#include <linux/idr.h>
#include <linux/notifier.h>
#include <linux/irqreturn.h>
+#include <linux/dmaengine.h>
+#include <linux/mic_bus.h>
#include "mic_intr.h"
@@ -87,6 +89,8 @@ enum mic_stepping {
* @cdev: Character device for MIC.
* @vdev_list: list of virtio devices.
* @pm_notifier: Handles PM notifications from the OS.
+ * @dma_mbdev: MIC BUS DMA device.
+ * @dma_ch: DMA channel reserved by this driver for use by virtio devices.
*/
struct mic_device {
struct mic_mw mmio;
@@ -124,6 +128,8 @@ struct mic_device {
struct cdev cdev;
struct list_head vdev_list;
struct notifier_block pm_notifier;
+ struct mbus_device *dma_mbdev;
+ struct dma_chan *dma_ch;
};
/**
@@ -144,6 +150,7 @@ struct mic_device {
* @load_mic_fw: Load firmware segments required to boot the card
* into card memory. This includes the kernel, command line, ramdisk etc.
* @get_postcode: Get post code status from firmware.
+ * @dma_filter: DMA filter function to be used.
*/
struct mic_hw_ops {
u8 aper_bar;
@@ -159,6 +166,7 @@ struct mic_hw_ops {
void (*send_firmware_intr)(struct mic_device *mdev);
int (*load_mic_fw)(struct mic_device *mdev, const char *buf);
u32 (*get_postcode)(struct mic_device *mdev);
+ bool (*dma_filter)(struct dma_chan *chan, void *param);
};
/**
@@ -187,6 +195,22 @@ mic_mmio_write(struct mic_mw *mw, u32 val, u32 offset)
iowrite32(val, mw->va + offset);
}
+static inline struct dma_chan *mic_request_dma_chan(struct mic_device *mdev)
+{
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ chan = dma_request_channel(mask, mdev->ops->dma_filter,
+ mdev->sdev->parent);
+ if (chan)
+ return chan;
+ dev_err(mdev->sdev->parent, "%s %d unable to acquire channel\n",
+ __func__, __LINE__);
+ return NULL;
+}
+
void mic_sysfs_init(struct mic_device *mdev);
int mic_start(struct mic_device *mdev, const char *buf);
void mic_stop(struct mic_device *mdev, bool force);
diff --git a/drivers/misc/mic/host/mic_intr.c b/drivers/misc/mic/host/mic_intr.c
index dbc5afde1392..d686f2846ac7 100644
--- a/drivers/misc/mic/host/mic_intr.c
+++ b/drivers/misc/mic/host/mic_intr.c
@@ -24,28 +24,29 @@
#include "../common/mic_dev.h"
#include "mic_device.h"
-/*
- * mic_invoke_callback - Invoke callback functions registered for
- * the corresponding source id.
- *
- * @mdev: pointer to the mic_device instance
- * @idx: The interrupt source id.
- *
- * Returns none.
- */
-static inline void mic_invoke_callback(struct mic_device *mdev, int idx)
+static irqreturn_t mic_thread_fn(int irq, void *dev)
{
+ struct mic_device *mdev = dev;
+ struct mic_intr_info *intr_info = mdev->intr_info;
+ struct mic_irq_info *irq_info = &mdev->irq_info;
struct mic_intr_cb *intr_cb;
struct pci_dev *pdev = container_of(mdev->sdev->parent,
- struct pci_dev, dev);
+ struct pci_dev, dev);
+ int i;
- spin_lock(&mdev->irq_info.mic_intr_lock);
- list_for_each_entry(intr_cb, &mdev->irq_info.cb_list[idx], list)
- if (intr_cb->func)
- intr_cb->func(pdev->irq, intr_cb->data);
- spin_unlock(&mdev->irq_info.mic_intr_lock);
+ spin_lock(&irq_info->mic_thread_lock);
+ for (i = intr_info->intr_start_idx[MIC_INTR_DB];
+ i < intr_info->intr_len[MIC_INTR_DB]; i++)
+ if (test_and_clear_bit(i, &irq_info->mask)) {
+ list_for_each_entry(intr_cb, &irq_info->cb_list[i],
+ list)
+ if (intr_cb->thread_fn)
+ intr_cb->thread_fn(pdev->irq,
+ intr_cb->data);
+ }
+ spin_unlock(&irq_info->mic_thread_lock);
+ return IRQ_HANDLED;
}
-
/**
* mic_interrupt - Generic interrupt handler for
* MSI and INTx based interrupts.
@@ -53,7 +54,11 @@ static inline void mic_invoke_callback(struct mic_device *mdev, int idx)
static irqreturn_t mic_interrupt(int irq, void *dev)
{
struct mic_device *mdev = dev;
- struct mic_intr_info *info = mdev->intr_info;
+ struct mic_intr_info *intr_info = mdev->intr_info;
+ struct mic_irq_info *irq_info = &mdev->irq_info;
+ struct mic_intr_cb *intr_cb;
+ struct pci_dev *pdev = container_of(mdev->sdev->parent,
+ struct pci_dev, dev);
u32 mask;
int i;
@@ -61,12 +66,19 @@ static irqreturn_t mic_interrupt(int irq, void *dev)
if (!mask)
return IRQ_NONE;
- for (i = info->intr_start_idx[MIC_INTR_DB];
- i < info->intr_len[MIC_INTR_DB]; i++)
- if (mask & BIT(i))
- mic_invoke_callback(mdev, i);
-
- return IRQ_HANDLED;
+ spin_lock(&irq_info->mic_intr_lock);
+ for (i = intr_info->intr_start_idx[MIC_INTR_DB];
+ i < intr_info->intr_len[MIC_INTR_DB]; i++)
+ if (mask & BIT(i)) {
+ list_for_each_entry(intr_cb, &irq_info->cb_list[i],
+ list)
+ if (intr_cb->handler)
+ intr_cb->handler(pdev->irq,
+ intr_cb->data);
+ set_bit(i, &irq_info->mask);
+ }
+ spin_unlock(&irq_info->mic_intr_lock);
+ return IRQ_WAKE_THREAD;
}
/* Return the interrupt offset from the index. Index is 0 based. */
@@ -99,14 +111,15 @@ static struct msix_entry *mic_get_available_vector(struct mic_device *mdev)
*
* @mdev: pointer to the mic_device instance
* @idx: The source id to be registered.
- * @func: The function to be called when the source id receives
+ * @handler: The function to be called when the source id receives
* the interrupt.
+ * @thread_fn: thread fn. corresponding to the handler
* @data: Private data of the requester.
* Return the callback structure that was registered or an
* appropriate error on failure.
*/
static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev,
- u8 idx, irqreturn_t (*func) (int irq, void *dev),
+ u8 idx, irq_handler_t handler, irq_handler_t thread_fn,
void *data)
{
struct mic_intr_cb *intr_cb;
@@ -117,7 +130,8 @@ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev,
if (!intr_cb)
return ERR_PTR(-ENOMEM);
- intr_cb->func = func;
+ intr_cb->handler = handler;
+ intr_cb->thread_fn = thread_fn;
intr_cb->data = data;
intr_cb->cb_id = ida_simple_get(&mdev->irq_info.cb_ida,
0, 0, GFP_KERNEL);
@@ -126,9 +140,11 @@ static struct mic_intr_cb *mic_register_intr_callback(struct mic_device *mdev,
goto ida_fail;
}
+ spin_lock(&mdev->irq_info.mic_thread_lock);
spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
list_add_tail(&intr_cb->list, &mdev->irq_info.cb_list[idx]);
spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
+ spin_unlock(&mdev->irq_info.mic_thread_lock);
return intr_cb;
ida_fail:
@@ -152,8 +168,9 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx)
unsigned long flags;
int i;
+ spin_lock(&mdev->irq_info.mic_thread_lock);
+ spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
for (i = 0; i < MIC_NUM_OFFSETS; i++) {
- spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) {
intr_cb = list_entry(pos, struct mic_intr_cb, list);
if (intr_cb->cb_id == idx) {
@@ -163,11 +180,13 @@ static u8 mic_unregister_intr_callback(struct mic_device *mdev, u32 idx)
kfree(intr_cb);
spin_unlock_irqrestore(
&mdev->irq_info.mic_intr_lock, flags);
+ spin_unlock(&mdev->irq_info.mic_thread_lock);
return i;
}
}
- spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
}
+ spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
+ spin_unlock(&mdev->irq_info.mic_thread_lock);
return MIC_NUM_OFFSETS;
}
@@ -242,6 +261,7 @@ static int mic_setup_callbacks(struct mic_device *mdev)
INIT_LIST_HEAD(&mdev->irq_info.cb_list[i]);
ida_init(&mdev->irq_info.cb_ida);
spin_lock_init(&mdev->irq_info.mic_intr_lock);
+ spin_lock_init(&mdev->irq_info.mic_thread_lock);
return 0;
}
@@ -258,14 +278,12 @@ static void mic_release_callbacks(struct mic_device *mdev)
struct mic_intr_cb *intr_cb;
int i;
+ spin_lock(&mdev->irq_info.mic_thread_lock);
+ spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
for (i = 0; i < MIC_NUM_OFFSETS; i++) {
- spin_lock_irqsave(&mdev->irq_info.mic_intr_lock, flags);
- if (list_empty(&mdev->irq_info.cb_list[i])) {
- spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock,
- flags);
+ if (list_empty(&mdev->irq_info.cb_list[i]))
break;
- }
list_for_each_safe(pos, tmp, &mdev->irq_info.cb_list[i]) {
intr_cb = list_entry(pos, struct mic_intr_cb, list);
@@ -274,8 +292,9 @@ static void mic_release_callbacks(struct mic_device *mdev)
intr_cb->cb_id);
kfree(intr_cb);
}
- spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
}
+ spin_unlock_irqrestore(&mdev->irq_info.mic_intr_lock, flags);
+ spin_unlock(&mdev->irq_info.mic_thread_lock);
ida_destroy(&mdev->irq_info.cb_ida);
kfree(mdev->irq_info.cb_list);
}
@@ -313,7 +332,8 @@ static int mic_setup_msi(struct mic_device *mdev, struct pci_dev *pdev)
goto err_nomem2;
}
- rc = request_irq(pdev->irq, mic_interrupt, 0 , "mic-msi", mdev);
+ rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn,
+ 0, "mic-msi", mdev);
if (rc) {
dev_err(&pdev->dev, "Error allocating MSI interrupt\n");
goto err_irq_req_fail;
@@ -353,8 +373,8 @@ static int mic_setup_intx(struct mic_device *mdev, struct pci_dev *pdev)
goto err_nomem;
}
- rc = request_irq(pdev->irq, mic_interrupt,
- IRQF_SHARED, "mic-intx", mdev);
+ rc = request_threaded_irq(pdev->irq, mic_interrupt, mic_thread_fn,
+ IRQF_SHARED, "mic-intx", mdev);
if (rc)
goto err;
@@ -391,13 +411,14 @@ int mic_next_db(struct mic_device *mdev)
#define MK_COOKIE(x, y) ((x) | (y) << COOKIE_ID_SHIFT)
/**
- * mic_request_irq - request an irq. mic_mutex needs
+ * mic_request_threaded_irq - request an irq. mic_mutex needs
* to be held before calling this function.
*
* @mdev: pointer to mic_device instance
- * @func: The callback function that handles the interrupt.
+ * @handler: The callback function that handles the interrupt.
* The function needs to call ack_interrupts
* (mdev->ops->ack_interrupt(mdev)) when handling the interrupts.
+ * @thread_fn: thread fn required by request_threaded_irq.
* @name: The ASCII name of the callee requesting the irq.
* @data: private data that is returned back when calling the
* function handler.
@@ -412,10 +433,11 @@ int mic_next_db(struct mic_device *mdev)
* error code.
*
*/
-struct mic_irq *mic_request_irq(struct mic_device *mdev,
- irqreturn_t (*func)(int irq, void *dev),
- const char *name, void *data, int intr_src,
- enum mic_intr_type type)
+struct mic_irq *
+mic_request_threaded_irq(struct mic_device *mdev,
+ irq_handler_t handler, irq_handler_t thread_fn,
+ const char *name, void *data, int intr_src,
+ enum mic_intr_type type)
{
u16 offset;
int rc = 0;
@@ -444,7 +466,8 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev,
goto err;
}
- rc = request_irq(msix->vector, func, 0, name, data);
+ rc = request_threaded_irq(msix->vector, handler, thread_fn,
+ 0, name, data);
if (rc) {
dev_dbg(mdev->sdev->parent,
"request irq failed rc = %d\n", rc);
@@ -458,8 +481,8 @@ struct mic_irq *mic_request_irq(struct mic_device *mdev,
dev_dbg(mdev->sdev->parent, "irq: %d assigned for src: %d\n",
msix->vector, intr_src);
} else {
- intr_cb = mic_register_intr_callback(mdev,
- offset, func, data);
+ intr_cb = mic_register_intr_callback(mdev, offset, handler,
+ thread_fn, data);
if (IS_ERR(intr_cb)) {
dev_err(mdev->sdev->parent,
"No available callback entries for use\n");
@@ -487,9 +510,9 @@ err:
* needs to be held before calling this function.
*
* @mdev: pointer to mic_device instance
- * @cookie: cookie obtained during a successful call to mic_request_irq
+ * @cookie: cookie obtained during a successful call to mic_request_threaded_irq
* @data: private data specified by the calling function during the
- * mic_request_irq
+ * mic_request_threaded_irq
*
* returns: none.
*/
diff --git a/drivers/misc/mic/host/mic_intr.h b/drivers/misc/mic/host/mic_intr.h
index 6091aa97e116..9f783d4ad7f1 100644
--- a/drivers/misc/mic/host/mic_intr.h
+++ b/drivers/misc/mic/host/mic_intr.h
@@ -21,12 +21,15 @@
#ifndef _MIC_INTR_H_
#define _MIC_INTR_H_
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
/*
* The minimum number of msix vectors required for normal operation.
* 3 for virtio network, console and block devices.
* 1 for card shutdown notifications.
+ * 4 for host owned DMA channels.
*/
-#define MIC_MIN_MSIX 4
+#define MIC_MIN_MSIX 8
#define MIC_NUM_OFFSETS 32
/**
@@ -68,7 +71,11 @@ struct mic_intr_info {
* @num_vectors: The number of MSI/MSI-x vectors that have been allocated.
* @cb_ida: callback ID allocator to track the callbacks registered.
* @mic_intr_lock: spinlock to protect the interrupt callback list.
+ * @mic_thread_lock: spinlock to protect the thread callback list.
+ * This lock is used to protect against thread_fn while
+ * mic_intr_lock is used to protect against interrupt handler.
* @cb_list: Array of callback lists one for each source.
+ * @mask: Mask used by the main thread fn to call the underlying thread fns.
*/
struct mic_irq_info {
int next_avail_src;
@@ -77,19 +84,23 @@ struct mic_irq_info {
u16 num_vectors;
struct ida cb_ida;
spinlock_t mic_intr_lock;
+ spinlock_t mic_thread_lock;
struct list_head *cb_list;
+ unsigned long mask;
};
/**
* struct mic_intr_cb - Interrupt callback structure.
*
- * @func: The callback function
+ * @handler: The callback function
+ * @thread_fn: The thread_fn.
* @data: Private data of the requester.
* @cb_id: The callback id. Identifies this callback.
* @list: list head pointing to the next callback structure.
*/
struct mic_intr_cb {
- irqreturn_t (*func) (int irq, void *data);
+ irq_handler_t handler;
+ irq_handler_t thread_fn;
void *data;
int cb_id;
struct list_head list;
@@ -124,11 +135,11 @@ struct mic_hw_intr_ops {
};
int mic_next_db(struct mic_device *mdev);
-struct mic_irq *mic_request_irq(struct mic_device *mdev,
- irqreturn_t (*func)(int irq, void *data),
- const char *name, void *data, int intr_src,
- enum mic_intr_type type);
-
+struct mic_irq *
+mic_request_threaded_irq(struct mic_device *mdev,
+ irq_handler_t handler, irq_handler_t thread_fn,
+ const char *name, void *data, int intr_src,
+ enum mic_intr_type type);
void mic_free_irq(struct mic_device *mdev,
struct mic_irq *cookie, void *data);
int mic_setup_interrupts(struct mic_device *mdev, struct pci_dev *pdev);
diff --git a/drivers/misc/mic/host/mic_main.c b/drivers/misc/mic/host/mic_main.c
index c04a021e20c7..fdc9c13430e7 100644
--- a/drivers/misc/mic/host/mic_main.c
+++ b/drivers/misc/mic/host/mic_main.c
@@ -389,8 +389,9 @@ static int mic_probe(struct pci_dev *pdev,
mutex_lock(&mdev->mic_mutex);
mdev->shutdown_db = mic_next_db(mdev);
- mdev->shutdown_cookie = mic_request_irq(mdev, mic_shutdown_db,
- "shutdown-interrupt", mdev, mdev->shutdown_db, MIC_INTR_DB);
+ mdev->shutdown_cookie = mic_request_threaded_irq(mdev, mic_shutdown_db,
+ NULL, "shutdown-interrupt", mdev,
+ mdev->shutdown_db, MIC_INTR_DB);
if (IS_ERR(mdev->shutdown_cookie)) {
rc = PTR_ERR(mdev->shutdown_cookie);
mutex_unlock(&mdev->mic_mutex);
diff --git a/drivers/misc/mic/host/mic_virtio.c b/drivers/misc/mic/host/mic_virtio.c
index 7e1ef0ebbb80..a020e4eb435a 100644
--- a/drivers/misc/mic/host/mic_virtio.c
+++ b/drivers/misc/mic/host/mic_virtio.c
@@ -21,60 +21,157 @@
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
-
+#include <linux/dmaengine.h>
#include <linux/mic_common.h>
+
#include "../common/mic_dev.h"
#include "mic_device.h"
#include "mic_smpt.h"
#include "mic_virtio.h"
/*
- * Initiates the copies across the PCIe bus from card memory to
- * a user space buffer.
+ * Size of the internal buffer used during DMA's as an intermediate buffer
+ * for copy to/from user.
*/
-static int mic_virtio_copy_to_user(struct mic_vdev *mvdev,
- void __user *ubuf, size_t len, u64 addr)
+#define MIC_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL)
+
+static int mic_sync_dma(struct mic_device *mdev, dma_addr_t dst,
+ dma_addr_t src, size_t len)
{
- int err;
- void __iomem *dbuf = mvdev->mdev->aper.va + addr;
- /*
- * We are copying from IO below an should ideally use something
- * like copy_to_user_fromio(..) if it existed.
- */
- if (copy_to_user(ubuf, (void __force *)dbuf, len)) {
- err = -EFAULT;
- dev_err(mic_dev(mvdev), "%s %d err %d\n",
+ int err = 0;
+ struct dma_async_tx_descriptor *tx;
+ struct dma_chan *mic_ch = mdev->dma_ch;
+
+ if (!mic_ch) {
+ err = -EBUSY;
+ goto error;
+ }
+
+ tx = mic_ch->device->device_prep_dma_memcpy(mic_ch, dst, src, len,
+ DMA_PREP_FENCE);
+ if (!tx) {
+ err = -ENOMEM;
+ goto error;
+ } else {
+ dma_cookie_t cookie = tx->tx_submit(tx);
+
+ err = dma_submit_error(cookie);
+ if (err)
+ goto error;
+ err = dma_sync_wait(mic_ch, cookie);
+ }
+error:
+ if (err)
+ dev_err(mdev->sdev->parent, "%s %d err %d\n",
__func__, __LINE__, err);
- goto err;
+ return err;
+}
+
+/*
+ * Initiates the copies across the PCIe bus from card memory to a user
+ * space buffer. When transfers are done using DMA, source/destination
+ * addresses and transfer length must follow the alignment requirements of
+ * the MIC DMA engine.
+ */
+static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, void __user *ubuf,
+ size_t len, u64 daddr, size_t dlen,
+ int vr_idx)
+{
+ struct mic_device *mdev = mvdev->mdev;
+ void __iomem *dbuf = mdev->aper.va + daddr;
+ struct mic_vringh *mvr = &mvdev->mvr[vr_idx];
+ size_t dma_alignment = 1 << mdev->dma_ch->device->copy_align;
+ size_t dma_offset;
+ size_t partlen;
+ int err;
+
+ dma_offset = daddr - round_down(daddr, dma_alignment);
+ daddr -= dma_offset;
+ len += dma_offset;
+
+ while (len) {
+ partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE);
+
+ err = mic_sync_dma(mdev, mvr->buf_da, daddr,
+ ALIGN(partlen, dma_alignment));
+ if (err)
+ goto err;
+
+ if (copy_to_user(ubuf, mvr->buf + dma_offset,
+ partlen - dma_offset)) {
+ err = -EFAULT;
+ goto err;
+ }
+ daddr += partlen;
+ ubuf += partlen;
+ dbuf += partlen;
+ mvdev->in_bytes_dma += partlen;
+ mvdev->in_bytes += partlen;
+ len -= partlen;
+ dma_offset = 0;
}
- mvdev->in_bytes += len;
- err = 0;
+ return 0;
err:
+ dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err);
return err;
}
/*
- * Initiates copies across the PCIe bus from a user space
- * buffer to card memory.
+ * Initiates copies across the PCIe bus from a user space buffer to card
+ * memory. When transfers are done using DMA, source/destination addresses
+ * and transfer length must follow the alignment requirements of the MIC
+ * DMA engine.
*/
-static int mic_virtio_copy_from_user(struct mic_vdev *mvdev,
- void __user *ubuf, size_t len, u64 addr)
+static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, void __user *ubuf,
+ size_t len, u64 daddr, size_t dlen,
+ int vr_idx)
{
+ struct mic_device *mdev = mvdev->mdev;
+ void __iomem *dbuf = mdev->aper.va + daddr;
+ struct mic_vringh *mvr = &mvdev->mvr[vr_idx];
+ size_t dma_alignment = 1 << mdev->dma_ch->device->copy_align;
+ size_t partlen;
int err;
- void __iomem *dbuf = mvdev->mdev->aper.va + addr;
+
+ if (daddr & (dma_alignment - 1)) {
+ mvdev->tx_dst_unaligned += len;
+ goto memcpy;
+ } else if (ALIGN(len, dma_alignment) > dlen) {
+ mvdev->tx_len_unaligned += len;
+ goto memcpy;
+ }
+
+ while (len) {
+ partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE);
+
+ if (copy_from_user(mvr->buf, ubuf, partlen)) {
+ err = -EFAULT;
+ goto err;
+ }
+ err = mic_sync_dma(mdev, daddr, mvr->buf_da,
+ ALIGN(partlen, dma_alignment));
+ if (err)
+ goto err;
+ daddr += partlen;
+ ubuf += partlen;
+ dbuf += partlen;
+ mvdev->out_bytes_dma += partlen;
+ mvdev->out_bytes += partlen;
+ len -= partlen;
+ }
+memcpy:
/*
* We are copying to IO below and should ideally use something
* like copy_from_user_toio(..) if it existed.
*/
if (copy_from_user((void __force *)dbuf, ubuf, len)) {
err = -EFAULT;
- dev_err(mic_dev(mvdev), "%s %d err %d\n",
- __func__, __LINE__, err);
goto err;
}
mvdev->out_bytes += len;
- err = 0;
+ return 0;
err:
+ dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err);
return err;
}
@@ -110,7 +207,8 @@ static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov)
* way to override the VRINGH xfer(..) routines as of v3.10.
*/
static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov,
- void __user *ubuf, size_t len, bool read, size_t *out_len)
+ void __user *ubuf, size_t len, bool read, int vr_idx,
+ size_t *out_len)
{
int ret = 0;
size_t partlen, tot_len = 0;
@@ -118,13 +216,15 @@ static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov,
while (len && iov->i < iov->used) {
partlen = min(iov->iov[iov->i].iov_len, len);
if (read)
- ret = mic_virtio_copy_to_user(mvdev,
- ubuf, partlen,
- (u64)iov->iov[iov->i].iov_base);
+ ret = mic_virtio_copy_to_user(mvdev, ubuf, partlen,
+ (u64)iov->iov[iov->i].iov_base,
+ iov->iov[iov->i].iov_len,
+ vr_idx);
else
- ret = mic_virtio_copy_from_user(mvdev,
- ubuf, partlen,
- (u64)iov->iov[iov->i].iov_base);
+ ret = mic_virtio_copy_from_user(mvdev, ubuf, partlen,
+ (u64)iov->iov[iov->i].iov_base,
+ iov->iov[iov->i].iov_len,
+ vr_idx);
if (ret) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
@@ -192,8 +292,8 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev,
ubuf = iov.iov_base;
}
/* Issue all the read descriptors first */
- ret = mic_vringh_copy(mvdev, riov, ubuf, len,
- MIC_VRINGH_READ, &out_len);
+ ret = mic_vringh_copy(mvdev, riov, ubuf, len, MIC_VRINGH_READ,
+ copy->vr_idx, &out_len);
if (ret) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
@@ -203,8 +303,8 @@ static int _mic_virtio_copy(struct mic_vdev *mvdev,
ubuf += out_len;
copy->out_len += out_len;
/* Issue the write descriptors next */
- ret = mic_vringh_copy(mvdev, wiov, ubuf, len,
- !MIC_VRINGH_READ, &out_len);
+ ret = mic_vringh_copy(mvdev, wiov, ubuf, len, !MIC_VRINGH_READ,
+ copy->vr_idx, &out_len);
if (ret) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
@@ -589,13 +689,19 @@ int mic_virtio_add_device(struct mic_vdev *mvdev,
dev_dbg(mdev->sdev->parent,
"%s %d index %d va %p info %p vr_size 0x%x\n",
__func__, __LINE__, i, vr->va, vr->info, vr_size);
+ mvr->buf = (void *)__get_free_pages(GFP_KERNEL,
+ get_order(MIC_INT_DMA_BUF_SIZE));
+ mvr->buf_da = mic_map_single(mvdev->mdev, mvr->buf,
+ MIC_INT_DMA_BUF_SIZE);
}
snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id,
mvdev->virtio_id);
mvdev->virtio_db = mic_next_db(mdev);
- mvdev->virtio_cookie = mic_request_irq(mdev, mic_virtio_intr_handler,
- irqname, mvdev, mvdev->virtio_db, MIC_INTR_DB);
+ mvdev->virtio_cookie = mic_request_threaded_irq(mdev,
+ mic_virtio_intr_handler,
+ NULL, irqname, mvdev,
+ mvdev->virtio_db, MIC_INTR_DB);
if (IS_ERR(mvdev->virtio_cookie)) {
ret = PTR_ERR(mvdev->virtio_cookie);
dev_dbg(mdev->sdev->parent, "request irq failed\n");
@@ -671,6 +777,11 @@ skip_hot_remove:
vqconfig = mic_vq_config(mvdev->dd);
for (i = 0; i < mvdev->dd->num_vq; i++) {
struct mic_vringh *mvr = &mvdev->mvr[i];
+
+ mic_unmap_single(mvdev->mdev, mvr->buf_da,
+ MIC_INT_DMA_BUF_SIZE);
+ free_pages((unsigned long)mvr->buf,
+ get_order(MIC_INT_DMA_BUF_SIZE));
vringh_kiov_cleanup(&mvr->riov);
vringh_kiov_cleanup(&mvr->wiov);
mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address),
diff --git a/drivers/misc/mic/host/mic_virtio.h b/drivers/misc/mic/host/mic_virtio.h
index 184f3c84805b..d574efb853d9 100644
--- a/drivers/misc/mic/host/mic_virtio.h
+++ b/drivers/misc/mic/host/mic_virtio.h
@@ -46,18 +46,23 @@
* @vrh: The host VRINGH used for accessing the card vrings.
* @riov: The VRINGH read kernel IOV.
* @wiov: The VRINGH write kernel IOV.
- * @head: The VRINGH head index address passed to vringh_getdesc_kern(..).
* @vr_mutex: Mutex for synchronizing access to the VRING.
+ * @buf: Temporary kernel buffer used to copy in/out data
+ * from/to the card via DMA.
+ * @buf_da: dma address of buf.
* @mvdev: Back pointer to MIC virtio device for vringh_notify(..).
+ * @head: The VRINGH head index address passed to vringh_getdesc_kern(..).
*/
struct mic_vringh {
struct mic_vring vring;
struct vringh vrh;
struct vringh_kiov riov;
struct vringh_kiov wiov;
- u16 head;
struct mutex vr_mutex;
+ void *buf;
+ dma_addr_t buf_da;
struct mic_vdev *mvdev;
+ u16 head;
};
/**
@@ -69,6 +74,14 @@ struct mic_vringh {
* @poll_wake - Used for waking up threads blocked in poll.
* @out_bytes - Debug stats for number of bytes copied from host to card.
* @in_bytes - Debug stats for number of bytes copied from card to host.
+ * @out_bytes_dma - Debug stats for number of bytes copied from host to card
+ * using DMA.
+ * @in_bytes_dma - Debug stats for number of bytes copied from card to host
+ * using DMA.
+ * @tx_len_unaligned - Debug stats for number of bytes copied to the card where
+ * the transfer length did not have the required DMA alignment.
+ * @tx_dst_unaligned - Debug stats for number of bytes copied where the
+ * destination address on the card did not have the required DMA alignment.
* @mvr - Store per VRING data structures.
* @virtio_bh_work - Work struct used to schedule virtio bottom half handling.
* @dd - Virtio device descriptor.
@@ -84,6 +97,10 @@ struct mic_vdev {
int poll_wake;
unsigned long out_bytes;
unsigned long in_bytes;
+ unsigned long out_bytes_dma;
+ unsigned long in_bytes_dma;
+ unsigned long tx_len_unaligned;
+ unsigned long tx_dst_unaligned;
struct mic_vringh mvr[MIC_MAX_VRINGS];
struct work_struct virtio_bh_work;
struct mic_device_desc *dd;
diff --git a/drivers/misc/mic/host/mic_x100.c b/drivers/misc/mic/host/mic_x100.c
index 5562fdd3ef4e..b7a21e11dcdf 100644
--- a/drivers/misc/mic/host/mic_x100.c
+++ b/drivers/misc/mic/host/mic_x100.c
@@ -549,6 +549,13 @@ struct mic_smpt_ops mic_x100_smpt_ops = {
.set = mic_x100_smpt_set,
};
+static bool mic_x100_dma_filter(struct dma_chan *chan, void *param)
+{
+ if (chan->device->dev->parent == (struct device *)param)
+ return true;
+ return false;
+}
+
struct mic_hw_ops mic_x100_ops = {
.aper_bar = MIC_X100_APER_BAR,
.mmio_bar = MIC_X100_MMIO_BAR,
@@ -563,6 +570,7 @@ struct mic_hw_ops mic_x100_ops = {
.send_firmware_intr = mic_x100_send_firmware_intr,
.load_mic_fw = mic_x100_load_firmware,
.get_postcode = mic_x100_get_postcode,
+ .dma_filter = mic_x100_dma_filter,
};
struct mic_hw_intr_ops mic_x100_intr_ops = {
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
index 9d3dbb28734b..45007be7cfb7 100644
--- a/drivers/misc/ti-st/st_kim.c
+++ b/drivers/misc/ti-st/st_kim.c
@@ -778,7 +778,7 @@ static int kim_probe(struct platform_device *pdev)
pr_info("sysfs entries created\n");
kim_debugfs_dir = debugfs_create_dir("ti-st", NULL);
- if (IS_ERR(kim_debugfs_dir)) {
+ if (!kim_debugfs_dir) {
pr_err(" debugfs entries creation failed ");
err = -EIO;
goto err_debugfs_dir;
@@ -788,7 +788,6 @@ static int kim_probe(struct platform_device *pdev)
kim_gdata, &version_debugfs_fops);
debugfs_create_file("protocols", S_IRUGO, kim_debugfs_dir,
kim_gdata, &list_debugfs_fops);
- pr_info(" debugfs entries created ");
return 0;
err_debugfs_dir:
OpenPOWER on IntegriCloud