Linux System Management of IO ports and IO memory

Source: Internet
Author: User
Tags ranges

I. I/O port

A port is the address of a register in an interface circuit that can be accessed directly by the CPU. Almost every peripheral is made from a register on a read-write device. The CPU sends commands to the registers in the interface circuit through these addresses, namely ports, to read the status and transmit the data. Peripheral registers, also known as "I/O ports", typically include: control registers, status registers, and data registers, and the registers of a peripheral are usually continually addressed.

Second, IO memory

For example, on a PC you can plug in a graphics card, have 2MB of storage space, and maybe even have rom with executable code installed.


Three, io port and IO memory distinguish and contact
 
         How to distinguish between the two is related to hardware knowledge, X86 system, There are two address spaces: IO Space and memory space, while the CPUs of the RISC instruction system (such as ARM, PowerPC, etc.) typically implement only one physical address space, or memory space.
Memory Space: Memory address addressing range, 32-bit operating system memory space is 2 of 32 power, that is, 4G.
IO space: A space unique to X86, an address space separate from the memory space, and a 64K IO space for 32-bit X86.
 
IO port: Called IO port when register or memory is in IO space. General registers are also known as I/O ports, or I/O ports, this I/O port can be mapped in memory space, or can be mapped in I/O space.
 
IO memory: Called IO memory when the register or memory is in memory space.
 
 
 
Iv. address of physical addresses of peripheral IO ports
 
        There are two ways the CPU addresses the physical address of the peripheral IO port: One is I/O mapping (i/o-mapped) and the other is Memory mapping (memory-mapped). Depending on the CPU architecture, whichever is used.
 
1, unified address
 
The CPU of the RISC instruction system (e.g., PowerPC, m68k, ARM, etc.) typically implements only one physical addressing space (RAM). In this case, the physical address of the peripheral I/O port is mapped to the single physical address space of the CPU and becomes part of the memory. At this point, the CPU can access the peripheral I/O port as if it were accessing a memory unit without having to set up specialized peripheral I/O instructions.
 
       Unified Addressing is also referred to as the "I/O Memory" mode, the peripheral registers are located in "Memory Space" (many peripherals have their own memory, buffers, peripheral registers and memory collectively known as "i/ O Space ").
 
2, stand-alone addressing

Other architectures, such as X86, typically implement a separate address space for the peripheral, called the I/O address space or I/O port space. This is a different address space than the physical address space of the CPU's RAM, and the I/O ports of all the peripherals are addressed in this space. The CPU accesses the address unit (also the I/O port) in this space by setting up specialized I/O directives, such as X86 's in and out instructions. The I/O address space is usually small compared to the physical address space of the RAM, such as the x86 CPU's I/O space is only 64KB (0-0XFFFF). This is a major drawback of the I/O mapping approach.

Stand-alone addressing is also known as an I/O port, and the peripheral registers are located in the I/O (address) space.

3. Advantages and Disadvantages

The main advantages of stand-alone addressing are:
1), I/O port address does not occupy memory space, the use of special I/O instructions to operate the port, I/O instructions are short, fast execution speed.
2), and because of the special I/O instruction and memory access instructions are clearly different, so that the program in the I/O operation and memory operation level is clear, the program readability is strong.
3), at the same time, because of the use of specialized I/O instruction access port, and I/O port address and memory address is separate, so I/O port address and memory address can overlap, and not confused with each other.
4), decoding circuit is relatively simple (because the address space of the i/0 port is generally small, the address line is less).
The disadvantage is: only with specialized i/0 instructions, access to the port method is not as much as the method of accessing the memory.

Unified Addressing Benefits:
1), because access to the I/O device is the use of access memory instructions, so the instruction type is many, full-featured, which not only enables the access I/O port to achieve input/output operations, but also the port content of the arithmetic logic operation, shift and so on;
2), in addition, can give the port has a large address space, which for large-scale control system and data communication system is very meaningful.
The disadvantage of this method is that the port occupies the memory address space, the memory capacity is reduced, and the instruction length is longer than the specialized I/O instruction, and therefore the execution speed is slower.
Which one to use depends on the overall design of the system. You can also use both methods in one system, provided that you first support I/O independent addressing. Intel's x86 microprocessors support I/O stand-alone addresses because they have I/O directives in their instruction systems, and control signal pins that can differentiate between I/O access and memory access are set. And some microprocessor or microcontroller, in order to reduce the PIN, thereby reducing the chip occupied area, does not support I/O stand-alone address, can only use the memory unified addressing.

