blob: cec82034875f63addb4e084934efb7fcd2fdc894 [file] [log] [blame]
Dasaratharaman Chandramoulia01e28f2013-09-05 16:41:41 -07001/*
2 * Intel MIC Platform Software Stack (MPSS)
3 *
4 * Copyright(c) 2013 Intel Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License, version 2, as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * The full GNU General Public License is included in this distribution in
16 * the file called "COPYING".
17 *
18 * Intel MIC Host driver.
19 *
20 */
21#include <linux/pci.h>
22
Sudeep Dutt4aa79962013-09-27 09:49:42 -070023#include "../common/mic_dev.h"
Dasaratharaman Chandramoulia01e28f2013-09-05 16:41:41 -070024#include "mic_device.h"
25#include "mic_smpt.h"
26
27static inline u64 mic_system_page_mask(struct mic_device *mdev)
28{
29 return (1ULL << mdev->smpt->info.page_shift) - 1ULL;
30}
31
32static inline u8 mic_sys_addr_to_smpt(struct mic_device *mdev, dma_addr_t pa)
33{
34 return (pa - mdev->smpt->info.base) >> mdev->smpt->info.page_shift;
35}
36
37static inline u64 mic_smpt_to_pa(struct mic_device *mdev, u8 index)
38{
39 return mdev->smpt->info.base + (index * mdev->smpt->info.page_size);
40}
41
42static inline u64 mic_smpt_offset(struct mic_device *mdev, dma_addr_t pa)
43{
44 return pa & mic_system_page_mask(mdev);
45}
46
47static inline u64 mic_smpt_align_low(struct mic_device *mdev, dma_addr_t pa)
48{
49 return ALIGN(pa - mic_system_page_mask(mdev),
50 mdev->smpt->info.page_size);
51}
52
53static inline u64 mic_smpt_align_high(struct mic_device *mdev, dma_addr_t pa)
54{
55 return ALIGN(pa, mdev->smpt->info.page_size);
56}
57
58/* Total Cumulative system memory accessible by MIC across all SMPT entries */
59static inline u64 mic_max_system_memory(struct mic_device *mdev)
60{
61 return mdev->smpt->info.num_reg * mdev->smpt->info.page_size;
62}
63
64/* Maximum system memory address accessible by MIC */
65static inline u64 mic_max_system_addr(struct mic_device *mdev)
66{
67 return mdev->smpt->info.base + mic_max_system_memory(mdev) - 1ULL;
68}
69
70/* Check if the DMA address is a MIC system memory address */
71static inline bool
72mic_is_system_addr(struct mic_device *mdev, dma_addr_t pa)
73{
74 return pa >= mdev->smpt->info.base && pa <= mic_max_system_addr(mdev);
75}
76
77/* Populate an SMPT entry and update the reference counts. */
78static void mic_add_smpt_entry(int spt, s64 *ref, u64 addr,
79 int entries, struct mic_device *mdev)
80{
81 struct mic_smpt_info *smpt_info = mdev->smpt;
82 int i;
83
84 for (i = spt; i < spt + entries; i++,
85 addr += smpt_info->info.page_size) {
86 if (!smpt_info->entry[i].ref_count &&
Ashutosh Dixitced2c602013-09-27 09:49:53 -070087 (smpt_info->entry[i].dma_addr != addr)) {
Dasaratharaman Chandramoulia01e28f2013-09-05 16:41:41 -070088 mdev->smpt_ops->set(mdev, addr, i);
89 smpt_info->entry[i].dma_addr = addr;
90 }
91 smpt_info->entry[i].ref_count += ref[i - spt];
92 }
93}
94
95/*
96 * Find an available MIC address in MIC SMPT address space
97 * for a given DMA address and size.
98 */
99static dma_addr_t mic_smpt_op(struct mic_device *mdev, u64 dma_addr,
100 int entries, s64 *ref, size_t size)
101{
102 int spt;
103 int ae = 0;
104 int i;
105 unsigned long flags;
106 dma_addr_t mic_addr = 0;
107 dma_addr_t addr = dma_addr;
108 struct mic_smpt_info *smpt_info = mdev->smpt;
109
110 spin_lock_irqsave(&smpt_info->smpt_lock, flags);
111
112 /* find existing entries */
113 for (i = 0; i < smpt_info->info.num_reg; i++) {
114 if (smpt_info->entry[i].dma_addr == addr) {
115 ae++;
116 addr += smpt_info->info.page_size;
117 } else if (ae) /* cannot find contiguous entries */
118 goto not_found;
119
120 if (ae == entries)
121 goto found;
122 }
123
124 /* find free entry */
125 for (ae = 0, i = 0; i < smpt_info->info.num_reg; i++) {
126 ae = (smpt_info->entry[i].ref_count == 0) ? ae + 1 : 0;
127 if (ae == entries)
128 goto found;
129 }
130
131not_found:
132 spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
133 return mic_addr;
134
135found:
136 spt = i - entries + 1;
137 mic_addr = mic_smpt_to_pa(mdev, spt);
138 mic_add_smpt_entry(spt, ref, dma_addr, entries, mdev);
139 smpt_info->map_count++;
140 smpt_info->ref_count += (s64)size;
141 spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
142 return mic_addr;
143}
144
145/*
146 * Returns number of smpt entries needed for dma_addr to dma_addr + size
147 * also returns the reference count array for each of those entries
148 * and the starting smpt address
149 */
150static int mic_get_smpt_ref_count(struct mic_device *mdev, dma_addr_t dma_addr,
151 size_t size, s64 *ref, u64 *smpt_start)
152{
153 u64 start = dma_addr;
154 u64 end = dma_addr + size;
155 int i = 0;
156
157 while (start < end) {
158 ref[i++] = min(mic_smpt_align_high(mdev, start + 1),
159 end) - start;
160 start = mic_smpt_align_high(mdev, start + 1);
161 }
162
163 if (smpt_start)
164 *smpt_start = mic_smpt_align_low(mdev, dma_addr);
165
166 return i;
167}
168
169/*
170 * mic_to_dma_addr - Converts a MIC address to a DMA address.
171 *
172 * @mdev: pointer to mic_device instance.
173 * @mic_addr: MIC address.
174 *
175 * returns a DMA address.
176 */
Sudeep Dutt74321d42015-04-29 05:32:38 -0700177dma_addr_t mic_to_dma_addr(struct mic_device *mdev, dma_addr_t mic_addr)
Dasaratharaman Chandramoulia01e28f2013-09-05 16:41:41 -0700178{
179 struct mic_smpt_info *smpt_info = mdev->smpt;
180 int spt;
181 dma_addr_t dma_addr;
182
183 if (!mic_is_system_addr(mdev, mic_addr)) {
184 dev_err(mdev->sdev->parent,
Ashutosh Dixitced2c602013-09-27 09:49:53 -0700185 "mic_addr is invalid. mic_addr = 0x%llx\n", mic_addr);
Dasaratharaman Chandramoulia01e28f2013-09-05 16:41:41 -0700186 return -EINVAL;
187 }
188 spt = mic_sys_addr_to_smpt(mdev, mic_addr);
189 dma_addr = smpt_info->entry[spt].dma_addr +
190 mic_smpt_offset(mdev, mic_addr);
191 return dma_addr;
192}
193
194/**
195 * mic_map - Maps a DMA address to a MIC physical address.
196 *
197 * @mdev: pointer to mic_device instance.
198 * @dma_addr: DMA address.
199 * @size: Size of the region to be mapped.
200 *
201 * This API converts the DMA address provided to a DMA address understood
202 * by MIC. Caller should check for errors by calling mic_map_error(..).
203 *
204 * returns DMA address as required by MIC.
205 */
206dma_addr_t mic_map(struct mic_device *mdev, dma_addr_t dma_addr, size_t size)
207{
208 dma_addr_t mic_addr = 0;
209 int num_entries;
210 s64 *ref;
211 u64 smpt_start;
212
213 if (!size || size > mic_max_system_memory(mdev))
214 return mic_addr;
215
Sudeep Dutt74321d42015-04-29 05:32:38 -0700216 ref = kmalloc_array(mdev->smpt->info.num_reg, sizeof(s64), GFP_ATOMIC);
Dasaratharaman Chandramoulia01e28f2013-09-05 16:41:41 -0700217 if (!ref)
218 return mic_addr;
219
220 num_entries = mic_get_smpt_ref_count(mdev, dma_addr, size,
221 ref, &smpt_start);
222
223 /* Set the smpt table appropriately and get 16G aligned mic address */
224 mic_addr = mic_smpt_op(mdev, smpt_start, num_entries, ref, size);
225
226 kfree(ref);
227
228 /*
229 * If mic_addr is zero then its an error case
230 * since mic_addr can never be zero.
231 * else generate mic_addr by adding the 16G offset in dma_addr
232 */
233 if (!mic_addr && MIC_FAMILY_X100 == mdev->family) {
234 dev_err(mdev->sdev->parent,
235 "mic_map failed dma_addr 0x%llx size 0x%lx\n",
236 dma_addr, size);
237 return mic_addr;
238 } else {
239 return mic_addr + mic_smpt_offset(mdev, dma_addr);
240 }
241}
242
243/**
244 * mic_unmap - Unmaps a MIC physical address.
245 *
246 * @mdev: pointer to mic_device instance.
247 * @mic_addr: MIC physical address.
248 * @size: Size of the region to be unmapped.
249 *
250 * This API unmaps the mappings created by mic_map(..).
251 *
252 * returns None.
253 */
254void mic_unmap(struct mic_device *mdev, dma_addr_t mic_addr, size_t size)
255{
256 struct mic_smpt_info *smpt_info = mdev->smpt;
257 s64 *ref;
258 int num_smpt;
259 int spt;
260 int i;
261 unsigned long flags;
262
263 if (!size)
264 return;
265
266 if (!mic_is_system_addr(mdev, mic_addr)) {
267 dev_err(mdev->sdev->parent,
268 "invalid address: 0x%llx\n", mic_addr);
269 return;
270 }
271
272 spt = mic_sys_addr_to_smpt(mdev, mic_addr);
Sudeep Dutt74321d42015-04-29 05:32:38 -0700273 ref = kmalloc_array(mdev->smpt->info.num_reg, sizeof(s64), GFP_ATOMIC);
Dasaratharaman Chandramoulia01e28f2013-09-05 16:41:41 -0700274 if (!ref)
275 return;
276
277 /* Get number of smpt entries to be mapped, ref count array */
278 num_smpt = mic_get_smpt_ref_count(mdev, mic_addr, size, ref, NULL);
279
280 spin_lock_irqsave(&smpt_info->smpt_lock, flags);
281 smpt_info->unmap_count++;
282 smpt_info->ref_count -= (s64)size;
283
284 for (i = spt; i < spt + num_smpt; i++) {
285 smpt_info->entry[i].ref_count -= ref[i - spt];
286 if (smpt_info->entry[i].ref_count < 0)
287 dev_warn(mdev->sdev->parent,
Ashutosh Dixitced2c602013-09-27 09:49:53 -0700288 "ref count for entry %d is negative\n", i);
Dasaratharaman Chandramoulia01e28f2013-09-05 16:41:41 -0700289 }
290 spin_unlock_irqrestore(&smpt_info->smpt_lock, flags);
291 kfree(ref);
292}
293
294/**
295 * mic_map_single - Maps a virtual address to a MIC physical address.
296 *
297 * @mdev: pointer to mic_device instance.
298 * @va: Kernel direct mapped virtual address.
299 * @size: Size of the region to be mapped.
300 *
301 * This API calls pci_map_single(..) for the direct mapped virtual address
302 * and then converts the DMA address provided to a DMA address understood
303 * by MIC. Caller should check for errors by calling mic_map_error(..).
304 *
305 * returns DMA address as required by MIC.
306 */
307dma_addr_t mic_map_single(struct mic_device *mdev, void *va, size_t size)
308{
309 dma_addr_t mic_addr = 0;
310 struct pci_dev *pdev = container_of(mdev->sdev->parent,
311 struct pci_dev, dev);
312 dma_addr_t dma_addr =
313 pci_map_single(pdev, va, size, PCI_DMA_BIDIRECTIONAL);
314
315 if (!pci_dma_mapping_error(pdev, dma_addr)) {
316 mic_addr = mic_map(mdev, dma_addr, size);
317 if (!mic_addr) {
318 dev_err(mdev->sdev->parent,
319 "mic_map failed dma_addr 0x%llx size 0x%lx\n",
320 dma_addr, size);
321 pci_unmap_single(pdev, dma_addr,
Ashutosh Dixitced2c602013-09-27 09:49:53 -0700322 size, PCI_DMA_BIDIRECTIONAL);
Dasaratharaman Chandramoulia01e28f2013-09-05 16:41:41 -0700323 }
324 }
325 return mic_addr;
326}
327
328/**
329 * mic_unmap_single - Unmaps a MIC physical address.
330 *
331 * @mdev: pointer to mic_device instance.
332 * @mic_addr: MIC physical address.
333 * @size: Size of the region to be unmapped.
334 *
335 * This API unmaps the mappings created by mic_map_single(..).
336 *
337 * returns None.
338 */
339void
340mic_unmap_single(struct mic_device *mdev, dma_addr_t mic_addr, size_t size)
341{
342 struct pci_dev *pdev = container_of(mdev->sdev->parent,
343 struct pci_dev, dev);
344 dma_addr_t dma_addr = mic_to_dma_addr(mdev, mic_addr);
345 mic_unmap(mdev, mic_addr, size);
346 pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL);
347}
348
349/**
350 * mic_smpt_init - Initialize MIC System Memory Page Tables.
351 *
352 * @mdev: pointer to mic_device instance.
353 *
354 * returns 0 for success and -errno for error.
355 */
356int mic_smpt_init(struct mic_device *mdev)
357{
358 int i, err = 0;
359 dma_addr_t dma_addr;
360 struct mic_smpt_info *smpt_info;
361
362 mdev->smpt = kmalloc(sizeof(*mdev->smpt), GFP_KERNEL);
363 if (!mdev->smpt)
364 return -ENOMEM;
365
366 smpt_info = mdev->smpt;
367 mdev->smpt_ops->init(mdev);
Ashutosh Dixitced2c602013-09-27 09:49:53 -0700368 smpt_info->entry = kmalloc_array(smpt_info->info.num_reg,
369 sizeof(*smpt_info->entry), GFP_KERNEL);
Dasaratharaman Chandramoulia01e28f2013-09-05 16:41:41 -0700370 if (!smpt_info->entry) {
371 err = -ENOMEM;
372 goto free_smpt;
373 }
374 spin_lock_init(&smpt_info->smpt_lock);
375 for (i = 0; i < smpt_info->info.num_reg; i++) {
376 dma_addr = i * smpt_info->info.page_size;
377 smpt_info->entry[i].dma_addr = dma_addr;
378 smpt_info->entry[i].ref_count = 0;
379 mdev->smpt_ops->set(mdev, dma_addr, i);
380 }
381 smpt_info->ref_count = 0;
382 smpt_info->map_count = 0;
383 smpt_info->unmap_count = 0;
384 return 0;
385free_smpt:
386 kfree(smpt_info);
387 return err;
388}
389
390/**
391 * mic_smpt_uninit - UnInitialize MIC System Memory Page Tables.
392 *
393 * @mdev: pointer to mic_device instance.
394 *
395 * returns None.
396 */
397void mic_smpt_uninit(struct mic_device *mdev)
398{
399 struct mic_smpt_info *smpt_info = mdev->smpt;
400 int i;
401
402 dev_dbg(mdev->sdev->parent,
403 "nodeid %d SMPT ref count %lld map %lld unmap %lld\n",
404 mdev->id, smpt_info->ref_count,
405 smpt_info->map_count, smpt_info->unmap_count);
406
407 for (i = 0; i < smpt_info->info.num_reg; i++) {
408 dev_dbg(mdev->sdev->parent,
409 "SMPT entry[%d] dma_addr = 0x%llx ref_count = %lld\n",
410 i, smpt_info->entry[i].dma_addr,
411 smpt_info->entry[i].ref_count);
412 if (smpt_info->entry[i].ref_count)
413 dev_warn(mdev->sdev->parent,
Ashutosh Dixitced2c602013-09-27 09:49:53 -0700414 "ref count for entry %d is not zero\n", i);
Dasaratharaman Chandramoulia01e28f2013-09-05 16:41:41 -0700415 }
416 kfree(smpt_info->entry);
417 kfree(smpt_info);
418}
419
420/**
421 * mic_smpt_restore - Restore MIC System Memory Page Tables.
422 *
423 * @mdev: pointer to mic_device instance.
424 *
425 * Restore the SMPT registers to values previously stored in the
426 * SW data structures. Some MIC steppings lose register state
427 * across resets and this API should be called for performing
428 * a restore operation if required.
429 *
430 * returns None.
431 */
432void mic_smpt_restore(struct mic_device *mdev)
433{
434 int i;
435 dma_addr_t dma_addr;
436
437 for (i = 0; i < mdev->smpt->info.num_reg; i++) {
438 dma_addr = mdev->smpt->entry[i].dma_addr;
439 mdev->smpt_ops->set(mdev, dma_addr, i);
440 }
441}