efi_io.c revision 76d05dc695b06c4e987bb8078f78032441e1430c
1/*
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
19FILE_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 */
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 ) {
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 */
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 ) {
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 */
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 ) {
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 */
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 ) {
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 */
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 );
206