308 lines
10 KiB
C
308 lines
10 KiB
C
/**
|
|
* Copyright (c) 2021, Systems Group, ETH Zurich
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification,
|
|
* are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "fpga_pops.h"
|
|
|
|
/*
|
|
██████╗ ██████╗ ██████╗ ███████╗
|
|
██╔══██╗██╔═══██╗██╔══██╗██╔════╝
|
|
██████╔╝██║ ██║██████╔╝███████╗
|
|
██╔═══╝ ██║ ██║██╔═══╝ ╚════██║
|
|
██║ ╚██████╔╝██║ ███████║
|
|
╚═╝ ╚═════╝ ╚═╝ ╚══════╝
|
|
*/
|
|
|
|
/**
|
|
* @brief Acquire a reconfiguration conrtoller
|
|
*
|
|
*/
|
|
int pr_open(struct inode *inode, struct file *file)
|
|
{
|
|
int minor = iminor(inode);
|
|
|
|
struct pr_dev *d = container_of(inode->i_cdev, struct pr_dev, cdev);
|
|
BUG_ON(!d);
|
|
|
|
dbg_info("reconfiguration device %d acquired, pid %d\n", minor, current->pid);
|
|
|
|
// set private data
|
|
file->private_data = (void *)d;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Release a region
|
|
*
|
|
*/
|
|
int pr_release(struct inode *inode, struct file *file)
|
|
{
|
|
int minor = iminor(inode);
|
|
|
|
struct pr_dev *d = container_of(inode->i_cdev, struct pr_dev, cdev);
|
|
BUG_ON(!d);
|
|
|
|
dbg_info("reconfiguration device %d released, pid %d\n", minor, current->pid);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief ioctl, control and status
|
|
*
|
|
*/
|
|
long pr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int ret_val = 0, i;
|
|
struct pr_dev *d = (struct pr_dev *)file->private_data;
|
|
struct bus_drvdata *pd;
|
|
uint64_t tmp[MAX_USER_WORDS];
|
|
uint64_t start_time, stop_time;
|
|
|
|
BUG_ON(!d);
|
|
pd = d->pd;
|
|
BUG_ON(!pd);
|
|
|
|
switch (cmd) {
|
|
case IOCTL_ALLOC_HOST_PR_MEM:
|
|
// read n_pages
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, 3 * sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
}
|
|
else {
|
|
ret_val = alloc_pr_buffers(d, tmp[0], tmp[1], tmp[2]);
|
|
dbg_info("buff_num %d, arg %lx\n", d->curr_buff.n_pages, arg);
|
|
if (ret_val != 0) {
|
|
pr_info("reconfig buffers could not be allocated\n");
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IOCTL_FREE_HOST_PR_MEM:
|
|
// read vaddr
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, 3 * sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
}
|
|
else {
|
|
ret_val = free_pr_buffers(d, tmp[0], tmp[1], tmp[2]);
|
|
dbg_info("reconfig buffers freed\n");
|
|
}
|
|
break;
|
|
|
|
// reconfig shell
|
|
case IOCTL_RECONFIGURE_SHELL:
|
|
// read vaddr + len
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, 4 * sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
} else {
|
|
dbg_info("trying to obtain reconfig lock, pid %d\n", current->pid);
|
|
|
|
// lock
|
|
mutex_lock(&d->rcnfg_lock);
|
|
|
|
// clean up current state
|
|
if(cyt_arch == CYT_ARCH_PCI) {
|
|
shell_pci_remove(pd);
|
|
} else {
|
|
shell_eci_remove(pd);
|
|
}
|
|
|
|
// decouple
|
|
pd->fpga_stat_cnfg->pr_dcpl_set = 0x1;
|
|
|
|
// reconfigure
|
|
start_time = ktime_get_ns();
|
|
|
|
ret_val = reconfigure_start(d, tmp[0], tmp[1], tmp[2], tmp[3]);
|
|
if (ret_val != 0) {
|
|
pr_info("reconfiguration not successful, return %d\n", ret_val);
|
|
return -1;
|
|
}
|
|
|
|
// completion
|
|
wait_event_interruptible(d->waitqueue_rcnfg, atomic_read(&d->wait_rcnfg) == FLAG_SET);
|
|
|
|
// time
|
|
stop_time = ktime_get_ns();
|
|
pr_info("shell reconfiguration time %llu ms\n", (stop_time - start_time) / (1000 * 1000));
|
|
|
|
atomic_set(&d->wait_rcnfg, FLAG_CLR);
|
|
|
|
// reset
|
|
pd->fpga_stat_cnfg->pr_eost_reset = 0x0;
|
|
pd->fpga_stat_cnfg->pr_eost_reset = 0x1;
|
|
|
|
// couple
|
|
dbg_info("releasing reconfig lock, coupling the shell\n");
|
|
pd->fpga_stat_cnfg->pr_dcpl_clr = 0x1;
|
|
|
|
// reinit
|
|
if(cyt_arch == CYT_ARCH_PCI) {
|
|
shell_pci_init(pd);
|
|
} else {
|
|
|
|
shell_eci_init(pd);
|
|
}
|
|
|
|
mutex_unlock(&d->rcnfg_lock);
|
|
}
|
|
break;
|
|
|
|
// reconfig app
|
|
case IOCTL_RECONFIGURE_APP:
|
|
// read vaddr + len + vfid
|
|
ret_val = copy_from_user(&tmp, (unsigned long *)arg, 5 * sizeof(unsigned long));
|
|
if (ret_val != 0) {
|
|
pr_info("user data could not be coppied, return %d\n", ret_val);
|
|
} else {
|
|
dbg_info("trying to obtain reconfig lock, pid %d\n", current->pid);
|
|
|
|
// lock
|
|
mutex_lock(&d->rcnfg_lock);
|
|
|
|
// decouple
|
|
pd->fpga_shell_cnfg->pr_dcpl_app_set = (1 << (uint32_t)tmp[4]);
|
|
|
|
// reconfigure
|
|
start_time = ktime_get_ns();
|
|
|
|
ret_val = reconfigure_start(d, tmp[0], tmp[1], tmp[2], tmp[3]);
|
|
if (ret_val != 0) {
|
|
pr_info("reconfiguration not successful, return %d\n", ret_val);
|
|
return -1;
|
|
}
|
|
|
|
// completion
|
|
wait_event_interruptible(d->waitqueue_rcnfg, atomic_read(&d->wait_rcnfg) == FLAG_SET);
|
|
|
|
// time
|
|
stop_time = ktime_get_ns();
|
|
pr_info("app reconfiguration time %llu ms\n", (stop_time - start_time) / (1000 * 1000));
|
|
|
|
atomic_set(&d->wait_rcnfg, FLAG_CLR);
|
|
|
|
// couple
|
|
dbg_info("releasing reconfig lock, coupling the design\n");
|
|
pd->fpga_shell_cnfg->pr_dcpl_app_clr = (1 << (uint32_t)tmp[3]);
|
|
|
|
mutex_unlock(&d->rcnfg_lock);
|
|
}
|
|
break;
|
|
|
|
// config
|
|
case IOCTL_PR_CNFG:
|
|
tmp[0] = (uint64_t)pd->en_pr;
|
|
dbg_info("reading pr config 0x%llx\n", tmp[0]);
|
|
ret_val = copy_to_user((unsigned long *)arg, &tmp, sizeof(unsigned long));
|
|
break;
|
|
|
|
// xdma status
|
|
case IOCTL_STATIC_XDMA_STATS:
|
|
dbg_info("retreiving xdma status");
|
|
for(i = 0; i < N_XDMA_STAT_CH_REGS; i++) {
|
|
tmp[i] = pd->fpga_stat_cnfg->xdma_debug[i];
|
|
}
|
|
ret_val = copy_to_user((unsigned long *)arg, &tmp, N_XDMA_STAT_CH_REGS * sizeof(unsigned long));
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return ret_val;
|
|
}
|
|
|
|
int pr_mmap(struct file *file, struct vm_area_struct *vma)
|
|
{
|
|
int i;
|
|
unsigned long vaddr;
|
|
unsigned long vaddr_tmp;
|
|
struct pr_dev *d;
|
|
struct pr_pages *new_buff;
|
|
struct bus_drvdata *pd;
|
|
|
|
d = (struct pr_dev *)file->private_data;
|
|
BUG_ON(!d);
|
|
pd = d->pd;
|
|
BUG_ON(!pd);
|
|
|
|
vaddr = vma->vm_start;
|
|
|
|
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
|
|
|
// map PR buffers
|
|
if (vma->vm_pgoff == MMAP_PR)
|
|
{
|
|
dbg_info("reconfig device, mmap\n");
|
|
|
|
// aligned page virtual address
|
|
vaddr = ((vma->vm_start + pd->ltlb_order->page_size - 1) >> pd->ltlb_order->page_shift) << pd->ltlb_order->page_shift;
|
|
vaddr_tmp = vaddr;
|
|
|
|
if (d->curr_buff.n_pages != 0 && d->curr_buff.pid == current->pid) {
|
|
// obtain mem lock
|
|
spin_lock(&d->mem_lock);
|
|
|
|
new_buff = kzalloc(sizeof(struct pr_pages), GFP_KERNEL);
|
|
BUG_ON(!new_buff);
|
|
|
|
// Map entry
|
|
new_buff->vaddr = vaddr;
|
|
new_buff->pid = current->pid;
|
|
new_buff->crid = d->curr_buff.crid;
|
|
new_buff->n_pages = d->curr_buff.n_pages;
|
|
new_buff->pages = d->curr_buff.pages;
|
|
|
|
hash_add(pr_buff_map, &new_buff->entry, vaddr);
|
|
|
|
for (i = 0; i < new_buff->n_pages; i++) {
|
|
// map to user space
|
|
if (remap_pfn_range(vma, vaddr_tmp, page_to_pfn(d->curr_buff.pages[i]),
|
|
pd->ltlb_order->page_size, vma->vm_page_prot)) {
|
|
return -EIO;
|
|
}
|
|
// next page vaddr
|
|
vaddr_tmp += pd->ltlb_order->page_size;
|
|
}
|
|
|
|
// Current host buff empty
|
|
d->curr_buff.n_pages = 0;
|
|
|
|
// release mem lock
|
|
spin_unlock(&d->mem_lock);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|