18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2004 Topspin Communications.  All rights reserved.
38c2ecf20Sopenharmony_ci * Copyright (c) 2005 Mellanox Technologies. All rights reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
68c2ecf20Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
78c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
88c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the
98c2ecf20Sopenharmony_ci * OpenIB.org BSD license below:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
128c2ecf20Sopenharmony_ci *     without modification, are permitted provided that the following
138c2ecf20Sopenharmony_ci *     conditions are met:
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci *      - Redistributions of source code must retain the above
168c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
178c2ecf20Sopenharmony_ci *        disclaimer.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
208c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
218c2ecf20Sopenharmony_ci *        disclaimer in the documentation and/or other materials
228c2ecf20Sopenharmony_ci *        provided with the distribution.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
258c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
268c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
278c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
288c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
298c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
308c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
318c2ecf20Sopenharmony_ci * SOFTWARE.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include <linux/slab.h>
358c2ecf20Sopenharmony_ci#include <linux/errno.h>
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#include "mthca_dev.h"
388c2ecf20Sopenharmony_ci#include "mthca_cmd.h"
398c2ecf20Sopenharmony_ci#include "mthca_memfree.h"
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistruct mthca_mtt {
428c2ecf20Sopenharmony_ci	struct mthca_buddy *buddy;
438c2ecf20Sopenharmony_ci	int                 order;
448c2ecf20Sopenharmony_ci	u32                 first_seg;
458c2ecf20Sopenharmony_ci};
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci * Must be packed because mtt_seg is 64 bits but only aligned to 32 bits.
498c2ecf20Sopenharmony_ci */
508c2ecf20Sopenharmony_cistruct mthca_mpt_entry {
518c2ecf20Sopenharmony_ci	__be32 flags;
528c2ecf20Sopenharmony_ci	__be32 page_size;
538c2ecf20Sopenharmony_ci	__be32 key;
548c2ecf20Sopenharmony_ci	__be32 pd;
558c2ecf20Sopenharmony_ci	__be64 start;
568c2ecf20Sopenharmony_ci	__be64 length;
578c2ecf20Sopenharmony_ci	__be32 lkey;
588c2ecf20Sopenharmony_ci	__be32 window_count;
598c2ecf20Sopenharmony_ci	__be32 window_count_limit;
608c2ecf20Sopenharmony_ci	__be64 mtt_seg;
618c2ecf20Sopenharmony_ci	__be32 mtt_sz;		/* Arbel only */
628c2ecf20Sopenharmony_ci	u32    reserved[2];
638c2ecf20Sopenharmony_ci} __packed;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci#define MTHCA_MPT_FLAG_SW_OWNS       (0xfUL << 28)
668c2ecf20Sopenharmony_ci#define MTHCA_MPT_FLAG_MIO           (1 << 17)
678c2ecf20Sopenharmony_ci#define MTHCA_MPT_FLAG_BIND_ENABLE   (1 << 15)
688c2ecf20Sopenharmony_ci#define MTHCA_MPT_FLAG_PHYSICAL      (1 <<  9)
698c2ecf20Sopenharmony_ci#define MTHCA_MPT_FLAG_REGION        (1 <<  8)
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci#define MTHCA_MTT_FLAG_PRESENT       1
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci#define MTHCA_MPT_STATUS_SW 0xF0
748c2ecf20Sopenharmony_ci#define MTHCA_MPT_STATUS_HW 0x00
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#define SINAI_FMR_KEY_INC 0x1000000
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci/*
798c2ecf20Sopenharmony_ci * Buddy allocator for MTT segments (currently not very efficient
808c2ecf20Sopenharmony_ci * since it doesn't keep a free list and just searches linearly
818c2ecf20Sopenharmony_ci * through the bitmaps)
828c2ecf20Sopenharmony_ci */
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_cistatic u32 mthca_buddy_alloc(struct mthca_buddy *buddy, int order)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	int o;
878c2ecf20Sopenharmony_ci	int m;
888c2ecf20Sopenharmony_ci	u32 seg;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	spin_lock(&buddy->lock);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	for (o = order; o <= buddy->max_order; ++o)
938c2ecf20Sopenharmony_ci		if (buddy->num_free[o]) {
948c2ecf20Sopenharmony_ci			m = 1 << (buddy->max_order - o);
958c2ecf20Sopenharmony_ci			seg = find_first_bit(buddy->bits[o], m);
968c2ecf20Sopenharmony_ci			if (seg < m)
978c2ecf20Sopenharmony_ci				goto found;
988c2ecf20Sopenharmony_ci		}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	spin_unlock(&buddy->lock);
1018c2ecf20Sopenharmony_ci	return -1;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci found:
1048c2ecf20Sopenharmony_ci	clear_bit(seg, buddy->bits[o]);
1058c2ecf20Sopenharmony_ci	--buddy->num_free[o];
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	while (o > order) {
1088c2ecf20Sopenharmony_ci		--o;
1098c2ecf20Sopenharmony_ci		seg <<= 1;
1108c2ecf20Sopenharmony_ci		set_bit(seg ^ 1, buddy->bits[o]);
1118c2ecf20Sopenharmony_ci		++buddy->num_free[o];
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	spin_unlock(&buddy->lock);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	seg <<= order;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	return seg;
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic void mthca_buddy_free(struct mthca_buddy *buddy, u32 seg, int order)
1228c2ecf20Sopenharmony_ci{
1238c2ecf20Sopenharmony_ci	seg >>= order;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	spin_lock(&buddy->lock);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	while (test_bit(seg ^ 1, buddy->bits[order])) {
1288c2ecf20Sopenharmony_ci		clear_bit(seg ^ 1, buddy->bits[order]);
1298c2ecf20Sopenharmony_ci		--buddy->num_free[order];
1308c2ecf20Sopenharmony_ci		seg >>= 1;
1318c2ecf20Sopenharmony_ci		++order;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	set_bit(seg, buddy->bits[order]);
1358c2ecf20Sopenharmony_ci	++buddy->num_free[order];
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	spin_unlock(&buddy->lock);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_cistatic int mthca_buddy_init(struct mthca_buddy *buddy, int max_order)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	int i, s;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	buddy->max_order = max_order;
1458c2ecf20Sopenharmony_ci	spin_lock_init(&buddy->lock);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	buddy->bits = kcalloc(buddy->max_order + 1, sizeof(long *),
1488c2ecf20Sopenharmony_ci			      GFP_KERNEL);
1498c2ecf20Sopenharmony_ci	buddy->num_free = kcalloc((buddy->max_order + 1), sizeof *buddy->num_free,
1508c2ecf20Sopenharmony_ci				  GFP_KERNEL);
1518c2ecf20Sopenharmony_ci	if (!buddy->bits || !buddy->num_free)
1528c2ecf20Sopenharmony_ci		goto err_out;
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	for (i = 0; i <= buddy->max_order; ++i) {
1558c2ecf20Sopenharmony_ci		s = BITS_TO_LONGS(1 << (buddy->max_order - i));
1568c2ecf20Sopenharmony_ci		buddy->bits[i] = kmalloc_array(s, sizeof(long), GFP_KERNEL);
1578c2ecf20Sopenharmony_ci		if (!buddy->bits[i])
1588c2ecf20Sopenharmony_ci			goto err_out_free;
1598c2ecf20Sopenharmony_ci		bitmap_zero(buddy->bits[i],
1608c2ecf20Sopenharmony_ci			    1 << (buddy->max_order - i));
1618c2ecf20Sopenharmony_ci	}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	set_bit(0, buddy->bits[buddy->max_order]);
1648c2ecf20Sopenharmony_ci	buddy->num_free[buddy->max_order] = 1;
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	return 0;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_cierr_out_free:
1698c2ecf20Sopenharmony_ci	for (i = 0; i <= buddy->max_order; ++i)
1708c2ecf20Sopenharmony_ci		kfree(buddy->bits[i]);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_cierr_out:
1738c2ecf20Sopenharmony_ci	kfree(buddy->bits);
1748c2ecf20Sopenharmony_ci	kfree(buddy->num_free);
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	return -ENOMEM;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic void mthca_buddy_cleanup(struct mthca_buddy *buddy)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	int i;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	for (i = 0; i <= buddy->max_order; ++i)
1848c2ecf20Sopenharmony_ci		kfree(buddy->bits[i]);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	kfree(buddy->bits);
1878c2ecf20Sopenharmony_ci	kfree(buddy->num_free);
1888c2ecf20Sopenharmony_ci}
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_cistatic u32 mthca_alloc_mtt_range(struct mthca_dev *dev, int order,
1918c2ecf20Sopenharmony_ci				 struct mthca_buddy *buddy)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	u32 seg = mthca_buddy_alloc(buddy, order);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (seg == -1)
1968c2ecf20Sopenharmony_ci		return -1;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (mthca_is_memfree(dev))
1998c2ecf20Sopenharmony_ci		if (mthca_table_get_range(dev, dev->mr_table.mtt_table, seg,
2008c2ecf20Sopenharmony_ci					  seg + (1 << order) - 1)) {
2018c2ecf20Sopenharmony_ci			mthca_buddy_free(buddy, seg, order);
2028c2ecf20Sopenharmony_ci			seg = -1;
2038c2ecf20Sopenharmony_ci		}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	return seg;
2068c2ecf20Sopenharmony_ci}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_cistatic struct mthca_mtt *__mthca_alloc_mtt(struct mthca_dev *dev, int size,
2098c2ecf20Sopenharmony_ci					   struct mthca_buddy *buddy)
2108c2ecf20Sopenharmony_ci{
2118c2ecf20Sopenharmony_ci	struct mthca_mtt *mtt;
2128c2ecf20Sopenharmony_ci	int i;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	if (size <= 0)
2158c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	mtt = kmalloc(sizeof *mtt, GFP_KERNEL);
2188c2ecf20Sopenharmony_ci	if (!mtt)
2198c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	mtt->buddy = buddy;
2228c2ecf20Sopenharmony_ci	mtt->order = 0;
2238c2ecf20Sopenharmony_ci	for (i = dev->limits.mtt_seg_size / 8; i < size; i <<= 1)
2248c2ecf20Sopenharmony_ci		++mtt->order;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	mtt->first_seg = mthca_alloc_mtt_range(dev, mtt->order, buddy);
2278c2ecf20Sopenharmony_ci	if (mtt->first_seg == -1) {
2288c2ecf20Sopenharmony_ci		kfree(mtt);
2298c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	return mtt;
2338c2ecf20Sopenharmony_ci}
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_cistruct mthca_mtt *mthca_alloc_mtt(struct mthca_dev *dev, int size)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	return __mthca_alloc_mtt(dev, size, &dev->mr_table.mtt_buddy);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_civoid mthca_free_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	if (!mtt)
2438c2ecf20Sopenharmony_ci		return;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	mthca_buddy_free(mtt->buddy, mtt->first_seg, mtt->order);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	mthca_table_put_range(dev, dev->mr_table.mtt_table,
2488c2ecf20Sopenharmony_ci			      mtt->first_seg,
2498c2ecf20Sopenharmony_ci			      mtt->first_seg + (1 << mtt->order) - 1);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	kfree(mtt);
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_cistatic int __mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt,
2558c2ecf20Sopenharmony_ci			     int start_index, u64 *buffer_list, int list_len)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct mthca_mailbox *mailbox;
2588c2ecf20Sopenharmony_ci	__be64 *mtt_entry;
2598c2ecf20Sopenharmony_ci	int err = 0;
2608c2ecf20Sopenharmony_ci	int i;
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
2638c2ecf20Sopenharmony_ci	if (IS_ERR(mailbox))
2648c2ecf20Sopenharmony_ci		return PTR_ERR(mailbox);
2658c2ecf20Sopenharmony_ci	mtt_entry = mailbox->buf;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	while (list_len > 0) {
2688c2ecf20Sopenharmony_ci		mtt_entry[0] = cpu_to_be64(dev->mr_table.mtt_base +
2698c2ecf20Sopenharmony_ci					   mtt->first_seg * dev->limits.mtt_seg_size +
2708c2ecf20Sopenharmony_ci					   start_index * 8);
2718c2ecf20Sopenharmony_ci		mtt_entry[1] = 0;
2728c2ecf20Sopenharmony_ci		for (i = 0; i < list_len && i < MTHCA_MAILBOX_SIZE / 8 - 2; ++i)
2738c2ecf20Sopenharmony_ci			mtt_entry[i + 2] = cpu_to_be64(buffer_list[i] |
2748c2ecf20Sopenharmony_ci						       MTHCA_MTT_FLAG_PRESENT);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci		/*
2778c2ecf20Sopenharmony_ci		 * If we have an odd number of entries to write, add
2788c2ecf20Sopenharmony_ci		 * one more dummy entry for firmware efficiency.
2798c2ecf20Sopenharmony_ci		 */
2808c2ecf20Sopenharmony_ci		if (i & 1)
2818c2ecf20Sopenharmony_ci			mtt_entry[i + 2] = 0;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci		err = mthca_WRITE_MTT(dev, mailbox, (i + 1) & ~1);
2848c2ecf20Sopenharmony_ci		if (err) {
2858c2ecf20Sopenharmony_ci			mthca_warn(dev, "WRITE_MTT failed (%d)\n", err);
2868c2ecf20Sopenharmony_ci			goto out;
2878c2ecf20Sopenharmony_ci		}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		list_len    -= i;
2908c2ecf20Sopenharmony_ci		start_index += i;
2918c2ecf20Sopenharmony_ci		buffer_list += i;
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ciout:
2958c2ecf20Sopenharmony_ci	mthca_free_mailbox(dev, mailbox);
2968c2ecf20Sopenharmony_ci	return err;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ciint mthca_write_mtt_size(struct mthca_dev *dev)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy ||
3028c2ecf20Sopenharmony_ci	    !(dev->mthca_flags & MTHCA_FLAG_FMR))
3038c2ecf20Sopenharmony_ci		/*
3048c2ecf20Sopenharmony_ci		 * Be friendly to WRITE_MTT command
3058c2ecf20Sopenharmony_ci		 * and leave two empty slots for the
3068c2ecf20Sopenharmony_ci		 * index and reserved fields of the
3078c2ecf20Sopenharmony_ci		 * mailbox.
3088c2ecf20Sopenharmony_ci		 */
3098c2ecf20Sopenharmony_ci		return PAGE_SIZE / sizeof (u64) - 2;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	/* For Arbel, all MTTs must fit in the same page. */
3128c2ecf20Sopenharmony_ci	return mthca_is_memfree(dev) ? (PAGE_SIZE / sizeof (u64)) : 0x7ffffff;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic void mthca_tavor_write_mtt_seg(struct mthca_dev *dev,
3168c2ecf20Sopenharmony_ci				      struct mthca_mtt *mtt, int start_index,
3178c2ecf20Sopenharmony_ci				      u64 *buffer_list, int list_len)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	u64 __iomem *mtts;
3208c2ecf20Sopenharmony_ci	int i;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	mtts = dev->mr_table.tavor_fmr.mtt_base + mtt->first_seg * dev->limits.mtt_seg_size +
3238c2ecf20Sopenharmony_ci		start_index * sizeof (u64);
3248c2ecf20Sopenharmony_ci	for (i = 0; i < list_len; ++i)
3258c2ecf20Sopenharmony_ci		mthca_write64_raw(cpu_to_be64(buffer_list[i] | MTHCA_MTT_FLAG_PRESENT),
3268c2ecf20Sopenharmony_ci				  mtts + i);
3278c2ecf20Sopenharmony_ci}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_cistatic void mthca_arbel_write_mtt_seg(struct mthca_dev *dev,
3308c2ecf20Sopenharmony_ci				      struct mthca_mtt *mtt, int start_index,
3318c2ecf20Sopenharmony_ci				      u64 *buffer_list, int list_len)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	__be64 *mtts;
3348c2ecf20Sopenharmony_ci	dma_addr_t dma_handle;
3358c2ecf20Sopenharmony_ci	int i;
3368c2ecf20Sopenharmony_ci	int s = start_index * sizeof (u64);
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	/* For Arbel, all MTTs must fit in the same page. */
3398c2ecf20Sopenharmony_ci	BUG_ON(s / PAGE_SIZE != (s + list_len * sizeof(u64) - 1) / PAGE_SIZE);
3408c2ecf20Sopenharmony_ci	/* Require full segments */
3418c2ecf20Sopenharmony_ci	BUG_ON(s % dev->limits.mtt_seg_size);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	mtts = mthca_table_find(dev->mr_table.mtt_table, mtt->first_seg +
3448c2ecf20Sopenharmony_ci				s / dev->limits.mtt_seg_size, &dma_handle);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	BUG_ON(!mtts);
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	dma_sync_single_for_cpu(&dev->pdev->dev, dma_handle,
3498c2ecf20Sopenharmony_ci				list_len * sizeof (u64), DMA_TO_DEVICE);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	for (i = 0; i < list_len; ++i)
3528c2ecf20Sopenharmony_ci		mtts[i] = cpu_to_be64(buffer_list[i] | MTHCA_MTT_FLAG_PRESENT);
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	dma_sync_single_for_device(&dev->pdev->dev, dma_handle,
3558c2ecf20Sopenharmony_ci				   list_len * sizeof (u64), DMA_TO_DEVICE);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ciint mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt,
3598c2ecf20Sopenharmony_ci		    int start_index, u64 *buffer_list, int list_len)
3608c2ecf20Sopenharmony_ci{
3618c2ecf20Sopenharmony_ci	int size = mthca_write_mtt_size(dev);
3628c2ecf20Sopenharmony_ci	int chunk;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy ||
3658c2ecf20Sopenharmony_ci	    !(dev->mthca_flags & MTHCA_FLAG_FMR))
3668c2ecf20Sopenharmony_ci		return __mthca_write_mtt(dev, mtt, start_index, buffer_list, list_len);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	while (list_len > 0) {
3698c2ecf20Sopenharmony_ci		chunk = min(size, list_len);
3708c2ecf20Sopenharmony_ci		if (mthca_is_memfree(dev))
3718c2ecf20Sopenharmony_ci			mthca_arbel_write_mtt_seg(dev, mtt, start_index,
3728c2ecf20Sopenharmony_ci						  buffer_list, chunk);
3738c2ecf20Sopenharmony_ci		else
3748c2ecf20Sopenharmony_ci			mthca_tavor_write_mtt_seg(dev, mtt, start_index,
3758c2ecf20Sopenharmony_ci						  buffer_list, chunk);
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci		list_len    -= chunk;
3788c2ecf20Sopenharmony_ci		start_index += chunk;
3798c2ecf20Sopenharmony_ci		buffer_list += chunk;
3808c2ecf20Sopenharmony_ci	}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	return 0;
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_cistatic inline u32 tavor_hw_index_to_key(u32 ind)
3868c2ecf20Sopenharmony_ci{
3878c2ecf20Sopenharmony_ci	return ind;
3888c2ecf20Sopenharmony_ci}
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_cistatic inline u32 tavor_key_to_hw_index(u32 key)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	return key;
3938c2ecf20Sopenharmony_ci}
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_cistatic inline u32 arbel_hw_index_to_key(u32 ind)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	return (ind >> 24) | (ind << 8);
3988c2ecf20Sopenharmony_ci}
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_cistatic inline u32 arbel_key_to_hw_index(u32 key)
4018c2ecf20Sopenharmony_ci{
4028c2ecf20Sopenharmony_ci	return (key << 24) | (key >> 8);
4038c2ecf20Sopenharmony_ci}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic inline u32 hw_index_to_key(struct mthca_dev *dev, u32 ind)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	if (mthca_is_memfree(dev))
4088c2ecf20Sopenharmony_ci		return arbel_hw_index_to_key(ind);
4098c2ecf20Sopenharmony_ci	else
4108c2ecf20Sopenharmony_ci		return tavor_hw_index_to_key(ind);
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic inline u32 key_to_hw_index(struct mthca_dev *dev, u32 key)
4148c2ecf20Sopenharmony_ci{
4158c2ecf20Sopenharmony_ci	if (mthca_is_memfree(dev))
4168c2ecf20Sopenharmony_ci		return arbel_key_to_hw_index(key);
4178c2ecf20Sopenharmony_ci	else
4188c2ecf20Sopenharmony_ci		return tavor_key_to_hw_index(key);
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic inline u32 adjust_key(struct mthca_dev *dev, u32 key)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	if (dev->mthca_flags & MTHCA_FLAG_SINAI_OPT)
4248c2ecf20Sopenharmony_ci		return ((key << 20) & 0x800000) | (key & 0x7fffff);
4258c2ecf20Sopenharmony_ci	else
4268c2ecf20Sopenharmony_ci		return key;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ciint mthca_mr_alloc(struct mthca_dev *dev, u32 pd, int buffer_size_shift,
4308c2ecf20Sopenharmony_ci		   u64 iova, u64 total_size, u32 access, struct mthca_mr *mr)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct mthca_mailbox *mailbox;
4338c2ecf20Sopenharmony_ci	struct mthca_mpt_entry *mpt_entry;
4348c2ecf20Sopenharmony_ci	u32 key;
4358c2ecf20Sopenharmony_ci	int i;
4368c2ecf20Sopenharmony_ci	int err;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	WARN_ON(buffer_size_shift >= 32);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	key = mthca_alloc(&dev->mr_table.mpt_alloc);
4418c2ecf20Sopenharmony_ci	if (key == -1)
4428c2ecf20Sopenharmony_ci		return -ENOMEM;
4438c2ecf20Sopenharmony_ci	key = adjust_key(dev, key);
4448c2ecf20Sopenharmony_ci	mr->ibmr.rkey = mr->ibmr.lkey = hw_index_to_key(dev, key);
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (mthca_is_memfree(dev)) {
4478c2ecf20Sopenharmony_ci		err = mthca_table_get(dev, dev->mr_table.mpt_table, key);
4488c2ecf20Sopenharmony_ci		if (err)
4498c2ecf20Sopenharmony_ci			goto err_out_mpt_free;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL);
4538c2ecf20Sopenharmony_ci	if (IS_ERR(mailbox)) {
4548c2ecf20Sopenharmony_ci		err = PTR_ERR(mailbox);
4558c2ecf20Sopenharmony_ci		goto err_out_table;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci	mpt_entry = mailbox->buf;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	mpt_entry->flags = cpu_to_be32(MTHCA_MPT_FLAG_SW_OWNS     |
4608c2ecf20Sopenharmony_ci				       MTHCA_MPT_FLAG_MIO         |
4618c2ecf20Sopenharmony_ci				       MTHCA_MPT_FLAG_REGION      |
4628c2ecf20Sopenharmony_ci				       access);
4638c2ecf20Sopenharmony_ci	if (!mr->mtt)
4648c2ecf20Sopenharmony_ci		mpt_entry->flags |= cpu_to_be32(MTHCA_MPT_FLAG_PHYSICAL);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	mpt_entry->page_size = cpu_to_be32(buffer_size_shift - 12);
4678c2ecf20Sopenharmony_ci	mpt_entry->key       = cpu_to_be32(key);
4688c2ecf20Sopenharmony_ci	mpt_entry->pd        = cpu_to_be32(pd);
4698c2ecf20Sopenharmony_ci	mpt_entry->start     = cpu_to_be64(iova);
4708c2ecf20Sopenharmony_ci	mpt_entry->length    = cpu_to_be64(total_size);
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	memset(&mpt_entry->lkey, 0,
4738c2ecf20Sopenharmony_ci	       sizeof *mpt_entry - offsetof(struct mthca_mpt_entry, lkey));
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_ci	if (mr->mtt)
4768c2ecf20Sopenharmony_ci		mpt_entry->mtt_seg =
4778c2ecf20Sopenharmony_ci			cpu_to_be64(dev->mr_table.mtt_base +
4788c2ecf20Sopenharmony_ci				    mr->mtt->first_seg * dev->limits.mtt_seg_size);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (0) {
4818c2ecf20Sopenharmony_ci		mthca_dbg(dev, "Dumping MPT entry %08x:\n", mr->ibmr.lkey);
4828c2ecf20Sopenharmony_ci		for (i = 0; i < sizeof (struct mthca_mpt_entry) / 4; ++i) {
4838c2ecf20Sopenharmony_ci			if (i % 4 == 0)
4848c2ecf20Sopenharmony_ci				printk("[%02x] ", i * 4);
4858c2ecf20Sopenharmony_ci			printk(" %08x", be32_to_cpu(((__be32 *) mpt_entry)[i]));
4868c2ecf20Sopenharmony_ci			if ((i + 1) % 4 == 0)
4878c2ecf20Sopenharmony_ci				printk("\n");
4888c2ecf20Sopenharmony_ci		}
4898c2ecf20Sopenharmony_ci	}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	err = mthca_SW2HW_MPT(dev, mailbox,
4928c2ecf20Sopenharmony_ci			      key & (dev->limits.num_mpts - 1));
4938c2ecf20Sopenharmony_ci	if (err) {
4948c2ecf20Sopenharmony_ci		mthca_warn(dev, "SW2HW_MPT failed (%d)\n", err);
4958c2ecf20Sopenharmony_ci		goto err_out_mailbox;
4968c2ecf20Sopenharmony_ci	}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci	mthca_free_mailbox(dev, mailbox);
4998c2ecf20Sopenharmony_ci	return err;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_cierr_out_mailbox:
5028c2ecf20Sopenharmony_ci	mthca_free_mailbox(dev, mailbox);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_cierr_out_table:
5058c2ecf20Sopenharmony_ci	mthca_table_put(dev, dev->mr_table.mpt_table, key);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_cierr_out_mpt_free:
5088c2ecf20Sopenharmony_ci	mthca_free(&dev->mr_table.mpt_alloc, key);
5098c2ecf20Sopenharmony_ci	return err;
5108c2ecf20Sopenharmony_ci}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ciint mthca_mr_alloc_notrans(struct mthca_dev *dev, u32 pd,
5138c2ecf20Sopenharmony_ci			   u32 access, struct mthca_mr *mr)
5148c2ecf20Sopenharmony_ci{
5158c2ecf20Sopenharmony_ci	mr->mtt = NULL;
5168c2ecf20Sopenharmony_ci	return mthca_mr_alloc(dev, pd, 12, 0, ~0ULL, access, mr);
5178c2ecf20Sopenharmony_ci}
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ciint mthca_mr_alloc_phys(struct mthca_dev *dev, u32 pd,
5208c2ecf20Sopenharmony_ci			u64 *buffer_list, int buffer_size_shift,
5218c2ecf20Sopenharmony_ci			int list_len, u64 iova, u64 total_size,
5228c2ecf20Sopenharmony_ci			u32 access, struct mthca_mr *mr)
5238c2ecf20Sopenharmony_ci{
5248c2ecf20Sopenharmony_ci	int err;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	mr->mtt = mthca_alloc_mtt(dev, list_len);
5278c2ecf20Sopenharmony_ci	if (IS_ERR(mr->mtt))
5288c2ecf20Sopenharmony_ci		return PTR_ERR(mr->mtt);
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	err = mthca_write_mtt(dev, mr->mtt, 0, buffer_list, list_len);
5318c2ecf20Sopenharmony_ci	if (err) {
5328c2ecf20Sopenharmony_ci		mthca_free_mtt(dev, mr->mtt);
5338c2ecf20Sopenharmony_ci		return err;
5348c2ecf20Sopenharmony_ci	}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	err = mthca_mr_alloc(dev, pd, buffer_size_shift, iova,
5378c2ecf20Sopenharmony_ci			     total_size, access, mr);
5388c2ecf20Sopenharmony_ci	if (err)
5398c2ecf20Sopenharmony_ci		mthca_free_mtt(dev, mr->mtt);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	return err;
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci/* Free mr */
5458c2ecf20Sopenharmony_cistatic void mthca_free_region(struct mthca_dev *dev, u32 lkey)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	mthca_table_put(dev, dev->mr_table.mpt_table,
5488c2ecf20Sopenharmony_ci			key_to_hw_index(dev, lkey));
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	mthca_free(&dev->mr_table.mpt_alloc, key_to_hw_index(dev, lkey));
5518c2ecf20Sopenharmony_ci}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_civoid mthca_free_mr(struct mthca_dev *dev, struct mthca_mr *mr)
5548c2ecf20Sopenharmony_ci{
5558c2ecf20Sopenharmony_ci	int err;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	err = mthca_HW2SW_MPT(dev, NULL,
5588c2ecf20Sopenharmony_ci			      key_to_hw_index(dev, mr->ibmr.lkey) &
5598c2ecf20Sopenharmony_ci			      (dev->limits.num_mpts - 1));
5608c2ecf20Sopenharmony_ci	if (err)
5618c2ecf20Sopenharmony_ci		mthca_warn(dev, "HW2SW_MPT failed (%d)\n", err);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	mthca_free_region(dev, mr->ibmr.lkey);
5648c2ecf20Sopenharmony_ci	mthca_free_mtt(dev, mr->mtt);
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ciint mthca_init_mr_table(struct mthca_dev *dev)
5688c2ecf20Sopenharmony_ci{
5698c2ecf20Sopenharmony_ci	phys_addr_t addr;
5708c2ecf20Sopenharmony_ci	int mpts, mtts, err, i;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	err = mthca_alloc_init(&dev->mr_table.mpt_alloc,
5738c2ecf20Sopenharmony_ci			       dev->limits.num_mpts,
5748c2ecf20Sopenharmony_ci			       ~0, dev->limits.reserved_mrws);
5758c2ecf20Sopenharmony_ci	if (err)
5768c2ecf20Sopenharmony_ci		return err;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	if (!mthca_is_memfree(dev) &&
5798c2ecf20Sopenharmony_ci	    (dev->mthca_flags & MTHCA_FLAG_DDR_HIDDEN))
5808c2ecf20Sopenharmony_ci		dev->limits.fmr_reserved_mtts = 0;
5818c2ecf20Sopenharmony_ci	else
5828c2ecf20Sopenharmony_ci		dev->mthca_flags |= MTHCA_FLAG_FMR;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	if (dev->mthca_flags & MTHCA_FLAG_SINAI_OPT)
5858c2ecf20Sopenharmony_ci		mthca_dbg(dev, "Memory key throughput optimization activated.\n");
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	err = mthca_buddy_init(&dev->mr_table.mtt_buddy,
5888c2ecf20Sopenharmony_ci			       fls(dev->limits.num_mtt_segs - 1));
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	if (err)
5918c2ecf20Sopenharmony_ci		goto err_mtt_buddy;
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	dev->mr_table.tavor_fmr.mpt_base = NULL;
5948c2ecf20Sopenharmony_ci	dev->mr_table.tavor_fmr.mtt_base = NULL;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	if (dev->limits.fmr_reserved_mtts) {
5978c2ecf20Sopenharmony_ci		i = fls(dev->limits.fmr_reserved_mtts - 1);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci		if (i >= 31) {
6008c2ecf20Sopenharmony_ci			mthca_warn(dev, "Unable to reserve 2^31 FMR MTTs.\n");
6018c2ecf20Sopenharmony_ci			err = -EINVAL;
6028c2ecf20Sopenharmony_ci			goto err_fmr_mpt;
6038c2ecf20Sopenharmony_ci		}
6048c2ecf20Sopenharmony_ci		mpts = mtts = 1 << i;
6058c2ecf20Sopenharmony_ci	} else {
6068c2ecf20Sopenharmony_ci		mtts = dev->limits.num_mtt_segs;
6078c2ecf20Sopenharmony_ci		mpts = dev->limits.num_mpts;
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	if (!mthca_is_memfree(dev) &&
6118c2ecf20Sopenharmony_ci	    (dev->mthca_flags & MTHCA_FLAG_FMR)) {
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci		addr = pci_resource_start(dev->pdev, 4) +
6148c2ecf20Sopenharmony_ci			((pci_resource_len(dev->pdev, 4) - 1) &
6158c2ecf20Sopenharmony_ci			 dev->mr_table.mpt_base);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		dev->mr_table.tavor_fmr.mpt_base =
6188c2ecf20Sopenharmony_ci			ioremap(addr, mpts * sizeof(struct mthca_mpt_entry));
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci		if (!dev->mr_table.tavor_fmr.mpt_base) {
6218c2ecf20Sopenharmony_ci			mthca_warn(dev, "MPT ioremap for FMR failed.\n");
6228c2ecf20Sopenharmony_ci			err = -ENOMEM;
6238c2ecf20Sopenharmony_ci			goto err_fmr_mpt;
6248c2ecf20Sopenharmony_ci		}
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci		addr = pci_resource_start(dev->pdev, 4) +
6278c2ecf20Sopenharmony_ci			((pci_resource_len(dev->pdev, 4) - 1) &
6288c2ecf20Sopenharmony_ci			 dev->mr_table.mtt_base);
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci		dev->mr_table.tavor_fmr.mtt_base =
6318c2ecf20Sopenharmony_ci			ioremap(addr, mtts * dev->limits.mtt_seg_size);
6328c2ecf20Sopenharmony_ci		if (!dev->mr_table.tavor_fmr.mtt_base) {
6338c2ecf20Sopenharmony_ci			mthca_warn(dev, "MTT ioremap for FMR failed.\n");
6348c2ecf20Sopenharmony_ci			err = -ENOMEM;
6358c2ecf20Sopenharmony_ci			goto err_fmr_mtt;
6368c2ecf20Sopenharmony_ci		}
6378c2ecf20Sopenharmony_ci	}
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (dev->limits.fmr_reserved_mtts) {
6408c2ecf20Sopenharmony_ci		err = mthca_buddy_init(&dev->mr_table.tavor_fmr.mtt_buddy, fls(mtts - 1));
6418c2ecf20Sopenharmony_ci		if (err)
6428c2ecf20Sopenharmony_ci			goto err_fmr_mtt_buddy;
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci		/* Prevent regular MRs from using FMR keys */
6458c2ecf20Sopenharmony_ci		err = mthca_buddy_alloc(&dev->mr_table.mtt_buddy, fls(mtts - 1));
6468c2ecf20Sopenharmony_ci		if (err)
6478c2ecf20Sopenharmony_ci			goto err_reserve_fmr;
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_ci		dev->mr_table.fmr_mtt_buddy =
6508c2ecf20Sopenharmony_ci			&dev->mr_table.tavor_fmr.mtt_buddy;
6518c2ecf20Sopenharmony_ci	} else
6528c2ecf20Sopenharmony_ci		dev->mr_table.fmr_mtt_buddy = &dev->mr_table.mtt_buddy;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	/* FMR table is always the first, take reserved MTTs out of there */
6558c2ecf20Sopenharmony_ci	if (dev->limits.reserved_mtts) {
6568c2ecf20Sopenharmony_ci		i = fls(dev->limits.reserved_mtts - 1);
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci		if (mthca_alloc_mtt_range(dev, i,
6598c2ecf20Sopenharmony_ci					  dev->mr_table.fmr_mtt_buddy) == -1) {
6608c2ecf20Sopenharmony_ci			mthca_warn(dev, "MTT table of order %d is too small.\n",
6618c2ecf20Sopenharmony_ci				  dev->mr_table.fmr_mtt_buddy->max_order);
6628c2ecf20Sopenharmony_ci			err = -ENOMEM;
6638c2ecf20Sopenharmony_ci			goto err_reserve_mtts;
6648c2ecf20Sopenharmony_ci		}
6658c2ecf20Sopenharmony_ci	}
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	return 0;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_cierr_reserve_mtts:
6708c2ecf20Sopenharmony_cierr_reserve_fmr:
6718c2ecf20Sopenharmony_ci	if (dev->limits.fmr_reserved_mtts)
6728c2ecf20Sopenharmony_ci		mthca_buddy_cleanup(&dev->mr_table.tavor_fmr.mtt_buddy);
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_cierr_fmr_mtt_buddy:
6758c2ecf20Sopenharmony_ci	if (dev->mr_table.tavor_fmr.mtt_base)
6768c2ecf20Sopenharmony_ci		iounmap(dev->mr_table.tavor_fmr.mtt_base);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_cierr_fmr_mtt:
6798c2ecf20Sopenharmony_ci	if (dev->mr_table.tavor_fmr.mpt_base)
6808c2ecf20Sopenharmony_ci		iounmap(dev->mr_table.tavor_fmr.mpt_base);
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_cierr_fmr_mpt:
6838c2ecf20Sopenharmony_ci	mthca_buddy_cleanup(&dev->mr_table.mtt_buddy);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_cierr_mtt_buddy:
6868c2ecf20Sopenharmony_ci	mthca_alloc_cleanup(&dev->mr_table.mpt_alloc);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	return err;
6898c2ecf20Sopenharmony_ci}
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_civoid mthca_cleanup_mr_table(struct mthca_dev *dev)
6928c2ecf20Sopenharmony_ci{
6938c2ecf20Sopenharmony_ci	/* XXX check if any MRs are still allocated? */
6948c2ecf20Sopenharmony_ci	if (dev->limits.fmr_reserved_mtts)
6958c2ecf20Sopenharmony_ci		mthca_buddy_cleanup(&dev->mr_table.tavor_fmr.mtt_buddy);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	mthca_buddy_cleanup(&dev->mr_table.mtt_buddy);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	if (dev->mr_table.tavor_fmr.mtt_base)
7008c2ecf20Sopenharmony_ci		iounmap(dev->mr_table.tavor_fmr.mtt_base);
7018c2ecf20Sopenharmony_ci	if (dev->mr_table.tavor_fmr.mpt_base)
7028c2ecf20Sopenharmony_ci		iounmap(dev->mr_table.tavor_fmr.mpt_base);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci	mthca_alloc_cleanup(&dev->mr_table.mpt_alloc);
7058c2ecf20Sopenharmony_ci}
706