162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2004 Topspin Communications. All rights reserved. 362306a36Sopenharmony_ci * Copyright (c) 2005 Mellanox Technologies. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This software is available to you under a choice of one of two 662306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 762306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 862306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 962306a36Sopenharmony_ci * OpenIB.org BSD license below: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1262306a36Sopenharmony_ci * without modification, are permitted provided that the following 1362306a36Sopenharmony_ci * conditions are met: 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1662306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1762306a36Sopenharmony_ci * disclaimer. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 2062306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2162306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2262306a36Sopenharmony_ci * provided with the distribution. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2562306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2662306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2762306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2862306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2962306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3062306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3162306a36Sopenharmony_ci * SOFTWARE. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <linux/slab.h> 3562306a36Sopenharmony_ci#include <linux/errno.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "mthca_dev.h" 3862306a36Sopenharmony_ci#include "mthca_cmd.h" 3962306a36Sopenharmony_ci#include "mthca_memfree.h" 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistruct mthca_mtt { 4262306a36Sopenharmony_ci struct mthca_buddy *buddy; 4362306a36Sopenharmony_ci int order; 4462306a36Sopenharmony_ci u32 first_seg; 4562306a36Sopenharmony_ci}; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* 4862306a36Sopenharmony_ci * Must be packed because mtt_seg is 64 bits but only aligned to 32 bits. 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_cistruct mthca_mpt_entry { 5162306a36Sopenharmony_ci __be32 flags; 5262306a36Sopenharmony_ci __be32 page_size; 5362306a36Sopenharmony_ci __be32 key; 5462306a36Sopenharmony_ci __be32 pd; 5562306a36Sopenharmony_ci __be64 start; 5662306a36Sopenharmony_ci __be64 length; 5762306a36Sopenharmony_ci __be32 lkey; 5862306a36Sopenharmony_ci __be32 window_count; 5962306a36Sopenharmony_ci __be32 window_count_limit; 6062306a36Sopenharmony_ci __be64 mtt_seg; 6162306a36Sopenharmony_ci __be32 mtt_sz; /* Arbel only */ 6262306a36Sopenharmony_ci u32 reserved[2]; 6362306a36Sopenharmony_ci} __packed; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci#define MTHCA_MPT_FLAG_SW_OWNS (0xfUL << 28) 6662306a36Sopenharmony_ci#define MTHCA_MPT_FLAG_MIO (1 << 17) 6762306a36Sopenharmony_ci#define MTHCA_MPT_FLAG_BIND_ENABLE (1 << 15) 6862306a36Sopenharmony_ci#define MTHCA_MPT_FLAG_PHYSICAL (1 << 9) 6962306a36Sopenharmony_ci#define MTHCA_MPT_FLAG_REGION (1 << 8) 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#define MTHCA_MTT_FLAG_PRESENT 1 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define MTHCA_MPT_STATUS_SW 0xF0 7462306a36Sopenharmony_ci#define MTHCA_MPT_STATUS_HW 0x00 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define SINAI_FMR_KEY_INC 0x1000000 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* 7962306a36Sopenharmony_ci * Buddy allocator for MTT segments (currently not very efficient 8062306a36Sopenharmony_ci * since it doesn't keep a free list and just searches linearly 8162306a36Sopenharmony_ci * through the bitmaps) 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic u32 mthca_buddy_alloc(struct mthca_buddy *buddy, int order) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci int o; 8762306a36Sopenharmony_ci int m; 8862306a36Sopenharmony_ci u32 seg; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci spin_lock(&buddy->lock); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci for (o = order; o <= buddy->max_order; ++o) 9362306a36Sopenharmony_ci if (buddy->num_free[o]) { 9462306a36Sopenharmony_ci m = 1 << (buddy->max_order - o); 9562306a36Sopenharmony_ci seg = find_first_bit(buddy->bits[o], m); 9662306a36Sopenharmony_ci if (seg < m) 9762306a36Sopenharmony_ci goto found; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci spin_unlock(&buddy->lock); 10162306a36Sopenharmony_ci return -1; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci found: 10462306a36Sopenharmony_ci __clear_bit(seg, buddy->bits[o]); 10562306a36Sopenharmony_ci --buddy->num_free[o]; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci while (o > order) { 10862306a36Sopenharmony_ci --o; 10962306a36Sopenharmony_ci seg <<= 1; 11062306a36Sopenharmony_ci __set_bit(seg ^ 1, buddy->bits[o]); 11162306a36Sopenharmony_ci ++buddy->num_free[o]; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci spin_unlock(&buddy->lock); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci seg <<= order; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return seg; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic void mthca_buddy_free(struct mthca_buddy *buddy, u32 seg, int order) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci seg >>= order; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci spin_lock(&buddy->lock); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci while (test_bit(seg ^ 1, buddy->bits[order])) { 12862306a36Sopenharmony_ci __clear_bit(seg ^ 1, buddy->bits[order]); 12962306a36Sopenharmony_ci --buddy->num_free[order]; 13062306a36Sopenharmony_ci seg >>= 1; 13162306a36Sopenharmony_ci ++order; 13262306a36Sopenharmony_ci } 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci __set_bit(seg, buddy->bits[order]); 13562306a36Sopenharmony_ci ++buddy->num_free[order]; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci spin_unlock(&buddy->lock); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic int mthca_buddy_init(struct mthca_buddy *buddy, int max_order) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci int i; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci buddy->max_order = max_order; 14562306a36Sopenharmony_ci spin_lock_init(&buddy->lock); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci buddy->bits = kcalloc(buddy->max_order + 1, sizeof(long *), 14862306a36Sopenharmony_ci GFP_KERNEL); 14962306a36Sopenharmony_ci buddy->num_free = kcalloc((buddy->max_order + 1), sizeof *buddy->num_free, 15062306a36Sopenharmony_ci GFP_KERNEL); 15162306a36Sopenharmony_ci if (!buddy->bits || !buddy->num_free) 15262306a36Sopenharmony_ci goto err_out; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci for (i = 0; i <= buddy->max_order; ++i) { 15562306a36Sopenharmony_ci buddy->bits[i] = bitmap_zalloc(1 << (buddy->max_order - i), 15662306a36Sopenharmony_ci GFP_KERNEL); 15762306a36Sopenharmony_ci if (!buddy->bits[i]) 15862306a36Sopenharmony_ci goto err_out_free; 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci __set_bit(0, buddy->bits[buddy->max_order]); 16262306a36Sopenharmony_ci buddy->num_free[buddy->max_order] = 1; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci return 0; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cierr_out_free: 16762306a36Sopenharmony_ci for (i = 0; i <= buddy->max_order; ++i) 16862306a36Sopenharmony_ci bitmap_free(buddy->bits[i]); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cierr_out: 17162306a36Sopenharmony_ci kfree(buddy->bits); 17262306a36Sopenharmony_ci kfree(buddy->num_free); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci return -ENOMEM; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void mthca_buddy_cleanup(struct mthca_buddy *buddy) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci int i; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci for (i = 0; i <= buddy->max_order; ++i) 18262306a36Sopenharmony_ci bitmap_free(buddy->bits[i]); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci kfree(buddy->bits); 18562306a36Sopenharmony_ci kfree(buddy->num_free); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic u32 mthca_alloc_mtt_range(struct mthca_dev *dev, int order, 18962306a36Sopenharmony_ci struct mthca_buddy *buddy) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci u32 seg = mthca_buddy_alloc(buddy, order); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci if (seg == -1) 19462306a36Sopenharmony_ci return -1; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (mthca_is_memfree(dev)) 19762306a36Sopenharmony_ci if (mthca_table_get_range(dev, dev->mr_table.mtt_table, seg, 19862306a36Sopenharmony_ci seg + (1 << order) - 1)) { 19962306a36Sopenharmony_ci mthca_buddy_free(buddy, seg, order); 20062306a36Sopenharmony_ci seg = -1; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return seg; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic struct mthca_mtt *__mthca_alloc_mtt(struct mthca_dev *dev, int size, 20762306a36Sopenharmony_ci struct mthca_buddy *buddy) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci struct mthca_mtt *mtt; 21062306a36Sopenharmony_ci int i; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci if (size <= 0) 21362306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci mtt = kmalloc(sizeof *mtt, GFP_KERNEL); 21662306a36Sopenharmony_ci if (!mtt) 21762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci mtt->buddy = buddy; 22062306a36Sopenharmony_ci mtt->order = 0; 22162306a36Sopenharmony_ci for (i = dev->limits.mtt_seg_size / 8; i < size; i <<= 1) 22262306a36Sopenharmony_ci ++mtt->order; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci mtt->first_seg = mthca_alloc_mtt_range(dev, mtt->order, buddy); 22562306a36Sopenharmony_ci if (mtt->first_seg == -1) { 22662306a36Sopenharmony_ci kfree(mtt); 22762306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci return mtt; 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistruct mthca_mtt *mthca_alloc_mtt(struct mthca_dev *dev, int size) 23462306a36Sopenharmony_ci{ 23562306a36Sopenharmony_ci return __mthca_alloc_mtt(dev, size, &dev->mr_table.mtt_buddy); 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_civoid mthca_free_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci if (!mtt) 24162306a36Sopenharmony_ci return; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci mthca_buddy_free(mtt->buddy, mtt->first_seg, mtt->order); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci mthca_table_put_range(dev, dev->mr_table.mtt_table, 24662306a36Sopenharmony_ci mtt->first_seg, 24762306a36Sopenharmony_ci mtt->first_seg + (1 << mtt->order) - 1); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci kfree(mtt); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_cistatic int __mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt, 25362306a36Sopenharmony_ci int start_index, u64 *buffer_list, int list_len) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct mthca_mailbox *mailbox; 25662306a36Sopenharmony_ci __be64 *mtt_entry; 25762306a36Sopenharmony_ci int err = 0; 25862306a36Sopenharmony_ci int i; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); 26162306a36Sopenharmony_ci if (IS_ERR(mailbox)) 26262306a36Sopenharmony_ci return PTR_ERR(mailbox); 26362306a36Sopenharmony_ci mtt_entry = mailbox->buf; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci while (list_len > 0) { 26662306a36Sopenharmony_ci mtt_entry[0] = cpu_to_be64(dev->mr_table.mtt_base + 26762306a36Sopenharmony_ci mtt->first_seg * dev->limits.mtt_seg_size + 26862306a36Sopenharmony_ci start_index * 8); 26962306a36Sopenharmony_ci mtt_entry[1] = 0; 27062306a36Sopenharmony_ci for (i = 0; i < list_len && i < MTHCA_MAILBOX_SIZE / 8 - 2; ++i) 27162306a36Sopenharmony_ci mtt_entry[i + 2] = cpu_to_be64(buffer_list[i] | 27262306a36Sopenharmony_ci MTHCA_MTT_FLAG_PRESENT); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * If we have an odd number of entries to write, add 27662306a36Sopenharmony_ci * one more dummy entry for firmware efficiency. 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci if (i & 1) 27962306a36Sopenharmony_ci mtt_entry[i + 2] = 0; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci err = mthca_WRITE_MTT(dev, mailbox, (i + 1) & ~1); 28262306a36Sopenharmony_ci if (err) { 28362306a36Sopenharmony_ci mthca_warn(dev, "WRITE_MTT failed (%d)\n", err); 28462306a36Sopenharmony_ci goto out; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci list_len -= i; 28862306a36Sopenharmony_ci start_index += i; 28962306a36Sopenharmony_ci buffer_list += i; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ciout: 29362306a36Sopenharmony_ci mthca_free_mailbox(dev, mailbox); 29462306a36Sopenharmony_ci return err; 29562306a36Sopenharmony_ci} 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ciint mthca_write_mtt_size(struct mthca_dev *dev) 29862306a36Sopenharmony_ci{ 29962306a36Sopenharmony_ci if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy || 30062306a36Sopenharmony_ci !(dev->mthca_flags & MTHCA_FLAG_FMR)) 30162306a36Sopenharmony_ci /* 30262306a36Sopenharmony_ci * Be friendly to WRITE_MTT command 30362306a36Sopenharmony_ci * and leave two empty slots for the 30462306a36Sopenharmony_ci * index and reserved fields of the 30562306a36Sopenharmony_ci * mailbox. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_ci return PAGE_SIZE / sizeof (u64) - 2; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci /* For Arbel, all MTTs must fit in the same page. */ 31062306a36Sopenharmony_ci return mthca_is_memfree(dev) ? (PAGE_SIZE / sizeof (u64)) : 0x7ffffff; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic void mthca_tavor_write_mtt_seg(struct mthca_dev *dev, 31462306a36Sopenharmony_ci struct mthca_mtt *mtt, int start_index, 31562306a36Sopenharmony_ci u64 *buffer_list, int list_len) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci u64 __iomem *mtts; 31862306a36Sopenharmony_ci int i; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci mtts = dev->mr_table.tavor_fmr.mtt_base + mtt->first_seg * dev->limits.mtt_seg_size + 32162306a36Sopenharmony_ci start_index * sizeof (u64); 32262306a36Sopenharmony_ci for (i = 0; i < list_len; ++i) 32362306a36Sopenharmony_ci mthca_write64_raw(cpu_to_be64(buffer_list[i] | MTHCA_MTT_FLAG_PRESENT), 32462306a36Sopenharmony_ci mtts + i); 32562306a36Sopenharmony_ci} 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_cistatic void mthca_arbel_write_mtt_seg(struct mthca_dev *dev, 32862306a36Sopenharmony_ci struct mthca_mtt *mtt, int start_index, 32962306a36Sopenharmony_ci u64 *buffer_list, int list_len) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci __be64 *mtts; 33262306a36Sopenharmony_ci dma_addr_t dma_handle; 33362306a36Sopenharmony_ci int i; 33462306a36Sopenharmony_ci int s = start_index * sizeof (u64); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci /* For Arbel, all MTTs must fit in the same page. */ 33762306a36Sopenharmony_ci BUG_ON(s / PAGE_SIZE != (s + list_len * sizeof(u64) - 1) / PAGE_SIZE); 33862306a36Sopenharmony_ci /* Require full segments */ 33962306a36Sopenharmony_ci BUG_ON(s % dev->limits.mtt_seg_size); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci mtts = mthca_table_find(dev->mr_table.mtt_table, mtt->first_seg + 34262306a36Sopenharmony_ci s / dev->limits.mtt_seg_size, &dma_handle); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci BUG_ON(!mtts); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci dma_sync_single_for_cpu(&dev->pdev->dev, dma_handle, 34762306a36Sopenharmony_ci list_len * sizeof (u64), DMA_TO_DEVICE); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci for (i = 0; i < list_len; ++i) 35062306a36Sopenharmony_ci mtts[i] = cpu_to_be64(buffer_list[i] | MTHCA_MTT_FLAG_PRESENT); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci dma_sync_single_for_device(&dev->pdev->dev, dma_handle, 35362306a36Sopenharmony_ci list_len * sizeof (u64), DMA_TO_DEVICE); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciint mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt, 35762306a36Sopenharmony_ci int start_index, u64 *buffer_list, int list_len) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci int size = mthca_write_mtt_size(dev); 36062306a36Sopenharmony_ci int chunk; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (dev->mr_table.fmr_mtt_buddy != &dev->mr_table.mtt_buddy || 36362306a36Sopenharmony_ci !(dev->mthca_flags & MTHCA_FLAG_FMR)) 36462306a36Sopenharmony_ci return __mthca_write_mtt(dev, mtt, start_index, buffer_list, list_len); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci while (list_len > 0) { 36762306a36Sopenharmony_ci chunk = min(size, list_len); 36862306a36Sopenharmony_ci if (mthca_is_memfree(dev)) 36962306a36Sopenharmony_ci mthca_arbel_write_mtt_seg(dev, mtt, start_index, 37062306a36Sopenharmony_ci buffer_list, chunk); 37162306a36Sopenharmony_ci else 37262306a36Sopenharmony_ci mthca_tavor_write_mtt_seg(dev, mtt, start_index, 37362306a36Sopenharmony_ci buffer_list, chunk); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci list_len -= chunk; 37662306a36Sopenharmony_ci start_index += chunk; 37762306a36Sopenharmony_ci buffer_list += chunk; 37862306a36Sopenharmony_ci } 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_cistatic inline u32 tavor_hw_index_to_key(u32 ind) 38462306a36Sopenharmony_ci{ 38562306a36Sopenharmony_ci return ind; 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic inline u32 tavor_key_to_hw_index(u32 key) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci return key; 39162306a36Sopenharmony_ci} 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_cistatic inline u32 arbel_hw_index_to_key(u32 ind) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci return (ind >> 24) | (ind << 8); 39662306a36Sopenharmony_ci} 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_cistatic inline u32 arbel_key_to_hw_index(u32 key) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci return (key << 24) | (key >> 8); 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_cistatic inline u32 hw_index_to_key(struct mthca_dev *dev, u32 ind) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci if (mthca_is_memfree(dev)) 40662306a36Sopenharmony_ci return arbel_hw_index_to_key(ind); 40762306a36Sopenharmony_ci else 40862306a36Sopenharmony_ci return tavor_hw_index_to_key(ind); 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic inline u32 key_to_hw_index(struct mthca_dev *dev, u32 key) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci if (mthca_is_memfree(dev)) 41462306a36Sopenharmony_ci return arbel_key_to_hw_index(key); 41562306a36Sopenharmony_ci else 41662306a36Sopenharmony_ci return tavor_key_to_hw_index(key); 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic inline u32 adjust_key(struct mthca_dev *dev, u32 key) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci if (dev->mthca_flags & MTHCA_FLAG_SINAI_OPT) 42262306a36Sopenharmony_ci return ((key << 20) & 0x800000) | (key & 0x7fffff); 42362306a36Sopenharmony_ci else 42462306a36Sopenharmony_ci return key; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ciint mthca_mr_alloc(struct mthca_dev *dev, u32 pd, int buffer_size_shift, 42862306a36Sopenharmony_ci u64 iova, u64 total_size, u32 access, struct mthca_mr *mr) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct mthca_mailbox *mailbox; 43162306a36Sopenharmony_ci struct mthca_mpt_entry *mpt_entry; 43262306a36Sopenharmony_ci u32 key; 43362306a36Sopenharmony_ci int i; 43462306a36Sopenharmony_ci int err; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci WARN_ON(buffer_size_shift >= 32); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci key = mthca_alloc(&dev->mr_table.mpt_alloc); 43962306a36Sopenharmony_ci if (key == -1) 44062306a36Sopenharmony_ci return -ENOMEM; 44162306a36Sopenharmony_ci key = adjust_key(dev, key); 44262306a36Sopenharmony_ci mr->ibmr.rkey = mr->ibmr.lkey = hw_index_to_key(dev, key); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (mthca_is_memfree(dev)) { 44562306a36Sopenharmony_ci err = mthca_table_get(dev, dev->mr_table.mpt_table, key); 44662306a36Sopenharmony_ci if (err) 44762306a36Sopenharmony_ci goto err_out_mpt_free; 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); 45162306a36Sopenharmony_ci if (IS_ERR(mailbox)) { 45262306a36Sopenharmony_ci err = PTR_ERR(mailbox); 45362306a36Sopenharmony_ci goto err_out_table; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci mpt_entry = mailbox->buf; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci mpt_entry->flags = cpu_to_be32(MTHCA_MPT_FLAG_SW_OWNS | 45862306a36Sopenharmony_ci MTHCA_MPT_FLAG_MIO | 45962306a36Sopenharmony_ci MTHCA_MPT_FLAG_REGION | 46062306a36Sopenharmony_ci access); 46162306a36Sopenharmony_ci if (!mr->mtt) 46262306a36Sopenharmony_ci mpt_entry->flags |= cpu_to_be32(MTHCA_MPT_FLAG_PHYSICAL); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci mpt_entry->page_size = cpu_to_be32(buffer_size_shift - 12); 46562306a36Sopenharmony_ci mpt_entry->key = cpu_to_be32(key); 46662306a36Sopenharmony_ci mpt_entry->pd = cpu_to_be32(pd); 46762306a36Sopenharmony_ci mpt_entry->start = cpu_to_be64(iova); 46862306a36Sopenharmony_ci mpt_entry->length = cpu_to_be64(total_size); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci memset_startat(mpt_entry, 0, lkey); 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci if (mr->mtt) 47362306a36Sopenharmony_ci mpt_entry->mtt_seg = 47462306a36Sopenharmony_ci cpu_to_be64(dev->mr_table.mtt_base + 47562306a36Sopenharmony_ci mr->mtt->first_seg * dev->limits.mtt_seg_size); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci if (0) { 47862306a36Sopenharmony_ci mthca_dbg(dev, "Dumping MPT entry %08x:\n", mr->ibmr.lkey); 47962306a36Sopenharmony_ci for (i = 0; i < sizeof (struct mthca_mpt_entry) / 4; ++i) { 48062306a36Sopenharmony_ci if (i % 4 == 0) 48162306a36Sopenharmony_ci printk("[%02x] ", i * 4); 48262306a36Sopenharmony_ci printk(" %08x", be32_to_cpu(((__be32 *) mpt_entry)[i])); 48362306a36Sopenharmony_ci if ((i + 1) % 4 == 0) 48462306a36Sopenharmony_ci printk("\n"); 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci } 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci err = mthca_SW2HW_MPT(dev, mailbox, 48962306a36Sopenharmony_ci key & (dev->limits.num_mpts - 1)); 49062306a36Sopenharmony_ci if (err) { 49162306a36Sopenharmony_ci mthca_warn(dev, "SW2HW_MPT failed (%d)\n", err); 49262306a36Sopenharmony_ci goto err_out_mailbox; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci mthca_free_mailbox(dev, mailbox); 49662306a36Sopenharmony_ci return err; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_cierr_out_mailbox: 49962306a36Sopenharmony_ci mthca_free_mailbox(dev, mailbox); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cierr_out_table: 50262306a36Sopenharmony_ci mthca_table_put(dev, dev->mr_table.mpt_table, key); 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cierr_out_mpt_free: 50562306a36Sopenharmony_ci mthca_free(&dev->mr_table.mpt_alloc, key); 50662306a36Sopenharmony_ci return err; 50762306a36Sopenharmony_ci} 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ciint mthca_mr_alloc_notrans(struct mthca_dev *dev, u32 pd, 51062306a36Sopenharmony_ci u32 access, struct mthca_mr *mr) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci mr->mtt = NULL; 51362306a36Sopenharmony_ci return mthca_mr_alloc(dev, pd, 12, 0, ~0ULL, access, mr); 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ciint mthca_mr_alloc_phys(struct mthca_dev *dev, u32 pd, 51762306a36Sopenharmony_ci u64 *buffer_list, int buffer_size_shift, 51862306a36Sopenharmony_ci int list_len, u64 iova, u64 total_size, 51962306a36Sopenharmony_ci u32 access, struct mthca_mr *mr) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci int err; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci mr->mtt = mthca_alloc_mtt(dev, list_len); 52462306a36Sopenharmony_ci if (IS_ERR(mr->mtt)) 52562306a36Sopenharmony_ci return PTR_ERR(mr->mtt); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci err = mthca_write_mtt(dev, mr->mtt, 0, buffer_list, list_len); 52862306a36Sopenharmony_ci if (err) { 52962306a36Sopenharmony_ci mthca_free_mtt(dev, mr->mtt); 53062306a36Sopenharmony_ci return err; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci err = mthca_mr_alloc(dev, pd, buffer_size_shift, iova, 53462306a36Sopenharmony_ci total_size, access, mr); 53562306a36Sopenharmony_ci if (err) 53662306a36Sopenharmony_ci mthca_free_mtt(dev, mr->mtt); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci return err; 53962306a36Sopenharmony_ci} 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci/* Free mr */ 54262306a36Sopenharmony_cistatic void mthca_free_region(struct mthca_dev *dev, u32 lkey) 54362306a36Sopenharmony_ci{ 54462306a36Sopenharmony_ci mthca_table_put(dev, dev->mr_table.mpt_table, 54562306a36Sopenharmony_ci key_to_hw_index(dev, lkey)); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci mthca_free(&dev->mr_table.mpt_alloc, key_to_hw_index(dev, lkey)); 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_civoid mthca_free_mr(struct mthca_dev *dev, struct mthca_mr *mr) 55162306a36Sopenharmony_ci{ 55262306a36Sopenharmony_ci int err; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci err = mthca_HW2SW_MPT(dev, NULL, 55562306a36Sopenharmony_ci key_to_hw_index(dev, mr->ibmr.lkey) & 55662306a36Sopenharmony_ci (dev->limits.num_mpts - 1)); 55762306a36Sopenharmony_ci if (err) 55862306a36Sopenharmony_ci mthca_warn(dev, "HW2SW_MPT failed (%d)\n", err); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci mthca_free_region(dev, mr->ibmr.lkey); 56162306a36Sopenharmony_ci mthca_free_mtt(dev, mr->mtt); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ciint mthca_init_mr_table(struct mthca_dev *dev) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci phys_addr_t addr; 56762306a36Sopenharmony_ci int mpts, mtts, err, i; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci err = mthca_alloc_init(&dev->mr_table.mpt_alloc, 57062306a36Sopenharmony_ci dev->limits.num_mpts, 57162306a36Sopenharmony_ci ~0, dev->limits.reserved_mrws); 57262306a36Sopenharmony_ci if (err) 57362306a36Sopenharmony_ci return err; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (!mthca_is_memfree(dev) && 57662306a36Sopenharmony_ci (dev->mthca_flags & MTHCA_FLAG_DDR_HIDDEN)) 57762306a36Sopenharmony_ci dev->limits.fmr_reserved_mtts = 0; 57862306a36Sopenharmony_ci else 57962306a36Sopenharmony_ci dev->mthca_flags |= MTHCA_FLAG_FMR; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci if (dev->mthca_flags & MTHCA_FLAG_SINAI_OPT) 58262306a36Sopenharmony_ci mthca_dbg(dev, "Memory key throughput optimization activated.\n"); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci err = mthca_buddy_init(&dev->mr_table.mtt_buddy, 58562306a36Sopenharmony_ci fls(dev->limits.num_mtt_segs - 1)); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (err) 58862306a36Sopenharmony_ci goto err_mtt_buddy; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci dev->mr_table.tavor_fmr.mpt_base = NULL; 59162306a36Sopenharmony_ci dev->mr_table.tavor_fmr.mtt_base = NULL; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (dev->limits.fmr_reserved_mtts) { 59462306a36Sopenharmony_ci i = fls(dev->limits.fmr_reserved_mtts - 1); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci if (i >= 31) { 59762306a36Sopenharmony_ci mthca_warn(dev, "Unable to reserve 2^31 FMR MTTs.\n"); 59862306a36Sopenharmony_ci err = -EINVAL; 59962306a36Sopenharmony_ci goto err_fmr_mpt; 60062306a36Sopenharmony_ci } 60162306a36Sopenharmony_ci mpts = mtts = 1 << i; 60262306a36Sopenharmony_ci } else { 60362306a36Sopenharmony_ci mtts = dev->limits.num_mtt_segs; 60462306a36Sopenharmony_ci mpts = dev->limits.num_mpts; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (!mthca_is_memfree(dev) && 60862306a36Sopenharmony_ci (dev->mthca_flags & MTHCA_FLAG_FMR)) { 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci addr = pci_resource_start(dev->pdev, 4) + 61162306a36Sopenharmony_ci ((pci_resource_len(dev->pdev, 4) - 1) & 61262306a36Sopenharmony_ci dev->mr_table.mpt_base); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci dev->mr_table.tavor_fmr.mpt_base = 61562306a36Sopenharmony_ci ioremap(addr, mpts * sizeof(struct mthca_mpt_entry)); 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci if (!dev->mr_table.tavor_fmr.mpt_base) { 61862306a36Sopenharmony_ci mthca_warn(dev, "MPT ioremap for FMR failed.\n"); 61962306a36Sopenharmony_ci err = -ENOMEM; 62062306a36Sopenharmony_ci goto err_fmr_mpt; 62162306a36Sopenharmony_ci } 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci addr = pci_resource_start(dev->pdev, 4) + 62462306a36Sopenharmony_ci ((pci_resource_len(dev->pdev, 4) - 1) & 62562306a36Sopenharmony_ci dev->mr_table.mtt_base); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci dev->mr_table.tavor_fmr.mtt_base = 62862306a36Sopenharmony_ci ioremap(addr, mtts * dev->limits.mtt_seg_size); 62962306a36Sopenharmony_ci if (!dev->mr_table.tavor_fmr.mtt_base) { 63062306a36Sopenharmony_ci mthca_warn(dev, "MTT ioremap for FMR failed.\n"); 63162306a36Sopenharmony_ci err = -ENOMEM; 63262306a36Sopenharmony_ci goto err_fmr_mtt; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci } 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (dev->limits.fmr_reserved_mtts) { 63762306a36Sopenharmony_ci err = mthca_buddy_init(&dev->mr_table.tavor_fmr.mtt_buddy, fls(mtts - 1)); 63862306a36Sopenharmony_ci if (err) 63962306a36Sopenharmony_ci goto err_fmr_mtt_buddy; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* Prevent regular MRs from using FMR keys */ 64262306a36Sopenharmony_ci err = mthca_buddy_alloc(&dev->mr_table.mtt_buddy, fls(mtts - 1)); 64362306a36Sopenharmony_ci if (err) 64462306a36Sopenharmony_ci goto err_reserve_fmr; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci dev->mr_table.fmr_mtt_buddy = 64762306a36Sopenharmony_ci &dev->mr_table.tavor_fmr.mtt_buddy; 64862306a36Sopenharmony_ci } else 64962306a36Sopenharmony_ci dev->mr_table.fmr_mtt_buddy = &dev->mr_table.mtt_buddy; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci /* FMR table is always the first, take reserved MTTs out of there */ 65262306a36Sopenharmony_ci if (dev->limits.reserved_mtts) { 65362306a36Sopenharmony_ci i = fls(dev->limits.reserved_mtts - 1); 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (mthca_alloc_mtt_range(dev, i, 65662306a36Sopenharmony_ci dev->mr_table.fmr_mtt_buddy) == -1) { 65762306a36Sopenharmony_ci mthca_warn(dev, "MTT table of order %d is too small.\n", 65862306a36Sopenharmony_ci dev->mr_table.fmr_mtt_buddy->max_order); 65962306a36Sopenharmony_ci err = -ENOMEM; 66062306a36Sopenharmony_ci goto err_reserve_mtts; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cierr_reserve_mtts: 66762306a36Sopenharmony_cierr_reserve_fmr: 66862306a36Sopenharmony_ci if (dev->limits.fmr_reserved_mtts) 66962306a36Sopenharmony_ci mthca_buddy_cleanup(&dev->mr_table.tavor_fmr.mtt_buddy); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cierr_fmr_mtt_buddy: 67262306a36Sopenharmony_ci if (dev->mr_table.tavor_fmr.mtt_base) 67362306a36Sopenharmony_ci iounmap(dev->mr_table.tavor_fmr.mtt_base); 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cierr_fmr_mtt: 67662306a36Sopenharmony_ci if (dev->mr_table.tavor_fmr.mpt_base) 67762306a36Sopenharmony_ci iounmap(dev->mr_table.tavor_fmr.mpt_base); 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cierr_fmr_mpt: 68062306a36Sopenharmony_ci mthca_buddy_cleanup(&dev->mr_table.mtt_buddy); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cierr_mtt_buddy: 68362306a36Sopenharmony_ci mthca_alloc_cleanup(&dev->mr_table.mpt_alloc); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci return err; 68662306a36Sopenharmony_ci} 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_civoid mthca_cleanup_mr_table(struct mthca_dev *dev) 68962306a36Sopenharmony_ci{ 69062306a36Sopenharmony_ci /* XXX check if any MRs are still allocated? */ 69162306a36Sopenharmony_ci if (dev->limits.fmr_reserved_mtts) 69262306a36Sopenharmony_ci mthca_buddy_cleanup(&dev->mr_table.tavor_fmr.mtt_buddy); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci mthca_buddy_cleanup(&dev->mr_table.mtt_buddy); 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci if (dev->mr_table.tavor_fmr.mtt_base) 69762306a36Sopenharmony_ci iounmap(dev->mr_table.tavor_fmr.mtt_base); 69862306a36Sopenharmony_ci if (dev->mr_table.tavor_fmr.mpt_base) 69962306a36Sopenharmony_ci iounmap(dev->mr_table.tavor_fmr.mpt_base); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci mthca_alloc_cleanup(&dev->mr_table.mpt_alloc); 70262306a36Sopenharmony_ci} 703