Five, Linux access to the IO port

For a given system, it is either a stand-alone address or a unified address, depending on the CPU's architecture. For example, PowerPC, m68k, such as the use of unified addressing, and X86, such as the use of independent addressing, there is the concept of IO space. Currently, most embedded microcontrollers such as ARM, PowerPC, etc. do not provide I/O space, only memory space, can be directly accessed with the address, pointer. However, for the Linux kernel, it may be used for different CPUs, so it must be considered in both ways, so it takes a new approach, the I/O map-based or memory-mapped I/O port is known as an I/O region (I/O regions), regardless of which way you use, Must first apply IO area: Request_resource (), release it at the end: Release_resource ().

Io region is an IO resource, so it can be described using the resource structure type.

There are 2 ways to access the IO port: I/O mapping mode (i/o-mapped), Memory Mapping (memory-mapped). The former path does not map to memory space, directly uses functions such as INTB ()/outb () to read and write IO ports, the latter Mmio is to first map IO ports to IO memory ("Memory Space"), and then access IO ports using functions that access IO memory.

1. I/O mapping method

Use the IO port operation function directly: when the device is on or the drive module is loaded, request the IO port area, then use INB (), OUTB () for port access, and finally release the IO port range when the device is shut down or the drive is unloaded.

In, out, ins, and outs assembly-language directives all have access to I/O ports. The following helper functions are included in the kernel to simplify this access:

INB (), INW (), INL ()
Read 1, 2, or 4 consecutive bytes from the I/O port, respectively. The suffix "B", "w", and "L" represent one byte (8 bits), one word (16 bits), and one long integer (32 bits), respectively.

Inb_p (), inw_p (), inl_p ()
Read 1, 2, or 4 consecutive bytes from the I/O port, and then execute a "dummy (dummy, empty instruction)" instruction to suspend the CPU.

Outb (), OUTW (), Outl ()
Write 1, 2, or 4 contiguous bytes to an I/O port, respectively.

Outb_p (), outw_p (), outl_p ()
Write 1, 2, or 4 consecutive bytes to an I/O port, and then execute a "dummy" instruction to halt the CPU.

INSB (), INSW (), inSL ()
Reads sequentially from the I/O port into a contiguous sequence of bytes in a group of 1, 2, or 4 bytes. The length of the byte sequence is given by the parameters of the function.

OUTSB (), OUTSW (), OUTSL ()
Writes to the I/O port a contiguous sequence of bytes in a group of 1, 2, or 4 bytes, respectively.

The process is as follows:


Although access to I/O ports is straightforward, it may not be as simple to detect which I/O ports have been allocated to I/O devices, especially for ISA bus-based systems. Typically, the I/O device driver needs to blindly write data to an I/O port in order to probe the hardware device, but the system crashes if other hardware devices already use the port. To prevent this from happening, the kernel must use "resources" to record the I/O ports assigned to each hardware device. A resource represents a part of an entity that is partially mutually exclusive to the device driver. Here, the resource represents a range of I/O port addresses. The information corresponding to each resource is stored in the resource data structure:


[Plain] 
1.struct Resource {   
 2.         Resource _size_t start;//The beginning of the resource range   
 3.          resource_size_t end;//The end of the resource range   
  4.         const char *name; The name of the resource owner   
 5.          unsigned long flags;//various flags   
 6.          struct Resource *parent, *sibling, *child;//Pointer to the father, brother, and child in the resource tree   
 7.};   
 

All the same resources are inserted into a tree data structure (father, brother, and child); For example, all resources that represent an I/O port address range are included in a tree with a root node of Ioport_resource. The child of the node is collected in a linked list, and its first element is pointed to by children. The sibling field points to the next node in the list.

Why use trees? For example, consider the I/O port address used by the IDE hard disk interface-for example, from 0xf000 to 0xf00f. Then, the Start field is 0xf000 and the end field is 0xf00f in the tree, and the general name of the controller is stored in the name fields. However, IDE device drivers need to remember additional information, that is, the IDE chain Master uses 0xf000 to 0xf007 sub-range, from the disk using 0xf008 to 0xf00f sub-ranges. To do this, the device driver inserts two child-scoped children into the resource corresponding to the entire range from 0xf000 to 0xf00f. In general, each node in the tree is definitely equivalent to a child range of the parent node's corresponding range. The root node of the I/O Port resource Tree (Ioport_resource) spans the entire I/O address space (from ports 0 to 65535).

