Commit 58ae2f5c authored by Jens Korinth's avatar Jens Korinth
Browse files

WIP: PCIe bugfixing

parent f2169730
......@@ -28,7 +28,6 @@ export MANPATH=$MANPATH:$TAPASCO_HOME/man
export MYVIVADO=$MYVIVADO:$TAPASCO_HOME/common
export XILINX_PATH=$XILINX_PATH:$TAPASCO_HOME/common
if [[ -n $LIBMPFR ]]; then
export LD_PRELOAD=$LIBMPFR
echo "LD_PRELOAD=$LIBMPFR"
else
echo "WARNING: awk in modern Linux is incompatible with Vivado's old libmpfr.so" >&2
......
......@@ -59,10 +59,10 @@ ssize_t tlkm_perfc_miscdev_read(struct file *file, char __user *usr, size_t sz,
TLKM_VERSION);
sl = strlen(tmp) + 1;
if (sl - *loff > 0) {
ssize_t rl = strlen(&tmp[*loff]) + 1;
*loff += rl - copy_to_user(usr, tmp, strlen(&tmp[*loff]) + 1);
LOG(TLKM_LF_PERFC, "new loff: %lld", *loff);
return rl;
ssize_t rl = strlen(&tmp[*loff]) + 1;
*loff += rl - copy_to_user(usr, tmp, strlen(&tmp[*loff]) + 1);
LOG(TLKM_LF_PERFC, "new loff: %lld", *loff);
return rl;
}
return 0;
}
......@@ -92,8 +92,8 @@ int tlkm_perfc_miscdev_init(struct tlkm_device *dev)
void tlkm_perfc_miscdev_exit(struct tlkm_device *dev)
{
misc_deregister(&dev->perfc_dev);
kfree(dev->perfc_dev.name);
misc_deregister(&dev->perfc_dev);
DEVLOG(dev->dev_id, TLKM_LF_PERFC, "removed performance counter miscdev");
}
#endif /* NPERFC */
......@@ -70,6 +70,7 @@ int tlkm_dma_init(struct dma_engine *dma, dev_id_t dev_id, void *base, int irq_n
dma->irq_no = irq_no;
atomic64_set(&dma->rq_processed, 0);
atomic64_set(&dma->wq_processed, 0);
DEVLOG(dev_id, TLKM_LF_DMA, "DMA engine initialized");
return 0;
err_dma_bufs:
......
......@@ -3,6 +3,7 @@
#include "tlkm_class.h"
#include "pcie/pcie_device.h"
#include "pcie/pcie_irq.h"
#define TLKM_PCI_NAME "tlkm"
#define XILINX_VENDOR_ID 0x10EE
......@@ -18,8 +19,10 @@ struct tlkm_class pcie_cls = {
.destroy = pcie_device_destroy,
.probe = pcie_init,
.remove = pcie_exit,
.status_base = 0x02800000ULL,
.pirq = pcie_irqs_request_platform_irq,
.rirq = pcie_irqs_release_platform_irq,
.npirqs = 4,
.status_base = 0x02800000ULL,
.private_data = NULL,
};
......
......@@ -23,79 +23,72 @@ ssize_t pcie_enumerate(void) { return num_devices; }
* @param pdev Pointer to pci-device, which should be allocated
* @return Returns error code or zero if success
* */
static int claim_device(struct pci_dev *pdev)
static int claim_device(struct tlkm_pcie_device *pdev)
{
int err = 0;
struct tlkm_pcie_device *pci_data;
struct tlkm_device *dev = tlkm_bus_new_device((struct tlkm_class *)&pcie_cls,
TLKM_PCI_NAME,
XILINX_VENDOR_ID,
XILINX_DEVICE_ID);
dev_id_t const did = pdev->parent->dev_id;
struct pci_dev *dev = pdev->pdev;
BUG_ON(! dev);
/* wake up the pci device */
err = pci_enable_device(pdev);
err = pci_enable_device(dev);
if (err) {
DEVWRN(dev->dev_id, "failed to enable PCIe-device: %d", err);
DEVWRN(did, "failed to enable PCIe-device: %d", err);
goto error_pci_en;
}
/* set busmaster bit in config register */
pci_set_master(pdev);
pci_set_master(dev);
/* setup BAR memory regions */
err = pci_request_regions(pdev, TLKM_PCI_NAME);
err = pci_request_regions(dev, TLKM_PCI_NAME);
if (err) {
DEVWRN(dev->dev_id, "failed to setup bar regions for pci device %d", err);
DEVWRN(did, "failed to setup bar regions for pci device %d", err);
goto error_pci_req;
}
pci_data = (struct tlkm_pcie_device *)dev->private_data;
BUG_ON(! pci_data);
dev_set_drvdata(&pdev->dev, pci_data);
dev_set_drvdata(&dev->dev, pdev);
/* read out pci bar 0 settings */
pci_data->pdev = pdev;
pci_data->phy_addr_bar0 = pci_resource_start(pdev, 0);
pci_data->phy_len_bar0 = pci_resource_len(pdev, 0);
pci_data->phy_flags_bar0 = pci_resource_flags(pdev, 0);
pdev->phy_addr_bar0 = pci_resource_start(dev, 0);
pdev->phy_len_bar0 = pci_resource_len(dev, 0);
pdev->phy_flags_bar0 = pci_resource_flags(dev, 0);
LOG(TLKM_LF_PCIE, "PCI bar 0: address= 0x%08llx length: 0x%08llx",
pci_data->phy_addr_bar0, pci_data->phy_len_bar0);
DEVLOG(did, TLKM_LF_PCIE, "PCI bar 0: address= 0x%08llx length: 0x%08llx",
pdev->phy_addr_bar0, pdev->phy_len_bar0);
/* map physical address to kernel space */
pci_data->kvirt_addr_bar0 = ioremap(pci_data->phy_addr_bar0, pci_data->phy_len_bar0);
if (IS_ERR(pci_data->kvirt_addr_bar0)) {
ERR("could not remap bar 0 address to kernel space");
pdev->kvirt_addr_bar0 = ioremap(pdev->phy_addr_bar0, pdev->phy_len_bar0);
if (IS_ERR(pdev->kvirt_addr_bar0)) {
DEVERR(did, "could not remap bar 0 address to kernel space");
goto error_pci_remap;
}
LOG(TLKM_LF_PCIE, "remapped bar 0 address: 0x%08llx", (u64)pci_data->kvirt_addr_bar0);
DEVLOG(did, TLKM_LF_PCIE, "remapped bar 0 address: 0x%08llx", (u64)pdev->kvirt_addr_bar0);
dev->base_offset = pci_data->phy_addr_bar0;
LOG(TLKM_LF_PCIE, "status core base: 0x%08llx", (u64)pcie_cls.status_base);
pdev->parent->base_offset = pdev->phy_addr_bar0;
DEVLOG(did, TLKM_LF_PCIE, "status core base: 0x%08llx => 0x%08llx",
(u64)pcie_cls.status_base, (u64)pcie_cls.status_base + pdev->parent->base_offset);
tlkm_perfc_link_speed_set(dev->dev_id, pci_data->link_speed);
tlkm_perfc_link_width_set(dev->dev_id, pci_data->link_width);
tlkm_perfc_link_speed_set(did, pdev->link_speed);
tlkm_perfc_link_width_set(did, pdev->link_width);
num_devices++;
return 0;
iounmap(pdev->kvirt_addr_bar0);
error_pci_remap:
pci_release_regions(pdev);
kfree(pci_data);
pci_release_regions(pdev->pdev);
error_pci_req:
pci_disable_device(pdev);
pci_disable_device(pdev->pdev);
error_pci_en:
return -ENODEV;
}
static void release_device(struct pci_dev *pdev)
static void release_device(struct tlkm_pcie_device *pdev)
{
struct tlkm_pcie_device *dev = (struct tlkm_pcie_device *)dev_get_drvdata(&pdev->dev);
BUG_ON(! dev);
iounmap(dev->kvirt_addr_bar0);
kfree(dev);
pci_release_regions(pdev);
pci_disable_device(pdev);
iounmap(pdev->kvirt_addr_bar0);
pci_release_regions(pdev->pdev);
pci_disable_device(pdev->pdev);
}
/**
......@@ -127,124 +120,163 @@ static int configure_device(struct pci_dev *pdev)
* @param pci_dev device, which should be allocated
* @return Returns error code or zero if success
* */
static int claim_msi(struct pci_dev *pdev)
static int claim_msi(struct tlkm_pcie_device *pdev)
{
int err = 0, i;
struct tlkm_pcie_device *pci_data = (struct tlkm_pcie_device *)dev_get_drvdata(&pdev->dev);
dev_id_t id;
BUG_ON(! pci_data);
id = TLKM_DEV_ID(pdev);
struct pci_dev *dev = pdev->pdev;
dev_id_t const did = pdev->parent->dev_id;
for (i = 0; i < REQUIRED_INTERRUPTS; i++) {
pci_data->irq_mapping[i] = -1;
pdev->irq_mapping[i] = -1;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)
pci_data->msix_entries[i].entry = i;
pdev->msix_entries[i].entry = i;
#endif
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)
err = pci_enable_msix_range(pdev,
pci_data->msix_entries,
err = pci_enable_msix_range(dev,
pdev->msix_entries,
REQUIRED_INTERRUPTS,
REQUIRED_INTERRUPTS);
#else
/* set up MSI interrupt vector to max size */
err = pci_alloc_irq_vectors(pdev,
err = pci_alloc_irq_vectors(dev,
REQUIRED_INTERRUPTS,
REQUIRED_INTERRUPTS,
PCI_IRQ_MSIX);
#endif
if (err <= 0) {
DEVERR(id, "cannot set MSI vector (%d)", err);
DEVERR(did, "cannot set MSI vector (%d)", err);
return -ENOSPC;
} else {
DEVLOG(id, TLKM_LF_IRQ, "got %d MSI vectors", err);
DEVLOG(did, TLKM_LF_IRQ, "got %d MSI vectors", err);
}
if ((err = pcie_irqs_init(pdev))) {
DEVERR(id, "failed to register interrupts: %d", err);
if ((err = pcie_irqs_init(pdev->parent))) {
DEVERR(did, "failed to register interrupts: %d", err);
return -ENOSPC;
}
return 0;
}
static void report_link_status(struct pci_dev *pdev)
static void release_msi(struct tlkm_pcie_device *pdev)
{
struct tlkm_pcie_device *dev = (struct tlkm_pcie_device *)dev_get_drvdata(&pdev->dev);
dev_id_t id;
pcie_irqs_exit(pdev->parent);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)
pci_disable_msix(pdev->pdev);
#else
pci_free_irq_vectors(pdev->pdev);
#endif
}
static void report_link_status(struct tlkm_pcie_device *pdev)
{
struct pci_dev *dev = pdev->pdev;
dev_id_t did = pdev->parent->dev_id;
u16 ctrl_reg = 0;
int gts = 0;
BUG_ON(! dev);
id = TLKM_DEV_ID(pdev);
pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &ctrl_reg);
pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &ctrl_reg);
dev->link_width = (ctrl_reg & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT;
dev->link_speed = ctrl_reg & PCI_EXP_LNKSTA_CLS;
pdev->link_width = (ctrl_reg & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT;
pdev->link_speed = ctrl_reg & PCI_EXP_LNKSTA_CLS;
switch (dev->link_speed) {
switch (pdev->link_speed) {
case PCI_EXP_LNKSTA_CLS_8_0GB: gts = 80; break;
case PCI_EXP_LNKSTA_CLS_5_0GB: gts = 50; break;
case PCI_EXP_LNKSTA_CLS_2_5GB: gts = 25; break;
default: gts = 0; break;
}
DEVLOG(id, TLKM_LF_PCIE, "PCIe link width: x%d", dev->link_width);
DEVLOG(id, TLKM_LF_PCIE, "PCIe link speed: %d.%d GT/s", gts / 10, gts % 10);
DEVLOG(did, TLKM_LF_PCIE, "PCIe link width: x%d", pdev->link_width);
DEVLOG(did, TLKM_LF_PCIE, "PCIe link speed: %d.%d GT/s", gts / 10, gts % 10);
}
int tlkm_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
LOG(TLKM_LF_PCIE, "init pcie-dev for bus mastering");
if (claim_device(pdev)) {
goto error_no_device;
}
if (configure_device(pdev)) {
goto error_cannot_configure;
}
if (claim_msi(pdev)) {
goto error_cannot_configure;
struct tlkm_device *dev;
LOG(TLKM_LF_PCIE, "found TaPaSCo PCIe device, registering ...");
dev = tlkm_bus_new_device((struct tlkm_class *)&pcie_cls,
TLKM_PCI_NAME,
XILINX_VENDOR_ID,
XILINX_DEVICE_ID,
pdev);
if (! dev) {
ERR("could not add device to bus");
return -ENOMEM;
}
report_link_status(pdev);
return 0;
error_cannot_configure:
release_device(pdev);
error_no_device:
return -ENOSPC;
}
void tlkm_pcie_remove(struct pci_dev *pdev)
{
DEVLOG(TLKM_DEV_ID(pdev), TLKM_LF_PCIE, "unload pcie-device");
pcie_irqs_deinit(pdev);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)
pci_disable_msix(pdev);
#else
pci_free_irq_vectors(pdev);
#endif
release_device(pdev);
struct tlkm_pcie_device *dev = (struct tlkm_pcie_device *)dev_get_drvdata(&pdev->dev);
BUG_ON(! dev);
DEVLOG(TLKM_DEV_ID(pdev), TLKM_LF_PCIE, "unload PCIedevice");
tlkm_bus_delete_device(dev->parent);
}
int pcie_device_create(struct tlkm_device *dev)
int pcie_device_create(struct tlkm_device *dev, void *data)
{
int ret = 0;
struct pci_dev *pci_dev = (struct pci_dev *)data;
struct tlkm_pcie_device *pdev;
BUG_ON(! dev);
BUG_ON(! pci_dev);
pdev = (struct tlkm_pcie_device *)kzalloc(sizeof(*pdev), GFP_KERNEL);
if (! pdev) {
DEVERR(dev->dev_id, "could not allocate private data struct");
ret = -ENOMEM;
goto err_pd;
}
pdev->parent = dev;
pdev->pdev = pci_dev;
dev->private_data = pdev;
DEVLOG(dev->dev_id, TLKM_LF_PCIE, "claiming PCIe device ...");
if ((ret = claim_device(pdev))) {
DEVERR(dev->dev_id, "failed to claim PCIe device: %d", ret);
goto err_no_device;
}
DEVLOG(dev->dev_id, TLKM_LF_PCIE, "configuring PCIe device ...");
if ((ret = configure_device(pdev->pdev))) {
DEVERR(dev->dev_id, "failed to configure device: %d", ret);
goto err_configure;
}
DEVLOG(dev->dev_id, TLKM_LF_PCIE, "claiming MSI-X interrupts ...");
if ((ret = claim_msi(pdev))) {
DEVERR(dev->dev_id, "failed to claim MSI-X interrupts: %d", ret);
goto err_configure;
}
report_link_status(pdev);
return 0;
release_msi(pdev);
err_configure:
release_device(pdev);
err_no_device:
kfree(pdev);
dev->private_data = NULL;
err_pd:
return ret;
}
void pcie_device_destroy(struct tlkm_device *dev)
{
BUG_ON(! dev);
if (dev->private_data) {
kfree(dev->private_data);
if (dev) {
struct tlkm_pcie_device *pdev = (struct tlkm_pcie_device *)dev->private_data;
BUG_ON(! pdev);
release_msi(pdev);
release_device(pdev);
kfree(pdev);
dev->private_data = NULL;
} else {
WRN("called with NULL device!");
}
}
......@@ -31,7 +31,7 @@
int tlkm_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id);
void tlkm_pcie_remove(struct pci_dev *pdev);
int pcie_device_create(struct tlkm_device *dev);
int pcie_device_create(struct tlkm_device *dev, void *data);
void pcie_device_destroy(struct tlkm_device *dev);
/* struct to hold data related to the pcie device */
......
......@@ -29,31 +29,31 @@ irqreturn_t tlkm_pcie_slot_irq_ ## nr(int irq, void *dev_id) \
TLKM_PCIE_SLOT_INTERRUPTS
#undef _INTR
int pcie_irqs_init(struct pci_dev *pdev)
int pcie_irqs_init(struct tlkm_device *dev)
{
struct tlkm_pcie_device *pdev = (struct tlkm_pcie_device *)dev->private_data;
#define _INTR(nr) + 1
size_t const n = 0 TLKM_PCIE_SLOT_INTERRUPTS;
#undef _INTR
struct tlkm_pcie_device *dev = (struct tlkm_pcie_device *)dev_get_drvdata(&pdev->dev);
int ret = 0, irqn, err[n];
BUG_ON(! dev);
DEVLOG(dev->parent->dev_id, TLKM_LF_PCIE, "registering %zu interrupts ...", n);
DEVLOG(dev->dev_id, TLKM_LF_PCIE, "registering %zu interrupts ...", n);
#define _INTR(nr) \
irqn = nr + TLKM_PLATFORM_INTERRUPTS; \
if ((err[nr] = request_irq(pci_irq_vector(pdev, irqn), \
if ((err[nr] = request_irq(pci_irq_vector(pdev->pdev, irqn), \
tlkm_pcie_slot_irq_ ## nr, \
IRQF_EARLY_RESUME, \
TLKM_PCI_NAME, \
pdev))) { \
DEVERR(dev->parent->dev_id, "could not request interrupt %d: %d", irqn, err[nr]); \
pdev->pdev))) { \
DEVERR(dev->dev_id, "could not request interrupt %d: %d", irqn, err[nr]); \
goto irq_error; \
} else { \
dev->irq_mapping[irqn] = pci_irq_vector(pdev, irqn); \
DEVLOG(dev->parent->dev_id, TLKM_LF_PCIE, "created mapping from interrupt %d -> %d", irqn, dev->irq_mapping[irqn]); \
DEVLOG(dev->parent->dev_id, TLKM_LF_PCIE, "interrupt line %d/%d assigned with return value %d", \
irqn, pci_irq_vector(pdev, irqn), err[nr]); \
INIT_WORK(&dev->irq_work[nr], tlkm_pcie_slot_irq_work_ ## nr); \
atomic_long_set(&dev->irq_work[nr].data, (long)dev); \
pdev->irq_mapping[irqn] = pci_irq_vector(pdev->pdev, irqn); \
DEVLOG(dev->dev_id, TLKM_LF_PCIE, "created mapping from interrupt %d -> %d", irqn, pdev->irq_mapping[irqn]); \
DEVLOG(dev->dev_id, TLKM_LF_PCIE, "interrupt line %d/%d assigned with return value %d", \
irqn, pci_irq_vector(pdev->pdev, irqn), err[nr]); \
INIT_WORK(&pdev->irq_work[nr], tlkm_pcie_slot_irq_work_ ## nr); \
atomic_long_set(&pdev->irq_work[nr].data, (long)pdev->pdev); \
}
TLKM_PCIE_SLOT_INTERRUPTS
#undef _INTR
......@@ -63,8 +63,8 @@ irq_error:
#define _INTR(nr) \
irqn = nr + TLKM_PLATFORM_INTERRUPTS; \
if (! err[nr]) { \
free_irq(dev->irq_mapping[irqn], pdev); \
dev->irq_mapping[irqn] = -1; \
free_irq(pdev->irq_mapping[irqn], pdev->pdev); \
pdev->irq_mapping[irqn] = -1; \
} else { \
ret = err[nr]; \
}
......@@ -73,25 +73,20 @@ irq_error:
return ret;
}
void pcie_irqs_deinit(struct pci_dev *pdev)
void pcie_irqs_exit(struct tlkm_device *dev)
{
struct tlkm_pcie_device *dev = (struct tlkm_pcie_device *)dev_get_drvdata(&pdev->dev);
struct tlkm_pcie_device *pdev = (struct tlkm_pcie_device *)dev->private_data;
int irqn;
BUG_ON(! dev);
#define _INTR(nr) \
irqn = nr + TLKM_PLATFORM_INTERRUPTS; \
if (dev->irq_mapping[irqn] != -1) { \
DEVLOG(dev->parent->dev_id, TLKM_LF_PCIE, "freeing interrupt %d with mappping %d", irqn, dev->irq_mapping[irqn]); \
free_irq(dev->irq_mapping[irqn], pdev); \
dev->irq_mapping[irqn] = -1; \
if (pdev->irq_mapping[irqn] != -1) { \
DEVLOG(dev->dev_id, TLKM_LF_PCIE, "freeing interrupt %d with mappping %d", irqn, pdev->irq_mapping[irqn]); \
free_irq(pdev->irq_mapping[irqn], pdev->pdev); \
pdev->irq_mapping[irqn] = -1; \
}
TLKM_PCIE_SLOT_INTERRUPTS
#undef _INTR
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,8,0)
pci_disable_msix(pdev);
#else
pci_free_irq_vectors(pdev);
#endif
DEVLOG(dev->dev_id, TLKM_LF_IRQ, "interrupts deactivated");
}
int pcie_irqs_request_platform_irq(struct tlkm_device *dev, int irq_no, irqreturn_t (*intr_handler)(int, void *))
......
......@@ -137,8 +137,8 @@
_INTR(126) \
_INTR(127)
int pcie_irqs_init(struct pci_dev *pdev);
void pcie_irqs_deinit(struct pci_dev *pdev);
int pcie_irqs_init(struct tlkm_device *dev);
void pcie_irqs_exit(struct tlkm_device *dev);
int pcie_irqs_request_platform_irq(struct tlkm_device *dev, int irq_no, intr_handler_f);
void pcie_irqs_release_platform_irq(struct tlkm_device *dev, int irq_no);
......
......@@ -49,7 +49,7 @@ void tlkm_bus_del_device(struct tlkm_device *pdev)
LOG(TLKM_LF_BUS, "removed device '%s' from bus", pdev->name);
}
struct tlkm_device *tlkm_bus_new_device(struct tlkm_class *cls, const char *name, int vendor_id, int product_id)
struct tlkm_device *tlkm_bus_new_device(struct tlkm_class *cls, const char *name, int vendor_id, int product_id, void *data)
{
int ret = 0;
struct tlkm_device *dev = (struct tlkm_device *)kzalloc(sizeof(*dev), GFP_KERNEL);
......@@ -59,21 +59,24 @@ struct tlkm_device *tlkm_bus_new_device(struct tlkm_class *cls, const char *name
dev->product_id = product_id;
dev->cls = cls;
tlkm_bus_add_device(dev);
if ((ret = tlkm_device_init(dev))) {
if ((ret = tlkm_device_init(dev, data))) {
DEVERR(dev->dev_id, "could not initialize device: %d", ret);
tlkm_bus_delete_device(dev);
tlkm_bus_del_device(dev);
kfree(dev);
return NULL;
}
mutex_init(&dev->mtx);
return dev;
} else {
ERR("could not allocate new tlkm_device");
return NULL;
}
ERR("could not allocate new tlkm_device");
return NULL;
}
void tlkm_bus_delete_device(struct tlkm_device *dev)
{
if (dev) {
tlkm_device_exit(dev);
tlkm_bus_del_device(dev);
kfree(dev);
}
......@@ -105,13 +108,19 @@ int tlkm_bus_init(void)
}
if (! n) {
ERR("did not find any TaPaSCo devices, cannot proceed");
return -ENXIO;
ret = -ENXIO;
goto err;
}
LOG(TLKM_LF_BUS, "found %zd TaPaSCo devices", n);
if ((ret = tlkm_init())) {
ERR("failed to initialize main ioctl file: %d", ret);
goto err;
}
return ret;
err:
tlkm_bus_exit();
return ret;
}
void tlkm_bus_exit(void)
......
......@@ -9,7 +9,11 @@ struct tlkm_class;
int tlkm_bus_init(void);
void tlkm_bus_exit(void);
struct tlkm_device *tlkm_bus_new_device(struct tlkm_class *cls, const char *name, int vendor_id, int product_id);
struct tlkm_device *tlkm_bus_new_device(struct tlkm_class *cls,
const char *name,
int vendor_id,
int product_id,
void *data);
void tlkm_bus_delete_device(struct tlkm_device *dev);
ssize_t tlkm_bus_enumerate(void);
......
#ifndef TLKM_CLASS_H__
#define TLKM_CLASS_H__
#include <linux/interrupt.h>
#include "tlkm_device.h"
#define TLKM_CLASS_NAME_LEN 32
typedef int (*tlkm_class_create_f)(struct tlkm_device *);
typedef int (*tlkm_class_create_f)(struct tlkm_device *, void *data);
typedef void(*tlkm_class_destroy_f)(struct tlkm_device *);
typedef int (*tlkm_class_probe_f)(struct tlkm_class *);
typedef void(*tlkm_class_remove_f)(struct tlkm_class *);
struct tlkm_device;
typedef long (*tlkm_device_ioctl_f)(struct tlkm_device *, unsigned int ioctl, unsigned long data);
typedef int (*tlkm_device_mmap_f) (struct tlkm_device *, struct vm_area_struct *vm);
typedef int (*tlkm_device_pirq_f) (struct tlkm_device *, int irq_no, irq_handler_t h);
typedef void (*tlkm_device_rirq_f) (struct tlkm_device *, int irq_no);
struct tlkm_class {
char name[TLKM_CLASS_NAME_LEN];
tlkm_class_create_f create;
tlkm_class_destroy_f destroy;
tlkm_class_probe_f probe;
tlkm_class_remove_f remove;
tlkm_device_ioctl_f ioctl; /* ioctl implementation */
tlkm_device_mmap_f mmap; /* mmap implementation */
tlkm_device_pirq_f pirq; /* request platform IRQ */
tlkm_device_rirq_f rirq; /* release platform IRQ */
dev_addr_t status_base; /* physical offset of status core in bitstream */
size_t npirqs; /* number of platform interrupts */
void *private_data;
......