#ifndef _IPXE_X86_IO_H #define _IPXE_X86_IO_H /** @file * * iPXE I/O API for x86 * * i386 uses direct pointer dereferences for accesses to memory-mapped * I/O space, and the inX/outX instructions for accesses to * port-mapped I/O space. * * 64-bit atomic accesses (readq() and writeq()) use MMX instructions, * and will crash original Pentium and earlier CPUs. Fortunately, no * hardware that requires atomic 64-bit accesses will physically fit * into a machine with such an old CPU anyway. */ FILE_LICENCE ( GPL2_OR_LATER ); #ifdef IOAPI_X86 #define IOAPI_PREFIX_x86 #else #define IOAPI_PREFIX_x86 __x86_ #endif /* * Memory space mappings * */ /* * Physical<->Bus and Bus<->I/O address mappings * */ static inline __always_inline unsigned long IOAPI_INLINE ( x86, phys_to_bus ) ( unsigned long phys_addr ) { return phys_addr; } static inline __always_inline unsigned long IOAPI_INLINE ( x86, bus_to_phys ) ( unsigned long bus_addr ) { return bus_addr; } static inline __always_inline void * IOAPI_INLINE ( x86, ioremap ) ( unsigned long bus_addr, size_t len __unused ) { return phys_to_virt ( bus_addr ); } static inline __always_inline void IOAPI_INLINE ( x86, iounmap ) ( volatile const void *io_addr __unused ) { /* Nothing to do */ } static inline __always_inline unsigned long IOAPI_INLINE ( x86, io_to_bus ) ( volatile const void *io_addr ) { return virt_to_phys ( io_addr ); } /* * MMIO reads and writes up to 32 bits * */ #define X86_READX( _api_func, _type ) \ static inline __always_inline _type \ IOAPI_INLINE ( x86, _api_func ) ( volatile _type *io_addr ) { \ return *io_addr; \ } X86_READX ( readb, uint8_t ); X86_READX ( readw, uint16_t ); X86_READX ( readl, uint32_t ); #define X86_WRITEX( _api_func, _type ) \ static inline __always_inline void \ IOAPI_INLINE ( x86, _api_func ) ( _type data, \ volatile _type *io_addr ) { \ *io_addr = data; \ } X86_WRITEX ( writeb, uint8_t ); X86_WRITEX ( writew, uint16_t ); X86_WRITEX ( writel, uint32_t ); /* * PIO reads and writes up to 32 bits * */ #define X86_INX( _insn_suffix, _type, _reg_prefix ) \ static inline __always_inline _type \ IOAPI_INLINE ( x86, in ## _insn_suffix ) ( volatile _type *io_addr ) { \ _type data; \ __asm__ __volatile__ ( "in" #_insn_suffix " %w1, %" _reg_prefix "0" \ : "=a" ( data ) : "Nd" ( io_addr ) ); \ return data; \ } \ static inline __always_inline void \ IOAPI_INLINE ( x86, ins ## _insn_suffix ) ( volatile _type *io_addr, \ _type *data, \ unsigned int count ) { \ unsigned int discard_D; \ __asm__ __volatile__ ( "rep ins" #_insn_suffix \ : "=D" ( discard_D ) \ : "d" ( io_addr ), "c" ( count ), \ "0" ( data ) ); \ } X86_INX ( b, uint8_t, "b" ); X86_INX ( w, uint16_t, "w" ); X86_INX ( l, uint32_t, "k" ); #define X86_OUTX( _insn_suffix, _type, _reg_prefix ) \ static inline __always_inline void \ IOAPI_INLINE ( x86, out ## _insn_suffix ) ( _type data, \ volatile _type *io_addr ) { \ __asm__ __volatile__ ( "out" #_insn_suffix " %" _reg_prefix "0, %w1" \ : : "a" ( data ), "Nd" ( io_addr ) ); \ } \ static inline __always_inline void \ IOAPI_INLINE ( x86, outs ## _insn_suffix ) ( volatile _type *io_addr, \ const _type *data, \ unsigned int count ) { \ unsigned int discard_S; \ __asm__ __volatile__ ( "rep outs" #_insn_suffix \ : "=S" ( discard_S ) \ : "d" ( io_addr ), "c" ( count ), \ "0" ( data ) ); \ } X86_OUTX ( b, uint8_t, "b" ); X86_OUTX ( w, uint16_t, "w" ); X86_OUTX ( l, uint32_t, "k" ); /* * Slow down I/O * */ static inline __always_inline void IOAPI_INLINE ( x86, iodelay ) ( void ) { __asm__ __volatile__ ( "outb %al, $0x80" ); } /* * Memory barrier * */ static inline __always_inline void IOAPI_INLINE ( x86, mb ) ( void ) { __asm__ __volatile__ ( "lock; addl $0, 0(%%esp)" : : : "memory" ); } #endif /* _IPXE_X86_IO_H */