blob: 83b7d5d3fc3e465d37f6eaf361f217279f8f8db7 [file] [log] [blame]
Kishon Vijay Abraham I5e8cb402017-04-10 19:25:10 +05301/**
2 * PCI Endpoint *Controller* Address Space Management
3 *
4 * Copyright (C) 2017 Texas Instruments
5 * Author: Kishon Vijay Abraham I <kishon@ti.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 of
9 * the License as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <linux/io.h>
21#include <linux/module.h>
22#include <linux/slab.h>
23
24#include <linux/pci-epc.h>
25
26/**
Kishon Vijay Abraham I52c92852017-08-18 20:27:56 +053027 * pci_epc_mem_get_order() - determine the allocation order of a memory size
28 * @mem: address space of the endpoint controller
29 * @size: the size for which to get the order
30 *
31 * Reimplement get_order() for mem->page_size since the generic get_order
32 * always gets order with a constant PAGE_SIZE.
33 */
34static int pci_epc_mem_get_order(struct pci_epc_mem *mem, size_t size)
35{
36 int order;
37 unsigned int page_shift = ilog2(mem->page_size);
38
39 size--;
40 size >>= page_shift;
41#if BITS_PER_LONG == 32
42 order = fls(size);
43#else
44 order = fls64(size);
45#endif
46 return order;
47}
48
49/**
50 * __pci_epc_mem_init() - initialize the pci_epc_mem structure
Kishon Vijay Abraham I5e8cb402017-04-10 19:25:10 +053051 * @epc: the EPC device that invoked pci_epc_mem_init
52 * @phys_base: the physical address of the base
53 * @size: the size of the address space
Kishon Vijay Abraham I52c92852017-08-18 20:27:56 +053054 * @page_size: size of each page
Kishon Vijay Abraham I5e8cb402017-04-10 19:25:10 +053055 *
56 * Invoke to initialize the pci_epc_mem structure used by the
57 * endpoint functions to allocate mapped PCI address.
58 */
Kishon Vijay Abraham I52c92852017-08-18 20:27:56 +053059int __pci_epc_mem_init(struct pci_epc *epc, phys_addr_t phys_base, size_t size,
60 size_t page_size)
Kishon Vijay Abraham I5e8cb402017-04-10 19:25:10 +053061{
62 int ret;
63 struct pci_epc_mem *mem;
64 unsigned long *bitmap;
Kishon Vijay Abraham I52c92852017-08-18 20:27:56 +053065 unsigned int page_shift;
66 int pages;
67 int bitmap_size;
68
69 if (page_size < PAGE_SIZE)
70 page_size = PAGE_SIZE;
71
72 page_shift = ilog2(page_size);
73 pages = size >> page_shift;
74 bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
Kishon Vijay Abraham I5e8cb402017-04-10 19:25:10 +053075
76 mem = kzalloc(sizeof(*mem), GFP_KERNEL);
77 if (!mem) {
78 ret = -ENOMEM;
79 goto err;
80 }
81
82 bitmap = kzalloc(bitmap_size, GFP_KERNEL);
83 if (!bitmap) {
84 ret = -ENOMEM;
85 goto err_mem;
86 }
87
88 mem->bitmap = bitmap;
89 mem->phys_base = phys_base;
Kishon Vijay Abraham I52c92852017-08-18 20:27:56 +053090 mem->page_size = page_size;
Kishon Vijay Abraham I5e8cb402017-04-10 19:25:10 +053091 mem->pages = pages;
92 mem->size = size;
93
94 epc->mem = mem;
95
96 return 0;
97
98err_mem:
99 kfree(mem);
100
101err:
102return ret;
103}
Kishon Vijay Abraham I52c92852017-08-18 20:27:56 +0530104EXPORT_SYMBOL_GPL(__pci_epc_mem_init);
Kishon Vijay Abraham I5e8cb402017-04-10 19:25:10 +0530105
106/**
107 * pci_epc_mem_exit() - cleanup the pci_epc_mem structure
108 * @epc: the EPC device that invoked pci_epc_mem_exit
109 *
110 * Invoke to cleanup the pci_epc_mem structure allocated in
111 * pci_epc_mem_init().
112 */
113void pci_epc_mem_exit(struct pci_epc *epc)
114{
115 struct pci_epc_mem *mem = epc->mem;
116
117 epc->mem = NULL;
118 kfree(mem->bitmap);
119 kfree(mem);
120}
121EXPORT_SYMBOL_GPL(pci_epc_mem_exit);
122
123/**
124 * pci_epc_mem_alloc_addr() - allocate memory address from EPC addr space
125 * @epc: the EPC device on which memory has to be allocated
126 * @phys_addr: populate the allocated physical address here
127 * @size: the size of the address space that has to be allocated
128 *
129 * Invoke to allocate memory address from the EPC address space. This
130 * is usually done to map the remote RC address into the local system.
131 */
132void __iomem *pci_epc_mem_alloc_addr(struct pci_epc *epc,
133 phys_addr_t *phys_addr, size_t size)
134{
135 int pageno;
136 void __iomem *virt_addr;
137 struct pci_epc_mem *mem = epc->mem;
Kishon Vijay Abraham I52c92852017-08-18 20:27:56 +0530138 unsigned int page_shift = ilog2(mem->page_size);
139 int order;
140
141 size = ALIGN(size, mem->page_size);
142 order = pci_epc_mem_get_order(mem, size);
Kishon Vijay Abraham I5e8cb402017-04-10 19:25:10 +0530143
144 pageno = bitmap_find_free_region(mem->bitmap, mem->pages, order);
145 if (pageno < 0)
146 return NULL;
147
Kishon Vijay Abraham I52c92852017-08-18 20:27:56 +0530148 *phys_addr = mem->phys_base + (pageno << page_shift);
Kishon Vijay Abraham I5e8cb402017-04-10 19:25:10 +0530149 virt_addr = ioremap(*phys_addr, size);
150 if (!virt_addr)
151 bitmap_release_region(mem->bitmap, pageno, order);
152
153 return virt_addr;
154}
155EXPORT_SYMBOL_GPL(pci_epc_mem_alloc_addr);
156
157/**
158 * pci_epc_mem_free_addr() - free the allocated memory address
159 * @epc: the EPC device on which memory was allocated
160 * @phys_addr: the allocated physical address
161 * @virt_addr: virtual address of the allocated mem space
162 * @size: the size of the allocated address space
163 *
164 * Invoke to free the memory allocated using pci_epc_mem_alloc_addr.
165 */
166void pci_epc_mem_free_addr(struct pci_epc *epc, phys_addr_t phys_addr,
167 void __iomem *virt_addr, size_t size)
168{
169 int pageno;
Kishon Vijay Abraham I5e8cb402017-04-10 19:25:10 +0530170 struct pci_epc_mem *mem = epc->mem;
Kishon Vijay Abraham I52c92852017-08-18 20:27:56 +0530171 unsigned int page_shift = ilog2(mem->page_size);
172 int order;
Kishon Vijay Abraham I5e8cb402017-04-10 19:25:10 +0530173
174 iounmap(virt_addr);
Kishon Vijay Abraham I52c92852017-08-18 20:27:56 +0530175 pageno = (phys_addr - mem->phys_base) >> page_shift;
176 size = ALIGN(size, mem->page_size);
177 order = pci_epc_mem_get_order(mem, size);
Kishon Vijay Abraham I5e8cb402017-04-10 19:25:10 +0530178 bitmap_release_region(mem->bitmap, pageno, order);
179}
180EXPORT_SYMBOL_GPL(pci_epc_mem_free_addr);
181
182MODULE_DESCRIPTION("PCI EPC Address Space Management");
183MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
184MODULE_LICENSE("GPL v2");