Commit 922a0524 authored by Greg Kroah-Hartman's avatar Greg Kroah-Hartman

staging: drop kpc2000 driver

It seems that the old developer is no longer with the company producing
this device, and the company has no plans on getting this out of the
staging directory at all, so let's drop the driver for now as it's
pretty much abandonded.

If someone want to support this and get it out of staging, we can easily
revert this change and bring it back.

Cc: Matt Sickler <matt.sickler@msk4.com>
Link: https://lore.kernel.org/r/20210610183153.2397760-1-gregkh@linuxfoundation.orgSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent c57f4f00
......@@ -98,8 +98,6 @@ source "drivers/staging/axis-fifo/Kconfig"
source "drivers/staging/fieldbus/Kconfig"
source "drivers/staging/kpc2000/Kconfig"
source "drivers/staging/qlge/Kconfig"
source "drivers/staging/wfx/Kconfig"
......
......@@ -39,7 +39,6 @@ obj-$(CONFIG_DMA_RALINK) += ralink-gdma/
obj-$(CONFIG_SOC_MT7621) += mt7621-dts/
obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/
obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/
obj-$(CONFIG_KPC2000) += kpc2000/
obj-$(CONFIG_QLGE) += qlge/
obj-$(CONFIG_WFX) += wfx/
obj-y += hikey9xx/
# SPDX-License-Identifier: GPL-2.0
config KPC2000
bool "Daktronics KPC Device support"
select MFD_CORE
depends on PCI
depends on UIO
help
Select this if you wish to use the Daktronics KPC PCI devices
If unsure, say N.
config KPC2000_CORE
tristate "Daktronics KPC PCI UIO device"
depends on KPC2000
help
Say Y here if you wish to support the Daktronics KPC PCI
device in UIO mode.
To compile this driver as a module, choose M here: the module
will be called kpc2000
If unsure, say N.
config KPC2000_SPI
tristate "Daktronics KPC SPI device"
depends on KPC2000 && SPI
help
Say Y here if you wish to support the Daktronics KPC PCI
device in SPI mode.
To compile this driver as a module, choose M here: the module
will be called kpc2000_spi
If unsure, say N.
config KPC2000_I2C
tristate "Daktronics KPC I2C device"
depends on KPC2000 && I2C
help
Say Y here if you wish to support the Daktronics KPC PCI
device in I2C mode.
To compile this driver as a module, choose M here: the module
will be called kpc2000_i2c
If unsure, say N.
config KPC2000_DMA
tristate "Daktronics KPC DMA controller"
depends on KPC2000
help
Say Y here if you wish to support the Daktronics DMA controller.
To compile this driver as a module, choose M here: the module
will be called kpc2000_dma
If unsure, say N.
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_KPC2000) += kpc2000/
obj-$(CONFIG_KPC2000_I2C) += kpc2000_i2c.o
obj-$(CONFIG_KPC2000_SPI) += kpc2000_spi.o
obj-$(CONFIG_KPC2000_DMA) += kpc_dma/
- the kpc_spi driver doesn't seem to let multiple transactions (to different instances of the core) happen in parallel...
- The kpc_i2c driver is a hot mess, it should probably be cleaned up a ton. It functions against current hardware though.
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef KPC_H_
#define KPC_H_
/* ***** Driver Names ***** */
#define KP_DRIVER_NAME_KP2000 "kp2000"
#define KP_DRIVER_NAME_INVALID "kpc_invalid"
#define KP_DRIVER_NAME_DMA_CONTROLLER "kpc_nwl_dma"
#define KP_DRIVER_NAME_UIO "uio_pdrv_genirq"
#define KP_DRIVER_NAME_I2C "kpc_i2c"
#define KP_DRIVER_NAME_SPI "kpc_spi"
struct kpc_core_device_platdata {
u32 card_id;
u32 build_version;
u32 hardware_revision;
u64 ssid;
u64 ddna;
};
#define PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0 0x4b03
#endif /* KPC_H_ */
# SPDX-License-Identifier: GPL-2.0
obj-m := kpc2000.o
kpc2000-objs += core.o cell_probe.o
This diff is collapsed.
This diff is collapsed.
/* 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 BIT(0)
#define KPC_DMA_CARD_IRQ_ACTIVE BIT(1)
#define KPC_DMA_CARD_IRQ_PENDING BIT(2)
#define KPC_DMA_CARD_IRQ_MSI BIT(3)
#define KPC_DMA_CARD_USER_INTERRUPT_MODE BIT(4)
#define KPC_DMA_CARD_USER_INTERRUPT_ACTIVE BIT(5)
#define KPC_DMA_CARD_IRQ_MSIX_MODE BIT(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
#endif /* KPC_DMA_COMMON_DEFS_H_ */
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef KP2000_PCIE_H
#define KP2000_PCIE_H
#include <linux/types.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 0x196C61482231894DULL
#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;
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_probe_cores(struct kp2000_device *pcard);
void kp2000_remove_cores(struct kp2000_device *pcard);
// Define this quick little macro because the expression is used frequently
#define PCARD_TO_DEV(pcard) (&(pcard->pdev->dev))
#endif /* KP2000_PCIE_H */
/* 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_ */
This diff is collapsed.
This diff is collapsed.
# SPDX-License-Identifier: GPL-2.0
obj-m := kpc_dma.o
kpc_dma-objs += dma.o
kpc_dma-objs += fileops.o
kpc_dma-objs += kpc_dma_driver.o
// SPDX-License-Identifier: GPL-2.0+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/rwsem.h>
#include "kpc_dma_driver.h"
/********** IRQ Handlers **********/
static
irqreturn_t ndd_irq_handler(int irq, void *dev_id)
{
struct kpc_dma_device *ldev = (struct kpc_dma_device *)dev_id;
if ((GetEngineControl(ldev) & ENG_CTL_IRQ_ACTIVE) ||
(ldev->desc_completed->MyDMAAddr != GetEngineCompletePtr(ldev)))
schedule_work(&ldev->irq_work);
return IRQ_HANDLED;
}
static
void ndd_irq_worker(struct work_struct *ws)
{
struct kpc_dma_descriptor *cur;
struct kpc_dma_device *eng = container_of(ws, struct kpc_dma_device, irq_work);
lock_engine(eng);
if (GetEngineCompletePtr(eng) == 0)
goto out;
if (eng->desc_completed->MyDMAAddr == GetEngineCompletePtr(eng))
goto out;
cur = eng->desc_completed;
do {
cur = cur->Next;
dev_dbg(&eng->pldev->dev, "Handling completed descriptor %p (acd = %p)\n",
cur, cur->acd);
BUG_ON(cur == eng->desc_next); // Ordering failure.
if (cur->DescControlFlags & DMA_DESC_CTL_SOP) {
eng->accumulated_bytes = 0;
eng->accumulated_flags = 0;
}
eng->accumulated_bytes += cur->DescByteCount;
if (cur->DescStatusFlags & DMA_DESC_STS_ERROR)
eng->accumulated_flags |= ACD_FLAG_ENG_ACCUM_ERROR;
if (cur->DescStatusFlags & DMA_DESC_STS_SHORT)
eng->accumulated_flags |= ACD_FLAG_ENG_ACCUM_SHORT;
if (cur->DescControlFlags & DMA_DESC_CTL_EOP) {
if (cur->acd)
transfer_complete_cb(cur->acd, eng->accumulated_bytes,
eng->accumulated_flags | ACD_FLAG_DONE);
}
eng->desc_completed = cur;
} while (cur->MyDMAAddr != GetEngineCompletePtr(eng));
out:
SetClearEngineControl(eng, ENG_CTL_IRQ_ACTIVE, 0);
unlock_engine(eng);
}
/********** DMA Engine Init/Teardown **********/
void start_dma_engine(struct kpc_dma_device *eng)
{
eng->desc_next = eng->desc_pool_first;
eng->desc_completed = eng->desc_pool_last;
// Setup the engine pointer registers
SetEngineNextPtr(eng, eng->desc_pool_first);
SetEngineSWPtr(eng, eng->desc_pool_first);
ClearEngineCompletePtr(eng);
WriteEngineControl(eng, ENG_CTL_DMA_ENABLE | ENG_CTL_IRQ_ENABLE);
}
int setup_dma_engine(struct kpc_dma_device *eng, u32 desc_cnt)
{
u32 caps;
struct kpc_dma_descriptor *cur;
struct kpc_dma_descriptor *next;
dma_addr_t next_handle;
dma_addr_t head_handle;
unsigned int i;
int rv;
caps = GetEngineCapabilities(eng);
if (WARN(!(caps & ENG_CAP_PRESENT), "%s() called for DMA Engine at %p which isn't present in hardware!\n", __func__, eng))
return -ENXIO;
if (caps & ENG_CAP_DIRECTION)
eng->dir = DMA_FROM_DEVICE;
else
eng->dir = DMA_TO_DEVICE;
eng->desc_pool_cnt = desc_cnt;
eng->desc_pool = dma_pool_create("KPC DMA Descriptors", &eng->pldev->dev,
sizeof(struct kpc_dma_descriptor),
DMA_DESC_ALIGNMENT, 4096);
eng->desc_pool_first = dma_pool_alloc(eng->desc_pool, GFP_KERNEL | GFP_DMA, &head_handle);
if (!eng->desc_pool_first) {
dev_err(&eng->pldev->dev, "%s: couldn't allocate desc_pool_first!\n", __func__);
dma_pool_destroy(eng->desc_pool);
return -ENOMEM;
}
eng->desc_pool_first->MyDMAAddr = head_handle;
clear_desc(eng->desc_pool_first);
cur = eng->desc_pool_first;
for (i = 1 ; i < eng->desc_pool_cnt ; i++) {
next = dma_pool_alloc(eng->desc_pool, GFP_KERNEL | GFP_DMA, &next_handle);
if (!next)
goto done_alloc;
clear_desc(next);
next->MyDMAAddr = next_handle;
cur->DescNextDescPtr = next_handle;
cur->Next = next;
cur = next;
}
done_alloc:
// Link the last descriptor back to the first, so it's a circular linked list
cur->Next = eng->desc_pool_first;
cur->DescNextDescPtr = eng->desc_pool_first->MyDMAAddr;
eng->desc_pool_last = cur;
eng->desc_completed = eng->desc_pool_last;
// Setup work queue
INIT_WORK(&eng->irq_work, ndd_irq_worker);
// Grab IRQ line
rv = request_irq(eng->irq, ndd_irq_handler, IRQF_SHARED,
KP_DRIVER_NAME_DMA_CONTROLLER, eng);
if (rv) {
dev_err(&eng->pldev->dev, "%s: failed to request_irq: %d\n", __func__, rv);
return rv;
}
// Turn on the engine!
start_dma_engine(eng);
unlock_engine(eng);
return 0;
}
void stop_dma_engine(struct kpc_dma_device *eng)
{
unsigned long timeout;
// Disable the descriptor engine
WriteEngineControl(eng, 0);
// Wait for descriptor engine to finish current operaion
timeout = jiffies + (HZ / 2);
while (GetEngineControl(eng) & ENG_CTL_DMA_RUNNING) {
if (time_after(jiffies, timeout)) {
dev_crit(&eng->pldev->dev, "DMA_RUNNING still asserted!\n");
break;
}
}
// Request a reset
WriteEngineControl(eng, ENG_CTL_DMA_RESET_REQUEST);
// Wait for reset request to be processed
timeout = jiffies + (HZ / 2);
while (GetEngineControl(eng) & (ENG_CTL_DMA_RUNNING | ENG_CTL_DMA_RESET_REQUEST)) {
if (time_after(jiffies, timeout)) {
dev_crit(&eng->pldev->dev, "ENG_CTL_DMA_RESET_REQUEST still asserted!\n");
break;
}
}
// Request a reset
WriteEngineControl(eng, ENG_CTL_DMA_RESET);
// And wait for reset to complete
timeout = jiffies + (HZ / 2);
while (GetEngineControl(eng) & ENG_CTL_DMA_RESET) {
if (time_after(jiffies, timeout)) {
dev_crit(&eng->pldev->dev, "DMA_RESET still asserted!\n");
break;
}
}
// Clear any persistent bits just to make sure there is no residue from the reset
SetClearEngineControl(eng, (ENG_CTL_IRQ_ACTIVE | ENG_CTL_DESC_COMPLETE |
ENG_CTL_DESC_ALIGN_ERR | ENG_CTL_DESC_FETCH_ERR |
ENG_CTL_SW_ABORT_ERR | ENG_CTL_DESC_CHAIN_END |
ENG_CTL_DMA_WAITING_PERSIST), 0);
// Reset performance counters
// Completely disable the engine
WriteEngineControl(eng, 0);
}
void destroy_dma_engine(struct kpc_dma_device *eng)
{
struct kpc_dma_descriptor *cur;
dma_addr_t cur_handle;
unsigned int i;
stop_dma_engine(eng);
cur = eng->desc_pool_first;
cur_handle = eng->desc_pool_first->MyDMAAddr;
for (i = 0 ; i < eng->desc_pool_cnt ; i++) {
struct kpc_dma_descriptor *next = cur->Next;
dma_addr_t next_handle = cur->DescNextDescPtr;
dma_pool_free(eng->desc_pool, cur, cur_handle);
cur_handle = next_handle;
cur = next;
}
dma_pool_destroy(eng->desc_pool);
free_irq(eng->irq, eng);
}
/********** Helper Functions **********/
int count_descriptors_available(struct kpc_dma_device *eng)
{
u32 count = 0;
struct kpc_dma_descriptor *cur = eng->desc_next;
while (cur != eng->desc_completed) {
BUG_ON(!cur);
count++;
cur = cur->Next;
}
return count;
}
void clear_desc(struct kpc_dma_descriptor *desc)
{
if (!desc)
return;
desc->DescByteCount = 0;
desc->DescStatusErrorFlags = 0;
desc->DescStatusFlags = 0;
desc->DescUserControlLS = 0;
desc->DescUserControlMS = 0;
desc->DescCardAddrLS = 0;
desc->DescBufferByteCount = 0;
desc->DescCardAddrMS = 0;
desc->DescControlFlags = 0;
desc->DescSystemAddrLS = 0;
desc->DescSystemAddrMS = 0;
desc->acd = NULL;
}
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0+
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/io.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/rwsem.h>
#include "kpc_dma_driver.h"
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Matt.Sickler@daktronics.com");
#define KPC_DMA_CHAR_MAJOR UNNAMED_MAJOR
#define KPC_DMA_NUM_MINORS BIT(MINORBITS)
static DEFINE_MUTEX(kpc_dma_mtx);
static int assigned_major_num;
static LIST_HEAD(kpc_dma_list);
/********** kpc_dma_list list management **********/
struct kpc_dma_device *kpc_dma_lookup_device(int minor)
{
struct kpc_dma_device *c;
mutex_lock(&kpc_dma_mtx);
list_for_each_entry(c, &kpc_dma_list, list) {
if (c->pldev->id == minor)
goto out;
}
c = NULL; // not-found case
out:
mutex_unlock(&kpc_dma_mtx);
return c;
}
static void kpc_dma_add_device(struct kpc_dma_device *ldev)
{
mutex_lock(&kpc_dma_mtx);
list_add(&ldev->list, &kpc_dma_list);
mutex_unlock(&kpc_dma_mtx);
}
static void kpc_dma_del_device(struct kpc_dma_device *ldev)
{
mutex_lock(&kpc_dma_mtx);
list_del(&ldev->list);
mutex_unlock(&kpc_dma_mtx);
}
/********** SysFS Attributes **********/
static ssize_t engine_regs_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct kpc_dma_device *ldev;
struct platform_device *pldev = to_platform_device(dev);
if (!pldev)
return 0;
ldev = platform_get_drvdata(pldev);
if (!ldev)
return 0;
return scnprintf(buf, PAGE_SIZE,
"EngineControlStatus = 0x%08x\n"
"RegNextDescPtr = 0x%08x\n"
"RegSWDescPtr = 0x%08x\n"
"RegCompletedDescPtr = 0x%08x\n"
"desc_pool_first = %p\n"
"desc_pool_last = %p\n"
"desc_next = %p\n"
"desc_completed = %p\n",
readl(ldev->eng_regs + 1),
readl(ldev->eng_regs + 2),
readl(ldev->eng_regs + 3),
readl(ldev->eng_regs + 4),
ldev->desc_pool_first,
ldev->desc_pool_last,
ldev->desc_next,
ldev->desc_completed
);
}
static DEVICE_ATTR_RO(engine_regs);
static const struct attribute *ndd_attr_list[] = {
&dev_attr_engine_regs.attr,
NULL,
};
static struct class *kpc_dma_class;
/********** Platform Driver Functions **********/
static
int kpc_dma_probe(struct platform_device *pldev)
{
struct resource *r = NULL;
int rv = 0;
dev_t dev;
struct kpc_dma_device *ldev = kzalloc(sizeof(*ldev), GFP_KERNEL);
if (!ldev) {
dev_err(&pldev->dev, "%s: unable to kzalloc space for kpc_dma_device\n", __func__);
rv = -ENOMEM;
goto err_rv;
}
INIT_LIST_HEAD(&ldev->list);
ldev->pldev = pldev;
platform_set_drvdata(pldev, ldev);
atomic_set(&ldev->open_count, 1);
mutex_init(&ldev->sem);
lock_engine(ldev);
// Get Engine regs resource
r = platform_get_resource(pldev, IORESOURCE_MEM, 0);
if (!r) {
dev_err(&ldev->pldev->dev, "%s: didn't get the engine regs resource!\n", __func__);
rv = -ENXIO;
goto err_kfree;
}
ldev->eng_regs = ioremap(r->start, resource_size(r));
if (!ldev->eng_regs) {
dev_err(&ldev->pldev->dev, "%s: failed to ioremap engine regs!\n", __func__);
rv = -ENXIO;
goto err_kfree;
}
r = platform_get_resource(pldev, IORESOURCE_IRQ, 0);
if (!r) {
dev_err(&ldev->pldev->dev, "%s: didn't get the IRQ resource!\n", __func__);
rv = -ENXIO;
goto err_kfree;
}
ldev->irq = r->start;
// Setup miscdev struct
dev = MKDEV(assigned_major_num, pldev->id);
ldev->kpc_dma_dev = device_create(kpc_dma_class, &pldev->dev, dev, ldev,
"kpc_dma%d", pldev->id);
if (IS_ERR(ldev->kpc_dma_dev)) {
rv = PTR_ERR(ldev->kpc_dma_dev);
dev_err(&ldev->pldev->dev, "%s: device_create failed: %d\n", __func__, rv);
goto err_kfree;
}
// Setup the DMA engine
rv = setup_dma_engine(ldev, 30);
if (rv) {
dev_err(&ldev->pldev->dev, "%s: failed to setup_dma_engine: %d\n", __func__, rv);
goto err_misc_dereg;
}
// Setup the sysfs files
rv = sysfs_create_files(&(ldev->pldev->dev.kobj), ndd_attr_list);
if (rv) {
dev_err(&ldev->pldev->dev, "%s: Failed to add sysfs files: %d\n", __func__, rv);
goto err_destroy_eng;
}
kpc_dma_add_device(ldev);
return 0;
err_destroy_eng:
destroy_dma_engine(ldev);
err_misc_dereg:
device_destroy(kpc_dma_class, dev);
err_kfree:
kfree(ldev);
err_rv:
return rv;
}
static
int kpc_dma_remove(struct platform_device *pldev)
{
struct kpc_dma_device *ldev = platform_get_drvdata(pldev);
if (!ldev)
return -ENXIO;
lock_engine(ldev);
sysfs_remove_files(&(ldev->pldev->dev.kobj), ndd_attr_list);
destroy_dma_engine(ldev);
kpc_dma_del_device(ldev);
device_destroy(kpc_dma_class, MKDEV(assigned_major_num, ldev->pldev->id));
kfree(ldev);
return 0;
}
/********** Driver Functions **********/
static struct platform_driver kpc_dma_plat_driver_i = {
.probe = kpc_dma_probe,
.remove = kpc_dma_remove,
.driver = {
.name = KP_DRIVER_NAME_DMA_CONTROLLER,
},
};
static
int __init kpc_dma_driver_init(void)
{
int err;
err = __register_chrdev(KPC_DMA_CHAR_MAJOR, 0, KPC_DMA_NUM_MINORS,
"kpc_dma", &kpc_dma_fops);
if (err < 0) {
pr_err("Can't allocate a major number (%d) for kpc_dma (err = %d)\n",
KPC_DMA_CHAR_MAJOR, err);
goto fail_chrdev_register;
}
assigned_major_num = err;
kpc_dma_class = class_create(THIS_MODULE, "kpc_dma");
err = PTR_ERR(kpc_dma_class);
if (IS_ERR(kpc_dma_class)) {
pr_err("Can't create class kpc_dma (err = %d)\n", err);
goto fail_class_create;
}
err = platform_driver_register(&kpc_dma_plat_driver_i);
if (err) {
pr_err("Can't register platform driver for kpc_dma (err = %d)\n", err);
goto fail_platdriver_register;
}
return err;
fail_platdriver_register:
class_destroy(kpc_dma_class);
fail_class_create:
__unregister_chrdev(KPC_DMA_CHAR_MAJOR, 0, KPC_DMA_NUM_MINORS, "kpc_dma");
fail_chrdev_register:
return err;
}
module_init(kpc_dma_driver_init);
static
void __exit kpc_dma_driver_exit(void)
{
platform_driver_unregister(&kpc_dma_plat_driver_i);
class_destroy(kpc_dma_class);
__unregister_chrdev(KPC_DMA_CHAR_MAJOR, 0, KPC_DMA_NUM_MINORS, "kpc_dma");
}
module_exit(kpc_dma_driver_exit);
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef KPC_DMA_DRIVER_H
#define KPC_DMA_DRIVER_H
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/kfifo.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/sched.h>
#include <linux/miscdevice.h>
#include <linux/rwsem.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/bitops.h>
#include "../kpc.h"
struct kp2000_device;
struct kpc_dma_device {
struct list_head list;
struct platform_device *pldev;
u32 __iomem *eng_regs;
struct device *kpc_dma_dev;
struct kobject kobj;
char name[16];
int dir; // DMA_FROM_DEVICE || DMA_TO_DEVICE
struct mutex sem;
unsigned int irq;
struct work_struct irq_work;
atomic_t open_count;
size_t accumulated_bytes;
u32 accumulated_flags;
// Descriptor "Pool" housekeeping
u32 desc_pool_cnt;
struct dma_pool *desc_pool;
struct kpc_dma_descriptor *desc_pool_first;
struct kpc_dma_descriptor *desc_pool_last;
struct kpc_dma_descriptor *desc_next;
struct kpc_dma_descriptor *desc_completed;
};
struct dev_private_data {
struct kpc_dma_device *ldev;
u64 card_addr;
u64 user_ctl;
u64 user_ctl_last;
u64 user_sts;
};
struct kpc_dma_device *kpc_dma_lookup_device(int minor);
extern const struct file_operations kpc_dma_fops;
#define ENG_CAP_PRESENT 0x00000001
#define ENG_CAP_DIRECTION 0x00000002
#define ENG_CAP_TYPE_MASK 0x000000F0
#define ENG_CAP_NUMBER_MASK 0x0000FF00
#define ENG_CAP_CARD_ADDR_SIZE_MASK 0x007F0000
#define ENG_CAP_DESC_MAX_BYTE_CNT_MASK 0x3F000000
#define ENG_CAP_PERF_SCALE_MASK 0xC0000000
#define ENG_CTL_IRQ_ENABLE BIT(0)
#define ENG_CTL_IRQ_ACTIVE BIT(1)
#define ENG_CTL_DESC_COMPLETE BIT(2)
#define ENG_CTL_DESC_ALIGN_ERR BIT(3)
#define ENG_CTL_DESC_FETCH_ERR BIT(4)
#define ENG_CTL_SW_ABORT_ERR BIT(5)
#define ENG_CTL_DESC_CHAIN_END BIT(7)
#define ENG_CTL_DMA_ENABLE BIT(8)
#define ENG_CTL_DMA_RUNNING BIT(10)
#define ENG_CTL_DMA_WAITING BIT(11)
#define ENG_CTL_DMA_WAITING_PERSIST BIT(12)
#define ENG_CTL_DMA_RESET_REQUEST BIT(14)
#define ENG_CTL_DMA_RESET BIT(15)
#define ENG_CTL_DESC_FETCH_ERR_CLASS_MASK 0x700000
struct aio_cb_data {
struct dev_private_data *priv;
struct kpc_dma_device *ldev;
struct completion *cpl;
unsigned char flags;
size_t len;
unsigned int page_count;
struct page **user_pages;
struct sg_table sgt;
int mapped_entry_count;
};
#define ACD_FLAG_DONE 0
#define ACD_FLAG_ABORT 1
#define ACD_FLAG_ENG_ACCUM_ERROR 4
#define ACD_FLAG_ENG_ACCUM_SHORT 5
struct kpc_dma_descriptor {
struct {
volatile u32 DescByteCount :20;
volatile u32 DescStatusErrorFlags :4;
volatile u32 DescStatusFlags :8;
};
volatile u32 DescUserControlLS;
volatile u32 DescUserControlMS;
volatile u32 DescCardAddrLS;
struct {
volatile u32 DescBufferByteCount :20;
volatile u32 DescCardAddrMS :4;
volatile u32 DescControlFlags :8;
};
volatile u32 DescSystemAddrLS;
volatile u32 DescSystemAddrMS;
volatile u32 DescNextDescPtr;
dma_addr_t MyDMAAddr;
struct kpc_dma_descriptor *Next;
struct aio_cb_data *acd;
} __attribute__((packed));
// DescControlFlags:
#define DMA_DESC_CTL_SOP BIT(7)
#define DMA_DESC_CTL_EOP BIT(6)
#define DMA_DESC_CTL_AFIFO BIT(2)
#define DMA_DESC_CTL_IRQONERR BIT(1)
#define DMA_DESC_CTL_IRQONDONE BIT(0)
// DescStatusFlags:
#define DMA_DESC_STS_SOP BIT(7)
#define DMA_DESC_STS_EOP BIT(6)
#define DMA_DESC_STS_ERROR BIT(4)
#define DMA_DESC_STS_USMSZ BIT(3)
#define DMA_DESC_STS_USLSZ BIT(2)
#define DMA_DESC_STS_SHORT BIT(1)
#define DMA_DESC_STS_COMPLETE BIT(0)
// DescStatusErrorFlags:
#define DMA_DESC_ESTS_ECRC BIT(2)
#define DMA_DESC_ESTS_POISON BIT(1)
#define DMA_DESC_ESTS_UNSUCCESSFUL BIT(0)
#define DMA_DESC_ALIGNMENT 0x20
static inline
u32 GetEngineCapabilities(struct kpc_dma_device *eng)
{
return readl(eng->eng_regs + 0);
}
static inline
void WriteEngineControl(struct kpc_dma_device *eng, u32 value)
{
writel(value, eng->eng_regs + 1);
}
static inline
u32 GetEngineControl(struct kpc_dma_device *eng)
{
return readl(eng->eng_regs + 1);
}
static inline
void SetClearEngineControl(struct kpc_dma_device *eng, u32 set_bits, u32 clear_bits)
{
u32 val = GetEngineControl(eng);
val |= set_bits;
val &= ~clear_bits;
WriteEngineControl(eng, val);
}
static inline
void SetEngineNextPtr(struct kpc_dma_device *eng, struct kpc_dma_descriptor *desc)
{
writel(desc->MyDMAAddr, eng->eng_regs + 2);
}
static inline
void SetEngineSWPtr(struct kpc_dma_device *eng, struct kpc_dma_descriptor *desc)
{
writel(desc->MyDMAAddr, eng->eng_regs + 3);
}
static inline
void ClearEngineCompletePtr(struct kpc_dma_device *eng)
{
writel(0, eng->eng_regs + 4);
}
static inline
u32 GetEngineCompletePtr(struct kpc_dma_device *eng)
{
return readl(eng->eng_regs + 4);
}
static inline
void lock_engine(struct kpc_dma_device *eng)
{
BUG_ON(!eng);
mutex_lock(&eng->sem);
}
static inline
void unlock_engine(struct kpc_dma_device *eng)
{
BUG_ON(!eng);
mutex_unlock(&eng->sem);
}
/// Shared Functions
void start_dma_engine(struct kpc_dma_device *eng);
int setup_dma_engine(struct kpc_dma_device *eng, u32 desc_cnt);
void stop_dma_engine(struct kpc_dma_device *eng);
void destroy_dma_engine(struct kpc_dma_device *eng);
void clear_desc(struct kpc_dma_descriptor *desc);
int count_descriptors_available(struct kpc_dma_device *eng);
void transfer_complete_cb(struct aio_cb_data *acd, size_t xfr_count, u32 flags);
#endif /* KPC_DMA_DRIVER_H */
/* SPDX-License-Identifier: GPL-2.0+ */
#ifndef KPC_DMA_DRIVER_UAPI_H_
#define KPC_DMA_DRIVER_UAPI_H_
#include <linux/ioctl.h>
#define KND_IOCTL_SET_CARD_ADDR _IOW('k', 1, __u32)
#define KND_IOCTL_SET_USER_CTL _IOW('k', 2, __u64)
#define KND_IOCTL_SET_USER_CTL_LAST _IOW('k', 4, __u64)
#define KND_IOCTL_GET_USER_STS _IOR('k', 3, __u64)
#endif /* KPC_DMA_DRIVER_UAPI_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment