This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/PRU-ICSS-INDUSTRIAL-SW: Driver control from PRU

Part Number: PRU-ICSS-INDUSTRIAL-SW
Other Parts Discussed in Thread: AM3358

Tool/software: Linux

How can I create a driver that will be probed when a PRU starts, and removed when it stops? One thought was to have my PRU firmware create an unused RPMsg channel and have RPMsg probe my driver by giving it a matching name, like what is done with the rpmsg-client-sample driver, but then have my code just do its own thing. This seems like a kludge though.

  • Hello Bill,

    My initial suggestion would be to check out my last post on the "original question" thread. I will do a bit more digging and let you know if I come across anything else interesting.

    Regards,

    Nick

  • Can you just create two different scripts for starting the PRU and stopping the PRU? Then you could build your driver as a module, and do something like

    pru_start_script:
    insmod myDriver
    sysfs to start PRU

    pru_stop_script:
    rmmod myDriver
    sysfs to stop PRU

    Regards,

    Nick

  • Greetings Nick,

    This will work, but still doesn't seem ideal. I'm going to mark it as solved for now because it may be a while before I can work on it more. I may chime back in later after some more digging. Thanks again for all of your help!

  • Greetings Nick,

    I think I might have found an option that will work. In remoteproc_core.c there is a function called rproc_add_subdev which allows for sub-devices to be added to an rproc as a rproc_subdev structure. The rproc_subdev structure has a probe and remove function that is called by remoteproc_core when a firmware is booted and shutdown, respectively. I think I can write a driver that creates a sub-device and adds it to the rproc on module init and removes it on module exit. I can initialize the probe and remove function pointers in rproc_subdev with the probe and remove function in my driver, and build up and tear down the character or block device infrastructure that way. Let me know what you think.

    Thanks,

    Bill

  • Greetings Nick,

    I did an extremely "rough and ready" test version of what I had in mind. The messages in dmesg seem to suggest it works (I will handle the rproc_subdev more correctly when I flesh out the probe and remove handlers so I can handle the two PRUs in the AM3358 independently). If you and/or Suman have any comments, I would be glad to hear them.

    #include <linux/init.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/of.h>
    #include <linux/of_device.h>
    #include <linux/remoteproc.h>
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Bill Merryman");
    MODULE_DESCRIPTION("A simple module to handle PRU carveouts like devices.");
    MODULE_VERSION("0.01");
    
    static struct rproc_subdev *rproc_subdev;
    
    int rproc_access_driver_probe(struct rproc_subdev *subdev)
    {
    	printk(KERN_INFO "Carveout subdevice successfully probed.\n");
    	return 0;
    }
    
    void rproc_access_driver_remove(struct rproc_subdev *subdev)
    {
    	printk(KERN_INFO "Carveout subdevice successfully removed.\n");
    }
    
    static int __init rproc_access_driver_init(void)
    {
    	struct device_node *np;
    	struct platform_device * pdev;
    	struct rproc *rproc;
    
    	np = of_find_node_by_path("/ocp/pruss_soc_bus@4a326004/pruss@0/pru@34000");
    	if(!np)
    	{
    		printk(KERN_INFO "pru device node could not be acquired at init.\n");
    		return -ENODEV;
    	}
    	printk(KERN_INFO "pru device acquired at init. full_name: %s.\n", np->full_name);
    	pdev = of_find_device_by_node(np);
    	of_node_put(np);
    
    	if (!pdev) return -EPROBE_DEFER;
    
    	if (!strstr(dev_name(&pdev->dev), "pru") && !strstr(dev_name(&pdev->dev), "rtu"))
    	{
    		put_device(&pdev->dev);
    		return -ENODEV;
    	}
    
    	rproc = platform_get_drvdata(pdev);
    	put_device(&pdev->dev);
    	if (!rproc) return -EPROBE_DEFER;
    
    	printk(KERN_INFO "rproc acquired at init. name: %s.\n", rproc->name);
    
    	rproc_subdev = kzalloc(sizeof(*rproc_subdev), GFP_KERNEL);
    	if(!rproc_subdev) return -ENOMEM;
    
    	rproc_add_subdev(rproc, rproc_subdev, rproc_access_driver_probe, rproc_access_driver_remove);
    
    	return 0;
    }
    
    static void __exit rproc_access_driver_exit(void)
    {
    	/*
    	 * Here we will have to account for the possibility that the rproc
    	 * may have been already removed by the time we remove our driver.
    	 * So we need to check for its existence, and if it exists, remove
    	 * the subdevice we created for the carveout. We will also have to
    	 * account if the related PRU is running, in which case we will have
    	 * to do a full teardown before the main exit logic.
    	 */
    	struct device_node *np;
    	struct platform_device * pdev;
    	struct rproc *rproc;
    
    	np = of_find_node_by_path("/ocp/pruss_soc_bus@4a326004/pruss@0/pru@34000");
    	if(np)
    	{
    		printk(KERN_INFO "pru device acquired at exit. full_name: %s.\n", np->full_name);
    		pdev = of_find_device_by_node(np);
    		of_node_put(np);
    
    		if (pdev)
    		{
    			if (strstr(dev_name(&pdev->dev), "pru") || strstr(dev_name(&pdev->dev), "rtu"))
    			{
    				rproc = platform_get_drvdata(pdev);
    				put_device(&pdev->dev);
    				if (rproc)
    				{
    					printk(KERN_INFO "rproc acquired at exit. name: %s.\n", rproc->name);
    					rproc_remove_subdev(rproc, rproc_subdev);
    				}
    			}
    			else
    			{
    				put_device(&pdev->dev);
    			}
    		}
    	}
    	printk(KERN_INFO "Freeing carveout subdevice memory.\n");
    	kfree(rproc_subdev);
    }
    
    module_init(rproc_access_driver_init);
    module_exit(rproc_access_driver_exit);
    
    
  • Hello Bill,

    My understanding is that subdevices are more commonly used with cores that autoboot (so not typically used with PRU). Since the PRU can be used for so many different applications, we typically give client drivers the ability to call the PRU, rather than giving the PRU the ability to make a client driver. RPMsg virtios seem to be an outlier in that pattern - I do not fully understand the design decisions there.

    So if you wanted to create a subdevice rather than using insmod / rmmod, I would expect you could get it working. The main challenge is that you need a way to get the rproc handle. I typically expect the rproc handle to get passed in through a device tree node, which doesn't seem to fit your use case. However, if you can get the rproc handle by hardcoding in the path, go for it.

    Regards,

    Nick

  • Greetings Nick,

    Thanks again for all of your input.  I think my game plan now is to stick with using mmap in the main application (since creating a character device from my driver would require some buffer copying which would be less efficient) and have my kernel module just export the carveout address and size through sysfs when the PRUs start up so my main application can consume them (my main app always starts the PRUs on startup and shuts them down on exit). That way I can eliminate the code module I wrote for reading the resource table from debugfs and parsing it to get the address, and I won't have to modify anything about the existing kernel (I'm using pre-built images that I download from Beagleboard.org). And I will probably for the short term stick with using insmod for the module rather than pull it in at boot. I can post my progress here periodically if that isn't violating any etiquette of this forum, or polluting your inbox too much.

    Thanks again,

    Bill

  • Hello Bill,

    No problem. You are poking around into a lot of areas of RemoteProc I haven't looked at before, so I enjoy getting to learn with you. Feel free to continue posting progress periodically. If we go for too long without a post the thread will lock itself, but you can always just open a new thread if that happens.

    Regards,

    Nick