Fork for kernel 5.18 API change.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

322 lines
8.7 KiB

#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/interrupt.h>
#include <linux/highmem.h>
#include <linux/spinlock.h>
#include <linux/spinlock_types.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/poll.h>
#define DMA_ADDR_OFFSET 32
#define PRINT(A) printk(KERN_ALERT ">>>>> #A %x\n", A);
MODULE_AUTHOR("Talaie");
MODULE_LICENSE("Dual BSD/GPL");
static struct pci_device_id pcie_ids[] =
{
{PCI_DEVICE(0x10ee, 0x7024)},
{0, },
};
MODULE_DEVICE_TABLE(pci, pcie_ids);
static irqreturn_t interrupt_handler(int irq, void *p);
static struct pci_dev *pcidev = NULL;
static unsigned int chrdev_major = 0;
static unsigned int chrdev_minor = 0;
static unsigned int ioctl_alloc_dma = 0;
static unsigned int ioctl_refresh_link = 1;
static unsigned long bar0_addr;
static unsigned long bar0_size = 4*1024; //TODO ask about size. 4KB
static void* bar0_ptr;
static unsigned int irq;
struct page** dma_pages = NULL;
unsigned int dma_pages_count = 0;
void* dma_addr = NULL;
unsigned long mmap_buffersize = 4 * 1024; //4KB
/*************************************************************************************************/
static int __init pcie_init(void);
static int chrdev_init(void);
static int usd_open(struct inode *inode, struct file *flip);
static int usd_mmap(struct file *filp, struct vm_area_struct *vma);
static unsigned int usd_poll (struct file *filp, poll_table *wait);
static int __init pcie_probe (struct pci_dev *dev, const struct pci_device_id *id);
static void pcie_remove(struct pci_dev *dev);
static void __exit pcie_exit(void);
/*************************************************************************************************/
static dev_t chrdev;
static struct cdev cdev;
static struct class *class = NULL;
#define DEV_NAME "usd_reg"
struct file_operations chrdev_fops =
{
.owner = THIS_MODULE,
.open = usd_open,
.mmap = usd_mmap,
.poll = usd_poll
};
/*************************************************************************************************/
static struct pci_driver pci_driver =
{
.name = "usdpci",
.id_table = pcie_ids,
.probe = pcie_probe,
.remove = __exit_p(pcie_remove),
};
/*************************************************************************************************/
#define PRINT_PREFIX "USDriver PCIe"
#define PRINT_ALERT(STR) printk(KERN_ALERT "%s %s\n", PRINT_PREFIX, STR)
#define PRINT_WARN(STR) printk(KERN_WARN "%s %s\n", PRINT_PREFIX, STR)
/*************************************************************************************************/
static int __init pcie_init(void)
{
int res = 0;
PRINT_ALERT("driver initializing");
res = pci_register_driver(&pci_driver);
if ( res ) {
PRINT_ALERT("device not found");
return res;
}
PRINT_ALERT("register driver success" );
res = chrdev_init();
if ( res ) {
PRINT_ALERT("character device file creation failed");
return res;
}
PRINT_ALERT("driver loaded");
return res;
}
/*************************************************************************************************/
static int chrdev_init(void)
{
PRINT_ALERT("chrdev_init");
int res = 0;
struct device *device = NULL;
res = alloc_chrdev_region(&chrdev, 0, 1, DEV_NAME);
if(res < 0)
{
return -1;
}
cdev_init(&cdev, &chrdev_fops);
res = cdev_add(&cdev, chrdev, 1);
if (res)
{
unregister_chrdev_region(chrdev, 1);
return res;
}
class = class_create(THIS_MODULE, "usdpci");
device = device_create(class, NULL, 0, NULL, "usd_regs0");
return 0;
}
/*************************************************************************************************/
static int usd_open(struct inode *inode, struct file *flip)
{
return 0;
}
/*************************************************************************************************/
static int usd_mmap(struct file *filp, struct vm_area_struct *vma)
{
return 0;
}
/*************************************************************************************************/
static unsigned int usd_poll (struct file *filp, poll_table *wait)
{
return 0;
}
/*************************************************************************************************/
static int __init pcie_probe (struct pci_dev *dev, const struct pci_device_id *id)
{
bar0_ptr = pci_iomap(dev, 0, 64 * 1024);//4KB
u8* bar0_data = (u8*)bar0_ptr;
int rc = 0;
if(bar0_data == 0)
{
PRINT_ALERT("driver failed to map BAR 0");
rc = 1;
goto probe_fail_release_region;
}
pci_set_master(dev);
// int i = 0;
// for( i = 0; i < 1; i++)
// {
// int r32 = ioread32(&bar0_data[i*4]);
// printk(KERN_ALERT "Read: %x (%d)\n", r32, i);
// }
iowrite32(0x45670000, &bar0_data[0x8018]);
int r32 = ioread32(&bar0_data[0x8018]);
printk(KERN_ALERT "Read: %x\n", r32);
return 0;
probe_fail_release_region:
pci_release_regions(dev);
probe_fail:
pci_disable_device(dev);
probe_fail_enable:
return rc;
}
/*************************************************************************************************/
static void pcie_remove(struct pci_dev *dev)
{
PRINT_ALERT("remove driver");
int i;
u8* bar0_data;
bar0_data = (u8*)bar0_ptr;
for ( i = 0; i < dma_pages_count; i++ ) {
pci_unmap_single(pcidev, bar0_data[DMA_ADDR_OFFSET + 4*i], PAGE_SIZE, DMA_BIDIRECTIONAL);
__free_page(dma_pages[i]);
}
if (dma_pages != NULL) kfree(dma_pages);
printk(KERN_ALERT "Freed DMA pages\n");
pci_clear_master(dev);
printk(KERN_ALERT "Cleared PCIe master\n");
pci_iounmap(dev, bar0_ptr);
printk(KERN_ALERT "IOunmap\n");
pci_release_regions(dev);
printk(KERN_ALERT "pci_release_regions\n");
pci_disable_device(dev);
printk(KERN_ALERT "pci_disable_device\n");
}
/*************************************************************************************************/
static void __exit pcie_exit(void)
{
int i;
u8* bar0_data;
bar0_data = (u8*)bar0_ptr;
printk(KERN_ALERT "USDriver PCIe driver unloading\n");
printk(KERN_ALERT "USDriver PCIe driver unregistering\n");
pci_unregister_driver(&pci_driver);
printk(KERN_ALERT "USDriver PCIe unregister_chrdev_region\n");
unregister_chrdev_region(chrdev, 1);
printk(KERN_ALERT "USDriver PCIe cdev_del\n");
cdev_del(&cdev);
printk(KERN_ALERT "USDriver PCIe device_destroy\n");
device_destroy(class, chrdev);
printk(KERN_ALERT "USDriver PCIe class_destroy\n");
class_destroy(class);
printk(KERN_ALERT "USDriver PCIe driver unloaded\n");
}
module_init(pcie_init);
module_exit(pcie_exit);
/*************************************************************************************************/
static int create_dma_buffer(unsigned int bufcount)
{
int i;
int bufidx = 0;
unsigned int gfp_mask = GFP_KERNEL | __GFP_DMA;
dma_addr_t bus_addr;
u8* bar0_data;
bar0_data = (u8*)bar0_ptr;
printk(KERN_ALERT "USDriver DMA buffer alloc request: %d pages\n", bufcount);
if( dma_pages != NULL)
{
printk(KERN_ALERT "USDriver DMA buffer already exist! Strange!\n");
}
dma_pages = kmalloc(4*1024*1024 * bufcount, GFP_KERNEL);
if(dma_pages == NULL)
{
printk(KERN_ERR "USDriver DMA dma_pages alloc failed! \n" );
return 1;
}
for(bufidx = 0; bufidx < bufcount; bufidx++)
{
void __iomem *maddr = NULL;
struct page *pages = alloc_pages(gfp_mask, 10);
if(pages == NULL)
{
printk(KERN_ERR "USDriver DMA buffer alloc failed! \n" );
return 1;
}
maddr = page_address(pages);
dma_pages[bufidx] = pages;
bus_addr = pci_map_single(pcidev, maddr, 1024 * PAGE_SIZE, DMA_BIDIRECTIONAL);
printk(KERN_ALERT ">>>>>[%d] 0x%x\n", bufidx, bus_addr);
iowrite32(bus_addr, &bar0_data[DMA_ADDR_OFFSET + (4 * bufidx)]);
wmb();
if(pci_dma_mapping_error(pcidev, bus_addr))
{
return 1;
}
}
dma_pages_count = bufcount;
printk(KERN_ALERT "USDriver DMA buffer alloc successful\n");
return 0;
}
/*************************************************************************************************/
/*************************************************************************************************/
/*************************************************************************************************/
/*************************************************************************************************/
/*************************************************************************************************/
/*************************************************************************************************/
/*************************************************************************************************/
/*************************************************************************************************/