Linux custom device driver probe and init functions are not being called


I have built a custom hardware configuration in Vivado for Xilinx SoC board, and used petalinux to create a custom driver to control the hardware logic. It seems like after running insmod command, the driver is never initialized and the ->probe() function is not called.

I am new to this domain, and wondering if anyone ran into a similar issue and has some pointers on where and what to check in order to see where the issue is. Any advice would be very helpful!

Running the dmesg command after inserting the driver into the kernel:

root@plzwork3:/proc/device-tree/amba_pl@0/simpleMultiplier@a0000000# dmesg 
[ 3351.680317] <1>Hello module world.
[ 3351.683735] <1>Module parameters were (0xdeadbeef) and "default"

Device Tree entity created by Vivado:

/ {
    amba_pl: amba_pl@0 {
        #address-cells = <2>;
        #size-cells = <2>;
        compatible = "simple-bus";
        ranges ;
        simpleMultiplier_0: simpleMultiplier@a0000000 {
            clock-names = "axiliteregport_aclk";
            clocks = <&zynqmp_clk 71>;
            compatible = "xlnx,simpleMultiplier-1.0";
            reg = <0x0 0xa0000000 0x0 0x10000>;
            xlnx,axiliteregport-addr-width = <0x4>;
            xlnx,axiliteregport-data-width = <0x20>;

Device driver code snippets:

#ifdef CONFIG_OF
static struct of_device_id simpmod_of_match[] = {
    { .compatible = "xlnx,simpleMultiplier-1.0", }, 
    { /* end of list */ },

Full device driver code:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h>

#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>

#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/fs.h>

/* Standard module information, edit as appropriate */
    ("Xilinx Inc.");

MODULE_DESCRIPTION("simpmod - loadable module template generated by petalinux-create -t modules");
#define DRIVER_NAME "simpmod"

static char ker_buf[100];
static int currLen = 0;
// Parts of the math operation (2,2,+) is 2+2
static int operand_1 = 0;
static int operand_2 = 0;
static int result = 0;
static struct class *driver_class = NULL;
static dev_t first;
static struct cdev c_dev; // Global variable for the character device
static struct device *ourDevice;

// Pointer to the IP registers
volatile unsigned int *regA;
volatile unsigned int *regB;
volatile unsigned int *regC;

// Structure to hold device specific data
struct simpmod_local {
    int irq;
    unsigned long mem_start;
    unsigned long mem_end;
    void __iomem *base_addr;

// Character callbacks prototype
static int dev_open(struct inode *inod, struct file *fil);
static ssize_t dev_read(struct file *fil, char *buf, size_t len, loff_t *off);
static ssize_t dev_write(struct file *fil, const char *buf, size_t len, loff_t *off);
static int dev_release(struct inode *inod, struct file *fil);

static struct file_operations fops = {

static irqreturn_t simpmod_irq(int irq, void *lp){
    printk("simpmod interrupt\n");
    return IRQ_HANDLED;

static int simpmod_probe(struct platform_device *pdev){
    struct resource *r_irq; /* Interrupt resources */
    struct resource *r_mem; /* IO mem resources */
    struct device *dev = &pdev->dev;
    static struct simpmod_local *lp = NULL;
    int rc = 0;

    printk("Device Tree Probing\n");

    // Get data of type IORESOURCE_MEM(reg-addr) from the device-tree
    // Other types defined here:
    r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!r_mem) {
        dev_err(dev, "invalid address\n");
        return -ENODEV;

    // Allocate memory (continuous physical)to hold simpmod_local struct
    lp = (struct simpmod_local *) kmalloc(sizeof(struct simpmod_local), GFP_KERNEL);
    if (!lp) {
        printk("Cound not allocate simpmod device\n");
        return -ENOMEM;

    dev_set_drvdata(dev, lp);

    // Save data on simpmod_local strucutre
    lp->mem_start = r_mem->start;
    lp->mem_end = r_mem->end;

    // Ask the kernel the memory region defined on the device-tree and
    // prevent other drivers to overlap on this region
    // This is needed before the ioremap
    if (!request_mem_region(lp->mem_start,
                lp->mem_end - lp->mem_start + 1,
                DRIVER_NAME)) {
        dev_err(dev, "Couldn't lock memory region at %p\n",
            (void *)lp->mem_start);
        rc = -EBUSY;
        goto error1;

    // Get an virtual address from the device physical address with a
    // range size: lp->mem_end - lp->mem_start + 1
    lp->base_addr = ioremap(lp->mem_start, lp->mem_end - lp->mem_start + 1);
    if (!lp->base_addr) {
        dev_err(dev, "simpmod: Could not allocate iomem\n");
        rc = -EIO;
        goto error2;
    // ****************** NORMAL Device diver *************************
    // register a range of char device numbers
    if (alloc_chrdev_region(&first, 0, 1, "Leonardo") < 0){
        printk(KERN_ALERT "alloc_chrdev_region failed\n");
        return -1;

    // Create class (/sysfs)
    driver_class = class_create(THIS_MODULE, CLASS_NAME);
    if (driver_class == NULL) {
        printk(KERN_ALERT "Create class failed\n");
        unregister_chrdev_region(first, 1);
        return -1;

    ourDevice = device_create(driver_class, NULL, first, NULL, "tutDevice");
    if ( ourDevice == NULL){
        printk(KERN_ALERT "Create device failed\n");
        unregister_chrdev_region(first, 1);
        return -1;

    // Create a character device /dev/tutDevice
    cdev_init(&c_dev, &fops);
    if (cdev_add(&c_dev, first, 1) == -1){
        printk(KERN_ALERT "Create character device failed\n");
        device_destroy(driver_class, first);
        unregister_chrdev_region(first, 1);
    return -1;petalinux-config -c rootfs

    // Create the attribute file on /sysfs/class/CLASS_TUT/ called
    // parCrtl and isBusy

    // Get data of type IORESOURCE_IRQ(interrupt) from the device-tree
    r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    if (!r_irq) {
        printk("no IRQ found\n");
        printk("simpmod at 0x%08x mapped to 0x%08x\n",
            (unsigned int __force)lp->mem_start,
            (unsigned int __force)lp->base_addr);

        // Configuring pointers to the IP registers
        regA = (unsigned int __force)lp->base_addr + 0x10;
        regB = (unsigned int __force)lp->base_addr + 0x18;
        regC = (unsigned int __force)lp->base_addr + 0x26;
        printk("regA: 0x%08x\n",(unsigned int)regA);
        printk("regB: 0x%08x\n",(unsigned int)regB);
        printk("regC: 0x%08x\n",(unsigned int)regC);
        return 0;
    lp->irq = r_irq->start;

    rc = request_irq(lp->irq, &simpmod_irq, 0, DRIVER_NAME, lp);
    if (rc) {
        dev_err(dev, "testmodule: Could not allocate interrupt %d.\n",
        goto error3;
    return 0;
    free_irq(lp->irq, lp);
    release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
    dev_set_drvdata(dev, NULL);
    return rc;

static int simpmod_remove(struct platform_device *pdev){
    struct device *dev = &pdev->dev;
    struct simpmod_local *lp = dev_get_drvdata(dev);
    free_irq(lp->irq, lp);
    release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
    dev_set_drvdata(dev, NULL);
    return 0;

// Indicate which type of hardware we handle on this case(simpleAlu-1.0)
#ifdef CONFIG_OF
static struct of_device_id simpmod_of_match[] = {
    { .compatible = "xlnx,simpleMultiplier-1.0", }, 
    { /* end of list */ },
MODULE_DEVICE_TABLE(of, simpmod_of_match);
# define simpmod_of_match

static struct platform_driver simpmod_driver = {
    .driver = {
        .name = DRIVER_NAME,
        .owner = THIS_MODULE,
        .of_match_table = simpmod_of_match,
    .probe      = simpmod_probe,
    .remove     = simpmod_remove,

static int __init simpmod_init(void)
    printk("<1>Simple device driver.\n");
  printk("Hussam was here, and he init the module\n");

    return platform_driver_register(&simpmod_driver);

static void __exit simpmod_exit(void)
    printk(KERN_ALERT "Goodbye module world.\n");

static int dev_open(struct inode *inod, struct file *fil){
    printk(KERN_ALERT "Character device opened\n");
    return 0;

// Just send to the user a string with the value of result
static ssize_t dev_read(struct file *fil, char *buf, size_t len, loff_t *off){
    // Return the result only once (otherwise a simple cat will loop)
    // Copy from kernel space to user space
    printk(KERN_ALERT "Reading device rx: %d\n",(int)len);
    int n = sprintf(ker_buf, "%d\n", *regC);
    // Copy back to user the result (to,from,size)
    printk(KERN_ALERT "Returning %s rx: %d\n",ker_buf,n);
    return n;

// Parse the input stream ex: "50,2,*" to some operand variables.
static ssize_t dev_write(struct file *fil, const char *buf, size_t len, loff_t *off){
    // Get data from user space to kernel space
    sscanf (ker_buf,"%d,%d,%c",&operand_1,&operand_2);
    ker_buf[len] = 0;
    // Change the IP registers to the parsed operands (on rega and regb)
    *regA = (unsigned int)operand_1;
    *regB = (unsigned int)operand_2;
    printk(KERN_ALERT "Receiving math operation <%d %d>\n",operand_1,operand_2);
    return len;

static int dev_release(struct inode *inod, struct file *fil){
    printk(KERN_ALERT "Device closed\n");
    return 0;

asked on Stack Overflow Feb 25, 2021 by John Jonson • edited Feb 25, 2021 by 0andriy

0 Answers

Nobody has answered this question yet.

User contributions licensed under CC BY-SA 3.0