Any device driver can use the following three functions, the parameters passed to them are the root node of the resource tree and the address of the new resource data structure to be inserted:

Request_resource ()//assign a given range to an I/O device.

Allocate_resource ()//Find out the available range for a given size and arrangement in the resource tree, and if so, assign this range to an I/O device (primarily used by the PCI device driver and can be configured with any port number and memory address on the motherboard).

Release_resource ()//releases the given range previously assigned to the I/O device.

The kernel also defines some of the shortcut functions applied to I/O ports for the above functions: Request_region () allocates a given range of I/O ports, and Release_region () frees the ranges previously allocated to I/O ports. The tree that is currently assigned to all I/O addresses for I/O devices can be obtained from the/proc/ioports file.

2. Memory Mapping method

The IO port is mapped to memory for access, when the device is open or the driver module is loaded, the IO port area is requested and mapped to memory using Ioport_map (), then the IO memory function is used for port access, and finally, the IO port is freed and the mapping is released when the device shuts down or the drive module is unloaded.

The prototype of the mapping function is:
void * IOREMAP (unsigned long phys_addr, unsigned long size, unsigned long flags);
With this function, you can remap the I/O port of size phys_addr start to a "memory space". These I/O ports can then be accessed on the address they return as if they were accessing I/O memory. Note, however, that the virtual address must also be assigned through Request_mem_region () before mapping.

When this mapping is no longer needed, you need to call the following function to undo:
void Iounmap (void *addr);

After the physical address of the device is mapped to the virtual address, although the addresses can be accessed directly through pointers, it is advisable to use the following set of functions of the Linux kernel to complete access to I/O Memory: • Read I/O memory
unsigned int ioread8 (void *addr);
unsigned int ioread16 (void *addr);
unsigned int ioread32 (void *addr);
Earlier versions of the functions corresponding to the above functions are (these functions are still supported in Linux 2.6):
unsigned readb (address);
unsigned readw (address);
unsigned readl (address);
• Write I/O memory
void Iowrite8 (U8 value, void *addr);
void Iowrite16 (U16 value, void *addr);
void Iowrite32 (u32 value, void *addr);
Earlier versions of the functions corresponding to the above functions are (these functions are still supported in Linux 2.6):
void Writeb (unsigned value, address);
void Writew (unsigned value, address);
void Writel (unsigned value, address);

The process is as follows:


VI. Access IO memory under Linux

Io Memory access method is: First call request_mem_region () request resources, and then the register address through IOREMAP () map to the virtual address of the kernel space, then the Linux device access to the programming interface to access these registers, after the completion of access, Use Iounmap () to release the requested virtual address and release the IO memory resources requested by the release_mem_region ().

struct Resource *requset_mem_region (unsigned long start, unsigned long Len,char *name);
This function requests the Len memory address (the virtual address between 3g~4g) from the kernel, where start is the I/O Physical address, and name is the device. Attention. If the assignment succeeds, it returns non-null, otherwise, NULL is returned.
In addition, you can view the memory range of the system to various devices through/PROC/IOMEM.

To release the requested I/O memory, you should use the Release_mem_region () function:
void Release_mem_region (unsigned long start, unsigned long len)

After requesting a set of I/O memory, call the Ioremap () function:
void * IOREMAP (unsigned long phys_addr, unsigned long size, unsigned long flags);
The meanings of three of these parameters are:
PHYS_ADDR: The same I/O Physical address as the parameter start in the Requset_mem_region function;
Size: The amount of space to map;
Flags: Flags to map the IO space and permissions;

Function: Maps an I/O address space to the virtual address space of the kernel (requested by release_mem_region ())

The process is as follows:


Vi. Ioremap and Ioport_map

The following specific look at the source code of IOPORT_MAP and Ioport_umap:


