Access the peripheral I/O ports by the Linux driver

Source: Internet
Author: User
Tags preg

How does the Linux Device Driver access the peripheral I/O port (Register)

Almost every type of peripherals is carried out by reading and writing registers on the device, usually including control registers, status registers, and data registers. The peripheral registers are usually continuously compiled. Depending on the CPU architecture, the CPU can address the I/O port in two ways:
(1) I/O ing (I/O-mapped)
Typically, x86 processors provide a dedicated address space for peripherals, which is called "I/O address space" or "I/O port space ", the CPU uses dedicated I/O commands (such as x86 In and out commands) to access the address units in this space.
(2) memory ing (memory-mapped)
Generally, the CPU (such as arm and PowerPC) of a CPU of a server-defined identity management system only implements one physical address space, and the peripheral I/O port is a part of the memory. In this case, the CPU can access the peripheral I/O port as it accesses a memory unit, without the need to set up a dedicated peripheral I/O command.
However, the hardware implementation differences between the two are completely transparent to the software, driver developers can regard memory ing I/O Ports and peripheral memory as "I/O memory" resources.
 
Generally, when the system is running, the physical address of the peripheral I/O memory resources is known and determined by the hardware design. However, the CPU generally does not provide physical addresses for these known peripheral I/O memory resources.
Predefine the virtual address range. drivers cannot directly access I/O memory resources through physical addresses. Instead, they must be mapped to the core virtual address space (through the page table ), then you can obtain the core according to the ing.
The heart virtual address range accesses these I/O memory resources through the inner access command. Linux declares the ioremap () function in the IO. h header file to map the physical addresses of I/O memory resources
In the core virtual address space (3 GB-4 GB), the prototype is as follows:
Void * ioremap (unsigned long phys_addr, unsigned long size, unsigned long flags );
The iounmap function is used to cancel the Iing of ioremap (). The prototype is as follows:
Void iounmap (void * ADDR );
Both functions are implemented in the mm/ioremap. c file.
 
