scieee AI-readable full text Open interactive document viewer

Embedded systems with Linux: programming the GPMC driver

Domínguez Pumar, Manuel

Full text

Embedded Systems with Linux Programming the GPMC driver Manuel Domínguez-Pumar Embedded Systems with Linux series Volume 3: Programming the GPMC driver Manuel Domínguez Pumar Electronic Engineering Department Technical University of Catalonia – BarcelonaTech First Edition – July 2018 ISBN 978-84-09-03815-2 AMG Editions, Barcelona, Spain Artwork: JP Productions 2Programming the GPMC driver Index 1 Drivers .................................................................................................. 4 1.1 Introduction ................................................................................................. 4 1.2 A first driver example .................................................................................. 6 1.3 Laboratory Work ........................................................................................ 12 2 GPMC Driver ...................................................................................... 13 2.1 The 'DE2 side' ............................................................................................ 13 2.2 The 'Beaglebone side' ................................................................................ 14 2.2.1 GPMC Signals. NOR-mode multiplexed bus ....................................... 14 2.2.2 GPMC Timing ...................................................................................... 15 2.2.3 Example: Asynchronous Flash NOR device ........................................ 18 2.3 Programming the GPMC driver for the DE2 ............................................. 20 2.3.1 Enable clock for GPMC module: ........................................................ 20 2.3.2 Reset the GPMC module: .................................................................... 21 2.3.3 Configure GPMC to NO IDLE ............................................................. 22 2.3.4 Mask all interrupts from the GPMC module ...................................... 23 2.3.5 Disable TIMEOUT_CONTROL ............................................................. 24 2.3.6 TIMING configuration for the GPMC bus .......................................... 25 2.3.7 GPMC_CONFIG1 ................................................................................. 25 2.3.8 GPMC_CONFIG2 ................................................................................. 28 2.3.9 GPMC_CONFIG3 ................................................................................. 29 2.3.10 GPMC_CONFIG4 ............................................................................... 30 2.3.11 GPMC_CONFIG5 ............................................................................... 32 2.3.12 GPMC_CONFIG6 ............................................................................... 34 2.3.13 GPMC_CONFIG7 ............................................................................... 35 2.4 Pin Configuration: MODE 0, Input Enable and PULL UP ......................... 38 3 Guided configuration ......................................................................... 40 3.1 Function void pointers_init(void) ............................................................... 40 3.2 Function uint gpmc_configuration(void) ................................................... 41 3.3 Function int pin_mux_configuration(void) ................................................. 44 4 Laboratory Work ................................................................................. 45 4.1 Mandatory work ......................................................................................... 45 4.2 Optional Work ............................................................................................ 46 3Programming the GPMC driver 1 Drivers 1.1 Introduction Drivers make up the layer of programs that have a direct contact with system hardware. We can see intuitively that, for example, in order to write a file in a hard disk it is necessary a series of operations of writing and reading in control and data registers at the disk controller. These operations will depend on the specific hard disk we have. However each hard disk will need its specific operations, completely different and in principle incompatible with the ones needed by any other disk. To use a unified approach for communicating with the hardware, operating systems provide the concept of 'module' or driver. All OS present an adequate interface common to all peripherals so that it is possible to communicate, at least to some extent, with different hardware using the same interface. The system devices are at /dev directory of the Linux or Unix tree. They have a format similar to files and therefore we can do with them usual operations such as open, close, read, write, etc. As an example, in the Beaglebone we can look into the urandom device, which provides random numbers: beaglebone# od -x /dev/urandom The od command with option -x reads the device in hexadecimal. Drivers are also named ‘modules’. You can load a driver using the command: insmod 'driver name' And we can see the drivers installed at the system with the command: lsmod Execute this command both in the beaglebone and in the host PC. The command to remove a driver is: rmmod 'driver name' Up to 15 different operations, each one related to a system call, can be used to work with files. Drivers must provide 'an answer' to at least some of them (for example, open, release, read and write). Furthermore, each time the module is loaded it is necessary to configure a certain piece of hardware so that later, when an application accesses the module, the peripheral will be correctly configured. With all this in mind, in the module/driver we must provide some code for the following items: 4Programming the GPMC driver •Module initialization: We must point out to the OS the code providing the initial configuration of the peripheral. •Module exit: Code providing the configuration we want to have in the peripheral once the module has been removed. •Normal file operations: open, release (=close), read, write. •Other configurations, such as ioctl. This system call is a way the application has to provide a value to a certain variable. We will see a simple implementation of this later. In order to pass this information to the operating system, we will generate a structured variable with the following fields: static struct file_operations sd_fops = { write: sd_write, read: sd_read, open: sd_open, release: sd_release, unlocked_ioctl: sd_ioctl, }; static struct miscdevice sd_devs = { minor: MISC_DYNAMIC_MINOR, name: DRIVER_NAME, fops: &sd_fops }; We have defined the variable sd_fops which has at least 5 fields: write, read, open, release and unlocked_ioctl. The values given to each one of these fields (sd_write, sd_read, etc.) are the names of the routines that will be included inside our module and that must be executed once the corresponding system call is made by an application to our driver. Assuming our module is related to the device /dev/driver_port, when we execute within a user application: f=open("/dev/driver_port", O_RDWR); we will be executing the routine indicated by the field open in the sd_fops variable. In our case, this routine is called sd_open. Summarizing, we are mapping the routines in our module to the system calls that can be done on our device. 5Programming the GPMC driver This operation is completed when executing: misc_register( &sd_devs ) This is an specific function that allows to register our device inclosed in the category of 'miscellaneous devices'. This is a simple way of registering our device. The name of our device, namely driver_port is contained in the field DRIVER_NAME of the variable sd_devs that is passed to misc_register(). 1.2 A first driver example The module below (module.c) is a very simple example. The only significant code is contained in the 'sd_read' function, where the ID of the microprocessor in the board is read. #include <linux/module.h> #include <linux/fs.h> #include <linux/miscdevice.h> #include <linux/gpio.h> #include <linux/slab.h> // kmalloc #include <linux/uaccess.h> #include <linux/semaphore.h> #include "soc_AM335x.h" #include "hw_cm_per.h" #include "hw_gpmc.h" #include "driver.h" static void __iomem *soc_device_id; struct semaphore mysem; static struct file_operations sd_fops = { write: sd_write, read: sd_read, open: sd_open, release: sd_release, unlocked_ioctl: sd_ioctl, }; static struct miscdevice sd_devs = { minor: MISC_DYNAMIC_MINOR, name: DRIVER_NAME, fops: &sd_fops }; /*********************************************************************** * @Function: pointers_init * This function must provide initial values to all (u32 *) pointers used * to address the internal registers that must be configured in order * to access the GPMC bus. Each assignation must be done using * the function ioremap ***********************************************************************/ 6Programming the GPMC driver void pointers_init(void) { soc_device_id = ioremap(SOC_DEVICE_ID_REGS, 4); } /*********************************************************************** * @Function: gpmc_configuration ***********************************************************************/ uint gpmc_configuration(void) { return 0; } /*********************************************************************** * @Function: pin_mux_configuration ***********************************************************************/ int pin_mux_configuration(void) { return 0; } /*********************************************************************** * @Function: gmpc_init * This function will be called each time the module is uploaded. * It must configure the GPMC registers (calling the gpmc_configuration() * routine) and it also has to adequately configure the pin multiplexation * of the GPMC interface. ***********************************************************************/ int gpmc_init(void) { gpmc_configuration(); pin_mux_configuration(); return 0; } /*********************************************************************** * @Function: sd_open ***********************************************************************/ static int sd_open( struct inode* pInode, struct file* pFile ) { return 0; } /*********************************************************************** * @Function: sd_release ***********************************************************************/ static int sd_release( struct inode* pInode, struct file* pFile ) { return 0; } /*********************************************************************** * @Function: sd_ioctl ***********************************************************************/ static long sd_ioctl( struct file* pFile, unsigned int cmd, unsigned long value) { return 0; } 7Programming the GPMC driver /*********************************************************************** * @Function: sd_read ***********************************************************************/ static ssize_t sd_read(struct file *filp, char __user *buffer, size_t count, loff_t *f_pos) { u32 number; u32 *pointer=kzalloc(sizeof(u32), GFP_KERNEL); if (!pointer) { printk( KERN_WARNING DRIVER_NAME \ ": Problem allocating memory\n" ); return(-1); } pointer[0] = hwreg(soc_device_id+DEVICE_FEATURE); number = copy_to_user(buffer,pointer,sizeof(u32)); kfree(pointer); return number; } /*********************************************************************** * @Function: sd_write ***********************************************************************/ static ssize_t sd_write(struct file *filp, const char __user *buffer, size_t count, loff_t *f_pos) { return 0; } /*********************************************************************** * @Function: sd_init_module ***********************************************************************/ static int __init sd_init_module( void ) { pointers_init(); gpmc_init(); if( misc_register( &sd_devs ) ) { printk( KERN_WARNING DRIVER_NAME \ ": The module cannot be registered\n" ); return (-ENODEV); } printk( KERN_INFO "Module module.ko uploaded ******\n" ); return 0; } /*********************************************************************** * @Function: sd_cleanup_module ***********************************************************************/ static void __exit sd_cleanup_module( void ) { misc_deregister( &sd_devs ); printk( KERN_INFO "Module module.ko cleaned up ******\n" ); } 8Programming the GPMC driver module_init( sd_init_module ); module_exit( sd_cleanup_module ); MODULE_LICENSE( "GPL" ); MODULE_AUTHOR("Digital Systems using Embedded Linux"); It is important to note that within a driver like this we have three different memory address spaces: a) Physical addresses: the address values specified in the datasheet of the microcontroller. For example, the register with the identification of the microcontroller can be found at address 0x44E10600. There is a label defined in the soc_AM335x.h file for this specific address, SOC_DEVICE_ID_REGS: static void_iomem *soc_device_id; // Global variable soc_device_id = ioremap(SOC_DEVICE_ID_REGS, 4); The ioremap function generates a pointer in the virtual memory space of the kernel that will be mapped onto the physical address specified by the first argument (SOC_DEVICE_ID_REGS). The second argument is the memory size, in bytes, that must be allocated to the kernel. Since the identification register is 32 bit long, we only need 4 bytes in this case. If, instead of this, we ever tried to execute: aux = *(u32 *)0x44E10600; // ERROR: not to be done like this we would get a segmentation fault in kernel space, since the kernel must always work within the virtual address space that has been allocated to it. The address translation to the physical address space (the real one) is provided by the Memory Management unit (MMU) of the microprocessor. Once the soc_device_id pointer has been initialized, we may access the contents of the ID register by simply executing: aux = *soc_device_id; The previous instruction reads the contents of the identification register of the microprocessor and puts its contents into a variable called aux. On the other hand, the following macro has been defined in the file driver.h: #define hwreg(x) *( volatile u32 *)((x)) The purpose of this definition is simply trying to make more evident that we are accessing an internal register of the microprocessor, not a typical kernel space address. If we want to use this macro for accessing the register we will execute: aux = hwreg(soc_device_id); 9Programming the GPMC driver For WRITE operations: Now, for write operations we have to program the following parameters: - WRCYCLETIME - CSONTIME: (the same value as for read operations) - CSWROFFTIME - ADVONTIME (the same value as for read operations) - ADVWROFFTIME - WEONTIME - WEOFFTIME - WRDATAONADMUXBUS The next page lists ALL the parameters that can be programmed as a function of the access type. 16 Programming the GPMC driver 17 Programming the GPMC driver 2.2.3 Example: Asynchronous Flash NOR device Now, we will proceed with the example provided in the technical reference of the microcontroller. First it provides the access time parameters of a given memory and then it gives a set of possible values for the timing parameters of the GPMC bus. Although we won't be using these parameters in this laboratory, this is a good example of how to provide good values to the numerous timing parameters of the GPMC bus. READ CYCLE : For this case, since tCAS(min)=0, we may activate CE# after activation of AVD#. 18 Programming the GPMC driver Now, from the point of view of the GPMC we have that the following parameters of the GPMC access can be programmed as follows: 19 Programming the GPMC driver 2.3 Programming the GPMC driver for the DE2 2.3.1 Enable clock for GPMC module: a) As a first step we will enable the clock for the GPMC module by writing the MODULEMODE field of the CM_PER_GPMC_CLKCTRL register. b) In a second step we will check that it is indeed enabled: we will wait till the IDLEST bits (bits 17-16) take the value '00' (corresponding to “Func”, so that the module is fully functional). The offset of the CM_PER_GPMC_CLKCTRL register, =0x30, defined in hw_cm_per.h, is specified from the base address given by SOC_PRCM_REGS, given in the file soc_AM335.h. 20 Programming the GPMC driver 2.3.2 Reset the GPMC module: a) We will reset the GPMC module (a software reset since this is done once the system has been initialized). To this effect we will activate the SOFTRESET bit in the GPMC_SYSCONFIG register (without changing the contents of all other bits, i.e. using masks). The offset of the GPMC_SYSCONFIG register, =0x10, is specified from the base address given by SOC_GPMC_0_REGS, given in the file soc_AM335.h. b) Now, as a second step we will check that the reset has been completed: to this effect we will check the GPMC_SYSSTATUS register. We will wait till the bit RESETDONE is equal to 1. 21 Programming the GPMC driver The offset of the GPMC_SYSSTATUS register, =0x14, defined in hw_gpmc.h, is specified from the base address given by SOC_GPMC_0_REGS, given in the file soc_AM335.h. 2.3.3 Configure GPMC to NO IDLE We will configure the SIDLEMODE bits (bits 4-3) of the GPMC_SYSCONFIG register to 0x1: 'No idle. An idle request is never acknowledged'. Use a temporary variable to do any intermediate operation so that you only write once into the GPMC_SYSCONFIG register. The reason for doing this is to avoid going through an intermediate configuration that sometimes may generate problems. For example, while writing the SIDLEMODE bits to 01 we might be using a mask so that first we put both bits to '00' and then, in a second operation, we switch to '01'. If we do this we will configuring the GPMC to be idle for some time, and we don't know the repercussions this may have. 22 Programming the GPMC driver 2.3.4 Mask all interrupts from the GPMC module We will mask all interrupts of the GPMC module by configuring the GPMC_IRQENABLE to 0x00. This register has an offset = 0x1C, defined in hw_gpmc.h, from the base address SOC_GPMC_0_REGS, given in the file soc_AM335.h 23 Programming the GPMC driver 2.3.5 Disable TIMEOUT_CONTROL Since the device we will be connecting to the GPMC bus is very fast we won't be using the TIMEOUT_CONTROL feature of the AM3359 processor. From the reference manual we have: We will therefore write a 0x00 in the GPMC_TIMEOUT_CONTROL register. This register has an offset = 0x1C, defined in hw_gpmc.h, from the base address SOC_GPMC_0_REGS, given in the file soc_AM335.h. 24 Programming the GPMC driver 2.3.6 TIMING configuration for the GPMC bus Now, we will be configuring in 7 steps the GPMC configuration for a given chip select. To this effect there are 7 configuration registers for each chip select. In order to clarify programs we will be using systematically the macros made by Texas Instruments and defined in hw_gpmc.h. #define GPMC_CONFIG1(n) (0x60 + (n * (0x30))) #define GPMC_CONFIG2(n) (0x64 + (n * (0x30))) #define GPMC_CONFIG3(n) (0x68 + (n * (0x30))) #define GPMC_CONFIG4(n) (0x6C + (n * (0x30))) #define GPMC_CONFIG5(n) (0x70 + (n * (0x30))) #define GPMC_CONFIG6(n) (0x74 + (n * (0x30))) #define GPMC_CONFIG7(n) (0x78 + (n * (0x30))) These macros provide the offset of the corresponding GPMC_CONFIGx register depending on number of chip select we will be using. This means that in order to write a particular value to the the GPMC_CONFIG1 register, for example, related to the chip select csNum we will be using: static void __iomem *soc_gpmc_base; // base pointer preparation soc_gpmc_base = ioremap(SOC_GPMC_0_REGS, SZ_16M); // Access to GPMC_CONFIG1_i, with i=csNum hwreg(soc_gpmc_base + GPMC_CONFIG1(csNum)) = XXX; 2.3.7 GPMC_CONFIG1 Let us now configure the GPMC bus for NOR accesses with x2 granularity and multiplexed bus AD0-AD15. First we will program the following content of the configuration bits: DEVICESIZE (bits 13-12) = 1h (16 bit device) ATTACHEDDEVICEPAGELENGTH (bits 24-23)=0 (burst length of the attached device = 4 words) MUXADDDATA (bits 9-8) = 0x2 (Address and Data multiplexed) All other bits can be set to 0. In particular note that from the DEVICETYPE field this means that we will be accessing a NOR device, with asynchronous reads and writes, in single mode (no burst), without monitoring of the WAIT signal and without dividing the GPMC_FCLOCK frequency. We will be using TIMEPARAGRANULARITY=0. This is an important parameter that allows scaling all the times we will be defining from here onwards. 25 Programming the GPMC driver Possible values for these parameters are: OEONTIME = 5 OEOFFTIME = 9 WEONTIME = 5 WEOFFTIME = 8 All other parameters can be set to 0. 2.3.11 GPMC_CONFIG5 In this section we will provide values for RDCYCLETIME, WRCYCLETIME and RDACCESSTIME. 32 Programming the GPMC driver Possible values for these parameters are: RDCYCLETIME = 10 WRCYCLETIME = 10 RDACCESSTIME = 8 All other parameters can be set to 0. 33 Programming the GPMC driver 2.3.12 GPMC_CONFIG6 In this section we will configure WRDATAONADMUXBUS. We may program WRDATAONADMUXBUS=4, and set all other parameters to 0. 34 Programming the GPMC driver 2.3.13 GPMC_CONFIG7 Address decoder and Chip Select: we will configure the address set for which our CS* signal will be accessed. First of the GPMC memory area of the microcontroller has been established at the beginning of the memory map: Therefore, the GPMC external memory then can be accessed at most in the address range 0x0000_0000 to 0x1FFF_FFFF. An important excerpt of the reference manual describes the generation of the address set for any CS: 35 Programming the GPMC driver The mask address is configured with the GPMC_CONFIG7 register, as well as the base address and finally the activation of the CS. 36 Programming the GPMC driver Configure this register so that the access to the GPMC device, using CS2*, is made from the address 0x0900_0000 onwards. OPTIONAL: Finally we may produce the L3 clock / 5 = 40 MHz in CLKOUT2: hwreg(soc_cm_device_base+CM_CLKOUT_CTRL) = 0xA1; 37 Programming the GPMC driver 2.4 Pin Configuration: MODE 0, Input Enable and PULL UP In page 157 of the reference manual (version H) we find that the following address range is reserved for the Control Module that controls the pin configuration. Now, the addresses of the control registers for each pin can be accessed as: address pin control register = 0x44E1_0000 + OFFSET registers The offsets of the registers you have to configure can be found in page 758 of the reference manual (rev. H) or page 1124 (rev. I): #define GPMC_AD0_OFFSET 0x800 #define GPMC_AD1_OFFSET 0x804 #define GPMC_AD2_OFFSET 0x808 #define GPMC_AD3_OFFSET 0x80C #define GPMC_AD4_OFFSET 0x810 #define GPMC_AD5_OFFSET 0x814 #define GPMC_AD6_OFFSET 0x818 #define GPMC_AD7_OFFSET 0x81C #define GPMC_AD8_OFFSET 0x820 #define GPMC_AD9_OFFSET 0x824 #define GPMC_AD10_OFFSET 0x828 #define GPMC_AD11_OFFSET 0x82C #define GPMC_AD12_OFFSET 0x830 #define GPMC_AD13_OFFSET 0x834 #define GPMC_AD14_OFFSET 0x838 #define GPMC_AD15_OFFSET 0x83C #define GPMC_CSN2_OFFSET 0x884 #define GPMC_ADVn_ALE_OFFSET 0x890 #define GPMC_BE0n_CLE_OFFSET 0x89C #define GPMC_BE1n_OFFSET 0x878 #define GPMC_CLK_OFFSET 0x88C #define GPMC_OE_REn_OFFSET 0x894 #define GPMC_WEn_OFFSET 0x898 You can use the above labels to access all the configuration registes of the pins we will be using. The description of the bit fields of the above registers is the following: 38 Programming the GPMC driver We will configure all the above 23 pins to the FAST, RECEIVER ENABLED, PULL-UP Enabled and MODE 0. 39 Programming the GPMC driver 3 Guided configuration The previous section contains large quantities of information that in general are difficult to assimilate. In order to ease out the task we propose to you, you may use the following programming instructions: 3.1 Function void pointers_init(void) This function must give values to the following pointers that must be defined as global variables: static void __iomem *soc_device_id; static void __iomem *soc_gpmc_base; static void __iomem *soc_prcm_base; static void __iomem *soc_cm_device_base; static void __iomem *soc_extmem_base; static void __iomem *soc_pin_mux_base; The idea is to have a different pointer, living in the kernel virtual memory space, with a value correctly assigned by the MMU to the corresponding physical address. As an example we have in our initial driver: soc_device_id = ioremap(SOC_DEVICE_ID_REGS, 4); As we have previously seen the SOC_DEVICE_ID_REGS is a label defined in the soc_AM335.h header file, with the value (0x44E10600). You should check in page 163 of the datasheet of the microprocessor that this is indeed the address of the identification register of the microprocessor. The values that must be assigned to each pointer must be obtained by calling to the function ioremap with the following parameters for each pointer. Kernel pointer Address label (physical address value) SIZE of the part of memory assigned to each pointer soc_extmem_base EXTMEM_BASE SZ_128K soc_gpmc_base SOC_GPMC_0_REGS SZ_16M soc_prcm_base SOC_PRCM_REGS 0x12FF soc_cm_device_base SOC_CM_DEVICE_REGS SZ_256 soc_pin_mux_base AM33XX_CTRL_BASE PAD_CTRL_SIZE soc_device_id SOC_DEVICE_ID_REGS 4 40 Programming the GPMC driver The size labels are standard and be used within the file module.c. The only one that has been defined for this case is PAD_CTRL_SIZE that can be found in driver.h, with value 0xA38. The first pointer, static void_iomem *soc_extmem_base, is very important because it will be constantly used to access the block of memory assigned to the GPMC bus. Since we will conneed AD0-AD15 to the FPGA, this memory block is 128 Kbytes long. 3.2 Function uint gpmc_configuration(void) This function must properly configure all the registers that are needed to specify how the GPMC must work. Since this means to program many different registers we propose to use the following steps. Step 1: enable clock to GPMC module Content addressed by OP VALUE oc_prcm_base + CM_PER_GPMC_CLKCTRL | = CM_PER_GPMC_CLKCTRL_MODULEMODE_ENABLE Step 2: Wait till GPMC module is enabled while((hwreg(soc_prcm_base + CM_PER_GPMC_CLKCTRL) & CM_PER_GPMC_CLKCTRL_IDLEST) != (CM_PER_GPMC_CLKCTRL_IDLEST_FUNC << CM_PER_GPMC_CLKCTRL_IDLEST_SHIFT)); Step 3: RESET the GPMC module Content addressed by OP VALUE soc_gpmc_base + GPMC_SYSCONFIG | = GPMC_SYSCONFIG_SOFTRESET Step 4: Wait till RESET DONE while((hwreg(soc_gpmc_base + GPMC_SYSSTATUS) & GPMC_SYSSTATUS_RESETDONE) == GPMC_SYSSTATUS_RESETDONE_RSTONGOING); 41 Programming the GPMC driver