[Plain]
1.void __iomem *ioport_map (unsigned long port, unsigned int nr)
0.9
3. if (Port > Pio_mask)
4. return NULL;
5. Return (void __iomem *) (unsigned long) (port + pio_offset);
4.?
7.
8.void ioport_unmap (void __iomem *addr)
9.{
*/* Nothing to do * /
One.}

Ioport_map just adds Port Pio_offset (64k), and Ioport_unmap does nothing. This way the Portio 64k space is mapped to the 64k~128k of the virtual address, and the virtual address returned by Ioremap must be above 3G. The purpose of the IOPORT_MAP function is to attempt to provide a virtual address space that is consistent with Ioremap. Analysis of the source code of IOPORT_MAP () can be found that the so-called mapping to the memory space behavior is actually a developer to create an "illusion", and does not map to the kernel virtual address, just for the engineer to use the unified I/O Memory Access Interface Ioread8/iowrite8 ( ......) Access the I/O port.
Finally take a look at the source code of the IOREAD8, its implementation is to determine the virtual address, to distinguish between IO port and IO memory, and then use INB/OUTB and readb/writeb to read and write respectively.


[Plain]
1.unsigned int fastcall ioread8 (void __iomem *addr)
0.9
3. Io_cond (addr, return inb (Port), return readb (addr));
4.}
5.
6. #define VERIFY_PIO (Port) bug_on ((Port & ~pio_mask)! = Pio_offset)
7. #define Io_cond (addr, Is_pio, Is_mmio) do {\
8. unsigned long port = (unsigned long __force) addr; \
9. If (Port < pio_reserved) {\
Verify_pio (port); \
Port &= Pio_mask; \
Is_pio; \
.} else {\
Is_mmio; \
15.} \
+.} while (0)
17.
18. Expand:
19.unsigned int fastcall ioread8 (void __iomem *addr)
20.{
unsigned long port = (unsigned long __force) addr;
if (Port < 0x40000ul) {
BUG_ON (Port & ~pio_mask)! = Pio_offset);
Port &= Pio_mask;
return INB (port);&NBSP;
 26.   }else{ &NBSP;
 27.        return readb (addr);  < Span class= "Apple-converted-space" >&NBSP;
 28.   }  &NBSP;
 29.}  &NBSP;
 
Vii. summary
 
          Peripheral IO Register address independently addressed CPU, this should be called the Peripheral IO Register IO port, Access IO Register can be mapped to the virtual address space through Ioport_map, but in fact this is a developer to create an "illusion", does not map to the kernel virtual address, simply to access the IO register using the same interface as IO memory, or to access the IO register directly using the in/out instruction.
 
          For example: the Intel x86 platform uses a technology called Memory Mapping (MMIO). The technology is part of the PCI specification, the IO device port is mapped to memory space, and the CPU accesses the IO port as if it were accessing memory after mapping.
 
          Peripheral IO Register address of the CPU, which should be called the Peripheral IO Register as IO memory, The Access IO Register can be mapped to the virtual address space by Ioremap and then accessed using the Read/write interface.

Viii. Ioremap and Ioport_map

The IO Register (memory) is mapped to the IO address space (portio) and mapped to the memory address space (MMIO) in two ways of addressing the address.
Before linux-2.6, the former uses Inb/outb (...). Access, the latter through Readb/writeb (...) To access (you must use Ioremap to map the IO Physical address to the virtual address before access).

The IOREMAP function acts as described above, which is to map the IO Physical address to a virtual address so that the READB/WRITEB can access the mapped address. The Mmio mapped IO memory, whose physical address is already in the 4G address space, is the same as the memory addressing method. So Ioremap's role is actually to create a page table for this physical address, returning the virtual address that the driver can access.

Access to I/O space


1. The process of accessing I/O memory (I/O memory must be mapped to memory space) is:

Request_mem_region (), Ioremap (), Ioread8 ()/iowrite8 (), Iounmap (), Release_mem_region ().

As mentioned earlier, IO memory is a unified address concept, for unified addressing, IO address space is a part of physical memory, for programming, we can only operate virtual memory, so, the first step of access is to map the physical address of the device to the virtual address, Linux2.6 under Ioremap (): void *ioremap (unsigned long offset, unsigned long size); Ioremap () is used to map the physical address of the IO resource to the kernel virtual address space (3GB-4GB), and the parameter addr is a pointer to the kernel virtual address. We can then access these addresses directly through pointers, but can also be read and written using a set of functions of the Linux kernel: Ioread8 (), Iowrite16 (), Ioread8_rep (), Iowrite8_rep () ...
2. Access the I/O port

There are 2 ways to access the IO port ( for stand-alone addressing system systems ):
I/O mapping mode (i/o-mapped), memory-mapped mode (memory-mapped).

The previous path does not map to memory space and directly uses functions such as INTB ()/outb () to read and write IO ports;

The latter Mmio is to first map the IO port to IO memory ("Memory space") and then access the IO port using a function that accesses IO memory. void Ioport_map (unsigned long port, unsigned int count); With this function, you can map the count of successive IO ports starting with Port to a "memory space", and then you can access these IO ports at the address they return as if they were accessing IO memory.

Linux System Management of IO ports and IO 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.