summaryrefslogtreecommitdiff
path: root/mali_kbase/mali_kbase_mem_lowlevel.h
blob: 629ca3a2c07b26b543ba24a4ff643d39a83a9c0b (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
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
 *
 * (C) COPYRIGHT 2012-2023 ARM Limited. All rights reserved.
 *
 * This program is free software and is provided to you under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation, and any use by you of this program is subject to the terms
 * of such GNU license.
 *
 * This program is distributed in 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, you can access it online at
 * http://www.gnu.org/licenses/gpl-2.0.html.
 *
 */

#ifndef _KBASE_MEM_LOWLEVEL_H
#define _KBASE_MEM_LOWLEVEL_H

#ifndef _KBASE_H_
#error "Don't include this file directly, use mali_kbase.h instead"
#endif

#include <linux/dma-mapping.h>

/* Kernel/CPU page size can be 4/16/64KB whereas GPU page size is fixed as 4KB */
#define GPU_PAGE_SIZE SZ_4K
#define GPU_PAGE_MASK (~(SZ_4K - 1))
#define GPU_PAGES_PER_CPU_PAGE (PAGE_SIZE / GPU_PAGE_SIZE)

/* Flags for kbase_phy_allocator_pages_alloc */
#define KBASE_PHY_PAGES_FLAG_DEFAULT (0) /** Default allocation flag */
#define KBASE_PHY_PAGES_FLAG_CLEAR (1 << 0) /** Clear the pages after allocation */
#define KBASE_PHY_PAGES_FLAG_POISON (1 << 1) /** Fill the memory with a poison value */

#define KBASE_PHY_PAGES_SUPPORTED_FLAGS \
	(KBASE_PHY_PAGES_FLAG_DEFAULT | KBASE_PHY_PAGES_FLAG_CLEAR | KBASE_PHY_PAGES_FLAG_POISON)

#define KBASE_PHY_PAGES_POISON_VALUE \
	0xFD /** Value to fill the memory with when KBASE_PHY_PAGES_FLAG_POISON is set */

enum kbase_sync_type { KBASE_SYNC_TO_CPU, KBASE_SYNC_TO_DEVICE };

struct tagged_addr {
	phys_addr_t tagged_addr;
};

#define HUGE_PAGE (1u << 0)
#define HUGE_HEAD (1u << 1)
#define FROM_PARTIAL (1u << 2)

#define NUM_PAGES_IN_2MB_LARGE_PAGE (SZ_2M / PAGE_SIZE)

#define KBASE_INVALID_PHYSICAL_ADDRESS (~(phys_addr_t)0 & PAGE_MASK)

/*
 * Note: if macro for converting physical address to page is not defined
 * in the kernel itself, it is defined hereby. This is to avoid build errors
 * which are reported during builds for some architectures.
 */
#ifndef phys_to_page
#define phys_to_page(phys) (pfn_to_page((phys) >> PAGE_SHIFT))
#endif

/**
 * as_phys_addr_t - Retrieve the physical address from tagged address by
 *                  masking the lower order 12 bits.
 * @t: tagged address to be translated.
 *
 * Return: physical address corresponding to tagged address.
 */
static inline phys_addr_t as_phys_addr_t(struct tagged_addr t)
{
	return t.tagged_addr & GPU_PAGE_MASK;
}

/**
 * as_page - Retrieve the struct page from a tagged address
 * @t: tagged address to be translated.
 *
 * Return: pointer to struct page corresponding to tagged address.
 */
static inline struct page *as_page(struct tagged_addr t)
{
	return phys_to_page(as_phys_addr_t(t));
}

/**
 * as_tagged - Convert the physical address to tagged address type though
 *             there is no tag info present, the lower order 12 bits will be 0
 * @phys: physical address to be converted to tagged type
 *
 * This is used for small physical pages allocated by the Driver or imported pages
 * and is needed as physical pages tracking object stores the reference for
 * physical pages using tagged address type in lieu of the type generally used
 * for physical addresses.
 *
 * Return: address of tagged address type.
 */
static inline struct tagged_addr as_tagged(phys_addr_t phys)
{
	struct tagged_addr t;

	t.tagged_addr = phys & GPU_PAGE_MASK;
	return t;
}

/**
 * as_tagged_tag - Form the tagged address by storing the tag or metadata in the
 *                 lower order 12 bits of physial address
 * @phys: physical address to be converted to tagged address
 * @tag:  tag to be stored along with the physical address.
 *
 * The tag info is used while freeing up the pages
 *
 * Return: tagged address storing physical address & tag.
 */
static inline struct tagged_addr as_tagged_tag(phys_addr_t phys, int tag)
{
	struct tagged_addr t;

	t.tagged_addr = (phys & GPU_PAGE_MASK) | (tag & ~GPU_PAGE_MASK);
	return t;
}

/**
 * is_huge - Check if the physical page is one of the small pages of the
 *           large page which was not split to be used partially
 * @t: tagged address storing the tag in the lower order bits.
 *
 * Return: true if page belongs to large page, or false
 */
static inline bool is_huge(struct tagged_addr t)
{
	return t.tagged_addr & HUGE_PAGE;
}

/**
 * is_huge_head - Check if the physical page is the first small page within
 *                a large page which was not split to be used partially
 * @t: tagged address storing the tag in the lower order bits.
 *
 * Return: true if page is the first page of a large page, or false
 */
static inline bool is_huge_head(struct tagged_addr t)
{
	phys_addr_t mask = HUGE_HEAD | HUGE_PAGE;

	return mask == (t.tagged_addr & mask);
}

/**
 * is_partial - Check if the physical page is one of the small pages of the
 *              large page which was split in small pages to be used
 *              partially for allocations >= 2 MB in size.
 * @t: tagged address storing the tag in the lower order bits.
 *
 * Return: true if page was taken from large page used partially, or false
 */
static inline bool is_partial(struct tagged_addr t)
{
	return t.tagged_addr & FROM_PARTIAL;
}

/**
 * index_in_large_page() - Get index of a small page within a 2MB page which
 *                         wasn't split to be used partially.
 *
 * @t:  Tagged physical address of the physical small page that lies within
 *      the large (or 2 MB) physical page.
 *
 * Return: Index of the small page within a 2MB page
 */
static inline unsigned int index_in_large_page(struct tagged_addr t)
{
	WARN_ON(!is_huge(t));

	return (PFN_DOWN(as_phys_addr_t(t)) & (NUM_PAGES_IN_2MB_LARGE_PAGE - 1));
}

/**
 * is_valid_addr() - Check if the physical page has a valid address
 *
 * @t: tagged address storing the tag in the lower order bits.
 *
 * Return: true if page has valid physical address, or false
 */
static inline bool is_valid_addr(struct tagged_addr t)
{
	return (as_phys_addr_t(t) != KBASE_INVALID_PHYSICAL_ADDRESS);
}

#endif /* _KBASE_LOWLEVEL_H */