Linux kernel Access Peripheral i/o--dynamic Mapping (IOREMAP) and static Mapping (MAP_DESC) (reprinted)

Source: Internet
Author: User
Tags function prototype

"Go" (GO) how the Linux kernel accesses peripheral I/O resources-static mapping (MAP_DESC) mode

How the Linux kernel accesses peripheral I/O resources
Author:dongas
date: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, etc.), and if you need access to the peripheral I/O resources, you must first map its address to kernel space before it can be accessed in kernel space.
The Linux kernel accesses peripheral I/O memory resources in two ways: dynamic mapping (IOREMAP) and static mapping (MAP_DESC).
One, dynamic mapping (IOREMAP) mode
The dynamic mapping method is the use of a lot of people, but also relatively simple. That is, directly through the kernel provided by the IOREMAP function dynamically create a piece of peripheral I/O memory resources to the kernel virtual address mapping table, so that I can access this I/O resources in the kernel space.
IOREMAP macros are defined within Asm/io.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: The starting IO address to be mapped
Size: The amount of space to map
Flags: Flags about the IO space and permissions to map
The function returns the mapped kernel virtual address (3g-4g). This I/O memory resource can then be accessed by reading and writing to the returned kernel virtual address.
Give a simple example: (IIS audio driver from s3c2410)
For example, we want to access the I2S register on the s3c2410 platform, see Datasheet know that the IIS physical address is 0x55000000, and we define it as a macro S3c2410_pa_iis, as follows:
#define S3C2410_PA_IIS (0x55000000)
To access this I/O Register (IIS) resource in kernel space (IIS driver) requires a mapping to the kernel address space first:
Our_card->regs = Ioremap (S3c2410_pa_iis, 0x100);
if (Our_card->regs = = NULL) {
err =-enxio;
Goto Exit_err;
}
Once created, we can access it through the IO interface functions such as READL (Our_card->regs) or Writel (value, Our_card->regs).
Second, static mapping (MAP_DESC) mode
The following focuses on static mapping by statically creating an I/O resource Mapping table from the MAP_DESC structure.
The kernel provides a way to create a linear mapping table (that is, page table) that statically creates I/O resources to the kernel address space at system boot time through the Map_desc struct, which is a map-by-table relationship. The programmer can define the virtual address of the I/O memory resource mapping itself. The static mapping table is created, and when accessing the I/O resource in the kernel or driver there is no need for ioreamp dynamic mapping, which can be accessed directly through the mapped I/O virtual address.
The following is a detailed analysis of the mechanics of this mechanism and an example of how to access peripheral I/O memory resources through this statically mapped approach.
The kernel provides an important struct struct machine_desc, which plays a very important role in kernel porting, and the kernel controls the initialization of related parts of the system architecture through the MACHINE_DESC structure.
Members of the MACHINE_DESC structure contain several of the most important initialization functions of the architecture-related parts, including Map_io, INIT_IRQ, Init_machine, and Phys_io, timer members, and so on.
The MACHINE_DESC structure is defined as follows:
struct MACHINE_DESC {
/*
* note! The first four elements is used
* by assembler 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);
};
The Map_io member here is the interface function that the kernel provides to the user to create the peripheral I/O resource to the kernel virtual address static mapping table. The Map_io member function is called during the system initialization process as follows:
Start_kernel, Setup_arch ()---Paging_init () and Devicemaps_init () are called
The MACHINE_DESC structure is initialized by Machine_start macros.
Note: The use of Machine_start and the invocation procedures for each member function are referred to:
http://blog.chinaunix.net/u2/60011/showart_1010489.html
The user can specify the interface function of the map_io when defining the MACHINE_DESC structure, which takes the s3c2410 platform as an example.
The S3C2410 MACHINE_DESC structure 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 >>) & 0XFFFC,
. 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 our own definition of a function that creates a static I/O mapping table. This function needs to be implemented by ourselves when porting the kernel to the new Development Board.
(Note: This function can usually be implemented very simply, as long as the direct call to Iotable_init to create the mapping table is OK, our board core is.) But the s3c2410 platform is a bit more complex to implement, mainly because the resources it will create for the IO mapping table are divided into three parts (Smdk2410_iodesc, S3c_iodesc, and S3c2410_iodesc) that are created at different stages. Here we take one part of the analysis, does not affect the understanding of the whole concept. )
The Smdk2410_map_io function of the s3c2410 platform is eventually called to the S3c2410_map_io function.
The process is as follows: S3c2410_map_io, S3c24xx_init_io, S3c2410_map_io
The following analysis of 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, as defined below:
/*
* Create the architecture specific mappings
*/
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 know, S3c2410_map_io finally calls Iotable_init to establish the mapping table.
There are two parameters to the Iotable_init function: one is the structure of the Map_desc type, and the other is the number nr of the struct. The key thing here is the struct map_desc. The MAP_DESC structure is defined as follows:
/* Include/asm-arm/mach/map.h */
struct MAP_DESC {
unsigned long virtual; /* Virtual address after mapping */
unsigned long PFN; /* The page frame number where the physical address of the I/O resource is located */
unsigned long length; /* I/O resource length */
unsigned int type; /* I/O resource type */
};
The Create_mapping function is to create a linear mapping table from the information provided by MAP_DESC.
Then we'll know. The approximate process for creating an I/O mapping table is to create a corresponding I/O resource to a mapping table of the kernel virtual address space as long as the MAP_DESC structure of the corresponding I/O resource is defined and passed to the Iotable_init function execution.
Let's see how s3c2410 defines the MAP_DESC structure (i.e. 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 macros are as follows:
#define Iodesc_ent (x) {(unsigned long) s3c24xx_va_# #x, __PHYS_TO_PFN (s3c24xx_pa_# #x), s3c24xx_sz_# #x, Mt_device}
After expansion 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
},
......
};
The S3c24xx_pa_ LCD and S3c24xx_va_ LCD are the physical addresses and virtual addresses of the LCD registers defined within the map.h. Here the virtual member of the Map_desc struct is initialized to S3c24xx_va_ LCD,PFN member value is computed by the __PHYS_TO_PFN kernel function, and only the physical address of the I/O resource is passed to it. Length is the size of the mapped resource. Mt_device is an I/O type and is typically defined as mt_device.
Here the most important is the value of the virtual member S3c24xx_va_ LCD, this value is the I/O resource mapping after the kernel virtual address, after the creation of the mapping table is successful, it can be in the kernel or driver directly through the virtual address to access the I/O resources.
The 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)//lcd a mapped virtual address
#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, where the I/O static mapping table created by s3c2410 is mapped to 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 other IO resource mapping addresses, but preferably after 0xf0000000.) )
(Note: In fact, the linear offset of the s3c2410_addr is just one of the s3c2410 platforms, and many other arm platforms use a common io_address macro to calculate the offsets before the physical address to the virtual address.)
The io_address macro is defined as follows:
/* Include/asm/arch-versatile/hardware.h */
/* Macro to get in IO space when running virtually */
#define IO_ADDRESS (x) (((x) & 0X0FFFFFFF) + (((((x) >> 4) & 0x0f000000) + 0xf0000000)
S3c2410_iodesc This mapping table has been successfully established, we can access the LCD register resource directly from the S3c24xx_va_ LCD in the kernel.
such as: s3c2410 LCD driver in the probe function
/* Stop the video and unset envid if set */
Info->regs.lcdcon1 &= ~s3c2410_lcdcon1_envid;
Lcdcon1 = Readl (S3c2410_lcdcon1); Read mapped register virtual address
Writel (Lcdcon1 & ~s3c2410_lcdcon1_envid, S3c2410_lcdcon1); The virtual address after the write map
The S3c2410_lcdcon1 register address is an address relative to the S3C24XX_VA_LCD offset, as defined below:
/* Include/asm/arch-s3c2410/regs-lcd.h */
#define S3C2410_LCDREG (x) ((x) + S3C24XX_VA_LCD)
/* LCD Control registers */
#define S3c2410_lcdcon1 S3c2410_lcdreg (0x00)
Here, we know the principle of creating a static mapping table for I/O memory resources through the MAP_DESC structure. To summarize the discovery, the process is simple, one creates a static mapping table by defining a map_desc struct, and two accesses the IO resource in the kernel by creating a mapped virtual address.
Three, I/O static Mapping method application Example
The I/O static mapping is usually used in the mapping of the Register resource, so that the kernel code or driver can be written without ioremap, directly using the mapped kernel virtual address access. The same IO resource needs to be mapped once during kernel initialization and can be used all the time.
Example of a register resource mapping the above principle has been introduced very clearly, here I give an example of SRAM describes how to apply this I/O static mapping method. Of course, the principle and operation process is the same as the register resources, you can think of SRAM as a large-size I/O register resource.
For example, my board has a 64KB-size SRAM in the 0x30000000 position. We now need to access the SRAM in a static mapping way. What we're going to do is to modify the kernel code, add the corresponding MAP_DESC structure of the SRAM resource, and create a static mapping table of the SRAM to the kernel address space. Write an SRAM module that accesses the SRAM directly through a statically mapped kernel virtual address within the SRAM module.
First step: Create an SRAM static mapping table
Add the corresponding Map_desc of the SRAM resource in the Map_des structure Array (XXX_IO_DESC) of my board. As follows:
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 my board, defined as 0x30000000. My kernel is to compute the kernel virtual address in io_address way, which is a bit different from the s3c2410 described earlier. But the principle is the same, for a linear offset, the range after 0xf0000000.
Step two: Write an SRAM Module that accesses the SRAM resource directly through the mapped virtual address in the module
The SRAM module 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); /* Get SRAM Mapped virtual address via io_address macro */
memcpy (sram_p, str, sizeof (str)); Copy the str character array into the SRAM
PRINTK (sram_p);
PRINTK ("\ n");
}
static int __init sram_init (void)
{
struct resource * RET;

The 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);
The results of the operation on the board are as follows:
/# Insmod Bin/sram.ko
Request for SRAM Mem region .....
hello,sram! ß This is the string in the print SRAM
/# Rmmod SRAM
Release SRAM Mem Region success!
SRAM is close
The experiment found that the SRAM can be accessed normally through the mapped address.
Finally, one of the reasons for using SRAM as an example is that by accessing the SRAM statically, we can know in advance the virtual address of the kernel after the SRAM mapping (computed by ioaddress). In this way, you can try to do some article on the SRAM. For example, write a memory allocation module to manage SRAM or other ways to put some critical data in the SRAM to run, which can improve the efficiency of some complex programs (SRAM faster than SDRAM), such as the audio and video codec in the process of using the larger buffer and so on.
This article comes from Chinaunix blog, if you look at the original point:
http://blog.chinaunix.net/u2/60011/showart_1101498.html

Linux kernel Access Peripheral i/o--dynamic Mapping (IOREMAP) and static Mapping (MAP_DESC) (reprinted)

Related Article

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.