Question

I'm developing with a PowerPC 405 embedded in a Virtex4 FPGA with Linux kernel 2.6.33.

Up until now, I've been writing drivers for platform devices implemented in the FPGA in the form of kernel loadable modules. The devices are registered using the flat Open Firmware Device Tree file. To create a device file, I use the OF functions to get the device node, and then register a new miscdevice which then automatically registers a minor device number and creates the device file for me. This also creates a device that is embedded in the miscdevice (i.e. miscdevice.this_device)

The problem is now I need to perform DMA operations. I tried to call the dma_alloc_coherent() function using the miscdevice.this_device, but this device isn't associated with any bus and always returns an error. I did some digging around and it turns out that the struct of_device also has a struct device embedded in it (i.e. of_device.dev). When I tried using this with dma_alloc_coherent(), it worked just fine.

So now I have two different struct device structures, one to manage my character device file, and one to manage the underlying Open Firmware system calls, the bus and DMA transactions. These devices are not associated with each other in the sysfs of course.

My question is, is it possible to somehow request that a device file be created for the device structure I get from the OF layer and not create a new device with the Misc Device API? That way everything will be associated with a single device structure.

Was it helpful?

Solution

I think your fix about dma_alloc_coherent() is correct.

But I don't think it's not right to use the device structure embedded in structure of_device to replace the miscdevice you created. The of_device is description of objects in Open Firmware database. And according to Linux device driver model, device structure is embedded in various device objects in Linux Kernel. And I think you registe miscdevice as one character device, there should be file_operations structure associated.

In one word, they are different views, and they can not replace each other.

OTHER TIPS

I wrote some experimental dma driver using miscdevice.this_device for raspberry pi

#include <linux/module.h>   /* Needed by all modules */
#include <linux/kernel.h>   /* Needed for KERN_INFO */
#include <linux/miscdevice.h>
#include "gpio.h"
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include "DMA.h"
#include <linux/of_irq.h>

static int my_open(struct inode *i, struct file *f)
{
    printk(KERN_INFO "Driver: open() %d\n", current->pid);
    return 0;
}
static int my_close(struct inode *i, struct file *f)
{
    printk(KERN_INFO "Driver: close()\n");
    return 0;
}

static ssize_t my_read(struct file *f, char __user *buf, size_t len, loff_t *off)
{
    printk(KERN_INFO "Driver: read()\n");
    return 0;
}

char databuf[100];
static ssize_t my_write(struct file *f, const char __user *buf, size_t len, loff_t *off)
{
    if(copy_from_user(databuf, buf, 100) != 0) return 0;
    printk("Data from the user: %s\n", databuf);
    return len;
}

  static struct file_operations sample_fops =
{
    .owner = THIS_MODULE,
    .open = my_open,
    .release = my_close,
    .read = my_read,
    .write = my_write
};

struct miscdevice sample_device = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "ledButton",
    .fops = &sample_fops,
    .mode = 0666,
};

//static struct dmadata_s *cpu_addr;
//dma_addr_t dma_addr;

struct dma_cb *virt_cb;
dma_addr_t phys_cb;

uint32_t *virt_src;
dma_addr_t phys_src;

uint32_t *virt_dst;
dma_addr_t phys_dst;

static irqreturn_t dma_irq_fn(int irq, void *dev_id)
{
    printk("destAddr %u\n", *virt_dst);
    dma_regs->CS.INT = 1;
    return IRQ_HANDLED;
}

static struct device *dev;

