summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/include/mach/iovmm.h
blob: e30d3dcf28591f9fd6c0925753cc4b419b144cc0 (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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
/*
 * arch/arm/mach-tegra/include/mach/iovmm.h
 *
 * Copyright (c) 2010, NVIDIA Corporation.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed i the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <linux/list.h>
#include <linux/platform_device.h>
#include <linux/rbtree.h>
#include <linux/rwsem.h>
#include <linux/spinlock.h>
#include <linux/types.h>

#ifndef _MACH_TEGRA_IOVMM_H_
#define _MACH_TEGRA_IOVMM_H_

#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
typedef u32 tegra_iovmm_addr_t;
#else
#error "Unsupported tegra architecture family"
#endif

struct tegra_iovmm_device_ops;

/* each I/O virtual memory manager unit should register a device with
 * the iovmm system
 */
struct tegra_iovmm_device {
	struct tegra_iovmm_device_ops	*ops;
	const char			*name;
	struct list_head		list;
	int				pgsize_bits;
};

/* tegra_iovmm_domain serves a purpose analagous to mm_struct as defined in
 * <linux/mm_types.h> - it defines a virtual address space within which
 * tegra_iovmm_areas can be created.
 */
struct tegra_iovmm_domain {
	atomic_t		clients;
	atomic_t		locks;
	spinlock_t		block_lock;
	unsigned long		flags;
	wait_queue_head_t	delay_lock;  /* when lock_client fails */
	struct rw_semaphore	map_lock;
	struct rb_root		all_blocks;  /* ordered by address */
	struct rb_root		free_blocks; /* ordered by size */
	struct tegra_iovmm_device *dev;
};

/* tegra_iovmm_client is analagous to an individual task in the task group
 * which owns an mm_struct.
 */

struct iovmm_share_group;

struct tegra_iovmm_client {
	const char 			*name;
	unsigned long			flags;
	struct iovmm_share_group	*group;
	struct tegra_iovmm_domain	*domain;
	struct list_head		list;
};

/* tegra_iovmm_area serves a purpose analagous to vm_area_struct as defined
 * in <linux/mm_types.h> - it defines a virtual memory area which can be
 * mapped to physical memory by a client-provided mapping function. */

struct tegra_iovmm_area {
	struct tegra_iovmm_domain	*domain;
	tegra_iovmm_addr_t		iovm_start;
	tegra_iovmm_addr_t		iovm_length;
	pgprot_t			pgprot;
	struct tegra_iovmm_area_ops	*ops;
};

struct tegra_iovmm_device_ops {
	/* maps a VMA using the page residency functions provided by the VMA */
	int (*map)(struct tegra_iovmm_device *dev,
		struct tegra_iovmm_area *io_vma);
	/* marks all PTEs in a VMA as invalid; decommits the virtual addres
	 * space (potentially freeing PDEs when decommit is true.) */
	void (*unmap)(struct tegra_iovmm_device *dev,
		struct tegra_iovmm_area *io_vma, bool decommit);
	void (*map_pfn)(struct tegra_iovmm_device *dev,
		struct tegra_iovmm_area *io_vma,
		tegra_iovmm_addr_t offs, unsigned long pfn);
	/* ensures that a domain is resident in the hardware's mapping region
	 * so that it may be used by a client */
	int (*lock_domain)(struct tegra_iovmm_device *dev,
		struct tegra_iovmm_domain *domain);
	void (*unlock_domain)(struct tegra_iovmm_device *dev,
		struct tegra_iovmm_domain *domain);
	/* allocates a vmm_domain for the specified client; may return the same
	 * domain for multiple clients */
	struct tegra_iovmm_domain* (*alloc_domain)(
		struct tegra_iovmm_device *dev,
		struct tegra_iovmm_client *client);
	void (*free_domain)(struct tegra_iovmm_device *dev,
		struct tegra_iovmm_domain *domain);
	int (*suspend)(struct tegra_iovmm_device *dev);
	void (*resume)(struct tegra_iovmm_device *dev);
};

struct tegra_iovmm_area_ops {
	/* ensures that the page of data starting at the specified offset
	 * from the start of the iovma is resident and pinned for use by
	 * DMA, returns the system pfn, or an invalid pfn if the
	 * operation fails. */
	unsigned long (*lock_makeresident)(struct tegra_iovmm_area *area,
		tegra_iovmm_addr_t offs);
	/* called when the page is unmapped from the I/O VMA */
	void (*release)(struct tegra_iovmm_area *area, tegra_iovmm_addr_t offs);
};

#ifdef CONFIG_TEGRA_IOVMM
/* called by clients to allocate an I/O VMM client mapping context which
 * will be shared by all clients in the same share_group */
struct tegra_iovmm_client *tegra_iovmm_alloc_client(const char *name,
	const char *share_group);

size_t tegra_iovmm_get_vm_size(struct tegra_iovmm_client *client);

void tegra_iovmm_free_client(struct tegra_iovmm_client *client);

/* called by clients to ensure that their mapping context is resident
 * before performing any DMA operations addressing I/O VMM regions.
 * client_lock may return -EINTR. */
int tegra_iovmm_client_lock(struct tegra_iovmm_client *client);
int tegra_iovmm_client_trylock(struct tegra_iovmm_client *client);

/* called by clients after DMA operations are complete */
void tegra_iovmm_client_unlock(struct tegra_iovmm_client *client);

/* called by clients to allocate a new iovmm_area and reserve I/O virtual
 * address space for it. if ops is NULL, clients should subsequently call
 * tegra_iovmm_vm_map_pages and/or tegra_iovmm_vm_insert_pfn to explicitly
 * map the I/O virtual address to an OS-allocated page or physical address,
 * respectively. VM operations may be called before this call returns */
struct tegra_iovmm_area *tegra_iovmm_create_vm(
	struct tegra_iovmm_client *client, struct tegra_iovmm_area_ops *ops,
	unsigned long size, pgprot_t pgprot);

/* called by clients to "zap" an iovmm_area, and replace all mappings
 * in it with invalid ones, without freeing the virtual address range */
void tegra_iovmm_zap_vm(struct tegra_iovmm_area *vm);

/* after zapping a demand-loaded iovmm_area, the client should unzap it
 * to allow the VMM device to remap the page range. */
void tegra_iovmm_unzap_vm(struct tegra_iovmm_area *vm);

/* called by clients to return an iovmm_area to the free pool for the domain */
void tegra_iovmm_free_vm(struct tegra_iovmm_area *vm);

/* returns size of largest free iovm block */
size_t tegra_iovmm_get_max_free(struct tegra_iovmm_client *client);

/* called by client software to map the page-aligned I/O address vaddr to
 * a specific physical address pfn. I/O VMA should have been created with
 * a NULL tegra_iovmm_area_ops structure. */
void tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *area,
	tegra_iovmm_addr_t vaddr, unsigned long pfn);

