[RFT PATCH v3 09/27] docs: driver-api: device-io: Document I/O access functions

Hector Martin marcan at marcan.st
Thu Mar 4 21:38:44 GMT 2021


From: Arnd Bergmann <arnd at arndb.de>

This adds more detailed descriptions of the various read/write
primitives available for use with I/O memory/ports.

Signed-off-by: Arnd Bergmann <arnd at arndb.de>
Signed-off-by: Hector Martin <marcan at marcan.st>
---
 Documentation/driver-api/device-io.rst | 138 +++++++++++++++++++++++++
 1 file changed, 138 insertions(+)

diff --git a/Documentation/driver-api/device-io.rst b/Documentation/driver-api/device-io.rst
index 764963876d08..b20864b3ddc7 100644
--- a/Documentation/driver-api/device-io.rst
+++ b/Documentation/driver-api/device-io.rst
@@ -146,6 +146,144 @@ There are also equivalents to memcpy. The ins() and
 outs() functions copy bytes, words or longs to the given
 port.
 
+__iomem pointer tokens
+======================
+
+The data type for an MMIO address is an ``__iomem`` qualified pointer, such as
+``void __iomem *reg``. On most architectures it is a regular pointer that
+points to a virtual memory address and can be offset or dereferenced, but in
+portable code, it must only be passed from and to functions that explicitly
+operated on an ``__iomem`` token, in particular the ioremap() and
+readl()/writel() functions. The 'sparse' semantic code checker can be used to
+verify that this is done correctly.
+
+While on most architectures, ioremap() creates a page table entry for an
+uncached virtual address pointing to the physical MMIO address, some
+architectures require special instructions for MMIO, and the ``__iomem`` pointer
+just encodes the physical address or an offsettable cookie that is interpreted
+by readl()/writel().
+
+Differences between I/O access functions
+========================================
+
+readq(), readl(), readw(), readb(), writeq(), writel(), writew(), writeb()
+
+  These are the most generic accessors, providing serialization against other
+  MMIO accesses and DMA accesses as well as fixed endianness for accessing
+  little-endian PCI devices and on-chip peripherals. Portable device drivers
+  should generally use these for any access to ``__iomem`` pointers.
+
+  Note that posted writes are not strictly ordered against a spinlock, see
+  Documentation/driver-api/io_ordering.rst.
+
+readq_relaxed(), readl_relaxed(), readw_relaxed(), readb_relaxed(),
+writeq_relaxed(), writel_relaxed(), writew_relaxed(), writeb_relaxed()
+
+  On architectures that require an expensive barrier for serializing against
+  DMA, these "relaxed" versions of the MMIO accessors only serialize against
+  each other, but contain a less expensive barrier operation. A device driver
+  might use these in a particularly performance sensitive fast path, with a
+  comment that explains why the usage in a specific location is safe without
+  the extra barriers.
+
+  See memory-barriers.txt for a more detailed discussion on the precise ordering
+  guarantees of the non-relaxed and relaxed versions.
+
+ioread64(), ioread32(), ioread16(), ioread8(),
+iowrite64(), iowrite32(), iowrite16(), iowrite8()
+
+  These are an alternative to the normal readl()/writel() functions, with almost
+  identical behavior, but they can also operate on ``__iomem`` tokens returned
+  for mapping PCI I/O space with pci_iomap() or ioport_map(). On architectures
+  that require special instructions for I/O port access, this adds a small
+  overhead for an indirect function call implemented in lib/iomap.c, while on
+  other architectures, these are simply aliases.
+
+ioread64be(), ioread32be(), ioread16be()
+iowrite64be(), iowrite32be(), iowrite16be()
+
+  These behave in the same way as the ioread32()/iowrite32() family, but with
+  reversed byte order, for accessing devices with big-endian MMIO registers.
+  Device drivers that can operate on either big-endian or little-endian
+  registers may have to implement a custom wrapper function that picks one or
+  the other depending on which device was found.
+
+  Note: On some architectures, the normal readl()/writel() functions
+  traditionally assume that devices are the same endianness as the CPU, while
+  using a hardware byte-reverse on the PCI bus when running a big-endian kernel.
+  Drivers that use readl()/writel() this way are generally not portable, but
+  tend to be limited to a particular SoC.
+
+hi_lo_readq(), lo_hi_readq(), hi_lo_readq_relaxed(), lo_hi_readq_relaxed(),
+ioread64_lo_hi(), ioread64_hi_lo(), ioread64be_lo_hi(), ioread64be_hi_lo(),
+hi_lo_writeq(), lo_hi_writeq(), hi_lo_writeq_relaxed(), lo_hi_writeq_relaxed(),
+iowrite64_lo_hi(), iowrite64_hi_lo(), iowrite64be_lo_hi(), iowrite64be_hi_lo()
+
+  Some device drivers have 64-bit registers that cannot be accessed atomically
+  on 32-bit architectures but allow two consecutive 32-bit accesses instead.
+  Since it depends on the particular device which of the two halves has to be
+  accessed first, a helper is provided for each combination of 64-bit accessors
+  with either low/high or high/low word ordering. A device driver must include
+  either <linux/io-64-nonatomic-lo-hi.h> or <linux/io-64-nonatomic-hi-lo.h> to
+  get the function definitions along with helpers that redirect the normal
+  readq()/writeq() to them on architectures that do not provide 64-bit access
+  natively.
+
+__raw_readq(), __raw_readl(), __raw_readw(), __raw_readb(),
+__raw_writeq(), __raw_writel(), __raw_writew(), __raw_writeb()
+
+  These are low-level MMIO accessors without barriers or byteorder changes and
+  architecture specific behavior. Accesses are usually atomic in the sense that
+  a four-byte __raw_readl() does not get split into individual byte loads, but
+  multiple consecutive accesses can be combined on the bus. In portable code, it
+  is only safe to use these to access memory behind a device bus but not MMIO
+  registers, as there are no ordering guarantees with regard to other MMIO
+  accesses or even spinlocks. The byte order is generally the same as for normal
+  memory, so unlike the other functions, these can be used to copy data between
+  kernel memory and device memory.
+
+inl(), inw(), inb(), outl(), outw(), outb()
+
+  PCI I/O port resources traditionally require separate helpers as they are
+  implemented using special instructions on the x86 architecture. On most other
+  architectures, these are mapped to readl()/writel() style accessors
+  internally, usually pointing to a fixed area in virtual memory. Instead of an
+  ``__iomem`` pointer, the address is a 32-bit integer token to identify a port
+  number. PCI requires I/O port access to be non-posted, meaning that an outb()
+  must complete before the following code executes, while a normal writeb() may
+  still be in progress. On architectures that correctly implement this, I/O port
+  access is therefore ordered against spinlocks. Many non-x86 PCI host bridge
+  implementations and CPU architectures however fail to implement non-posted I/O
+  space on PCI, so they can end up being posted on such hardware.
+
+  In some architectures, the I/O port number space has a 1:1 mapping to
+  ``__iomem`` pointers, but this is not recommended and device drivers should
+  not rely on that for portability. Similarly, an I/O port number as described
+  in a PCI base address register may not correspond to the port number as seen
+  by a device driver. Portable drivers need to read the port number for the
+  resource provided by the kernel.
+
+  There are no direct 64-bit I/O port accessors, but pci_iomap() in combination
+  with ioread64/iowrite64 can be used instead.
+
+inl_p(), inw_p(), inb_p(), outl_p(), outw_p(), outb_p()
+
+  On ISA devices that require specific timing, the _p versions of the I/O
+  accessors add a small delay. On architectures that do not have ISA buses,
+  these are aliases to the normal inb/outb helpers.
+
+readsq, readsl, readsw, readsb
+writesq, writesl, writesw, writesb
+ioread64_rep, ioread32_rep, ioread16_rep, ioread8_rep
+iowrite64_rep, iowrite32_rep, iowrite16_rep, iowrite8_rep
+insl, insw, insb, outsl, outsw, outsb
+
+  These are helpers that access the same address multiple times, usually to copy
+  data between kernel memory byte stream and a FIFO buffer. Unlike the normal
+  MMIO accessors, these do not perform a byteswap on big-endian kernels, so the
+  first byte in the FIFO register corresponds to the first byte in the memory
+  buffer regardless of the architecture.
+
 Public Functions Provided
 =========================
 
-- 
2.30.0




More information about the linux-arm-kernel mailing list