1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * PCI Endpoint *Controller* Address Space Management |
4 | * |
5 | * Copyright (C) 2017 Texas Instruments |
6 | * Author: Kishon Vijay Abraham I <kishon@ti.com> |
7 | */ |
8 | |
9 | #include <linux/io.h> |
10 | #include <linux/module.h> |
11 | #include <linux/slab.h> |
12 | |
13 | #include <linux/pci-epc.h> |
14 | |
15 | /** |
16 | * pci_epc_mem_get_order() - determine the allocation order of a memory size |
17 | * @mem: address space of the endpoint controller |
18 | * @size: the size for which to get the order |
19 | * |
20 | * Reimplement get_order() for mem->page_size since the generic get_order |
21 | * always gets order with a constant PAGE_SIZE. |
22 | */ |
23 | static int pci_epc_mem_get_order(struct pci_epc_mem *mem, size_t size) |
24 | { |
25 | int order; |
26 | unsigned int page_shift = ilog2(mem->window.page_size); |
27 | |
28 | size--; |
29 | size >>= page_shift; |
30 | #if BITS_PER_LONG == 32 |
31 | order = fls(size); |
32 | #else |
33 | order = fls64(x: size); |
34 | #endif |
35 | return order; |
36 | } |
37 | |
38 | /** |
39 | * pci_epc_multi_mem_init() - initialize the pci_epc_mem structure |
40 | * @epc: the EPC device that invoked pci_epc_mem_init |
41 | * @windows: pointer to windows supported by the device |
42 | * @num_windows: number of windows device supports |
43 | * |
44 | * Invoke to initialize the pci_epc_mem structure used by the |
45 | * endpoint functions to allocate mapped PCI address. |
46 | */ |
47 | int pci_epc_multi_mem_init(struct pci_epc *epc, |
48 | struct pci_epc_mem_window *windows, |
49 | unsigned int num_windows) |
50 | { |
51 | struct pci_epc_mem *mem = NULL; |
52 | unsigned long *bitmap = NULL; |
53 | unsigned int page_shift; |
54 | size_t page_size; |
55 | int bitmap_size; |
56 | int pages; |
57 | int ret; |
58 | int i; |
59 | |
60 | epc->num_windows = 0; |
61 | |
62 | if (!windows || !num_windows) |
63 | return -EINVAL; |
64 | |
65 | epc->windows = kcalloc(n: num_windows, size: sizeof(*epc->windows), GFP_KERNEL); |
66 | if (!epc->windows) |
67 | return -ENOMEM; |
68 | |
69 | for (i = 0; i < num_windows; i++) { |
70 | page_size = windows[i].page_size; |
71 | if (page_size < PAGE_SIZE) |
72 | page_size = PAGE_SIZE; |
73 | page_shift = ilog2(page_size); |
74 | pages = windows[i].size >> page_shift; |
75 | bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); |
76 | |
77 | mem = kzalloc(size: sizeof(*mem), GFP_KERNEL); |
78 | if (!mem) { |
79 | ret = -ENOMEM; |
80 | i--; |
81 | goto err_mem; |
82 | } |
83 | |
84 | bitmap = kzalloc(size: bitmap_size, GFP_KERNEL); |
85 | if (!bitmap) { |
86 | ret = -ENOMEM; |
87 | kfree(objp: mem); |
88 | i--; |
89 | goto err_mem; |
90 | } |
91 | |
92 | mem->window.phys_base = windows[i].phys_base; |
93 | mem->window.size = windows[i].size; |
94 | mem->window.page_size = page_size; |
95 | mem->bitmap = bitmap; |
96 | mem->pages = pages; |
97 | mutex_init(&mem->lock); |
98 | epc->windows[i] = mem; |
99 | } |
100 | |
101 | epc->mem = epc->windows[0]; |
102 | epc->num_windows = num_windows; |
103 | |
104 | return 0; |
105 | |
106 | err_mem: |
107 | for (; i >= 0; i--) { |
108 | mem = epc->windows[i]; |
109 | kfree(objp: mem->bitmap); |
110 | kfree(objp: mem); |
111 | } |
112 | kfree(objp: epc->windows); |
113 | |
114 | return ret; |
115 | } |
116 | EXPORT_SYMBOL_GPL(pci_epc_multi_mem_init); |
117 | |
118 | /** |
119 | * pci_epc_mem_init() - Initialize the pci_epc_mem structure |
120 | * @epc: the EPC device that invoked pci_epc_mem_init |
121 | * @base: Physical address of the window region |
122 | * @size: Total Size of the window region |
123 | * @page_size: Page size of the window region |
124 | * |
125 | * Invoke to initialize a single pci_epc_mem structure used by the |
126 | * endpoint functions to allocate memory for mapping the PCI host memory |
127 | */ |
128 | int pci_epc_mem_init(struct pci_epc *epc, phys_addr_t base, |
129 | size_t size, size_t page_size) |
130 | { |
131 | struct pci_epc_mem_window mem_window; |
132 | |
133 | mem_window.phys_base = base; |
134 | mem_window.size = size; |
135 | mem_window.page_size = page_size; |
136 | |
137 | return pci_epc_multi_mem_init(epc, &mem_window, 1); |
138 | } |
139 | EXPORT_SYMBOL_GPL(pci_epc_mem_init); |
140 | |
141 | /** |
142 | * pci_epc_mem_exit() - cleanup the pci_epc_mem structure |
143 | * @epc: the EPC device that invoked pci_epc_mem_exit |
144 | * |
145 | * Invoke to cleanup the pci_epc_mem structure allocated in |
146 | * pci_epc_mem_init(). |
147 | */ |
148 | void pci_epc_mem_exit(struct pci_epc *epc) |
149 | { |
150 | struct pci_epc_mem *mem; |
151 | int i; |
152 | |
153 | if (!epc->num_windows) |
154 | return; |
155 | |
156 | for (i = 0; i < epc->num_windows; i++) { |
157 | mem = epc->windows[i]; |
158 | kfree(objp: mem->bitmap); |
159 | kfree(objp: mem); |
160 | } |
161 | kfree(objp: epc->windows); |
162 | |
163 | epc->windows = NULL; |
164 | epc->mem = NULL; |
165 | epc->num_windows = 0; |
166 | } |
167 | EXPORT_SYMBOL_GPL(pci_epc_mem_exit); |
168 | |
169 | /** |
170 | * pci_epc_mem_alloc_addr() - allocate memory address from EPC addr space |
171 | * @epc: the EPC device on which memory has to be allocated |
172 | * @phys_addr: populate the allocated physical address here |
173 | * @size: the size of the address space that has to be allocated |
174 | * |
175 | * Invoke to allocate memory address from the EPC address space. This |
176 | * is usually done to map the remote RC address into the local system. |
177 | */ |
178 | void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc, |
179 | phys_addr_t *phys_addr, size_t size) |
180 | { |
181 | void __iomem *virt_addr = NULL; |
182 | struct pci_epc_mem *mem; |
183 | unsigned int page_shift; |
184 | size_t align_size; |
185 | int pageno; |
186 | int order; |
187 | int i; |
188 | |
189 | for (i = 0; i < epc->num_windows; i++) { |
190 | mem = epc->windows[i]; |
191 | mutex_lock(&mem->lock); |
192 | align_size = ALIGN(size, mem->window.page_size); |
193 | order = pci_epc_mem_get_order(mem, size: align_size); |
194 | |
195 | pageno = bitmap_find_free_region(bitmap: mem->bitmap, bits: mem->pages, |
196 | order); |
197 | if (pageno >= 0) { |
198 | page_shift = ilog2(mem->window.page_size); |
199 | *phys_addr = mem->window.phys_base + |
200 | ((phys_addr_t)pageno << page_shift); |
201 | virt_addr = ioremap(offset: *phys_addr, size: align_size); |
202 | if (!virt_addr) { |
203 | bitmap_release_region(bitmap: mem->bitmap, |
204 | pos: pageno, order); |
205 | mutex_unlock(lock: &mem->lock); |
206 | continue; |
207 | } |
208 | mutex_unlock(lock: &mem->lock); |
209 | return virt_addr; |
210 | } |
211 | mutex_unlock(lock: &mem->lock); |
212 | } |
213 | |
214 | return virt_addr; |
215 | } |
216 | EXPORT_SYMBOL_GPL(pci_epc_mem_alloc_addr); |
217 | |
218 | static struct pci_epc_mem *pci_epc_get_matching_window(struct pci_epc *epc, |
219 | phys_addr_t phys_addr) |
220 | { |
221 | struct pci_epc_mem *mem; |
222 | int i; |
223 | |
224 | for (i = 0; i < epc->num_windows; i++) { |
225 | mem = epc->windows[i]; |
226 | |
227 | if (phys_addr >= mem->window.phys_base && |
228 | phys_addr < (mem->window.phys_base + mem->window.size)) |
229 | return mem; |
230 | } |
231 | |
232 | return NULL; |
233 | } |
234 | |
235 | /** |
236 | * pci_epc_mem_free_addr() - free the allocated memory address |
237 | * @epc: the EPC device on which memory was allocated |
238 | * @phys_addr: the allocated physical address |
239 | * @virt_addr: virtual address of the allocated mem space |
240 | * @size: the size of the allocated address space |
241 | * |
242 | * Invoke to free the memory allocated using pci_epc_mem_alloc_addr. |
243 | */ |
244 | void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr, |
245 | void __iomem *virt_addr, size_t size) |
246 | { |
247 | struct pci_epc_mem *mem; |
248 | unsigned int page_shift; |
249 | size_t page_size; |
250 | int pageno; |
251 | int order; |
252 | |
253 | mem = pci_epc_get_matching_window(epc, phys_addr); |
254 | if (!mem) { |
255 | pr_err("failed to get matching window\n" ); |
256 | return; |
257 | } |
258 | |
259 | page_size = mem->window.page_size; |
260 | page_shift = ilog2(page_size); |
261 | iounmap(addr: virt_addr); |
262 | pageno = (phys_addr - mem->window.phys_base) >> page_shift; |
263 | size = ALIGN(size, page_size); |
264 | order = pci_epc_mem_get_order(mem, size); |
265 | mutex_lock(&mem->lock); |
266 | bitmap_release_region(bitmap: mem->bitmap, pos: pageno, order); |
267 | mutex_unlock(lock: &mem->lock); |
268 | } |
269 | EXPORT_SYMBOL_GPL(pci_epc_mem_free_addr); |
270 | |
271 | MODULE_DESCRIPTION("PCI EPC Address Space Management" ); |
272 | MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>" ); |
273 | |