/* called by clients to return the iovmm_area containing addr, or NULL if
 * addr has not been allocated. caller should call tegra_iovmm_put_area when
 * finished using the returned pointer */
struct tegra_iovmm_area *tegra_iovmm_find_area_get(
	struct tegra_iovmm_client *client, tegra_iovmm_addr_t addr);

struct tegra_iovmm_area *tegra_iovmm_area_get(struct tegra_iovmm_area *vm);
void tegra_iovmm_area_put(struct tegra_iovmm_area *vm);

/* called by drivers to initialize a tegra_iovmm_domain structure */
int tegra_iovmm_domain_init(struct tegra_iovmm_domain *domain,
	struct tegra_iovmm_device *dev, tegra_iovmm_addr_t start,
	tegra_iovmm_addr_t end);

/* called by drivers to register an I/O VMM device with the system */
int tegra_iovmm_register(struct tegra_iovmm_device *dev);

/* called by drivers to remove an I/O VMM device from the system */
int tegra_iovmm_unregister(struct tegra_iovmm_device *dev);

/* called by platform suspend code to save IOVMM context */
int tegra_iovmm_suspend(void);

/* restores IOVMM context */
void tegra_iovmm_resume(void);

#else /* CONFIG_TEGRA_IOVMM */

static inline struct tegra_iovmm_client *tegra_iovmm_alloc_client(
	const char *name, const char *share_group)
{
	return NULL;
}

static inline size_t tegra_iovmm_get_vm_size(struct tegra_iovmm_client *client)
{
	return 0;
}

static inline void tegra_iovmm_free_client(struct tegra_iovmm_client *client)
{}

static inline int tegra_iovmm_client_lock(struct tegra_iovmm_client *client)
{
	return 0;
}

static inline int tegra_iovmm_client_trylock(struct tegra_iovmm_client *client)
{
	return 0;
}

static inline void tegra_iovmm_client_unlock(struct tegra_iovmm_client *client)
{}

static inline struct tegra_iovmm_area *tegra_iovmm_create_vm(
	struct tegra_iovmm_client *client, struct tegra_iovmm_area_ops *ops,
	unsigned long size, pgprot_t pgprot)
{
	return NULL;
}

static inline void tegra_iovmm_zap_vm(struct tegra_iovmm_area *vm) { }

static inline void tegra_iovmm_unzap_vm(struct tegra_iovmm_area *vm) { }

static inline void tegra_iovmm_free_vm(struct tegra_iovmm_area *vm) { }

static inline void tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *area,
	tegra_iovmm_addr_t vaddr, unsigned long pfn) { }

static inline struct tegra_iovmm_area *tegra_iovmm_find_area_get(
	struct tegra_iovmm_client *client, tegra_iovmm_addr_t addr)
{
	return NULL;
}

static inline struct tegra_iovmm_area *tegra_iovmm_area_get(
	struct tegra_iovmm_area *vm)
{
	return NULL;
}

static inline void tegra_iovmm_area_put(struct tegra_iovmm_area *vm) { }

static inline int tegra_iovmm_domain_init(struct tegra_iovmm_domain *domain,
	struct tegra_iovmm_device *dev, tegra_iovmm_addr_t start,
	tegra_iovmm_addr_t end)
{
	return 0;
}

static inline int tegra_iovmm_register(struct tegra_iovmm_device *dev)
{
	return 0;
}

static inline int tegra_iovmm_unregister(struct tegra_iovmm_device *dev)
{
	return 0;
}

static inline int tegra_iovmm_suspend(void)
{
	return 0;
}

static inline void tegra_iovmm_resume(void) { }
#endif /* CONFIG_TEGRA_IOVMM */


#endif