aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/kpc2000/kpc2000
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--drivers/staging/kpc2000/kpc2000/Makefile4
-rw-r--r--drivers/staging/kpc2000/kpc2000/cell_probe.c466
-rw-r--r--drivers/staging/kpc2000/kpc2000/core.c436
-rw-r--r--drivers/staging/kpc2000/kpc2000/dma_common_defs.h43
-rw-r--r--drivers/staging/kpc2000/kpc2000/fileops.c128
-rw-r--r--drivers/staging/kpc2000/kpc2000/kp2000_module.c55
-rw-r--r--drivers/staging/kpc2000/kpc2000/pcie.h112
-rw-r--r--drivers/staging/kpc2000/kpc2000/uapi.h22
8 files changed, 1266 insertions, 0 deletions
diff --git a/drivers/staging/kpc2000/kpc2000/Makefile b/drivers/staging/kpc2000/kpc2000/Makefile
new file mode 100644
index 000000000000..28ab1e185f9f
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-m := kpc2000.o
+kpc2000-objs += kp2000_module.o core.o cell_probe.o fileops.o
diff --git a/drivers/staging/kpc2000/kpc2000/cell_probe.c b/drivers/staging/kpc2000/kpc2000/cell_probe.c
new file mode 100644
index 000000000000..ad2cc0a3bfa1
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/cell_probe.c
@@ -0,0 +1,466 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/uio_driver.h>
+#include "pcie.h"
+
+/* Core (Resource) Table Layout:
+ * one Resource per record (8 bytes)
+ * 6 5 4 3 2 1 0
+ * 3210987654321098765432109876543210987654321098765432109876543210
+ * IIIIIIIIIIII Core Type [up to 4095 types]
+ * D S2C DMA Present
+ * DDD S2C DMA Channel Number [up to 8 channels]
+ * LLLLLLLLLLLLLLLL Register Count (64-bit registers) [up to 65535 registers]
+ * OOOOOOOOOOOOOOOO Core Offset (in 4kB blocks) [up to 65535 cores]
+ * D C2S DMA Present
+ * DDD C2S DMA Channel Number [up to 8 channels]
+ * II IRQ Count [0 to 3 IRQs per core]
+ 1111111000
+ * IIIIIII IRQ Base Number [up to 128 IRQs per card]
+ * ___ Spare
+ *
+ */
+
+#define KPC_OLD_DMA_CH_NUM(present, channel) ((present) ? (0x8 | ((channel) & 0x7)) : 0)
+#define KPC_OLD_S2C_DMA_CH_NUM(cte) KPC_OLD_DMA_CH_NUM(cte.s2c_dma_present, cte.s2c_dma_channel_num)
+#define KPC_OLD_C2S_DMA_CH_NUM(cte) KPC_OLD_DMA_CH_NUM(cte.c2s_dma_present, cte.c2s_dma_channel_num)
+
+#define KP_CORE_ID_INVALID 0
+#define KP_CORE_ID_I2C 3
+#define KP_CORE_ID_SPI 5
+
+struct core_table_entry {
+ u16 type;
+ u32 offset;
+ u32 length;
+ bool s2c_dma_present;
+ u8 s2c_dma_channel_num;
+ bool c2s_dma_present;
+ u8 c2s_dma_channel_num;
+ u8 irq_count;
+ u8 irq_base_num;
+};
+
+static
+void parse_core_table_entry_v0(struct core_table_entry *cte, const u64 read_val)
+{
+ cte->type = ((read_val & 0xFFF0000000000000) >> 52);
+ cte->offset = ((read_val & 0x00000000FFFF0000) >> 16) * 4096;
+ cte->length = ((read_val & 0x0000FFFF00000000) >> 32) * 8;
+ cte->s2c_dma_present = ((read_val & 0x0008000000000000) >> 51);
+ cte->s2c_dma_channel_num = ((read_val & 0x0007000000000000) >> 48);
+ cte->c2s_dma_present = ((read_val & 0x0000000000008000) >> 15);
+ cte->c2s_dma_channel_num = ((read_val & 0x0000000000007000) >> 12);
+ cte->irq_count = ((read_val & 0x0000000000000C00) >> 10);
+ cte->irq_base_num = ((read_val & 0x00000000000003F8) >> 3);
+}
+
+static
+void dbg_cte(struct kp2000_device *pcard, struct core_table_entry *cte)
+{
+ dev_dbg(&pcard->pdev->dev, "CTE: type:%3d offset:%3d (%3d) length:%3d (%3d) s2c:%d c2s:%d irq_count:%d base_irq:%d\n",
+ cte->type,
+ cte->offset,
+ cte->offset / 4096,
+ cte->length,
+ cte->length / 8,
+ (cte->s2c_dma_present ? cte->s2c_dma_channel_num : -1),
+ (cte->c2s_dma_present ? cte->c2s_dma_channel_num : -1),
+ cte->irq_count,
+ cte->irq_base_num
+ );
+}
+
+static
+void parse_core_table_entry(struct core_table_entry *cte, const u64 read_val, const u8 entry_rev)
+{
+ switch (entry_rev) {
+ case 0: parse_core_table_entry_v0(cte, read_val); break;
+ default: cte->type = 0; break;
+ }
+}
+
+
+int probe_core_basic(unsigned int core_num, struct kp2000_device *pcard, char *name, const struct core_table_entry cte)
+{
+ struct mfd_cell cell = {0};
+ struct resource resources[2] = {0};
+
+ struct kpc_core_device_platdata core_pdata = {
+ .card_id = pcard->card_id,
+ .build_version = pcard->build_version,
+ .hardware_revision = pcard->hardware_revision,
+ .ssid = pcard->ssid,
+ .ddna = pcard->ddna,
+ };
+
+ dev_dbg(&pcard->pdev->dev, "Found Basic core: type = %02d dma = %02x / %02x offset = 0x%x length = 0x%x (%d regs)\n", cte.type, KPC_OLD_S2C_DMA_CH_NUM(cte), KPC_OLD_C2S_DMA_CH_NUM(cte), cte.offset, cte.length, cte.length / 8);
+
+
+ cell.platform_data = &core_pdata;
+ cell.pdata_size = sizeof(struct kpc_core_device_platdata);
+ cell.name = name;
+ cell.id = core_num;
+ cell.num_resources = 2;
+
+ resources[0].start = cte.offset;
+ resources[0].end = cte.offset + (cte.length - 1);
+ resources[0].flags = IORESOURCE_MEM;
+
+ resources[1].start = pcard->pdev->irq;
+ resources[1].end = pcard->pdev->irq;
+ resources[1].flags = IORESOURCE_IRQ;
+
+ cell.resources = resources;
+
+ return mfd_add_devices(
+ PCARD_TO_DEV(pcard), // parent
+ pcard->card_num * 100, // id
+ &cell, // struct mfd_cell *
+ 1, // ndevs
+ &pcard->regs_base_resource,
+ 0, // irq_base
+ NULL // struct irq_domain *
+ );
+}
+
+
+struct kpc_uio_device {
+ struct list_head list;
+ struct kp2000_device *pcard;
+ struct device *dev;
+ struct uio_info uioinfo;
+ struct core_table_entry cte;
+ u16 core_num;
+};
+
+static ssize_t show_attr(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct kpc_uio_device *kudev = dev_get_drvdata(dev);
+
+ #define ATTR_NAME_CMP(v) (strcmp(v, attr->attr.name) == 0)
+ if ATTR_NAME_CMP("offset"){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.offset);
+ } else if ATTR_NAME_CMP("size"){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.length);
+ } else if ATTR_NAME_CMP("type"){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.type);
+ }
+ else if ATTR_NAME_CMP("s2c_dma"){
+ if (kudev->cte.s2c_dma_present){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.s2c_dma_channel_num);
+ } else {
+ return scnprintf(buf, PAGE_SIZE, "not present\n");
+ }
+ } else if ATTR_NAME_CMP("c2s_dma"){
+ if (kudev->cte.c2s_dma_present){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.c2s_dma_channel_num);
+ } else {
+ return scnprintf(buf, PAGE_SIZE, "not present\n");
+ }
+ }
+ else if ATTR_NAME_CMP("irq_count"){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.irq_count);
+ } else if ATTR_NAME_CMP("irq_base_num"){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->cte.irq_base_num);
+ } else if ATTR_NAME_CMP("core_num"){
+ return scnprintf(buf, PAGE_SIZE, "%u\n", kudev->core_num);
+ } else {
+ return 0;
+ }
+ #undef ATTR_NAME_CMP
+}
+
+
+DEVICE_ATTR(offset, 0444, show_attr, NULL);
+DEVICE_ATTR(size, 0444, show_attr, NULL);
+DEVICE_ATTR(type, 0444, show_attr, NULL);
+DEVICE_ATTR(s2c_dma_ch, 0444, show_attr, NULL);
+DEVICE_ATTR(c2s_dma_ch, 0444, show_attr, NULL);
+DEVICE_ATTR(s2c_dma, 0444, show_attr, NULL);
+DEVICE_ATTR(c2s_dma, 0444, show_attr, NULL);
+DEVICE_ATTR(irq_count, 0444, show_attr, NULL);
+DEVICE_ATTR(irq_base_num, 0444, show_attr, NULL);
+DEVICE_ATTR(core_num, 0444, show_attr, NULL);
+struct attribute * kpc_uio_class_attrs[] = {
+ &dev_attr_offset.attr,
+ &dev_attr_size.attr,
+ &dev_attr_type.attr,
+ &dev_attr_s2c_dma_ch.attr,
+ &dev_attr_c2s_dma_ch.attr,
+ &dev_attr_s2c_dma.attr,
+ &dev_attr_c2s_dma.attr,
+ &dev_attr_irq_count.attr,
+ &dev_attr_irq_base_num.attr,
+ &dev_attr_core_num.attr,
+ NULL,
+};
+
+
+static
+int kp2000_check_uio_irq(struct kp2000_device *pcard, u32 irq_num)
+{
+ u64 interrupt_active = readq(pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE);
+ u64 interrupt_mask_inv = ~readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK);
+ u64 irq_check_mask = (1 << irq_num);
+ if (interrupt_active & irq_check_mask){ // if it's active (interrupt pending)
+ if (interrupt_mask_inv & irq_check_mask){ // and if it's not masked off
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static
+irqreturn_t kuio_handler(int irq, struct uio_info *uioinfo)
+{
+ struct kpc_uio_device *kudev = uioinfo->priv;
+ if (irq != kudev->pcard->pdev->irq)
+ return IRQ_NONE;
+
+ if (kp2000_check_uio_irq(kudev->pcard, kudev->cte.irq_base_num)){
+ writeq((1 << kudev->cte.irq_base_num), kudev->pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE); // Clear the active flag
+ return IRQ_HANDLED;
+ }
+ return IRQ_NONE;
+}
+
+static
+int kuio_irqcontrol(struct uio_info *uioinfo, s32 irq_on)
+{
+ struct kpc_uio_device *kudev = uioinfo->priv;
+ struct kp2000_device *pcard = kudev->pcard;
+ u64 mask;
+
+ lock_card(pcard);
+ mask = readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK);
+ if (irq_on){
+ mask &= ~(1 << (kudev->cte.irq_base_num));
+ } else {
+ mask |= (1 << (kudev->cte.irq_base_num));
+ }
+ writeq(mask, pcard->sysinfo_regs_base + REG_INTERRUPT_MASK);
+ unlock_card(pcard);
+
+ return 0;
+}
+
+int probe_core_uio(unsigned int core_num, struct kp2000_device *pcard, char *name, const struct core_table_entry cte)
+{
+ struct kpc_uio_device *kudev;
+ int rv;
+
+ dev_dbg(&pcard->pdev->dev, "Found UIO core: type = %02d dma = %02x / %02x offset = 0x%x length = 0x%x (%d regs)\n", cte.type, KPC_OLD_S2C_DMA_CH_NUM(cte), KPC_OLD_C2S_DMA_CH_NUM(cte), cte.offset, cte.length, cte.length / 8);
+
+ kudev = kzalloc(sizeof(struct kpc_uio_device), GFP_KERNEL);
+ if (!kudev){
+ dev_err(&pcard->pdev->dev, "probe_core_uio: failed to kzalloc kpc_uio_device\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&kudev->list);
+ kudev->pcard = pcard;
+ kudev->cte = cte;
+ kudev->core_num = core_num;
+
+ kudev->uioinfo.priv = kudev;
+ kudev->uioinfo.name = name;
+ kudev->uioinfo.version = "0.0";
+ if (cte.irq_count > 0){
+ kudev->uioinfo.irq_flags = IRQF_SHARED;
+ kudev->uioinfo.irq = pcard->pdev->irq;
+ kudev->uioinfo.handler = kuio_handler;
+ kudev->uioinfo.irqcontrol = kuio_irqcontrol;
+ } else {
+ kudev->uioinfo.irq = 0;
+ }
+
+ kudev->uioinfo.mem[0].name = "uiomap";
+ kudev->uioinfo.mem[0].addr = pci_resource_start(pcard->pdev, REG_BAR) + cte.offset;
+ kudev->uioinfo.mem[0].size = (cte.length + PAGE_SIZE-1) & ~(PAGE_SIZE-1); // Round up to nearest PAGE_SIZE boundary
+ kudev->uioinfo.mem[0].memtype = UIO_MEM_PHYS;
+
+ kudev->dev = device_create(kpc_uio_class, &pcard->pdev->dev, MKDEV(0,0), kudev, "%s.%d.%d.%d", kudev->uioinfo.name, pcard->card_num, cte.type, kudev->core_num);
+ if (IS_ERR(kudev->dev)) {
+ dev_err(&pcard->pdev->dev, "probe_core_uio device_create failed!\n");
+ return -ENODEV;
+ }
+ dev_set_drvdata(kudev->dev, kudev);
+
+ rv = uio_register_device(kudev->dev, &kudev->uioinfo);
+ if (rv){
+ dev_err(&pcard->pdev->dev, "probe_core_uio failed uio_register_device: %d\n", rv);
+ return rv;
+ }
+
+ list_add_tail(&kudev->list, &pcard->uio_devices_list);
+
+ return 0;
+}
+
+
+static int create_dma_engine_core(struct kp2000_device *pcard, size_t engine_regs_offset, int engine_num, int irq_num)
+{
+ struct mfd_cell cell = {0};
+ struct resource resources[2] = {0};
+
+ dev_dbg(&pcard->pdev->dev, "create_dma_core(pcard = [%p], engine_regs_offset = %zx, engine_num = %d)\n", pcard, engine_regs_offset, engine_num);
+
+ cell.platform_data = NULL;
+ cell.pdata_size = 0;
+ cell.id = engine_num;
+ cell.name = KP_DRIVER_NAME_DMA_CONTROLLER;
+ cell.num_resources = 2;
+
+ resources[0].start = engine_regs_offset;
+ resources[0].end = engine_regs_offset + (KPC_DMA_ENGINE_SIZE - 1);
+ resources[0].flags = IORESOURCE_MEM;
+
+ resources[1].start = irq_num;
+ resources[1].end = irq_num;
+ resources[1].flags = IORESOURCE_IRQ;
+
+ cell.resources = resources;
+
+ return mfd_add_devices(
+ PCARD_TO_DEV(pcard), // parent
+ pcard->card_num * 100, // id
+ &cell, // struct mfd_cell *
+ 1, // ndevs
+ &pcard->dma_base_resource,
+ 0, // irq_base
+ NULL // struct irq_domain *
+ );
+}
+
+static int kp2000_setup_dma_controller(struct kp2000_device *pcard)
+{
+ int err;
+ unsigned int i;
+ u64 capabilities_reg;
+
+ // S2C Engines
+ for (i = 0 ; i < 32 ; i++){
+ capabilities_reg = readq( pcard->dma_bar_base + KPC_DMA_S2C_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i) );
+ if (capabilities_reg & ENGINE_CAP_PRESENT_MASK){
+ err = create_dma_engine_core(pcard, (KPC_DMA_S2C_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i)), i, pcard->pdev->irq);
+ if (err) goto err_out;
+ }
+ }
+ // C2S Engines
+ for (i = 0 ; i < 32 ; i++){
+ capabilities_reg = readq( pcard->dma_bar_base + KPC_DMA_C2S_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i) );
+ if (capabilities_reg & ENGINE_CAP_PRESENT_MASK){
+ err = create_dma_engine_core(pcard, (KPC_DMA_C2S_BASE_OFFSET + (KPC_DMA_ENGINE_SIZE * i)), 32+i, pcard->pdev->irq);
+ if (err) goto err_out;
+ }
+ }
+
+ return 0;
+
+err_out:
+ dev_err(&pcard->pdev->dev, "kp2000_setup_dma_controller: failed to add a DMA Engine: %d\n", err);
+ return err;
+}
+
+int kp2000_probe_cores(struct kp2000_device *pcard)
+{
+ int err = 0;
+ int i;
+ int current_type_id;
+ u64 read_val;
+ unsigned int highest_core_id = 0;
+ struct core_table_entry cte;
+
+ dev_dbg(&pcard->pdev->dev, "kp2000_probe_cores(pcard = %p / %d)\n", pcard, pcard->card_num);
+
+ err = kp2000_setup_dma_controller(pcard);
+ if (err) return err;
+
+ INIT_LIST_HEAD(&pcard->uio_devices_list);
+
+ // First, iterate the core table looking for the highest CORE_ID
+ for (i = 0 ; i < pcard->core_table_length ; i++){
+ read_val = readq(pcard->sysinfo_regs_base + ((pcard->core_table_offset + i) * 8));
+ parse_core_table_entry(&cte, read_val, pcard->core_table_rev);
+ dbg_cte(pcard, &cte);
+ if (cte.type > highest_core_id){
+ highest_core_id = cte.type;
+ }
+ if (cte.type == KP_CORE_ID_INVALID){
+ dev_info(&pcard->pdev->dev, "Found Invalid core: %016llx\n", read_val);
+ }
+ }
+ // Then, iterate over the possible core types.
+ for (current_type_id = 1 ; current_type_id <= highest_core_id ; current_type_id++){
+ unsigned int core_num = 0;
+ // Foreach core type, iterate the whole table and instantiate subdevices for each core.
+ // Yes, this is O(n*m) but the actual runtime is small enough that it's an acceptable tradeoff.
+ for (i = 0 ; i < pcard->core_table_length ; i++){
+ read_val = readq(pcard->sysinfo_regs_base + ((pcard->core_table_offset + i) * 8));
+ parse_core_table_entry(&cte, read_val, pcard->core_table_rev);
+
+ if (cte.type == current_type_id){
+ switch (cte.type){
+ case KP_CORE_ID_I2C:
+ err = probe_core_basic(core_num, pcard, KP_DRIVER_NAME_I2C, cte);
+ break;
+
+ case KP_CORE_ID_SPI:
+ err = probe_core_basic(core_num, pcard, KP_DRIVER_NAME_SPI, cte);
+ break;
+
+ default:
+ err = probe_core_uio(core_num, pcard, "kpc_uio", cte);
+ break;
+ }
+ if (err){
+ dev_err(&pcard->pdev->dev, "kp2000_probe_cores: failed to add core %d: %d\n", i, err);
+ return err;
+ }
+ core_num++;
+ }
+ }
+ }
+
+ // Finally, instantiate a UIO device for the core_table.
+ cte.type = 0; // CORE_ID_BOARD_INFO
+ cte.offset = 0; // board info is always at the beginning
+ cte.length = 512*8;
+ cte.s2c_dma_present = false;
+ cte.s2c_dma_channel_num = 0;
+ cte.c2s_dma_present = false;
+ cte.c2s_dma_channel_num = 0;
+ cte.irq_count = 0;
+ cte.irq_base_num = 0;
+ err = probe_core_uio(0, pcard, "kpc_uio", cte);
+ if (err){
+ dev_err(&pcard->pdev->dev, "kp2000_probe_cores: failed to add board_info core: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+void kp2000_remove_cores(struct kp2000_device *pcard)
+{
+ struct list_head *ptr;
+ struct list_head *next;
+ list_for_each_safe(ptr, next, &pcard->uio_devices_list){
+ struct kpc_uio_device *kudev = list_entry(ptr, struct kpc_uio_device, list);
+ uio_unregister_device(&kudev->uioinfo);
+ device_unregister(kudev->dev);
+ list_del(&kudev->list);
+ kfree(kudev);
+ }
+}
+
diff --git a/drivers/staging/kpc2000/kpc2000/core.c b/drivers/staging/kpc2000/kpc2000/core.c
new file mode 100644
index 000000000000..35b87cdc4fb0
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/core.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <asm/io.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include "pcie.h"
+
+
+/*******************************************************
+ * SysFS Attributes
+ ******************************************************/
+static ssize_t show_attr(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct kp2000_device *pcard;
+
+ if (!pdev) return -ENXIO;
+ pcard = pci_get_drvdata(pdev);
+ if (!pcard) return -ENXIO;
+
+ if (strcmp("ssid", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%016llx\n", pcard->ssid); } else
+ if (strcmp("ddna", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%016llx\n", pcard->ddna); } else
+ if (strcmp("card_id", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->card_id); } else
+ if (strcmp("hw_rev", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->hardware_revision); } else
+ if (strcmp("build", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->build_version); } else
+ if (strcmp("build_date", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->build_datestamp); } else
+ if (strcmp("build_time", attr->attr.name) == 0){ return scnprintf(buf, PAGE_SIZE, "%08x\n", pcard->build_timestamp); } else
+ { return -ENXIO; }
+}
+
+static ssize_t show_cpld_config_reg(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ struct kp2000_device *pcard;
+ u64 val;
+
+ if (!pdev)
+ return -ENXIO;
+
+ pcard = pci_get_drvdata(pdev);
+ if (!pcard)
+ return -ENXIO;
+
+ val = readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG);
+ return scnprintf(buf, PAGE_SIZE, "%016llx\n", val);
+}
+static ssize_t cpld_reconfigure(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+ long wr_val;
+ struct kp2000_device *pcard;
+ int rv;
+
+ if (!pdev) return -ENXIO;
+ pcard = pci_get_drvdata(pdev);
+ if (!pcard) return -ENXIO;
+
+ rv = kstrtol(buf, 0, &wr_val);
+ if (rv < 0) return rv;
+ if (wr_val > 7) return -EINVAL;
+
+ wr_val = wr_val << 8;
+ wr_val |= 0x1; // Set the "Configure Go" bit
+ writeq(wr_val, pcard->sysinfo_regs_base + REG_CPLD_CONFIG);
+ return count;
+}
+
+
+DEVICE_ATTR(ssid, 0444, show_attr, NULL);
+DEVICE_ATTR(ddna, 0444, show_attr, NULL);
+DEVICE_ATTR(card_id, 0444, show_attr, NULL);
+DEVICE_ATTR(hw_rev, 0444, show_attr, NULL);
+DEVICE_ATTR(build, 0444, show_attr, NULL);
+DEVICE_ATTR(build_date, 0444, show_attr, NULL);
+DEVICE_ATTR(build_time, 0444, show_attr, NULL);
+DEVICE_ATTR(cpld_reg, 0444, show_cpld_config_reg, NULL);
+DEVICE_ATTR(cpld_reconfigure, 0220, NULL, cpld_reconfigure);
+
+static const struct attribute * kp_attr_list[] = {
+ &dev_attr_ssid.attr,
+ &dev_attr_ddna.attr,
+ &dev_attr_card_id.attr,
+ &dev_attr_hw_rev.attr,
+ &dev_attr_build.attr,
+ &dev_attr_build_date.attr,
+ &dev_attr_build_time.attr,
+ &dev_attr_cpld_reg.attr,
+ &dev_attr_cpld_reconfigure.attr,
+ NULL,
+};
+
+
+/*******************************************************
+ * Functions
+ ******************************************************/
+
+static void wait_and_read_ssid(struct kp2000_device *pcard)
+{
+ u64 read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_SSID);
+ unsigned long timeout;
+
+ if (read_val & 0x8000000000000000){
+ pcard->ssid = read_val;
+ return;
+ }
+
+ timeout = jiffies + (HZ * 2);
+ do {
+ read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_SSID);
+ if (read_val & 0x8000000000000000){
+ pcard->ssid = read_val;
+ return;
+ }
+ cpu_relax();
+ //schedule();
+ } while (time_before(jiffies, timeout));
+
+ dev_notice(&pcard->pdev->dev, "SSID didn't show up!\n");
+
+ #if 0
+ // Timed out waiting for the SSID to show up, just use the DDNA instead?
+ read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_DDNA);
+ pcard->ssid = read_val;
+ #else
+ // Timed out waiting for the SSID to show up, stick all zeros in the value
+ pcard->ssid = 0;
+ #endif
+}
+
+static int read_system_regs(struct kp2000_device *pcard)
+{
+ u64 read_val;
+
+ read_val = readq(pcard->sysinfo_regs_base + REG_MAGIC_NUMBER);
+ if (read_val != KP2000_MAGIC_VALUE){
+ dev_err(&pcard->pdev->dev, "Invalid magic! Got: 0x%016llx Want: 0x%016lx\n", read_val, KP2000_MAGIC_VALUE);
+ return -EILSEQ;
+ }
+
+ read_val = readq(pcard->sysinfo_regs_base + REG_CARD_ID_AND_BUILD);
+ pcard->card_id = (read_val & 0xFFFFFFFF00000000) >> 32;
+ pcard->build_version = (read_val & 0x00000000FFFFFFFF) >> 0;
+
+ read_val = readq(pcard->sysinfo_regs_base + REG_DATE_AND_TIME_STAMPS);
+ pcard->build_datestamp = (read_val & 0xFFFFFFFF00000000) >> 32;
+ pcard->build_timestamp = (read_val & 0x00000000FFFFFFFF) >> 0;
+
+ read_val = readq(pcard->sysinfo_regs_base + REG_CORE_TABLE_OFFSET);
+ pcard->core_table_length = (read_val & 0xFFFFFFFF00000000) >> 32;
+ pcard->core_table_offset = (read_val & 0x00000000FFFFFFFF) >> 0;
+
+ wait_and_read_ssid(pcard);
+
+ read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_HW_ID);
+ pcard->core_table_rev = (read_val & 0x0000000000000F00) >> 8;
+ pcard->hardware_revision = (read_val & 0x000000000000001F);
+
+ read_val = readq(pcard->sysinfo_regs_base + REG_FPGA_DDNA);
+ pcard->ddna = read_val;
+
+ dev_info(&pcard->pdev->dev, "system_regs: %08x %08x %08x %08x %02x %d %d %016llx %016llx\n",
+ pcard->card_id,
+ pcard->build_version,
+ pcard->build_datestamp,
+ pcard->build_timestamp,
+ pcard->hardware_revision,
+ pcard->core_table_rev,
+ pcard->core_table_length,
+ pcard->ssid,
+ pcard->ddna
+ );
+
+ if (pcard->core_table_rev > 1){
+ dev_err(&pcard->pdev->dev, "core table entry revision is higher than we can deal with, cannot continue with this card!\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+irqreturn_t kp2000_irq_handler(int irq, void *dev_id)
+{
+ struct kp2000_device *pcard = (struct kp2000_device*)dev_id;
+ SetBackEndControl(pcard->dma_common_regs, KPC_DMA_CARD_IRQ_ENABLE | KPC_DMA_CARD_USER_INTERRUPT_MODE | KPC_DMA_CARD_USER_INTERRUPT_ACTIVE);
+ return IRQ_HANDLED;
+}
+
+int kp2000_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int err = 0;
+ struct kp2000_device *pcard;
+ static int card_count = 1;
+ int rv;
+ unsigned long reg_bar_phys_addr;
+ unsigned long reg_bar_phys_len;
+ unsigned long dma_bar_phys_addr;
+ unsigned long dma_bar_phys_len;
+ u16 regval;
+
+ dev_dbg(&pdev->dev, "kp2000_pcie_probe(pdev = [%p], id = [%p])\n", pdev, id);
+
+ //{ Step 1: Allocate a struct for the pcard
+ pcard = kzalloc(sizeof(struct kp2000_device), GFP_KERNEL);
+ if (NULL == pcard){
+ dev_err(&pdev->dev, "probe: failed to allocate private card data\n");
+ return -ENOMEM;
+ }
+ dev_dbg(&pdev->dev, "probe: allocated struct kp2000_device @ %p\n", pcard);
+ //}
+
+ //{ Step 2: Initialize trivial pcard elements
+ pcard->card_num = card_count;
+ card_count++;
+ scnprintf(pcard->name, 16, "kpcard%d", pcard->card_num);
+
+ mutex_init(&pcard->sem);
+ lock_card(pcard);
+
+ pcard->pdev = pdev;
+ pci_set_drvdata(pdev, pcard);
+ //}
+
+ //{ Step 3: Enable PCI device
+ err = pci_enable_device(pcard->pdev);
+ if (err){
+ dev_err(&pcard->pdev->dev, "probe: failed to enable PCIE2000 PCIe device (%d)\n", err);
+ goto out3;
+ }
+ //}
+
+ //{ Step 4: Setup the Register BAR
+ reg_bar_phys_addr = pci_resource_start(pcard->pdev, REG_BAR);
+ reg_bar_phys_len = pci_resource_len(pcard->pdev, REG_BAR);
+
+ pcard->regs_bar_base = ioremap_nocache(reg_bar_phys_addr, PAGE_SIZE);
+ if (NULL == pcard->regs_bar_base){
+ dev_err(&pcard->pdev->dev, "probe: REG_BAR could not remap memory to virtual space\n");
+ err = -ENODEV;
+ goto out4;
+ }
+ dev_dbg(&pcard->pdev->dev, "probe: REG_BAR virt hardware address start [%p]\n", pcard->regs_bar_base);
+
+ err = pci_request_region(pcard->pdev, REG_BAR, KP_DRIVER_NAME_KP2000);
+ if (err){
+ iounmap(pcard->regs_bar_base);
+ dev_err(&pcard->pdev->dev, "probe: failed to acquire PCI region (%d)\n", err);
+ err = -ENODEV;
+ goto out4;
+ }
+
+ pcard->regs_base_resource.start = reg_bar_phys_addr;
+ pcard->regs_base_resource.end = reg_bar_phys_addr + reg_bar_phys_len - 1;
+ pcard->regs_base_resource.flags = IORESOURCE_MEM;
+ //}
+
+ //{ Step 5: Setup the DMA BAR
+ dma_bar_phys_addr = pci_resource_start(pcard->pdev, DMA_BAR);
+ dma_bar_phys_len = pci_resource_len(pcard->pdev, DMA_BAR);
+
+ pcard->dma_bar_base = ioremap_nocache(dma_bar_phys_addr, dma_bar_phys_len);
+ if (NULL == pcard->dma_bar_base){
+ dev_err(&pcard->pdev->dev, "probe: DMA_BAR could not remap memory to virtual space\n");
+ err = -ENODEV;
+ goto out5;
+ }
+ dev_dbg(&pcard->pdev->dev, "probe: DMA_BAR virt hardware address start [%p]\n", pcard->dma_bar_base);
+
+ pcard->dma_common_regs = pcard->dma_bar_base + KPC_DMA_COMMON_OFFSET;
+
+ err = pci_request_region(pcard->pdev, DMA_BAR, "kp2000_pcie");
+ if (err){
+ iounmap(pcard->dma_bar_base);
+ dev_err(&pcard->pdev->dev, "probe: failed to acquire PCI region (%d)\n", err);
+ err = -ENODEV;
+ goto out5;
+ }
+
+ pcard->dma_base_resource.start = dma_bar_phys_addr;
+ pcard->dma_base_resource.end = dma_bar_phys_addr + dma_bar_phys_len - 1;
+ pcard->dma_base_resource.flags = IORESOURCE_MEM;
+ //}
+
+ //{ Step 6: System Regs
+ pcard->sysinfo_regs_base = pcard->regs_bar_base;
+ err = read_system_regs(pcard);
+ if (err)
+ goto out6;
+
+ // Disable all "user" interrupts because they're not used yet.
+ writeq(0xFFFFFFFFFFFFFFFF, pcard->sysinfo_regs_base + REG_INTERRUPT_MASK);
+ //}
+
+ //{ Step 7: Configure PCI thingies
+ // let the card master PCIe
+ pci_set_master(pcard->pdev);
+ // enable IO and mem if not already done
+ pci_read_config_word(pcard->pdev, PCI_COMMAND, &regval);
+ regval |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ pci_write_config_word(pcard->pdev, PCI_COMMAND, regval);
+
+ // Clear relaxed ordering bit
+ pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN, 0);
+
+ // Set Max_Payload_Size and Max_Read_Request_Size
+ regval = (0x0) << 5; // Max_Payload_Size = 128 B
+ pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_PAYLOAD, regval);
+ regval = (0x0) << 12; // Max_Read_Request_Size = 128 B
+ pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_READRQ, regval);
+
+ // Enable error reporting for: Correctable Errors, Non-Fatal Errors, Fatal Errors, Unsupported Requests
+ pcie_capability_clear_and_set_word(pcard->pdev, PCI_EXP_DEVCTL, 0, PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE);
+
+ err = dma_set_mask(PCARD_TO_DEV(pcard), DMA_BIT_MASK(64));
+ if (err){
+ dev_err(&pcard->pdev->dev, "CANNOT use DMA mask %0llx\n", DMA_BIT_MASK(64));
+ goto out7;
+ }
+ dev_dbg(&pcard->pdev->dev, "Using DMA mask %0llx\n", dma_get_mask(PCARD_TO_DEV(pcard)));
+ //}
+
+ //{ Step 8: Configure IRQs
+ err = pci_enable_msi(pcard->pdev);
+ if (err < 0)
+ goto out8a;
+
+ rv = request_irq(pcard->pdev->irq, kp2000_irq_handler, IRQF_SHARED, pcard->name, pcard);
+ if (rv){
+ dev_err(&pcard->pdev->dev, "kp2000_pcie_probe: failed to request_irq: %d\n", rv);
+ goto out8b;
+ }
+ //}
+
+ //{ Step 9: Setup sysfs attributes
+ err = sysfs_create_files(&(pdev->dev.kobj), kp_attr_list);
+ if (err){
+ dev_err(&pdev->dev, "Failed to add sysfs files: %d\n", err);
+ goto out9;
+ }
+ //}
+
+ //{ Step 10: Setup misc device
+ pcard->miscdev.minor = MISC_DYNAMIC_MINOR;
+ pcard->miscdev.fops = &kp2000_fops;
+ pcard->miscdev.parent = &pcard->pdev->dev;
+ pcard->miscdev.name = pcard->name;
+
+ err = misc_register(&pcard->miscdev);
+ if (err){
+ dev_err(&pcard->pdev->dev, "kp2000_pcie_probe: misc_register failed: %d\n", err);
+ goto out10;
+ }
+ //}
+
+ //{ Step 11: Probe cores
+ err = kp2000_probe_cores(pcard);
+ if (err)
+ goto out11;
+ //}
+
+ //{ Step 12: Enable IRQs in HW
+ SetBackEndControl(pcard->dma_common_regs, KPC_DMA_CARD_IRQ_ENABLE | KPC_DMA_CARD_USER_INTERRUPT_MODE);
+ //}
+
+ dev_dbg(&pcard->pdev->dev, "kp2000_pcie_probe() complete!\n");
+ unlock_card(pcard);
+ return 0;
+
+ out11:
+ misc_deregister(&pcard->miscdev);
+ out10:
+ sysfs_remove_files(&(pdev->dev.kobj), kp_attr_list);
+ out9:
+ free_irq(pcard->pdev->irq, pcard);
+ out8b:
+ pci_disable_msi(pcard->pdev);
+ out8a:
+ out7:
+ out6:
+ iounmap(pcard->dma_bar_base);
+ pci_release_region(pdev, DMA_BAR);
+ pcard->dma_bar_base = NULL;
+ out5:
+ iounmap(pcard->regs_bar_base);
+ pci_release_region(pdev, REG_BAR);
+ pcard->regs_bar_base = NULL;
+ out4:
+ pci_disable_device(pcard->pdev);
+ out3:
+ unlock_card(pcard);
+ kfree(pcard);
+ return err;
+}
+
+
+void kp2000_pcie_remove(struct pci_dev *pdev)
+{
+ struct kp2000_device *pcard = pci_get_drvdata(pdev);
+
+ dev_dbg(&pdev->dev, "kp2000_pcie_remove(pdev=%p)\n", pdev);
+
+ if (pcard == NULL) return;
+
+ lock_card(pcard);
+ kp2000_remove_cores(pcard);
+ mfd_remove_devices(PCARD_TO_DEV(pcard));
+ misc_deregister(&pcard->miscdev);
+ sysfs_remove_files(&(pdev->dev.kobj), kp_attr_list);
+ free_irq(pcard->pdev->irq, pcard);
+ pci_disable_msi(pcard->pdev);
+ if (pcard->dma_bar_base != NULL){
+ iounmap(pcard->dma_bar_base);
+ pci_release_region(pdev, DMA_BAR);
+ pcard->dma_bar_base = NULL;
+ }
+ if (pcard->regs_bar_base != NULL){
+ iounmap(pcard->regs_bar_base);
+ pci_release_region(pdev, REG_BAR);
+ pcard->regs_bar_base = NULL;
+ }
+ pci_disable_device(pcard->pdev);
+ pci_set_drvdata(pdev, NULL);
+ unlock_card(pcard);
+ kfree(pcard);
+}
diff --git a/drivers/staging/kpc2000/kpc2000/dma_common_defs.h b/drivers/staging/kpc2000/kpc2000/dma_common_defs.h
new file mode 100644
index 000000000000..f35e636b1fb7
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/dma_common_defs.h
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef KPC_DMA_COMMON_DEFS_H_
+#define KPC_DMA_COMMON_DEFS_H_
+
+#define KPC_DMA_COMMON_OFFSET 0x4000
+#define KPC_DMA_S2C_BASE_OFFSET 0x0000
+#define KPC_DMA_C2S_BASE_OFFSET 0x2000
+#define KPC_DMA_ENGINE_SIZE 0x0100
+#define ENGINE_CAP_PRESENT_MASK 0x1
+
+
+#define KPC_DMA_CARD_IRQ_ENABLE (1 << 0)
+#define KPC_DMA_CARD_IRQ_ACTIVE (1 << 1)
+#define KPC_DMA_CARD_IRQ_PENDING (1 << 2)
+#define KPC_DMA_CARD_IRQ_MSI (1 << 3)
+#define KPC_DMA_CARD_USER_INTERRUPT_MODE (1 << 4)
+#define KPC_DMA_CARD_USER_INTERRUPT_ACTIVE (1 << 5)
+#define KPC_DMA_CARD_IRQ_MSIX_MODE (1 << 6)
+#define KPC_DMA_CARD_MAX_PAYLOAD_SIZE_MASK 0x0700
+#define KPC_DMA_CARD_MAX_READ_REQUEST_SIZE_MASK 0x7000
+#define KPC_DMA_CARD_S2C_INTERRUPT_STATUS_MASK 0x00FF0000
+#define KPC_DMA_CARD_C2S_INTERRUPT_STATUS_MASK 0xFF000000
+
+static inline void SetBackEndControl(void __iomem *regs, u32 value)
+{
+ writel(value, regs + 0);
+}
+static inline u32 GetBackEndStatus(void __iomem *regs)
+{
+ return readl(regs + 0);
+}
+
+static inline u32 BackEndControlSetClear(void __iomem *regs, u32 set_bits, u32 clear_bits)
+{
+ u32 start_val = GetBackEndStatus(regs);
+ u32 new_val = start_val;
+ new_val &= ~clear_bits;
+ new_val |= set_bits;
+ SetBackEndControl(regs, new_val);
+ return start_val;
+}
+
+#endif /* KPC_DMA_COMMON_DEFS_H_ */
diff --git a/drivers/staging/kpc2000/kpc2000/fileops.c b/drivers/staging/kpc2000/kpc2000/fileops.c
new file mode 100644
index 000000000000..4bfba59570e6
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/fileops.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h> /* printk() */
+#include <linux/slab.h> /* kmalloc() */
+#include <linux/fs.h> /* everything... */
+#include <linux/errno.h> /* error codes */
+#include <linux/types.h> /* size_t */
+#include <linux/cdev.h>
+#include <linux/uaccess.h> /* copy_*_user */
+#include <linux/rwsem.h>
+#include <linux/idr.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include "pcie.h"
+#include "uapi.h"
+
+int kp2000_cdev_open(struct inode *inode, struct file *filp)
+{
+ struct kp2000_device *pcard = container_of(filp->private_data, struct kp2000_device, miscdev);
+
+ dev_dbg(&pcard->pdev->dev, "kp2000_cdev_open(filp = [%p], pcard = [%p])\n", filp, pcard);
+
+ filp->private_data = pcard; /* so other methods can access it */
+
+ return 0;
+}
+
+int kp2000_cdev_close(struct inode *inode, struct file *filp)
+{
+ struct kp2000_device *pcard = filp->private_data;
+
+ dev_dbg(&pcard->pdev->dev, "kp2000_cdev_close(filp = [%p], pcard = [%p])\n", filp, pcard);
+ return 0;
+}
+
+
+ssize_t kp2000_cdev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
+{
+ struct kp2000_device *pcard = filp->private_data;
+ int cnt = 0;
+ int ret;
+#define BUFF_CNT 1024
+ char buff[BUFF_CNT] = {0}; //NOTE: Increase this so it is at least as large as all the scnprintfs. And don't use unbounded strings. "%s"
+ //NOTE: also, this is a really shitty way to implement the read() call, but it will work for any size 'count'.
+
+ if (WARN(NULL == buf, "kp2000_cdev_read: buf is a NULL pointer!\n"))
+ return -EINVAL;
+
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Card ID : 0x%08x\n", pcard->card_id);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Build Version : 0x%08x\n", pcard->build_version);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Build Date : 0x%08x\n", pcard->build_datestamp);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Build Time : 0x%08x\n", pcard->build_timestamp);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Core Table Offset : 0x%08x\n", pcard->core_table_offset);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Core Table Length : 0x%08x\n", pcard->core_table_length);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "Hardware Revision : 0x%08x\n", pcard->hardware_revision);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "SSID : 0x%016llx\n", pcard->ssid);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "DDNA : 0x%016llx\n", pcard->ddna);
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "IRQ Mask : 0x%016llx\n", readq(pcard->sysinfo_regs_base + REG_INTERRUPT_MASK));
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "IRQ Active : 0x%016llx\n", readq(pcard->sysinfo_regs_base + REG_INTERRUPT_ACTIVE));
+ cnt += scnprintf(buff+cnt, BUFF_CNT-cnt, "CPLD : 0x%016llx\n", readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG));
+
+ if (*f_pos >= cnt)
+ return 0;
+
+ if (count > cnt)
+ count = cnt;
+
+ ret = copy_to_user(buf, buff + *f_pos, count);
+ if (ret)
+ return -EFAULT;
+ *f_pos += count;
+ return count;
+}
+
+ssize_t kp2000_cdev_write(struct file *filp, const char __user *buf, size_t count, loff_t *f_pos)
+{
+ return -EINVAL;
+}
+
+long kp2000_cdev_ioctl(struct file *filp, unsigned int ioctl_num, unsigned long ioctl_param)
+{
+ struct kp2000_device *pcard = filp->private_data;
+
+ dev_dbg(&pcard->pdev->dev, "kp2000_cdev_ioctl(filp = [%p], ioctl_num = 0x%08x, ioctl_param = 0x%016lx) pcard = [%p]\n", filp, ioctl_num, ioctl_param, pcard);
+
+ switch (ioctl_num){
+ case KP2000_IOCTL_GET_CPLD_REG: return readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG);
+ case KP2000_IOCTL_GET_PCIE_ERROR_REG: return readq(pcard->sysinfo_regs_base + REG_PCIE_ERROR_COUNT);
+
+ case KP2000_IOCTL_GET_EVERYTHING: {
+ struct kp2000_regs temp;
+ int ret;
+ temp.card_id = pcard->card_id;
+ temp.build_version = pcard->build_version;
+ temp.build_datestamp = pcard->build_datestamp;
+ temp.build_timestamp = pcard->build_timestamp;
+ temp.hw_rev = pcard->hardware_revision;
+ temp.ssid = pcard->ssid;
+ temp.ddna = pcard->ddna;
+ temp.cpld_reg = readq(pcard->sysinfo_regs_base + REG_CPLD_CONFIG);
+
+ ret = copy_to_user((void*)ioctl_param, (void*)&temp, sizeof(temp));
+ if (ret)
+ return -EFAULT;
+
+ return sizeof(temp);
+ }
+
+ default:
+ return -ENOTTY;
+ }
+ return -ENOTTY;
+}
+
+
+struct file_operations kp2000_fops = {
+ .owner = THIS_MODULE,
+ .open = kp2000_cdev_open,
+ .release = kp2000_cdev_close,
+ .read = kp2000_cdev_read,
+ //.write = kp2000_cdev_write,
+ //.poll = kp2000_cdev_poll,
+ //.fasync = kp2000_cdev_fasync,
+ .llseek = noop_llseek,
+ .unlocked_ioctl = kp2000_cdev_ioctl,
+};
+
diff --git a/drivers/staging/kpc2000/kpc2000/kp2000_module.c b/drivers/staging/kpc2000/kpc2000/kp2000_module.c
new file mode 100644
index 000000000000..661b0b74ed66
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/kp2000_module.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <linux/io.h>
+#include <linux/mfd/core.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+#include "pcie.h"
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Lee.Brooke@Daktronics.com, Matt.Sickler@Daktronics.com");
+MODULE_SOFTDEP("pre: uio post: kpc_nwl_dma kpc_i2c kpc_spi");
+
+struct class *kpc_uio_class;
+ATTRIBUTE_GROUPS(kpc_uio_class);
+
+static const struct pci_device_id kp2000_pci_device_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_DAKTRONICS, PCI_DEVICE_ID_DAKTRONICS) },
+ { PCI_DEVICE(PCI_VENDOR_ID_DAKTRONICS, PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, kp2000_pci_device_ids);
+
+static struct pci_driver kp2000_driver_inst = {
+ .name = "kp2000_pcie",
+ .id_table = kp2000_pci_device_ids,
+ .probe = kp2000_pcie_probe,
+ .remove = kp2000_pcie_remove
+};
+
+
+static int __init kp2000_pcie_init(void)
+{
+ kpc_uio_class = class_create(THIS_MODULE, "kpc_uio");
+ if (IS_ERR(kpc_uio_class))
+ return PTR_ERR(kpc_uio_class);
+
+ kpc_uio_class->dev_groups = kpc_uio_class_groups;
+ return pci_register_driver(&kp2000_driver_inst);
+}
+module_init(kp2000_pcie_init);
+
+static void __exit kp2000_pcie_exit(void)
+{
+ pci_unregister_driver(&kp2000_driver_inst);
+ class_destroy(kpc_uio_class);
+}
+module_exit(kp2000_pcie_exit);
diff --git a/drivers/staging/kpc2000/kpc2000/pcie.h b/drivers/staging/kpc2000/kpc2000/pcie.h
new file mode 100644
index 000000000000..893aebfd1449
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/pcie.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef KP2000_PCIE_H
+#define KP2000_PCIE_H
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include "../kpc.h"
+#include "dma_common_defs.h"
+
+
+/* System Register Map (BAR 1, Start Addr 0)
+ *
+ * BAR Size:
+ * 1048576 (0x100000) bytes = 131072 (0x20000) registers = 256 pages (4K)
+ *
+ * 6 5 4 3 2 1 0
+ * 3210987654321098765432109876543210987654321098765432109876543210
+ * 0 <--------------------------- MAGIC ---------------------------->
+ * 1 <----------- Card ID ---------><----------- Revision ---------->
+ * 2 <--------- Date Stamp --------><--------- Time Stamp ---------->
+ * 3 <-------- Core Tbl Len -------><-------- Core Tbl Offset ------>
+ * 4 <---------------------------- SSID ---------------------------->
+ * 5 < HWID >
+ * 6 <------------------------- FPGA DDNA -------------------------->
+ * 7 <------------------------ CPLD Config ------------------------->
+ * 8 <----------------------- IRQ Mask Flags ----------------------->
+ * 9 <---------------------- IRQ Active Flags ---------------------->
+ */
+
+#define REG_WIDTH 8
+#define REG_MAGIC_NUMBER (0 * REG_WIDTH)
+#define REG_CARD_ID_AND_BUILD (1 * REG_WIDTH)
+#define REG_DATE_AND_TIME_STAMPS (2 * REG_WIDTH)
+#define REG_CORE_TABLE_OFFSET (3 * REG_WIDTH)
+#define REG_FPGA_SSID (4 * REG_WIDTH)
+#define REG_FPGA_HW_ID (5 * REG_WIDTH)
+#define REG_FPGA_DDNA (6 * REG_WIDTH)
+#define REG_CPLD_CONFIG (7 * REG_WIDTH)
+#define REG_INTERRUPT_MASK (8 * REG_WIDTH)
+#define REG_INTERRUPT_ACTIVE (9 * REG_WIDTH)
+#define REG_PCIE_ERROR_COUNT (10 * REG_WIDTH)
+
+#define KP2000_MAGIC_VALUE 0x196C61482231894D
+
+#define PCI_VENDOR_ID_DAKTRONICS 0x1c33
+#define PCI_DEVICE_ID_DAKTRONICS 0x6021
+
+#define DMA_BAR 0
+#define REG_BAR 1
+
+struct kp2000_device {
+ struct pci_dev *pdev;
+ struct miscdevice miscdev;
+ char name[16];
+
+ unsigned int card_num;
+ struct mutex sem;
+
+ void __iomem *sysinfo_regs_base;
+ void __iomem *regs_bar_base;
+ struct resource regs_base_resource;
+ void __iomem *dma_bar_base;
+ void __iomem *dma_common_regs;
+ struct resource dma_base_resource;
+
+ // "System Registers"
+ u32 card_id;
+ u32 build_version;
+ u32 build_datestamp;
+ u32 build_timestamp;
+ u32 core_table_offset;
+ u32 core_table_length;
+ u8 core_table_rev;
+ u8 hardware_revision;
+ u64 ssid;
+ u64 ddna;
+
+ // IRQ stuff
+ unsigned int irq;
+
+ struct list_head uio_devices_list;
+};
+
+extern struct class *kpc_uio_class;
+extern struct attribute *kpc_uio_class_attrs[];
+
+int kp2000_pcie_probe(struct pci_dev *dev, const struct pci_device_id *id);
+void kp2000_pcie_remove(struct pci_dev *pdev);
+int kp2000_probe_cores(struct kp2000_device *pcard);
+void kp2000_remove_cores(struct kp2000_device *pcard);
+
+extern struct file_operations kp2000_fops;
+
+
+// Define this quick little macro because the expression is used frequently
+#define PCARD_TO_DEV(pcard) (&(pcard->pdev->dev))
+
+static inline void
+lock_card(struct kp2000_device *pcard)
+{
+ BUG_ON(pcard == NULL);
+ mutex_lock(&pcard->sem);
+}
+static inline void
+unlock_card(struct kp2000_device *pcard)
+{
+ BUG_ON(pcard == NULL);
+ mutex_unlock(&pcard->sem);
+}
+
+
+#endif /* KP2000_PCIE_H */
diff --git a/drivers/staging/kpc2000/kpc2000/uapi.h b/drivers/staging/kpc2000/kpc2000/uapi.h
new file mode 100644
index 000000000000..ef8008bcd33d
--- /dev/null
+++ b/drivers/staging/kpc2000/kpc2000/uapi.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+#ifndef KP2000_CDEV_UAPI_H_
+#define KP2000_CDEV_UAPI_H_
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+struct kp2000_regs {
+ __u32 card_id;
+ __u32 build_version;
+ __u32 build_datestamp;
+ __u32 build_timestamp;
+ __u32 hw_rev;
+ __u64 ssid;
+ __u64 ddna;
+ __u64 cpld_reg;
+};
+
+#define KP2000_IOCTL_GET_CPLD_REG _IOR('k', 9, __u32)
+#define KP2000_IOCTL_GET_PCIE_ERROR_REG _IOR('k', 11, __u32)
+#define KP2000_IOCTL_GET_EVERYTHING _IOR('k', 8, struct kp2000_regs*)
+
+#endif /* KP2000_CDEV_UAPI_H_ */