Linux Kernel access to peripheral I/O resources)

Source: Internet
Author: User
Linux Kernel access to peripheral I/O resources by means of (conversion) 3/15/2009 1:27:34 pmhttp: // export Author: dongasdate: 08-08-02 we know that the default peripheral I/O resources are not in the Linux kernel space (such as SRAM or hardware interface registers). If you need to access the peripheral I/O resources, the address must be mapped to the kernel space before it can be accessed in the kernel space. There are two ways to access peripheral I/O memory resources in the Linux kernel: Dynamic ing (ioremap) and static ing (map_desc ). I. The dynamic ing (ioremap) method is quite simple and widely used. That is, you can use the ioremap function provided by the kernel to dynamically create a ing table from the peripheral I/O memory resources to the kernel virtual address, so that you can access this I/O resource in the kernel space. Ioremap macro is defined in ASM/Io. in H: # define ioremap (cookie, size) _ ioremap (cookie, size, 0) _ ioremap function prototype is (ARM/MM/ioremap. c): void _ iomem * _ ioremap (unsigned long phys_addr, size_t size, unsigned long flags); phys_addr: Start Io address size to map: the size of the space to be mapped. Flags: indicates the IO space to be mapped and the permission-related identifier. This function returns the mapped kernel virtual address (3g-4G ). then, you can read and write the returned kernel virtual address to access this segment of I/O memory resources. For example, to access the I2S register on the S3C2410 platform, check that datasheet knows that the physical address of IIS is 0x55000000, we define it as macro s3c2410_pa_iis, as follows: # define s3c2410_pa_iis (0x55000000) to access this I/O register (IIS) in the kernel space (IIS driver) resources must first be mapped to the kernel address space: our_card-> regs = ioremap (s3c2410_pa_iis, 0x100); If (our_card-> regs = NULL) {err =-enxio; goto exit_err;} after creation, we can use readl (our_card-> regs) or writel (value, our_card-> regs) and other IO Interface functions to access it. Ii. Static ing (map_desc) method The following describes the static ing method, that is, creating an I/O resource ing table through the map_desc struct. The kernel allows you to create a linear ing table (page table) from the I/O resource to the kernel address space through the map_desc struct during system startup, this ing table is a one-to-one ing relationship. Programmers can define their own virtual addresses after the I/O memory resource ing. After creating a static ing table, you do not need to perform ioreamp dynamic ing when accessing this I/O resource in the kernel or driver, you can directly access it through the mapped I/O virtual address. The following describes the principle of this mechanism in detail and illustrates how to use this static ing method to access peripheral I/O memory resources. The kernel provides an important struct machine_desc, which plays a very important role in internal nuclear transfer. The kernel uses the machine_desc struct to control the initialization of the system architecture. Members of the machine_desc struct contain several important initialization functions related to the architecture, including map_io, init_irq, init_machine, phys_io, and timer. The machine_desc struct is defined as follows: struct machine_desc {
/*
* Note! The first four elements are used
* By Cycler code in head-armv.S
*/
Unsigned int NR;/* architecture Number */
Unsigned int phys_io;/* Start of physical Io */
Unsigned int io_pg_offst;/* byte offset for Io
* Page tabe entry */const char * Name;/* architecture name */
Unsigned long boot_params;/* tagged list */unsigned int video_start;/* Start of video RAM */
Unsigned int video_end;/* end of video RAM */unsigned int reserve_lp0: 1;/* Never has lp0 */
Unsigned int reserve_lp1: 1;/* Never has lp1 */
Unsigned int reserve_lp2: 1;/* Never has lp2 */
Unsigned int soft_reboot: 1;/* Soft reboot */
Void (* fixup) (struct machine_desc *,
Struct tag *, char **,
Struct meminfo *);
Void (* map_io) (void);/* IO mapping function */
Void (* init_irq) (void );
Struct sys_timer * timer;/* system tick timer */
Void (* init_machine) (void );
}; Here, map_io members are the interface functions provided by the kernel for users to create a static ing table from peripheral I/O resources to kernel virtual addresses. The map_io member function is called during system initialization. The process is as follows: start_kernel-> setup_arch () --> paging_init () --> devicemaps_init () the machine_desc struct is called by using the machine_start macro. Note: The use of machine_start and the call process of each member function can be referred to: http://blog.chinaunix.net/u2/60011/showart_1010489.html users can specify map_io interface function when defining the machine_desc struct, here the S3C2410 platform is used as an example. The structure of S3C2410 machine_desc is defined as follows:/* ARCH/ARM/mach-s3c2410/Mach-smdk2410.c */
Machine_start (smdk2410, "smdk2410")/* @ todo: request a new identifier and switch
* To smdk2410 */
/* Maintainer: Jonas dietsche */
. Phys_io = s3c2410_pa_uart,
. Io_pg_offst = (u32) s3c24xx_va_uart)> 18) & 0 xfffc,
. Boot_params = s3c2410_sdram_pa + 0x100,
. Map_io = smdk2410_map_io,
. Init_irq = s3c24xx_init_irq,
. Init_machine = smdk2410_init,
. Timer = & s3c24xx_timer,
Machine_end as above, map_io is initialized to smdk2410_map_io. Smdk2410_map_io is the function we define to create a static I/O ing table. When the porting kernel is added to the new development board, we need to implement this function by ourselves. (Note: This function can usually be implemented very easily. Just call iotable_init to create a ing table. Our board kernel is. However, the implementation of this function on the S3C2410 platform is a little complicated, mainly because the resources of the I/O ing table to be created are divided into three parts (smdk2410_iodesc, initi_iodesc, and s3c2410_iodesc) created in different stages. Here we take one of the parts for analysis, without affecting our understanding of the entire concept .) The smdk2410_map_io function of the S3C2410 platform will eventually call the s3c2410_map_io function. The process is as follows: s3c2410_map_io-> s3c24xx_init_io-> s3c2410_map_io: analyze the s3c2410_map_io function: void _ init s3c2410_map_io (struct map_desc * mach_desc, int mach_size)
{
/* Register our io-tables */
Iotable_init (s3c2410_iodesc, array_size (s3c2410_iodesc ));
......
}
The iotable_init kernel is provided and defined as follows :/*
* Create the architecture specific ings
*/
Void _ init iotable_init (struct map_desc * io_desc, int nr)
{
Int I; for (I = 0; I <NR; I ++)
Create_mapping (io_desc + I );
}
As you can see, s3c2410_map_io finally calls iotable_init to create a ing table. The iotable_init function has two parameters: one is a structure of the map_desc type, and the other is the number of NR struct. The most important thing here is struct map_desc. The map_desc struct is defined as follows:/* include/ASM-arm/Mach/map. H */
Struct map_desc {
Unsigned long virtual;/* mapped virtual address */
Unsigned long PFN;/* The page frame number of the physical address of the I/O resource */
Unsigned long length;/* I/O resource length */
Unsigned int type;/* I/O resource type */
};
The create_mapping function creates a linear ing table based on the information provided by map_desc. In this way, we know that the general process for creating an I/O ing table is: as long as the map_desc struct of the corresponding I/O resources is defined and the struct is passed to the iotable_init function for execution, you can create a ing table from the corresponding I/O resources to the kernel virtual address space. Let's take a look at how S3C2410 defines the map_desc struct (that is, the s3c2410_iodesc in the above s3c2410_map_io function ). /* ARCH/ARM/mach-s3c2410/S3C2410. C */
Static struct map_desc s3c2410_iodesc [] _ initdata = {
Iodesc_ent (usbhost ),
Iodesc_ent (clkpwr ),
Iodesc_ent (LCD ),
Iodesc_ent (timer ),
Iodesc_ent (ADC ),
Iodesc_ent (watchdog ),
};
Iodesc_ent macro: # define iodesc_ent (x) {(unsigned long) s3c24xx_va _ # X, _ phys_to_pfn (s3c24xx_pa _ # X), s3c24xx_sz _ # X, mt_device} is equivalent to static struct map_desc s3c2410_iodesc [] _ initdata = {
{
. Virtual = (unsigned long) s3c24xx_va _ LCD ),
. PFN = _ phys_to_pfn (s3c24xx_pa _ LCD ),
. Length = s3c24xx_sz _ LCD,
. Type = mt_device
},
......
};
S3c24xx_pa _ LCD and s3c24xx_va _ LCD are physical and virtual addresses of LCD registers defined in map. h. Here, the Virtual Member of the map_desc struct is initialized to s3c24xx_va _ LCD, And the PFN member value is calculated using the _ phys_to_pfn kernel function, you only need to pass the physical address of the I/O resource to it. Length indicates the size of the mapped resource. Mt_device is of the I/O type and is usually defined as mt_device. The most important value here is the virtual member value s3c24xx_va _ LCD. This value is the kernel virtual address mapped to the I/O resource. After the ing table is created successfully, you can directly access this I/O resource through the Virtual Address in the kernel or driver. S3c24xx_va _ LCD and s3c24xx_pa _ LCD are defined as follows:/* include/ASM-arm/arch-s3c2410/map. H * // * LCD controller */# define s3c24xx_va_ LCD s3c2410_addr (0x00600000) // The virtual address mapped to the LCD # define s3c2410_pa_ LCD (0x4d000000) // LCD register physical address # define s3c24xx_sz_ LCD sz_1m // LCD register size s3c2410_addr is defined as follows: # define s3c2410_addr (x) (void _ iomem *) 0xf0000000 + (x )) here is a linear offset relationship, that is, the I/O static ing table created by S3C2410 will be mapped after 0xf0000000. (This linear offset value can be changed, or you can manually define a value in the Virtual Member, as long as it does not conflict with the ing address of other IO resources, but preferably after 0xf0000000 .) (Note: in fact, the linear offset of s3c2410_addr is only one method of the S3C2410 platform. Many other arm platforms use the general io_address macro to calculate the offset from the physical address to the virtual address. The io_address macro is defined as follows:/* include/ASM/arch-versatile/hardware. H * // * macro to get at Io space when running always */# define io_address (x) & 0x0fffffff) + (x)> 4) & 0x0f000000) + 0xf0000000) after the s3c24ing table s3c2410_iodesc is created successfully, we can directly access the LCD register resources through the s3c24xx_va _ LCD in the kernel. For example, in the probe function of the S3C2410 LCD Driver/* Stop the video and unset ENVID if set */
Info-> regs. lcdcon1 & = ~ S3c2410_lcdcon1_envid;
Lcdcon1 = readl (s3c2410_lcdcon1); // register virtual address mapped to read
Writel (lcdcon1 &~ S3c2410_lcdcon1_envid, s3c2410_lcdcon1); // The virtual address mapped by write
The s3c2410_lcdcon1 Register address is an address relative to the s3c24xx_va_ LCD offset, defined as follows:/* include/ASM/arch-s3c2410/regs-lcd.h */# define s3c2410_lcdreg (x) + s3c24xx_va_ LCD) /* LCD control registers */# define s3c2410_lcdcon1 s3c2410_lcdreg (0x00) Here, we know how to create a static I/O memory resource ing table through the map_desc struct. In summary, the process is very simple. First, create a static ing table by defining the map_desc struct, and second, access this IO resource by creating a ing virtual address in the kernel. Iii. I/O static ing the application instance I/O static ing mode is usually used for register resource ing, so that ioremap is not required when writing the kernel code or driver, directly use the mapped kernel virtual address for access. The same IO resources only need to be mapped once during kernel initialization and will be available for later use. The example of register resource ing has been clearly described in the above principles. Here I will give an example of SRAM to introduce how to apply this I/O static ing method. Of course, the principle and operation process are the same as the register resource. we can regard the SRAM as a large I/O register resource. For example, my board has a 64kb SRAM at 0x30000000. Now we need to access this SRAM through static ing. What we need to do includes modifying the kernel code, adding the corresponding map_desc structure of the SRAM resource, and creating a static SRAM ing table from the SRAM to the kernel address space. Write an SRAM module and access the SRAM directly through the static ing kernel virtual address. Step 1: Create an SRAM static ing table and add the corresponding map_desc of the SRAM resource to the map_des struct array (xxx_io_desc) of the sub-board. Static struct map_desc xxx_io_desc [] _ initdata = {
............
{
. Virtual = io_address (xxx _ uart2_base ),
. PFN = _ phys_to_pfn (xxx _ uart2_base ),
. Length = sz_4k,
. Type = mt_device
},{
. Virtual = io_address (xxx_sram_base ),
. PFN = _ phys_to_pfn (xxx_sram_base ),
. Length = sz_4k,
. Type = mt_device
},
};
Macro xxx_sram_base is the physical address of the SRAM on our board, defined as 0x30000000. My kernel uses the io_address method to calculate the kernel virtual address, which is a little different from the previously introduced S3C2410. However, the principle is the same. It is a linear offset with a range after 0xf0000000. Step 2: Write an SRAM module. In the module, use the mapped virtual address to directly access the SRAM module. The Code is as follows:/* SRAM testing module */
......
Static void sram_test (void)
{
Void * sram_p;
Char STR [] = "Hello, SRAM! /N ";

Sram_p = (void *) io_address (xxx_sram_base);/* obtain the virtual address mapped to the SRAM through the io_address macro */
Memcpy (sram_p, STR, sizeof (STR); // copy the STR character array to the SRAM.
Printk (sram_p );
Printk ("/N ");
} Static int _ init sram_init (void)
{
Struct resource * ret;

Printk ("request SRAM mem region.../N ");
Ret = request_mem_region (sram_base, sram_size, "SRAM region ");

If (ret = NULL ){
Printk ("request SRAM mem Region failed! /N ");
Return-1;
}

Sram_test ();
Return 0;
} Static void _ exit sram_exit (void)
{
Release_mem_region (sram_base, sram_size );

Printk ("Release SRAM mem region success! /N ");
Printk ("SRAM is closed/N ");
} Module_init (sram_init );
Module_exit (sram_exit );
Run the following result on the Development Board: // # insmod bin/SRAM. Ko request SRAM mem region ...... hello, SRAM! # Rmmod sramrelease SRAM mem region success! The SRAM is close experiment shows that the mapped address can be used to access the SRAM normally. Finally, another reason for using the SRAM as an example is to access the SRAM through static ing. We can know in advance the kernel virtual address after the SRAM ing (calculated through ioaddress ). In this way, you can try to write an article on the SRAM. For example, write a module for memory allocation to manage the SRAM or other methods, and place some critical data to run in the SRAM, this improves the efficiency of some complex programs (the speed of SRAM is much faster than that of SDRAM), such as the large buffer used in audio/video coding and decoding. Article Source: http://www.diybl.com/course/6_system/linux/Linuxjs/200888/135069.html

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.