2
* apei-io.c - APEI IO memory pre-mapping/post-unmapping and access
4
* Copyright (C) 2009-2010, Intel Corp.
5
* Author: Huang Ying <ying.huang@intel.com>
6
* Ported by: Liu, Jinsong <jinsong.liu@intel.com>
8
* This program is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU General Public License version
10
* 2 as published by the Free Software Foundation.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
#include <xen/kernel.h>
23
#include <xen/errno.h>
24
#include <xen/delay.h>
25
#include <xen/string.h>
26
#include <xen/xmalloc.h>
27
#include <xen/types.h>
28
#include <xen/spinlock.h>
31
#include <xen/prefetch.h>
32
#include <asm/fixmap.h>
34
#include <acpi/acpi.h>
35
#include <acpi/apei.h>
37
static LIST_HEAD(apei_iomaps);
39
* Used for mutual exclusion between writers of apei_iomaps list, for
40
* synchronization between readers and writer.
42
static DEFINE_SPINLOCK(apei_iomaps_lock);
45
struct list_head list;
51
static struct apei_iomap *__apei_find_iomap(paddr_t paddr,
54
struct apei_iomap *map;
56
list_for_each_entry(map, &apei_iomaps, list) {
57
if (map->paddr + map->size >= paddr + size &&
64
static void __iomem *__apei_ioremap_fast(paddr_t paddr,
67
struct apei_iomap *map;
69
map = __apei_find_iomap(paddr, size);
71
return map->vaddr + (paddr - map->paddr);
76
static int apei_range_nr;
78
static void __iomem *__init apei_range_map(paddr_t paddr, unsigned long size)
83
pg = ((((paddr + size -1) & PAGE_MASK)
84
- (paddr & PAGE_MASK)) >> PAGE_SHIFT) + 1;
85
if (apei_range_nr + pg > FIX_APEI_RANGE_MAX)
88
start_nr = apei_range_nr + pg -1;
89
for (i = 0; i < pg; i++) {
90
cur_nr = start_nr - i;
91
set_fixmap_nocache(FIX_APEI_RANGE_BASE + cur_nr,
92
paddr + (i << PAGE_SHIFT));
96
return (void __iomem *)fix_to_virt(FIX_APEI_RANGE_BASE + start_nr);
100
* Used to pre-map the specified IO memory area. First try to find
101
* whether the area is already pre-mapped, if it is, return; otherwise,
102
* do the real map, and add the mapping into apei_iomaps list.
104
void __iomem *__init apei_pre_map(paddr_t paddr, unsigned long size)
107
struct apei_iomap *map;
110
spin_lock_irqsave(&apei_iomaps_lock, flags);
111
vaddr = __apei_ioremap_fast(paddr, size);
112
spin_unlock_irqrestore(&apei_iomaps_lock, flags);
116
map = xmalloc(struct apei_iomap);
120
vaddr = apei_range_map(paddr, size);
126
INIT_LIST_HEAD(&map->list);
127
map->paddr = paddr & PAGE_MASK;
128
map->size = (((paddr + size + PAGE_SIZE -1) & PAGE_MASK)
129
- (paddr & PAGE_MASK));
132
spin_lock_irqsave(&apei_iomaps_lock, flags);
133
list_add_tail(&map->list, &apei_iomaps);
134
spin_unlock_irqrestore(&apei_iomaps_lock, flags);
136
return map->vaddr + (paddr - map->paddr);
140
* Used to post-unmap the specified IO memory area.
142
static void __init apei_post_unmap(paddr_t paddr, unsigned long size)
144
struct apei_iomap *map;
147
spin_lock_irqsave(&apei_iomaps_lock, flags);
148
map = __apei_find_iomap(paddr, size);
152
list_del(&map->list);
153
spin_unlock_irqrestore(&apei_iomaps_lock, flags);
158
/* In NMI handler, should set silent = 1 */
159
static int apei_check_gar(struct acpi_generic_address *reg,
160
u64 *paddr, int silent)
164
width = reg->bit_width;
165
space_id = reg->space_id;
166
/* Handle possible alignment issues */
167
memcpy(paddr, ®->address, sizeof(*paddr));
171
"Invalid physical address in GAR\n");
175
if ((width != 8) && (width != 16) && (width != 32) && (width != 64)) {
178
"Invalid bit width in GAR\n");
182
if (space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY &&
183
space_id != ACPI_ADR_SPACE_SYSTEM_IO) {
186
"Invalid address space type in GAR\n");
193
/* Pre-map, working on GAR */
194
int __init apei_pre_map_gar(struct acpi_generic_address *reg)
200
if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
203
rc = apei_check_gar(reg, &paddr, 0);
207
vaddr = apei_pre_map(paddr, reg->bit_width / 8);
214
/* Post-unmap, working on GAR */
215
int __init apei_post_unmap_gar(struct acpi_generic_address *reg)
220
if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_MEMORY)
223
rc = apei_check_gar(reg, &paddr, 0);
227
apei_post_unmap(paddr, reg->bit_width / 8);
232
static int apei_read_mem(u64 paddr, u64 *val, u32 width)
237
addr = __apei_ioremap_fast(paddr, width);
249
tmpval = (u64)readl(addr);
250
tmpval |= ((u64)readl(addr+4)) << 32;
260
static int apei_write_mem(u64 paddr, u64 val, u32 width)
265
addr = __apei_ioremap_fast(paddr, width);
278
writel(tmpval, addr);
279
tmpval = (u32)(val >> 32);
280
writel(tmpval, addr+4);
289
int apei_read(u64 *val, struct acpi_generic_address *reg)
294
rc = apei_check_gar(reg, &paddr, 1);
300
/* currently all erst implementation take bit_width as real range */
301
switch (reg->space_id) {
302
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
303
return apei_read_mem(paddr, val, reg->bit_width);
304
case ACPI_ADR_SPACE_SYSTEM_IO:
305
return acpi_os_read_port(paddr, (u32 *)val, reg->bit_width);
311
int apei_write(u64 val, struct acpi_generic_address *reg)
316
rc = apei_check_gar(reg, &paddr, 1);
320
switch (reg->space_id) {
321
case ACPI_ADR_SPACE_SYSTEM_MEMORY:
322
return apei_write_mem(paddr, val, reg->bit_width);
323
case ACPI_ADR_SPACE_SYSTEM_IO:
324
return acpi_os_write_port(paddr, val, reg->bit_width);