Home | History | Annotate | Download | only in efi
      1 /*
      2  * Copyright (C) 2008 Michael Brown <mbrown (at) fensystems.co.uk>.
      3  *
      4  * This program is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU General Public License as
      6  * published by the Free Software Foundation; either version 2 of the
      7  * License, or any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful, but
     10  * WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12  * General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program; if not, write to the Free Software
     16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     17  */
     18 
     19 FILE_LICENCE ( GPL2_OR_LATER );
     20 
     21 #include <assert.h>
     22 #include <gpxe/io.h>
     23 #include <gpxe/efi/efi.h>
     24 #include <gpxe/efi/Protocol/CpuIo.h>
     25 #include <gpxe/efi/efi_io.h>
     26 
     27 /** @file
     28  *
     29  * gPXE I/O API for EFI
     30  *
     31  */
     32 
     33 /** CPU I/O protocol */
     34 static EFI_CPU_IO_PROTOCOL *cpu_io;
     35 EFI_REQUIRE_PROTOCOL ( EFI_CPU_IO_PROTOCOL, &cpu_io );
     36 
     37 /** Maximum address that can be used for port I/O */
     38 #define MAX_PORT_ADDRESS 0xffff
     39 
     40 /**
     41  * Determine whether or not address is a port I/O address
     42  *
     43  * @v io_addr		I/O address
     44  * @v is_port		I/O address is a port I/O address
     45  */
     46 #define IS_PORT_ADDRESS(io_addr) \
     47 	( ( ( intptr_t ) (io_addr) ) <= MAX_PORT_ADDRESS )
     48 
     49 /**
     50  * Determine EFI CPU I/O width code
     51  *
     52  * @v size		Size of value
     53  * @ret width		EFI width code
     54  *
     55  * Someone at Intel clearly gets paid by the number of lines of code
     56  * they write.  No-one should ever be able to make I/O this
     57  * convoluted.  The EFI_CPU_IO_PROTOCOL_WIDTH enum is my favourite
     58  * idiocy.
     59  */
     60 static EFI_CPU_IO_PROTOCOL_WIDTH efi_width ( size_t size ) {
     61 	switch ( size ) {
     62 	case 1 :	return EfiCpuIoWidthFifoUint8;
     63 	case 2 :	return EfiCpuIoWidthFifoUint16;
     64 	case 4 :	return EfiCpuIoWidthFifoUint32;
     65 	case 8 :	return EfiCpuIoWidthFifoUint64;
     66 	default :
     67 		assert ( 0 );
     68 		/* I wonder what this will actually do... */
     69 		return EfiCpuIoWidthMaximum;
     70 	}
     71 }
     72 
     73 /**
     74  * Read from device
     75  *
     76  * @v io_addr		I/O address
     77  * @v size		Size of value
     78  * @ret data		Value read
     79  */
     80 unsigned long long efi_ioread ( volatile void *io_addr, size_t size ) {
     81 	EFI_CPU_IO_PROTOCOL_IO_MEM read;
     82 	unsigned long long data = 0;
     83 	EFI_STATUS efirc;
     84 
     85 	read = ( IS_PORT_ADDRESS ( io_addr ) ?
     86 		 cpu_io->Io.Read : cpu_io->Mem.Read );
     87 
     88 	if ( ( efirc = read ( cpu_io, efi_width ( size ),
     89 			      ( intptr_t ) io_addr, 1,
     90 			      ( void * ) &data ) ) != 0 ) {
     91 		DBG ( "EFI I/O read at %p failed: %s\n",
     92 		      io_addr, efi_strerror ( efirc ) );
     93 		return -1ULL;
     94 	}
     95 
     96 	return data;
     97 }
     98 
     99 /**
    100  * Write to device
    101  *
    102  * @v data		Value to write
    103  * @v io_addr		I/O address
    104  * @v size		Size of value
    105  */
    106 void efi_iowrite ( unsigned long long data, volatile void *io_addr,
    107 		   size_t size ) {
    108 	EFI_CPU_IO_PROTOCOL_IO_MEM write;
    109 	EFI_STATUS efirc;
    110 
    111 	write = ( IS_PORT_ADDRESS ( io_addr ) ?
    112 		  cpu_io->Io.Write : cpu_io->Mem.Write );
    113 
    114 	if ( ( efirc = write ( cpu_io, efi_width ( size ),
    115 			       ( intptr_t ) io_addr, 1,
    116 			       ( void * ) &data ) ) != 0 ) {
    117 		DBG ( "EFI I/O write at %p failed: %s\n",
    118 		      io_addr, efi_strerror ( efirc ) );
    119 	}
    120 }
    121 
    122 /**
    123  * String read from device
    124  *
    125  * @v io_addr		I/O address
    126  * @v data		Data buffer
    127  * @v size		Size of values
    128  * @v count		Number of values to read
    129  */
    130 void efi_ioreads ( volatile void *io_addr, void *data,
    131 		   size_t size, unsigned int count ) {
    132 	EFI_CPU_IO_PROTOCOL_IO_MEM read;
    133 	EFI_STATUS efirc;
    134 
    135 	read = ( IS_PORT_ADDRESS ( io_addr ) ?
    136 		 cpu_io->Io.Read : cpu_io->Mem.Read );
    137 
    138 	if ( ( efirc = read ( cpu_io, efi_width ( size ),
    139 			      ( intptr_t ) io_addr, count,
    140 			      ( void * ) data ) ) != 0 ) {
    141 		DBG ( "EFI I/O string read at %p failed: %s\n",
    142 		      io_addr, efi_strerror ( efirc ) );
    143 	}
    144 }
    145 
    146 /**
    147  * String write to device
    148  *
    149  * @v io_addr		I/O address
    150  * @v data		Data buffer
    151  * @v size		Size of values
    152  * @v count		Number of values to write
    153  */
    154 void efi_iowrites ( volatile void *io_addr, const void *data,
    155 		    size_t size, unsigned int count ) {
    156 	EFI_CPU_IO_PROTOCOL_IO_MEM write;
    157 	EFI_STATUS efirc;
    158 
    159 	write = ( IS_PORT_ADDRESS ( io_addr ) ?
    160 		 cpu_io->Io.Write : cpu_io->Mem.Write );
    161 
    162 	if ( ( efirc = write ( cpu_io, efi_width ( size ),
    163 			       ( intptr_t ) io_addr, count,
    164 			       ( void * ) data ) ) != 0 ) {
    165 		DBG ( "EFI I/O write at %p failed: %s\n",
    166 		      io_addr, efi_strerror ( efirc ) );
    167 	}
    168 }
    169 
    170 /**
    171  * Wait for I/O-mapped operation to complete
    172  *
    173  */
    174 static void efi_iodelay ( void ) {
    175 	/* Write to non-existent port.  Probably x86-only. */
    176 	outb ( 0, 0x80 );
    177 }
    178 
    179 PROVIDE_IOAPI_INLINE ( efi, phys_to_bus );
    180 PROVIDE_IOAPI_INLINE ( efi, bus_to_phys );
    181 PROVIDE_IOAPI_INLINE ( efi, ioremap );
    182 PROVIDE_IOAPI_INLINE ( efi, iounmap );
    183 PROVIDE_IOAPI_INLINE ( efi, io_to_bus );
    184 PROVIDE_IOAPI_INLINE ( efi, readb );
    185 PROVIDE_IOAPI_INLINE ( efi, readw );
    186 PROVIDE_IOAPI_INLINE ( efi, readl );
    187 PROVIDE_IOAPI_INLINE ( efi, readq );
    188 PROVIDE_IOAPI_INLINE ( efi, writeb );
    189 PROVIDE_IOAPI_INLINE ( efi, writew );
    190 PROVIDE_IOAPI_INLINE ( efi, writel );
    191 PROVIDE_IOAPI_INLINE ( efi, writeq );
    192 PROVIDE_IOAPI_INLINE ( efi, inb );
    193 PROVIDE_IOAPI_INLINE ( efi, inw );
    194 PROVIDE_IOAPI_INLINE ( efi, inl );
    195 PROVIDE_IOAPI_INLINE ( efi, outb );
    196 PROVIDE_IOAPI_INLINE ( efi, outw );
    197 PROVIDE_IOAPI_INLINE ( efi, outl );
    198 PROVIDE_IOAPI_INLINE ( efi, insb );
    199 PROVIDE_IOAPI_INLINE ( efi, insw );
    200 PROVIDE_IOAPI_INLINE ( efi, insl );
    201 PROVIDE_IOAPI_INLINE ( efi, outsb );
    202 PROVIDE_IOAPI_INLINE ( efi, outsw );
    203 PROVIDE_IOAPI_INLINE ( efi, outsl );
    204 PROVIDE_IOAPI ( efi, iodelay, efi_iodelay );
    205 PROVIDE_IOAPI_INLINE ( efi, mb );
    206