After ing the physical address of the I/O memory resources into the core virtual address, theoretically we can directly read and write the I/O memory resources like the read/write Ram. To ensure cross-platform portability of the driver
In Linux, we should use a specific function to access I/O memory resources, instead of a pointer to the core virtual address. For example, on the X86 platform, read/write I/O functions are as follows:
# Define readb (ADDR) (* (volatile unsigned char *) _ io_virt (ADDR ))
# Define readw (ADDR) (* (volatile unsigned short *) _ io_virt (ADDR ))
# Define readl (ADDR) (* (volatile unsigned int *) _ io_virt (ADDR ))
# Define writeb (B, ADDR) (* (volatile unsigned char *) _ io_virt (ADDR) = (B ))
# Define writew (B, ADDR) (* (volatile unsigned short *) _ io_virt (ADDR) = (B ))
# Define writel (B, ADDR) (* (volatile unsigned int *) _ io_virt (ADDR) = (B ))
# Define memset_io (a, B, c) memset (_ io_virt (a), (B), (c ))
# Define memcpy_fromio (a, B, c) memcpy (a) ,__ io_virt (B), (c ))
# Define memcpy_toio (a, B, c) memcpy (_ io_virt (a), (B), (c ))
Finally, we need to emphasize the implementation of MMAP functions in the driver. MMAP is used to map a device, which means that an address in the user space is associated with the device memory. This allows the program to read or write data within the allocated address range, it is actually access to the device.
In the Linux source code, I searched for text containing "ioremap" and found that there were quite a few ioremap objects. Therefore, the author tries to find the physical address translation from the I/O operation to the real location of the virtual address, and finds that Linux has a statement to replace ioremap, but this conversion process is indispensable.
For example, we extract a short segment from the RTC (Real-time clock) Driver of the ARM chip S3C2410:
Static void get_rtc_time (INT Alm, struct rtc_time * rtc_tm)
{
Spin_lock_irq (& rtc_lock );
If (ALM = 1 ){
Rtc_tm-> tm_year = (unsigned char) almyear & msk_rtcyear;
Rtc_tm-> tm_mon = (unsigned char) almmon & msk_rtcmon;
Rtc_tm-> tm_mday = (unsigned char) almday & msk_rtcday;
Rtc_tm-> tm_hour = (unsigned char) almhour & msk_rtchour;
Rtc_tm-> tm_min = (unsigned char) almmin & msk_rtcmin;
Rtc_tm-> tm_sec = (unsigned char) almsec & msk_rtcsec;
}
Else {
Read_rtc_bcd_time:
Rtc_tm-> tm_year = (unsigned char) bcdyear & msk_rtcyear;
Rtc_tm-> tm_mon = (unsigned char) bcdmon & msk_rtcmon;
Rtc_tm-> tm_mday = (unsigned char) bcdday & msk_rtcday;
Rtc_tm-> tm_hour = (unsigned char) bcdhour & msk_rtchour;
Rtc_tm-> tm_min = (unsigned char) bcdmin & msk_rtcmin;
Rtc_tm-> tm_sec = (unsigned char) bcdsec & msk_rtcsec;
If (rtc_tm-> tm_sec = 0 ){
/* Re-read all BCD registers in case of bcdsec is 0.
See RTC section at the manual for more info .*/
Goto read_rtc_bcd_time;
}
}
Spin_unlock_irq (& rtc_lock );
Bcd_to_bin (rtc_tm-> tm_year );
Bcd_to_bin (rtc_tm-> tm_mon );
Bcd_to_bin (rtc_tm-> tm_mday );
Bcd_to_bin (rtc_tm-> tm_hour );
Bcd_to_bin (rtc_tm-> tm_min );
Bcd_to_bin (rtc_tm-> tm_sec );
/* The epoch of tm_year is 1900 */
Rtc_tm-> tm_year + = rtc_leap_year-1900;
/* Tm_mon starts at 0, but RTC month starts at 1 */
Rtc_tm-> tm_mon --;
}
I/O operations seem to be performed on registers defined by almyear, almmon, and almday. Why are these macros defined?
# Define almday brtc (0x60)
# Define almmon brtc (0x64)
# Define almyear brtc (0x68)
With the help of macro brtc, this macro is defined:
# Define brtc (NB) _ reg (0x57000000 + (NB ))
The macro _ reg is used, and _ reg is defined:
# DEFINE _ reg (x) io_p2v (X)
The final io_p2v is the place where the real "play" virtual address and physical address are converted:
# Define io_p2v (x) | 0xa0000000)
There is a _ preg corresponding to _ reg:
# DEFINE _ preg (x) io_v2p (X)
There is an io_v2p corresponding to io_p2v:
# Define io_v2p (x )&~ 0xa0000000)
It can be seen that ioremap is secondary. The key issue is whether there is a conversion between virtual addresses and physical addresses!
The following program retains a piece of memory at startup, then uses ioremap to map it to the kernel virtual space, and maps it to the User Virtual space with remap_page_range, both the kernel and the user can access it. If the memory initialization string "ABCD" is added to the virtual address in the kernel, you can read it from the virtual address:
/************ Mmap_ioremap.c **************/
# Include
# Include
# Include
# Include
# Include/* For mem_map _ (un) Reserve */
# Include/* for each _to_phys */
# Include/* For kmalloc and kfree */
Module_parm (mem_start, "I ");
Module_parm (mem_size, "I ");
Static int mem_start = 101, mem_size = 10;
Static char * reserve_virt_addr;
Static int Major;
Int mmapdrv_open (struct inode * inode, struct file * file );
Int mmapdrv_release (struct inode * inode, struct file * file );
Int mmapdrv_mmap (struct file * file, struct vm_area_struct * VMA );
Static struct file_operations mmapdrv_fops =
{
Owner: this_module, MMAP: mmapdrv_mmap, open: mmapdrv_open, release:
Mmapdrv_release,
};
Int init_module (void)
{
If (Major = register_chrdev (0, "mmapdrv", & mmapdrv_fops) vm_pgoff vm_end-VMA-> vm_start;
If (size> mem_size * 1024*1024)
{
Printk ("size too big/N ");
Return (-enxio );
}
Offset = offset + mem_start * 1024*1024;
/* We do not want to have this area swapped out, lock it */
VMA-> vm_flags | = vm_locked;
If (remap_page_range (VMA, VMA-> vm_start, offset, size, page_shared ))
{
Printk ("remap page range failed/N ");
Return-enxio;
}
Return (0 );
}
The remap_page_range function constructs a new page table for ing a physical address to map the kernel space to the user space. Its prototype is as follows:
Int remap_page_range (vma_area_struct * VMA, unsigned long from, unsigned long to, unsigned long size, pgprot_tprot );
The most typical example of MMAP is the driver of the display card. ing the display space directly from the kernel to the user space will provide the Read and Write efficiency of the display memory.

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.