int IRQ_DMA0;
static int __init ofcd_init(void) /* Constructor */
{
    int error, mret;
    struct device_node * np = NULL;
    
    error = misc_register(&sample_device);
    if (error) {
        pr_err("can't misc_register :(\n");
        return error;
    }

    dev = sample_device.this_device;
    dev->coherent_dma_mask = ~0;
    dev->dma_mask = &dev->coherent_dma_mask;

//  dev_set_name(dev, "mydmadev");
    
//  cpu_addr = (struct dmadata_s*)kmalloc(sizeof(struct dmadata_s), GFP_KERNEL | GFP_DMA);
    //dma_addr = dma_map_single(dev, cpu_addr, sizeof(struct dmadata_s), DMA_BIDIRECTIONAL);
    virt_cb = dma_alloc_coherent(dev, 32, &phys_cb, GFP_KERNEL | GFP_DMA);
    if(virt_cb == 0 || phys_cb == 0){
        printk("DMA cb error\n");
    }

    virt_src = dma_alloc_coherent(dev, 4, &phys_src, GFP_KERNEL | GFP_DMA);
    if(virt_src == 0 || phys_src == 0){
        printk("DMA src error\n");
    }

    virt_dst = dma_alloc_coherent(dev, 4, &phys_dst, GFP_KERNEL | GFP_DMA);
    if(virt_dst == 0 || phys_dst == 0){
        printk("DMA dst error\n");
    }

    memset(virt_cb, 0, sizeof(*virt_cb));
    dma_regs = (struct dma_ch *)ioremap(DMA_BASE, sizeof(struct dma_ch));
    
    
//  strcpy(cpu_addr->srcAddr, "DMA0");
    *virt_src = 200;
    virt_cb->TI.SRC_INC = 1;
    virt_cb->TI.DEST_INC = 1;
    virt_cb->TI.INTEN = 1;
    virt_cb->SOURCE_AD = (uint32_t)phys_src;
    virt_cb->DEST_AD = (uint32_t)phys_dst;
    virt_cb->TXFR_LEN = 4;
    virt_cb->reserved[0] = 0;
    virt_cb->reserved[1] = 0;
    
    printk("srcAddr %u\n", *virt_src);
    printk("destAddr %u\n", *virt_dst);
    
    //dma_regs->CS = (DMA_CS_t){.RESET = 1, .END = 1};
    dma_regs->CS.RESET = 1;
    udelay(10);
//  dma_regs->CS = (DMA_CS_t){.END = 1, .INT = 1};
    dma_regs->CS.INT = 1;
    dma_regs->CS.END = 1;
    dma_regs->CONBLK_AD = (uint32_t)phys_cb;
    //dma_regs->DEBUG = (DMA_DEBUG_t){.READ_LAST_NOT_SET_ERROR = 1, .FIFO_ERROR = 1, .READ_ERROR = 1};
    dma_regs->DEBUG.READ_LAST_NOT_SET_ERROR = 1;
    dma_regs->DEBUG.FIFO_ERROR = 1;
    dma_regs->DEBUG.READ_ERROR =1;
    udelay(10);
//  dma_regs->CS = (DMA_CS_t){.RESET = 1, .PRIORITY = 8, .PANIC_PRIORITY = 8, .ACTIVE = 1};
    dma_regs->CS.RESET = 1;
    udelay(10);
    dma_regs->CS.PRIORITY = 8;
    dma_regs->CS.PANIC_PRIORITY = 8;
    dma_regs->CS.ACTIVE = 1;
    if(dma_regs->CS.ERROR) printk("ERROR %d %d\n", dma_regs->CS.ACTIVE, dma_regs->CS.PANIC_PRIORITY);



    //np = of_find_compatible_node(NULL,NULL,"brcm,bcm2835-system-timer");
    np = of_find_node_by_path("/soc/dma@7e007000");
    if (np == NULL){
        printk("Error node not found\n");
    }
//  printk("node name %s\n", np->name);
    
    IRQ_DMA0 = irq_of_parse_and_map(np, 0);
    if (IRQ_DMA0 <= 0) {
        printk("Can't parse IRQ\n");
    }

    mret = request_irq(IRQ_DMA0, dma_irq_fn, IRQF_SHARED, "dma", &dma_irq_fn);
    if (mret < 0) printk(KERN_ALERT "%s: dma request_irg failed with %d\n", __func__, mret);

    return 0;
}
 
static void __exit ofcd_exit(void) /* Destructor */
{
    free_irq( IRQ_DMA0, &dma_irq_fn );
    //dma_unmap_single(dev, dma_addr, sizeof(struct dmadata_s), DMA_BIDIRECTIONAL);
    //kfree(cpu_addr);
    dma_free_coherent(dev, 32, virt_cb, phys_cb);
    dma_free_coherent(dev, 4, virt_src, phys_src);
    dma_free_coherent(dev, 4, virt_dst, phys_dst);
    iounmap(dma_regs);
//  device_unregister(dev);
    misc_deregister(&sample_device);
    printk(KERN_INFO "Module unregistered\n");
}

module_init(ofcd_init);
module_exit(ofcd_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("MBajor>");
MODULE_DESCRIPTION("PiCNC driver");
MODULE_VERSION("0.1");

I hope this help.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top