Basic Introduction to Device Tree Driver Development
Basic Introduction to Device Tree Driver Development
© 2020 Fraser Innovation Inc ALL RIGHTS RESERVED
Without written permission of Fraser Innovation Inc, no unit or individual may extract or modify part of or all the contents of this manual. Offenders will be held liable for their legal responsibility.
Thank you for purchasing the FPGA development board. Please read the manual carefully before using the product and make sure that you know how to use the product correctly. Improper operation may damage the development board. This manual is constantly updated, and it is recommended that you download the latest version when using.
Official Shopping Website:
*In order to follow along to this tutorial, you will be required to have Ubuntu 16.04.01 already installed on your Virtual Machine using Oracle’s VirtualBox. Because this tutorial is geared towards Ubuntu 16.04.01, using other versions may have differences between the execution of commands. You will also need to have your development board connection already set up, along with PetaLinux.
We will first create a folder titled “work” in our home folder and go into its directory from the terminal.
We will next execute the following two commands to create a new PetaLinux project in the directory.
sptl
petalinux-create -t project –template zynq -n fii-module
Note that we named the project fii-module to match the development board we will be using.
We will then copy the FPGA SDK directory FII_7030.sdk to the work folder we just created and edit fii_module. Use the following command to enter the fii_module project directory:
cd fii-module
Next, we will execute the sptl command, and configure the project by using the following command:
petalinux-config –get-hw-description ~/work/FII_7030.sdk/
This will bring up the settings page as shown on the next page.
We don’t need to make any changes, and we can simply choose < Save > and < Exit >.
Next, we will insert and create the fpga device tree driver by using the following command:
petalinux-create -t modules –name fii-dt-driver
We can now go on to edit the driver by going into the ~/work/fii-module/project-spec/meta-user/recipes-modules/fii-dt-driver/files directory and editing the fii-dt-driver.c file.
Once the file is open, we will modify it as such:
/* fii-dt-driver.c – The simplest kernel module.
* Copyright (C) 2013 – 2016 Xilinx, Inc
*
* This program is free software; you can redistribute it and/or modify
* it unde r the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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/uaccess.h> /* for put_user */
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h>
/* Standard module information, edit as appropriate */
MODULE_LICENSE(“GPL”);
MODULE_AUTHOR
(“FII inc.”);
MODULE_DESCRIPTION
(“fii-dt-driver – loadable module template generated by petalinux-create -t modules”);
#define DRIVER_NAME “fii-dt-driver”
#if 0
static int fii_dt_driver_dev_init(void)
{
platform_device_register(&fii_dt_driver_dev);
return 0;
};
static struct platform_device fii_dt_driver_dev={
.name = “fii-dt-driver”,
.id = -1,
.num_resourse = ARRAY_SIZE(fii_dt_driver_resourse),
.resourse = fii_dt_driver_resourse,
.dev={
.release = fii_dt_driver_release,
.of_match_table = of_match_fii_dt_driver,
},
};
Static struct resourse fii_dt_driver_resource[]={
[0]= {
.strart = mx , /* MX will be replaced byconfig register addrss*/
.end = MX+1,
.flag = IORESOURCE_MEM,
},
//[1]{},
};
#endif
static const struct of_device_id of_match_fii_dt_driver[] = {
// {.compatible = “fii,fii-dt-driver”, .data = NULL},
{.compatible = “fii,fii-dt-driver”, },
{/*sentinel*/},
};
//platform driver
static struct class *fii_dt_driver_class;
static unsigned int major;
static unsigned int * gpio_base;
static unsigned int * gpio_data;
static unsigned int * gpio_dir;
static int fii_dt_driver_init(void);
static int fii_dt_driver_open(struct inode* node,struct file* filp){
gpio_data = ioremap(gpio_base, 8);
if(gpio_data){
printk(“kernel: ioremap(0x%08x)=0x%08x \n”, gpio_base, gpio_data);
}
else{
return -EINVAL;
}
gpio_dir = gpio_data + 1;
return 0;
}
static int fii_dt_driver_release(struct platform_device * dev)
{
iounmap(gpio_data);
unregister_chrdev(major, “fii-dt-driver”);
device_destroy(fii_dt_driver_class,MKDEV(major,0));
class_destroy(fii_dt_driver_class);
return 0;
}
/*
0x43c0_0000 + 4 = value
0x43c0_0000 + 8 = direction
*/
static ssize_t fii_dt_driver_read(struct file *filp, char *buf, size_t size, loff_t *offset)
{
unsigned char val[32];
unsigned int temp;
unsigned int *addr_p;
int i,cnt;
printk(“kernel: fii-dt-driver read start…. size = %d\n”, size);
// cnt = size – 1;
cnt = size;
addr_p = gpio_data;
temp = *addr_p ;
for(i = 0; i < cnt/4; i++ )
{
val[i*4 + 3] = temp & 0xff;
val[i*4 + 2] = (temp >> 8) & 0xff;
val[i*4 + 1] = (temp >> 16) & 0xff;
val[i*4 + 0] = (temp >> 24) & 0xff;
addr_p ++;
temp = *addr_p;
}
if(i % 4 == 1)
{
val[i*4 + 0] = temp & 0xff;
}
if(i % 4 == 2)
{
val[i*4 + 1] = temp & 0xff;
val[i*4 + 0] = (temp >> 8) & 0xff;
}
if(i % 4 == 3)
{
val[i*4 + 2] = temp & 0xff;
val[i*4 + 1] = (temp >> 8) & 0xff;
val[i*4 + 0] = (temp >> 16) & 0xff;
}
copy_to_user(buf, val, cnt);
// printk(“val[0] = 0x%x, val[1] = 0x%x, val[2] = 0x%x, val[3] = 0x%x \n”,
// val[0], val[1], val[2], val[3]);
return cnt;
// return size;
}
static ssize_t fii_dt_driver_write(struct file * filp, const char __user *buf, size_t size, loff_t * offset) {
unsigned char val[32];
unsigned int temp = 0;
unsigned int * addr_p;
int i,cnt;
memset(val,0,32);
addr_p = gpio_data;
printk(“kernel: fii-dt-driver write start…. size = %d\n”, size);
copy_from_user(&val, buf, size);
cnt = size – 1;
printk(“kernel: val[0] = 0x%08x \n”, val[0]);
if(val[0] == ‘w’)
{
temp = val[2];
temp = temp << 8 | val[3];
temp = temp << 8 | val[4];
temp = temp << 8 | val[5];
*addr_p = temp;
printk(“kernel: gpio_data = 0x%08x \n”, temp);
}
else if(val[0] == ‘d’)
{
addr_p ++;
temp = val[2];
temp = temp << 8 | val[3];
temp = temp << 8 | val[4];
temp = temp << 8 | val[5];
*addr_p = temp;
printk(“kernel: gpio_dir = 0x%08x \n”, temp);
}
else
{
printk(“kernel: invalid parameter \n”);
}
#if 0
temp = val[0] & 0xff;
temp = temp << 8 | (val[1] & 0xff);
temp = temp << 8 | (val[2] & 0xff);
temp = temp << 8 | (val[3] & 0xff);
printk(“val[0] = 0x%x, val[1] = 0x%x, val[2] = 0x%x, val[3] = 0x%x \n”,
val[0], val[1], val[2], val[3]);
*gpio_data = temp;
printk(“gpio_data = 0x%08x \n”, temp);
for(i = 0; i < cnt/4; i++)
{
temp = val[i*4 + 0];
temp = temp << 8 | val[i*4 + 1];
temp = temp << 8 | val[i*4 + 2];
temp = temp << 8 | val[i*4 + 3];
*addr_p = temp;
addr_p ++;
printk(“kernel: gpio_data[%d] = 0x%08x \n”, i, temp);
}
if(cnt %4 == 1)
{
temp = val[i*4 + 0];
*addr_p = temp;
printk(“kernel: gpio_data[%d] = 0x%08x \n”, i, temp);
}
if(cnt %4 == 2)
{
temp = val[i*4 + 0];
temp = temp << 8 | val[i*4 + 1];
*addr_p = temp;
printk(“kernel: gpio_data[%d] = 0x%08x \n”, i, temp);
}
if(cnt %4 == 3)
{
temp = val[i*4 + 0];
temp = temp << 8 | val[i*4 + 1];
temp = temp << 8 | val[i*4 + 2];
*addr_p = temp;
printk(“kernel: gpio_data[%d] = 0x%08x \n”, i, temp);
}
#endif
return size;
}
static struct file_operations fii_dt_driver_oprs = {
.owner = THIS_MODULE,
.open = fii_dt_driver_open,
.write = fii_dt_driver_write,
.read = fii_dt_driver_read,
// .release = fii_dt_driver_release,
};
static int fii_dt_driver_probe(struct platform_device *pdev)
{
struct resource *res;
printk (“kernel: enter fii_dt_driver probe …… \n”);
printk (“kernel: enter fii_dt_driver probe …… \n”);
printk (“kernel: enter fii_dt_driver probe …… \n”);
printk (“kernel: enter fii_dt_driver probe …… \n”);
printk (“kernel: enter fii_dt_driver probe …… \n”);
printk (“kernel: enter fii_dt_driver probe …… \n”);
res = platform_get_resource(pdev, IORESOURCE_MEM,0);
if(res){
gpio_base = res->start;
}
#if 0
else
{
of_property_read_s32(pdev->dev.of_node,”pin”,&led_pin);//led_pin
}
if(!led_pin)
{
printk(“can not get property for led\n”);
return -EINVAL;
}
#endif
// dtpl_swled_init();
major=register_chrdev(0, “fii-dt-driver”, &fii_dt_driver_oprs);
if (major < 0) {
printk (“Registering the character device failed with %d\n”, major);
return major;
}
fii_dt_driver_class = class_create(THIS_MODULE, “fii-dt-driver_class”);
device_create(fii_dt_driver_class,NULL,MKDEV(major,0),NULL,”fii-dt-driver”);
return 0;
}
MODULE_DEVICE_TABLE(of, of_match_fii_dt_driver);
static struct platform_driver fii_dt_driver_drv = {
.driver = {
.name = “fii-dt-driver”,
.owner = THIS_MODULE,
.of_match_table = of_match_fii_dt_driver,
// .of_match_table = of_match_ptr(of_match_fii_dt_driver),
},
.probe = fii_dt_driver_probe,
.remove = fii_dt_driver_release,
};
static int fii_dt_driver_init(void){ /* register device */
return platform_driver_register(&fii_dt_driver_drv);
}
static void fii_dt_driver_exit(void){
platform_driver_unregister(&fii_dt_driver_drv);
return;
};
//module_platform_driver(fii_dt_driver_drv);
module_init(fii_dt_driver_init);
module_exit(fii_dt_driver_exit);
MODULE_ALIAS(“platform:fii-dt-driver”);
When you finish writing the code, please execute the following command to edit the PetaLinux driver settings.
petalinux-config -c rootfs
Once the graphical interface opens, go into modules and include fii-dt-driver by pressing [Y]. Make sure you save and exit when you are finished.
We will now edit the user’s device tree system-user.dtsi located in ~/work/fii-module/project-spec/meta-user/recipes-bsp/device-tree/files/.
Please edit it as follows:
/include/ “system-conf.dtsi”
/ {
fii-dt-driver {
#address-cells = <1>;
#size-cells = <1>;
compatible = “fii,fii-dt-driver”;
reg = <0x43c00000 8>;
/* pin = <0xmmmm>; */
};
};
Make sure the compatible portion of the code matches with that in the previous file we edited, or else the system will not be able to find the driver. Note that because it is a string, you cannot have excess spaces.
We will then proceed to compile the core by using the following command:
petalinux-build -c kernel
Next, we will use the following command to compile the driver:
petalinux-build -c fii-dt-driver
Now we can go ahead and compile the entire project by using the following command:
petalinux-build
We will now create the project files by executing the following command:
petalinux-package –boot –fsbl ./images/linux/zynq_fsbl.elf –fpga –u-boot –force
We will now copy the BOOT.BIN file and the image.ub file from ~/work/fii-module/images/linux/ to partition 1 of the SD card, which should be the boot partition; and extract rootfs.cpio to the other partition, which should be titled rootfs.
When finished, please plug the SD card into the fii_7030 development board and boot it up. Make sure you are connected to the board via PuTTY. Recall that both the username and the password is root.
We will now start testing and configuring the system. We can start by mounting both the SD card partitions.
cd /mnt
mkdir sd_1
mount /dev/mmcblk0p1 /mnt/sd_1
mkdir sd_2
mount /dev/mmcblk0p2 /mnt/sd_2
As we can see, both the partitions are now successfully mounted and accessible.
We can now test to see if the /dev directory has successfully loaded the fii-dt-driver driver by executing the following:
cd /mnt/sd_2
ls -l /dev/fii-dt-driver
If it exists, it means that the driver was successfully loaded.
Next, we will go on to set up the DDR by using the following commands:
echo d 0000 > /dev/fii-dt-driver
Go back to your Ubuntu system and create a folder named test_func in your home directory. Create a .c file called fii_dt_app.c within the test_func directory we just created.
We will edit its contents to the following:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h> /* File Control Definitions */
#include <termios.h> /* POSIX Terminal Control Definitions */
#include <unistd.h> /* UNIX Standard Definitions */
#include <errno.h> /* ERROR Number Definitions */
#include <string.h>
// Number of strings in array argv
//int main( int argc, char *argv[],
//void main(void)
static void print_msg(void)
{
printf(“=============================================================\n”);
printf(“Usage :\n”);
printf(“./fii_dt_app.o dir <val> : val : 0 => output; 1 => input\n”);
printf(“./fii_dt_app.o wr <val> : val : 0x43c0_0004 address value\n”);
printf(“./fii_dt_app.o rd : \n”);
printf(“=============================================================\n”);
return;
}
static void func_read(int fd, int read_cnt)
{
char rd_buf[32]; /* Buffer to store the data received */
int bytes_read = 0; /* Number of bytes read by the read() system call */
unsigned int temp;
printf(“Press ctrl+C to exit \n”);
tcflush(fd, TCIFLUSH); /* Discards old data in the rx buffer */
lseek(fd, 0, SEEK_SET);
bytes_read = read(fd, rd_buf, read_cnt);
printf(“\n\nBytes Rxed = %d \n”, bytes_read); /* Print the number of bytes read */
temp = rd_buf[0];
temp = (temp << 8) | rd_buf[1];
temp = (temp << 8) | rd_buf[2];
temp = (temp << 8) | rd_buf[3];
printf(“read value = 0x%08x \n”, temp);
temp = rd_buf[4];
temp = (temp << 8) | rd_buf[5];
temp = (temp << 8) | rd_buf[6];
temp = (temp << 8) | rd_buf[7];
printf(“read dir = 0x%08x \n”, temp);
printf(“\n +———————————-+\n\n\n”);
// usleep(1000000);
return;
}
static void func_write(int fd, char *p)
{
char wr_buf[32];
ssize_t bytes_written = 0;
unsigned int value;
memset (wr_buf, 0x0a, 32);
value = atoi(p);
printf(“value = %x \n” , value);
wr_buf[0] = ‘w’;
wr_buf[1] = ‘ ‘;
wr_buf[2] = value >> 24;
wr_buf[3] = (value >> 16) & 0xff;
wr_buf[4] = (value >> 8) & 0xff;
wr_buf[5] = value & 0xff;
bytes_written = write(fd, wr_buf, 7);
return;
}
static void func_direction(int fd, char *p)
{
char wr_buf[32];
ssize_t bytes_written = 0;
unsigned int value;
memset (wr_buf, 0x0a, 32);
value = atoi(p);
printf(“value = %x \n” , value);
wr_buf[0] = ‘d’;
wr_buf[1] = ‘ ‘;
wr_buf[2] = value >> 24;
wr_buf[3] = (value >> 16) & 0xff;
wr_buf[3] = (value >> 8) & 0xff;
wr_buf[5] = value & 0xff;
bytes_written = write(fd, wr_buf, 7);
}
int main( int argc, char *argv[])
{
int fd;/*File Descriptor*/
unsigned int value = 0;
char * cmd_p;
/* O_RDWR – Read/Write access to serial port */
/* O_RDONLY – Read access to serial port */
/* O_WRONLY – Write access to serial port */
/* O_NOCTTY – No terminal will control the process */
/* Open in blocking mode,read will wait */
fd = open(“/dev/fii-dt-driver”,O_RDWR );
printf(“\n +———————————-+”);
printf(“\n | fii-driver function |”);
printf(“\n +———————————-+”);
if(fd == -1) /* Error Checking */
printf(“\n Error! in Opening swled \n”);
else
printf(“\n fii-driver Opened Successfully \n”);
cmd_p = argv[1];
if(argc == 2)
{
if( strcmp (cmd_p , “rd”) == 0 )
{
func_read(fd, 8);
return 0;
}
else
{
print_msg();
close(fd); /* Close the serial port */
return 1;
}
}
else if (argc == 3)
{
if( strcmp (cmd_p , “wr”) == 0 )
{
func_write(fd, argv[2]);
close(fd); /* Close the serial port */
return 0;
}
else if( strcmp (cmd_p , “dir”) == 0 )
{
func_direction(fd, argv[2]);
close(fd); /* Close the serial port */
return 0;
}
}
else
{
print_msg();
close(fd); /* Close the serial port */
return 1;
}
return 0;
}
Next, start up the terminal from the fii_dt_app.c file directory and execute the following commands:
sptl
arm-linux-gnueabihf-gcc fii_dt_app.c -o fii_dt_app.o
This will create a fii_dt_app.o file that you will copy to the SD card.
We will now boot up the development board once more and mount the SD partition where you copied the file. Next, execute the following command to start up fii_dt_app.
./fii_dt_app.o
Note the help instructions that are also displayed, which we wrote in the code. We can read and write operations.
Some examples are:
./fii_dt_app.o dir 0 Sets all gpio as outputs.
./fii_dt_app.o wr 0 Sets the lowest gpio as 0.
./fii_dt_app.o wr 7 Sets the lowest 3 gpio as 0.
./fii_dt_app.o dir 7 Sets the lowest 3 gpio as input. The 3 lights on the left light up (7 is binary 111).
./fii_dt_app.o rd Reads the gpio value; the user can flip the switches to see gpio status.
Notice how the LED lights on the development board change with the commands.