blob: 0ba16f8f64299c11f4d949ae483fa0802508b7ec [file] [log] [blame]
Michael Brown81d92c62008-10-12 01:55:55 +01001/*
2 * Copyright (C) 2008 Michael Brown <mbrown@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
Michael Brownc44a1932009-05-01 15:41:06 +010019FILE_LICENCE ( GPL2_OR_LATER );
20
Michael Brown81d92c62008-10-12 01:55:55 +010021#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 */
34static EFI_CPU_IO_PROTOCOL *cpu_io;
35EFI_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 */
60static 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 */
80unsigned 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 ) {
Michael Brown3f856262008-11-18 19:45:44 -080091 DBG ( "EFI I/O read at %p failed: %s\n",
92 io_addr, efi_strerror ( efirc ) );
Michael Brown81d92c62008-10-12 01:55:55 +010093 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 */
106void 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 ) {
Michael Brown3f856262008-11-18 19:45:44 -0800117 DBG ( "EFI I/O write at %p failed: %s\n",
118 io_addr, efi_strerror ( efirc ) );
Michael Brown81d92c62008-10-12 01:55:55 +0100119 }
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 */
130void 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 ) {
Michael Brown3f856262008-11-18 19:45:44 -0800141 DBG ( "EFI I/O string read at %p failed: %s\n",
142 io_addr, efi_strerror ( efirc ) );
Michael Brown81d92c62008-10-12 01:55:55 +0100143 }
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 */
154void 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 ) {
Michael Brown3f856262008-11-18 19:45:44 -0800165 DBG ( "EFI I/O write at %p failed: %s\n",
166 io_addr, efi_strerror ( efirc ) );
Michael Brown81d92c62008-10-12 01:55:55 +0100167 }
168}
169
170/**
171 * Wait for I/O-mapped operation to complete
172 *
173 */
174static void efi_iodelay ( void ) {
175 /* Write to non-existent port. Probably x86-only. */
176 outb ( 0, 0x80 );
177}
178
179PROVIDE_IOAPI_INLINE ( efi, phys_to_bus );
180PROVIDE_IOAPI_INLINE ( efi, bus_to_phys );
181PROVIDE_IOAPI_INLINE ( efi, ioremap );
182PROVIDE_IOAPI_INLINE ( efi, iounmap );
183PROVIDE_IOAPI_INLINE ( efi, io_to_bus );
184PROVIDE_IOAPI_INLINE ( efi, readb );
185PROVIDE_IOAPI_INLINE ( efi, readw );
186PROVIDE_IOAPI_INLINE ( efi, readl );
187PROVIDE_IOAPI_INLINE ( efi, readq );
188PROVIDE_IOAPI_INLINE ( efi, writeb );
189PROVIDE_IOAPI_INLINE ( efi, writew );
190PROVIDE_IOAPI_INLINE ( efi, writel );
191PROVIDE_IOAPI_INLINE ( efi, writeq );
192PROVIDE_IOAPI_INLINE ( efi, inb );
193PROVIDE_IOAPI_INLINE ( efi, inw );
194PROVIDE_IOAPI_INLINE ( efi, inl );
195PROVIDE_IOAPI_INLINE ( efi, outb );
196PROVIDE_IOAPI_INLINE ( efi, outw );
197PROVIDE_IOAPI_INLINE ( efi, outl );
198PROVIDE_IOAPI_INLINE ( efi, insb );
199PROVIDE_IOAPI_INLINE ( efi, insw );
200PROVIDE_IOAPI_INLINE ( efi, insl );
201PROVIDE_IOAPI_INLINE ( efi, outsb );
202PROVIDE_IOAPI_INLINE ( efi, outsw );
203PROVIDE_IOAPI_INLINE ( efi, outsl );
204PROVIDE_IOAPI ( efi, iodelay, efi_iodelay );
205PROVIDE_IOAPI_INLINE ( efi, mb );