1 // SPDX-License-Identifier: GPL-2.0
3 * PCI Endpoint *Controller* Address Space Management
5 * Copyright (C) 2017 Texas Instruments
6 * Author: Kishon Vijay Abraham I <kishon@ti.com>
10 #include <linux/module.h>
11 #include <linux/slab.h>
13 #include <linux/pci-epc.h>
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
20 * Reimplement get_order() for mem->page_size since the generic get_order
21 * always gets order with a constant PAGE_SIZE.
23 static int pci_epc_mem_get_order(struct pci_epc_mem
*mem
, size_t size
)
26 unsigned int page_shift
= ilog2(mem
->window
.page_size
);
30 #if BITS_PER_LONG == 32
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
44 * Invoke to initialize the pci_epc_mem structure used by the
45 * endpoint functions to allocate mapped PCI address.
47 int pci_epc_multi_mem_init(struct pci_epc
*epc
,
48 struct pci_epc_mem_window
*windows
,
49 unsigned int num_windows
)
51 struct pci_epc_mem
*mem
= NULL
;
52 unsigned long *bitmap
= NULL
;
53 unsigned int page_shift
;
62 if (!windows
|| !num_windows
)
65 epc
->windows
= kcalloc(num_windows
, sizeof(*epc
->windows
), GFP_KERNEL
);
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);
77 mem
= kzalloc(sizeof(*mem
), GFP_KERNEL
);
84 bitmap
= kzalloc(bitmap_size
, GFP_KERNEL
);
92 mem
->window
.phys_base
= windows
[i
].phys_base
;
93 mem
->window
.size
= windows
[i
].size
;
94 mem
->window
.page_size
= page_size
;
97 mutex_init(&mem
->lock
);
98 epc
->windows
[i
] = mem
;
101 epc
->mem
= epc
->windows
[0];
102 epc
->num_windows
= num_windows
;
107 for (; i
>= 0; i
--) {
108 mem
= epc
->windows
[i
];
116 EXPORT_SYMBOL_GPL(pci_epc_multi_mem_init
);
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
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
128 int pci_epc_mem_init(struct pci_epc
*epc
, phys_addr_t base
,
129 size_t size
, size_t page_size
)
131 struct pci_epc_mem_window mem_window
;
133 mem_window
.phys_base
= base
;
134 mem_window
.size
= size
;
135 mem_window
.page_size
= page_size
;
137 return pci_epc_multi_mem_init(epc
, &mem_window
, 1);
139 EXPORT_SYMBOL_GPL(pci_epc_mem_init
);
142 * pci_epc_mem_exit() - cleanup the pci_epc_mem structure
143 * @epc: the EPC device that invoked pci_epc_mem_exit
145 * Invoke to cleanup the pci_epc_mem structure allocated in
146 * pci_epc_mem_init().
148 void pci_epc_mem_exit(struct pci_epc
*epc
)
150 struct pci_epc_mem
*mem
;
153 if (!epc
->num_windows
)
156 for (i
= 0; i
< epc
->num_windows
; i
++) {
157 mem
= epc
->windows
[i
];
165 epc
->num_windows
= 0;
167 EXPORT_SYMBOL_GPL(pci_epc_mem_exit
);
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
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.
178 void __iomem
*pci_epc_mem_alloc_addr(struct pci_epc
*epc
,
179 phys_addr_t
*phys_addr
, size_t size
)
181 void __iomem
*virt_addr
;
182 struct pci_epc_mem
*mem
;
183 unsigned int page_shift
;
189 for (i
= 0; i
< epc
->num_windows
; i
++) {
190 mem
= epc
->windows
[i
];
191 if (size
> mem
->window
.size
)
194 align_size
= ALIGN(size
, mem
->window
.page_size
);
195 order
= pci_epc_mem_get_order(mem
, align_size
);
197 mutex_lock(&mem
->lock
);
198 pageno
= bitmap_find_free_region(mem
->bitmap
, mem
->pages
,
201 page_shift
= ilog2(mem
->window
.page_size
);
202 *phys_addr
= mem
->window
.phys_base
+
203 ((phys_addr_t
)pageno
<< page_shift
);
204 virt_addr
= ioremap(*phys_addr
, align_size
);
206 bitmap_release_region(mem
->bitmap
,
208 mutex_unlock(&mem
->lock
);
211 mutex_unlock(&mem
->lock
);
214 mutex_unlock(&mem
->lock
);
219 EXPORT_SYMBOL_GPL(pci_epc_mem_alloc_addr
);
221 static struct pci_epc_mem
*pci_epc_get_matching_window(struct pci_epc
*epc
,
222 phys_addr_t phys_addr
)
224 struct pci_epc_mem
*mem
;
227 for (i
= 0; i
< epc
->num_windows
; i
++) {
228 mem
= epc
->windows
[i
];
230 if (phys_addr
>= mem
->window
.phys_base
&&
231 phys_addr
< (mem
->window
.phys_base
+ mem
->window
.size
))
239 * pci_epc_mem_free_addr() - free the allocated memory address
240 * @epc: the EPC device on which memory was allocated
241 * @phys_addr: the allocated physical address
242 * @virt_addr: virtual address of the allocated mem space
243 * @size: the size of the allocated address space
245 * Invoke to free the memory allocated using pci_epc_mem_alloc_addr.
247 void pci_epc_mem_free_addr(struct pci_epc
*epc
, phys_addr_t phys_addr
,
248 void __iomem
*virt_addr
, size_t size
)
250 struct pci_epc_mem
*mem
;
251 unsigned int page_shift
;
256 mem
= pci_epc_get_matching_window(epc
, phys_addr
);
258 pr_err("failed to get matching window\n");
262 page_size
= mem
->window
.page_size
;
263 page_shift
= ilog2(page_size
);
265 pageno
= (phys_addr
- mem
->window
.phys_base
) >> page_shift
;
266 size
= ALIGN(size
, page_size
);
267 order
= pci_epc_mem_get_order(mem
, size
);
268 mutex_lock(&mem
->lock
);
269 bitmap_release_region(mem
->bitmap
, pageno
, order
);
270 mutex_unlock(&mem
->lock
);
272 EXPORT_SYMBOL_GPL(pci_epc_mem_free_addr
);
274 MODULE_DESCRIPTION("PCI EPC Address Space Management");
275 MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");