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