summaryrefslogtreecommitdiffstats
path: root/arch/sh/mm/ioremap_fixed.c
blob: 551b513e8fce71cc562aab1408fac23e72b7df55 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 * Re-map IO memory to kernel address space so that we can access it.
 *
 * These functions should only be used when it is necessary to map a
 * physical address space into the kernel address space before ioremap()
 * can be used, e.g. early in boot before paging_init().
 *
 * Copyright (C) 2009  Matt Fleming
 */

#include <linux/vmalloc.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <linux/bootmem.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <asm/fixmap.h>
#include <asm/page.h>
#include <asm/pgalloc.h>
#include <asm/addrspace.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
#include <asm/mmu.h>
#include <asm/mmu_context.h>

struct ioremap_map {
	void __iomem *addr;
	unsigned long size;
	unsigned long fixmap_addr;
};

static struct ioremap_map ioremap_maps[FIX_N_IOREMAPS];

void __init ioremap_fixed_init(void)
{
	struct ioremap_map *map;
	int i;

	for (i = 0; i < FIX_N_IOREMAPS; i++) {
		map = &ioremap_maps[i];
		map->fixmap_addr = __fix_to_virt(FIX_IOREMAP_BEGIN + i);
	}
}

void __init __iomem *
ioremap_fixed(resource_size_t phys_addr, unsigned long offset,
	      unsigned long size, pgprot_t prot)
{
	enum fixed_addresses idx0, idx;
	struct ioremap_map *map;
	unsigned int nrpages;
	int i, slot;

	slot = -1;
	for (i = 0; i < FIX_N_IOREMAPS; i++) {
		map = &ioremap_maps[i];
		if (!map->addr) {
			map->size = size;
			slot = i;
			break;
		}
	}

	if (slot < 0)
		return NULL;

	/*
	 * Mappings have to fit in the FIX_IOREMAP area.
	 */
	nrpages = size >> PAGE_SHIFT;
	if (nrpages > FIX_N_IOREMAPS)
		return NULL;

	/*
	 * Ok, go for it..
	 */
	idx0 = FIX_IOREMAP_BEGIN + slot;
	idx = idx0;
	while (nrpages > 0) {
		pgprot_val(prot) |= _PAGE_WIRED;
		__set_fixmap(idx, phys_addr, prot);
		phys_addr += PAGE_SIZE;
		idx++;
		--nrpages;
	}

	map->addr = (void __iomem *)(offset + map->fixmap_addr);
	return map->addr;
}

int iounmap_fixed(void __iomem *addr)
{
	enum fixed_addresses idx;
	unsigned long virt_addr;
	struct ioremap_map *map;
	unsigned long offset;
	unsigned int nrpages;
	int i, slot;

	slot = -1;
	for (i = 0; i < FIX_N_IOREMAPS; i++) {
		map = &ioremap_maps[i];
		if (map->addr == addr) {
			slot = i;
			break;
		}
	}

	/*
	 * If we don't match, it's not for us.
	 */
	if (slot < 0)
		return -EINVAL;

	virt_addr = (unsigned long)addr;

	offset = virt_addr & ~PAGE_MASK;
	nrpages = PAGE_ALIGN(offset + map->size - 1) >> PAGE_SHIFT;

	idx = FIX_IOREMAP_BEGIN + slot + nrpages;
	while (nrpages > 0) {
		__clear_fixmap(idx, __pgprot(_PAGE_WIRED));
		--idx;
		--nrpages;
	}

	map->size = 0;
	map->addr = NULL;

	return 0;
}