@@ -5,5 +5,6 @@ hfi_core-objs:= hfidd_adpt.o \
hfidd_window.o \
hfidd_init.o \
hfidd_xlat.o \
+ hfidd_map.o \
hfidd_hcalls.o
obj-$(CONFIG_HFI) += hfi_core.o
new file mode 100644
@@ -0,0 +1,99 @@
+/*
+ * hfidd_map.c
+ *
+ * HFI device driver for IBM System p
+ *
+ * Authors:
+ * Fu-Chung Chang <fcchang@linux.vnet.ibm.com>
+ * William S. Cadden <wscadden@linux.vnet.ibm.com>
+ * Wen C. Chen <winstonc@linux.vnet.ibm.com>
+ * Scot Sakolish <sakolish@linux.vnet.ibm.com>
+ * Jian Xiao <jian@linux.vnet.ibm.com>
+ * Carol L. Soto <clsoto@linux.vnet.ibm.com>
+ * Sarah J. Sheppard <sjsheppa@linux.vnet.ibm.com>
+ *
+ * (C) Copyright IBM Corp. 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/io.h>
+#include <linux/hfi/hfidd_internal.h>
+
+int hfidd_mmap(struct hfidd_acs *p_acs, void **eaddr, int size,
+ unsigned long vm_flag, unsigned long long busaddr,
+ unsigned long long offset)
+{
+ struct vm_area_struct *vma;
+ unsigned long vsize;
+ int rc;
+
+ down_write(¤t->mm->mmap_sem);
+ *eaddr = (void *)do_mmap(NULL, (unsigned long)*eaddr, size, PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS,
+ offset);
+ up_write(¤t->mm->mmap_sem);
+ if (*eaddr) {
+ vma = find_vma(current->mm, (u64) *eaddr);
+ if (!vma) {
+ dev_printk(KERN_ERR, p_acs->hfidd_dev,
+ "hfidd_mmap: find_vma failed\n");
+ return -ENOMEM;
+ }
+ } else {
+ dev_printk(KERN_ERR, p_acs->hfidd_dev,
+ "hfidd_mmap: do_mmap failed\n");
+ return -ENOMEM;
+ }
+
+ vsize = vma->vm_end - vma->vm_start;
+ if (vsize != size) {
+ dev_printk(KERN_ERR, p_acs->hfidd_dev,
+ "hfidd_mmap: Wrong sizes: vsize = %ld "
+ "size = %d\n", vsize, size);
+ return -EINVAL;
+ }
+
+ if (vm_flag == VM_RESERVED)
+ vma->vm_page_prot = pgprot_val(vma->vm_page_prot);
+ else
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_flags |= vm_flag;
+
+ rc = remap_pfn_range(vma, vma->vm_start, busaddr >> PAGE_SHIFT, size,
+ vma->vm_page_prot);
+ if (rc) {
+ dev_printk(KERN_ERR, p_acs->hfidd_dev,
+ "hfidd_mmap: remap_pfn_range failed\n");
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+int hfidd_unmap(void *addr, int size)
+{
+ int rc = 0;
+ struct mm_struct *mm = current->mm;
+
+ if (mm && (addr != NULL)) {
+ down_write(&mm->mmap_sem);
+ rc = do_munmap(mm, (unsigned long)addr, size);
+ up_write(&mm->mmap_sem);
+ }
+
+ return rc;
+}
@@ -57,6 +57,10 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
int hfi_register_rpages(struct hfidd_acs *p_acs, unsigned long long mr_handle,
unsigned int submr, struct hfidd_vlxmem *xtab_p,
unsigned int *mapped_pages);
+int hfidd_mmap(struct hfidd_acs *p_acs, void **eaddr, int size,
+ unsigned long vm_flag, unsigned long long busaddr,
+ unsigned long long offset);
+int hfidd_unmap(void *addr, int size);
int hfidd_get_phyp_page(struct hfidd_acs *p_acs, caddr_t *page,
caddr_t *laddr, int size);
void hfidd_release_phyp_page(caddr_t page, int size);
@@ -875,6 +875,44 @@ setup_window_parm_err1:
return rc;
}
+/* Map the window mmio registers - only user space window */
+static int hfi_map_mmio_regs(struct hfidd_acs *p_acs,
+ unsigned int is_userspace,
+ struct hfidd_window *win_p,
+ struct hfi_client_info *client_p)
+{
+ int rc = 0;
+ unsigned long long offset;
+ void *tmp_eaddr;
+
+ if (!is_userspace) {
+ /* No translation, just pass back the logical address */
+ client_p->mmio_regs.use.kptr = (void *)win_p->mmio_regs;
+ win_p->client_info.mmio_regs.use.kptr =
+ (void *)win_p->mmio_regs;
+ } else {
+ /*
+ * Translate mmio_regs from logical to effective address: 1st
+ * page
+ */
+ tmp_eaddr = 0;
+ offset = (client_p->window) << PAGE_SHIFT_64K;
+ rc = hfidd_mmap(p_acs, &tmp_eaddr, PAGE_SIZE_64K,
+ VM_RESERVED | VM_IO,
+ (long long)win_p->mmio_regs, offset);
+ if (rc) {
+ dev_printk(KERN_ERR, p_acs->hfidd_dev,
+ "hfi_map_mmio_regs: hfidd_mmap mmio_regs "
+ "failed, rc = 0x%x, mmio_regs = 0x%llx\n",
+ rc, (unsigned long long)win_p->mmio_regs);
+ return rc;
+ }
+ client_p->mmio_regs.use.kptr = tmp_eaddr;
+ win_p->client_info.mmio_regs.use.kptr = tmp_eaddr;
+ }
+ return 0;
+}
+
/*
* Allows an user/kernel window to send/receive network traffic thru HFI
* adapter. This function will allocate the system resources needed to open
@@ -941,6 +979,14 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
goto hfidd_open_window_func_err4;
}
+ rc = hfi_map_mmio_regs(p_acs, is_userspace, win_p, local_p);
+ if (rc) {
+ dev_printk(KERN_ERR, p_acs->hfidd_dev,
+ "hfidd_open_window_func: hfi_map_mmio_regs "
+ "failed, rc = 0x%x\n", rc);
+ goto hfidd_open_window_func_err4;
+ }
+
/* tell user the local ISR id */
local_p->local_isrid = p_acs->isr;
win_p->client_info.local_isrid = p_acs->isr;
@@ -952,7 +998,7 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
dev_printk(KERN_ERR, p_acs->hfidd_dev,
"hfidd_open_window_func: hfi_copy_to_user "
"failed, rc = 0x%x\n", rc);
- goto hfidd_open_window_func_err4;
+ goto hfidd_open_window_func_err5;
}
spin_lock(&(win_p->win_lock));
@@ -964,6 +1010,9 @@ int hfidd_open_window_func(struct hfidd_acs *p_acs, unsigned int is_userspace,
kfree(local_p);
return rc;
+hfidd_open_window_func_err5:
+ if (is_userspace)
+ hfidd_unmap(local_p->mmio_regs.use.kptr, PAGE_SIZE_64K);
hfidd_open_window_func_err4:
hfi_destroy_window_parm(p_acs, is_userspace, win_p, local_p);
hfidd_open_window_func_err3: