add everything from https://storage.kayainstruments.com/s/vision-point except for archived software versions
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
#TODO: CONFIG_MODULE_SIG=n
|
||||
# This is a special makefile format for compiling kernel drivers
|
||||
ifeq ($(shell uname -m),ppc64)
|
||||
EXTRA_CFLAGS += -DACLPCI_BIGENDIAN
|
||||
endif
|
||||
|
||||
EXTRA_CFLAGS += -DKYDRIVER_BUILD
|
||||
|
||||
#PS++ For some kernels the following is mandatory, tegra for example
|
||||
CONFIG_SECTION_MISMATCH_WARN_ONLY=y
|
||||
|
||||
# Final module
|
||||
obj-m := predator_driver.o
|
||||
|
||||
# List of object files to compile for the final module.
|
||||
predator_driver-y := aclpci.o aclpci_fileio.o aclpci_cmd.o aclpci_dma.o
|
||||
|
@@ -0,0 +1,269 @@
|
||||
int board_id = DE4; //DE4 by default
|
||||
//struct board_parameters boards[NUM_SUPPORTED_BOARDS];
|
||||
|
||||
//void acl_init_de4_parameters(void) {
|
||||
// boards[DE4].board_name = "de4";
|
||||
// boards[DE4].acl_pci_global_mem_bar = 0;
|
||||
// boards[DE4].acl_pci_cra_bar = 2;
|
||||
// boards[DE4].acl_pci_cra_size = 0x4000;
|
||||
// boards[DE4].acl_kernel_csr_bar = 2;
|
||||
// boards[DE4].acl_kernel_csr_offset = 0x4000;
|
||||
// boards[DE4].acl_kernel_option0_csr_size = 0x10;
|
||||
// boards[DE4].acl_configuration_storage_bar = 2;
|
||||
// boards[DE4].acl_configuration_storage_offset = 0x5000;
|
||||
// boards[DE4].acl_pcie_dma_bar = 2;
|
||||
// boards[DE4].acl_pcie_dma_offset = 0x8000;
|
||||
// boards[DE4].acl_pcie_dma_descriptor_bar = 2;
|
||||
// boards[DE4].acl_pcie_dma_descriptor_offset = 0x8000;
|
||||
// boards[DE4].acl_pcie_pio_in_bar = 2;
|
||||
// boards[DE4].acl_pcie_pio_in_offset = 0x20006080;
|
||||
// boards[DE4].acl_pcie_pio_out_bar = 2;
|
||||
// boards[DE4].acl_pcie_pio_out_offset = 0x20006100;
|
||||
// boards[DE4].acl_pcie_tx_port = 0x00000000;
|
||||
// boards[DE4].acl_pcie_memwindow_bar = 2;
|
||||
// boards[DE4].acl_pcie_memwindow_cra = 0x20006030;
|
||||
// boards[DE4].acl_pcie_memwindow_base = 0x00000000;
|
||||
// boards[DE4].acl_pcie_memwindow_size = 0x20000000;
|
||||
// boards[DE4].acl_pcie_kernwindow_bar = 2;
|
||||
// boards[DE4].acl_pcie_kernwindow_cra = 0x20006038;
|
||||
// boards[DE4].acl_pcie_kernwindow_base = 0x00000000;
|
||||
// boards[DE4].acl_pcie_kernwindow_size = 0x1000;
|
||||
// boards[DE4].acl_pcie_em_bar = 2;
|
||||
// boards[DE4].acl_pcie_em_offset = 0x20008000;
|
||||
// boards[DE4].acl_pcie_em2_bar = 2;
|
||||
// boards[DE4].acl_pcie_em2_offset = 0x2000c000;
|
||||
//byte offsets
|
||||
// boards[DE4].kernel_offset_csr = 0x00;
|
||||
// boards[DE4].kernel_offset_printf_buffer_size = 0x04;
|
||||
// boards[DE4].kernel_offset_invocation_image = 0x0c;
|
||||
// boards[DE4].kernel_csr_go = 0;
|
||||
// boards[DE4].kernel_csr_done = 1;
|
||||
// boards[DE4].kernel_csr_stalled = 3;
|
||||
// boards[DE4].kernel_csr_unstall = 4;
|
||||
// boards[DE4].kernel_csr_last_status_bit = boards[DE4].kernel_csr_unstall;
|
||||
// boards[DE4].kernel_csr_status_bits_mask =(unsigned) (( 1<< (boards[DE4].kernel_csr_last_status_bit+1) )-1);
|
||||
// boards[DE4].kernel_csr_lmem_invalid_bank = 11;
|
||||
// boards[DE4].kernel_csr_lsu_active = 12;
|
||||
// boards[DE4].kernel_csr_wr_active = 13;
|
||||
// boards[DE4].kernel_csr_valid_in = 14;
|
||||
// boards[DE4].kernel_csr_started = 15;
|
||||
// boards[DE4].kernel_csr_first_version_bit = 16;
|
||||
// boards[DE4].kernel_csr_last_version_bit = 31;
|
||||
// boards[DE4].pcie_cra_irq_status = 0x0040;
|
||||
// boards[DE4].pcie_cra_irq_enable = 0x0050;
|
||||
// boards[DE4].pcie_cra_addr_trans = 0x1000;
|
||||
// boards[DE4].acl_pcie_kernel_irq_vec = 0;
|
||||
// boards[DE4].acl_pcie_dma_irq_vec = 1;
|
||||
// boards[DE4].use_kernelpll_reconfig = 0;
|
||||
// boards[DE4].acl_pcie_kernelpll_reconfig_bar = 0;
|
||||
// boards[DE4].acl_pcie_kernelpll_reconfig_offset = 0;
|
||||
// boards[DE4].acl_pcie_kernelpll_rom_bar = 0;
|
||||
// boards[DE4].acl_pcie_kernelpll_rom_offset = 0;
|
||||
// boards[DE4].acl_pcie_counter_bar = 0;
|
||||
// boards[DE4].acl_pcie_counter_offset = 0;
|
||||
//#ifndef QSYS_IFACE
|
||||
// boards[DE4].pcie_cra_irq_rxmirq = 7;
|
||||
// boards[DE4].pcie_cra_avl_irq_vec_lo = 8;
|
||||
// boards[DE4].pcie_cra_avl_irq_vec_hi = 13;
|
||||
//#endif
|
||||
// boards[DE4].dma_alignment_bytes = 32;
|
||||
// boards[DE4].dma_dc_transfer_complete_irq_mask = 14;
|
||||
// boards[DE4].dma_dc_early_done_enable = 24;
|
||||
// boards[DE4].dma_dc_go = 31;
|
||||
// boards[DE4].dma_csr_status = 0x00;
|
||||
// boards[DE4].dma_csr_control = 0x04;
|
||||
// boards[DE4].dma_status_busy = 0;
|
||||
// boards[DE4].dma_status_descriptor_empty = 1;
|
||||
// boards[DE4].dma_status_resetting = 6;
|
||||
// boards[DE4].dma_status_irq = 9;
|
||||
// boards[DE4].dma_status_count_lo = 16;
|
||||
// boards[DE4].dma_status_count_hi = 31;
|
||||
// boards[DE4].dma_ctrl_stop = 0;
|
||||
// boards[DE4].dma_ctrl_reset = 1;
|
||||
// boards[DE4].dma_ctrl_irq_enable = 4;
|
||||
// boards[DE4].perfmon_status_reg = 0x00*4;
|
||||
// boards[DE4].perfmon_build_number = 0x01*4;
|
||||
// boards[DE4].perfmon_perf_registers = 0x02*4;
|
||||
// boards[DE4].perfmon_log_start_reg = 0x03*4;
|
||||
// boards[DE4].perfmon_log_end_reg = 0x04*4;
|
||||
// boards[DE4].perfmon_log_registers = 0x05*4;
|
||||
// boards[DE4].perfmon_trigger_fifo_register = 0x06*4;
|
||||
// boards[DE4].perfmon_enable_monitoring = 0x01;
|
||||
// boards[DE4].perfmon_enable_logging = 0x02;
|
||||
// boards[DE4].perfmon_buffer_is_empty = 0x04;
|
||||
// boards[DE4].perfmon_reset = 0x10;
|
||||
// boards[DE4].perfmon_trigger_log_on_fifo_activity = 0x20;
|
||||
// boards[DE4].pio_data = 0*4;
|
||||
// boards[DE4].pio_set = 4*4;
|
||||
// boards[DE4].pio_clr = 5*4;
|
||||
// boards[DE4].pio_out_swreset = 31;
|
||||
// boards[DE4].pio_out_pllreset = 30;
|
||||
// boards[DE4].pio_out_interleave_mode = 8;
|
||||
// boards[DE4].pio_in_kpll_locked = 8;
|
||||
// boards[DE4].pio_in_a_init_done = 0;
|
||||
// boards[DE4].pio_in_a_cal_success = 1;
|
||||
// boards[DE4].pio_in_b_init_done = 2;
|
||||
// boards[DE4].pio_in_b_cal_success = 3;
|
||||
// boards[DE4].has_temp_sensor = 0;
|
||||
// boards[DE4].temp_sensor_addr = 0x0000; // No sensor
|
||||
//}
|
||||
|
||||
//void acl_init_de4_expcard_parameters(void) {
|
||||
// boards[DE4_EXPCARD] = boards[DE4];
|
||||
// boards[DE4_EXPCARD].board_name = "ede4";
|
||||
// boards[DE4_EXPCARD].acl_pcie_dma_offset = 0x4006000;
|
||||
// boards[DE4_EXPCARD].acl_pcie_dma_descriptor_offset = 0x4006020;
|
||||
// boards[DE4_EXPCARD].acl_pcie_pio_in_offset = 0x4006080;
|
||||
// boards[DE4_EXPCARD].acl_pcie_pio_out_offset = 0x4006100;
|
||||
// boards[DE4_EXPCARD].acl_pcie_memwindow_cra = 0x4006030;
|
||||
// boards[DE4_EXPCARD].acl_pcie_memwindow_size = 0x4000000;
|
||||
// boards[DE4_EXPCARD].acl_pcie_kernwindow_cra = 0x4006038;
|
||||
// boards[DE4_EXPCARD].acl_pcie_em_offset = 0x4008000;
|
||||
// boards[DE4_EXPCARD].acl_pcie_em2_bar = 2;
|
||||
// boards[DE4_EXPCARD].acl_pcie_em2_offset = 0x400c000;
|
||||
//}
|
||||
|
||||
//void acl_init_pcie385_parameters(void) {
|
||||
// boards[PCIE385] = boards[DE4];
|
||||
// boards[PCIE385].board_name = "e385";
|
||||
// boards[PCIE385].acl_pcie_pio_in_offset = 0x4006100;
|
||||
// boards[PCIE385].acl_pcie_pio_out_offset = 0x4006200;
|
||||
// boards[PCIE385].use_kernelpll_reconfig = 1;
|
||||
// boards[PCIE385].acl_pcie_kernelpll_reconfig_bar = 2;
|
||||
// boards[PCIE385].acl_pcie_kernelpll_reconfig_offset = 0x4006800;
|
||||
// boards[PCIE385].acl_pcie_kernelpll_rom_bar = 2;
|
||||
// boards[PCIE385].acl_pcie_kernelpll_rom_offset = 0x4006400;
|
||||
// boards[PCIE385].acl_pcie_counter_bar = 2;
|
||||
// boards[PCIE385].acl_pcie_counter_offset = 0x4006080;
|
||||
// boards[PCIE385].dma_alignment_bytes = 64;
|
||||
// boards[PCIE385].acl_pcie_dma_offset = 0x4006000;
|
||||
// boards[PCIE385].acl_pcie_dma_descriptor_offset = 0x4006020;
|
||||
// boards[PCIE385].acl_pcie_memwindow_cra = 0x4006030;
|
||||
// boards[PCIE385].acl_pcie_memwindow_size = 0x4000000;
|
||||
// boards[PCIE385].acl_pcie_kernwindow_cra = 0x4006038;
|
||||
// boards[PCIE385].acl_pcie_em_offset = 0x4008000;
|
||||
// boards[PCIE385].acl_pcie_em2_bar = 2;
|
||||
// boards[PCIE385].acl_pcie_em2_offset = 0x400c000;
|
||||
// boards[PCIE385].has_temp_sensor = 1;
|
||||
// boards[PCIE385].temp_sensor_addr = 0x6040;
|
||||
//}
|
||||
|
||||
//void acl_init_bsp_parameters(void) {
|
||||
// boards[BSP] = boards[PCIE385];
|
||||
// boards[BSP].board_name = "bsp";
|
||||
// boards[BSP].acl_pci_global_mem_bar = 0;
|
||||
// boards[BSP].acl_pci_cra_offset = 0;
|
||||
// boards[BSP].acl_pci_cra_bar = 0;
|
||||
// boards[BSP].acl_pci_cra_size = 0x4000;
|
||||
// boards[BSP].acl_kernel_csr_bar = 0;
|
||||
// boards[BSP].acl_kernel_csr_offset = 0x4000;
|
||||
// boards[BSP].acl_configuration_storage_bar = 0;
|
||||
// boards[BSP].acl_configuration_storage_offset = 0x6000;
|
||||
// boards[BSP].acl_pcie_dma_bar = 0;
|
||||
// boards[BSP].acl_pcie_dma_offset = 0x0c800;
|
||||
// boards[BSP].acl_pcie_dma_descriptor_bar = 0;
|
||||
// boards[BSP].acl_pcie_dma_descriptor_offset = 0x0c820;
|
||||
// boards[BSP].acl_pcie_pio_in_bar = 0;
|
||||
// boards[BSP].acl_pcie_pio_in_offset = 0x20006080;
|
||||
// boards[BSP].acl_pcie_pio_out_bar = 0;
|
||||
// boards[BSP].acl_pcie_pio_out_offset = 0x20006100;
|
||||
// boards[BSP].acl_pcie_tx_port = 0x80000000;
|
||||
// boards[BSP].acl_pcie_memwindow_bar = 0;
|
||||
// boards[BSP].acl_pcie_memwindow_cra = 0x0c870;
|
||||
// boards[BSP].acl_pcie_memwindow_base = 0x10000;
|
||||
// boards[BSP].acl_pcie_memwindow_size = 0x10000;
|
||||
// boards[BSP].acl_pcie_kernwindow_bar = 0;
|
||||
// boards[BSP].acl_pcie_kernwindow_cra = 0x4100;
|
||||
// boards[BSP].acl_pcie_kernwindow_base = 0x4000;
|
||||
// boards[BSP].acl_pcie_kernwindow_size = 0x1000;
|
||||
// boards[BSP].acl_pcie_em_bar = 0;
|
||||
// boards[BSP].acl_pcie_em_offset = 0x20000;
|
||||
// boards[BSP].acl_pcie_em2_bar = 0;
|
||||
// boards[BSP].acl_pcie_em2_offset = 0x24000;
|
||||
// boards[BSP].acl_pcie_kernel_irq_vec = 0;
|
||||
// boards[BSP].acl_pcie_dma_irq_vec = 1;
|
||||
// boards[BSP].use_kernelpll_reconfig = 1;
|
||||
// boards[BSP].acl_pcie_kernelpll_reconfig_bar = 0;
|
||||
// boards[BSP].acl_pcie_kernelpll_reconfig_offset = 0x0c000;
|
||||
// boards[BSP].acl_pcie_kernelpll_rom_bar = 0;
|
||||
// boards[BSP].acl_pcie_kernelpll_rom_offset = 0x0c400;
|
||||
// boards[BSP].acl_pcie_counter_bar = 0;
|
||||
// boards[BSP].acl_pcie_counter_offset = 0x0c100;
|
||||
// boards[BSP].dma_alignment_bytes = 64;
|
||||
// boards[BSP].pio_data = 0*4;
|
||||
// boards[BSP].pio_set = 4*4;
|
||||
// boards[BSP].pio_clr = 5*4;
|
||||
// boards[BSP].pio_out_swreset = 31;
|
||||
// boards[BSP].pio_out_pllreset = 30;
|
||||
// boards[BSP].pio_out_interleave_mode = 8;
|
||||
// boards[BSP].pio_in_kpll_locked = 8;
|
||||
// boards[BSP].pio_in_a_init_done = 0;
|
||||
// boards[BSP].pio_in_a_cal_success = 1;
|
||||
// boards[BSP].pio_in_b_init_done = 0;
|
||||
// boards[BSP].pio_in_b_cal_success = 3;
|
||||
// boards[BSP].has_temp_sensor = 0;
|
||||
// boards[BSP].temp_sensor_addr = 0x0000; // No sensor
|
||||
//}
|
||||
|
||||
//void acl_init_c5dk_parameters(void) {
|
||||
// boards[C5DK] = boards[PCIE385];
|
||||
// boards[C5DK].board_name = "c5dk";
|
||||
|
||||
// boards[C5DK].acl_pci_global_mem_bar = 1;
|
||||
// boards[C5DK].acl_pci_cra_bar = 1;
|
||||
// boards[C5DK].acl_pci_cra_size = 0x4000;
|
||||
// boards[C5DK].acl_kernel_csr_bar = 1;
|
||||
// boards[C5DK].acl_kernel_csr_offset = 0x4000;
|
||||
|
||||
// boards[C5DK].acl_pcie_pio_in_offset = 0x4006100;
|
||||
// boards[C5DK].acl_pcie_pio_out_offset = 0x4006200;
|
||||
// boards[C5DK].use_kernelpll_reconfig = 1;
|
||||
// boards[C5DK].acl_pcie_kernelpll_reconfig_bar = 0;
|
||||
// boards[C5DK].acl_pcie_kernelpll_reconfig_offset = 0x4006800;
|
||||
// boards[C5DK].acl_pcie_kernelpll_rom_bar = 0;
|
||||
// boards[C5DK].acl_pcie_kernelpll_rom_offset = 0x4006400;
|
||||
// boards[C5DK].acl_pcie_counter_bar = 0;
|
||||
// boards[C5DK].acl_pcie_counter_offset = 0x4006080;
|
||||
// boards[C5DK].dma_alignment_bytes = 64;
|
||||
// boards[C5DK].acl_pcie_dma_offset = 0x4006000;
|
||||
// boards[C5DK].acl_pcie_dma_descriptor_offset = 0x4006020;
|
||||
// boards[C5DK].acl_pcie_memwindow_cra = 0x4006030;
|
||||
// boards[C5DK].acl_pcie_memwindow_size = 0x4000000;
|
||||
// boards[C5DK].acl_pcie_kernwindow_cra = 0x4006038;
|
||||
// boards[C5DK].acl_pcie_em_offset = 0x4008000;
|
||||
// boards[C5DK].acl_pcie_em2_bar = 0;
|
||||
// boards[C5DK].acl_pcie_em2_offset = 0x400c000;
|
||||
// boards[C5DK].has_temp_sensor = 0;
|
||||
// boards[C5DK].temp_sensor_addr = 0x0;
|
||||
//}
|
||||
|
||||
|
||||
//void acl_init_board_parameters(void) {
|
||||
// acl_init_de4_parameters();
|
||||
//acl_init_de4_expcard_parameters();
|
||||
/*acl_init_pcie385_parameters();
|
||||
acl_init_bsp_parameters();
|
||||
acl_init_c5dk_parameters();*/
|
||||
//}
|
||||
|
||||
extern struct pci_device_id aclpci_ids[];
|
||||
|
||||
void acl_set_board_id_based_on_device_id(int device_id)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; true; ++i)
|
||||
{
|
||||
if (0 == aclpci_ids[i].device)
|
||||
{
|
||||
board_id = -1;
|
||||
break;
|
||||
}
|
||||
else
|
||||
if (device_id == aclpci_ids[i].device)
|
||||
{
|
||||
board_id = DE4;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,739 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Altera Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Top-level file for the driver.
|
||||
* Deal with device init and shutdown, BAR mapping, and interrupts. */
|
||||
|
||||
#include "aclpci.h"
|
||||
#include "acl_init.c"
|
||||
#include <asm/siginfo.h> //siginfo
|
||||
#include <linux/rcupdate.h> //rcu_read_lock
|
||||
#include <linux/version.h> //kernel_version
|
||||
#include <linux/ktime.h>
|
||||
|
||||
//#include <linux/sched.h> // send_sig_info
|
||||
//NOTE: declaration of send_sig_info() was moved from <linux/sched.h> to <linux/sched/signal.h> in Linux 4.11.
|
||||
//NOTE: <linux/sched/signal.h> gets included via "aclpci.h" -> <linux/fs.h> -> <linux/percpu-rwsem.h> -> <linux/rcuwait.h> on Linux 5.7 and newer, but let's include this explicitely:
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0)
|
||||
#include <linux/sched/signal.h>
|
||||
#else
|
||||
#include <linux/sched.h>
|
||||
#endif
|
||||
// see also https://elixir.bootlin.com/linux/latest/ident/send_sig_info
|
||||
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR ("KAYA Instruments Ltd.");
|
||||
MODULE_DESCRIPTION ("Driver for KAYA Instruments Predator Frame Grabber");
|
||||
#ifdef MODULE_SUPPORTED_DEVICE
|
||||
MODULE_SUPPORTED_DEVICE ("KAYA Instruments Predator");
|
||||
#endif
|
||||
|
||||
|
||||
/* Use Message Signalled Interrupt (MSI).
|
||||
* If not used will get many visibly-distinct interrupts for a single
|
||||
* logical one (because it takes a while to reset the interrupt in the FPGA).
|
||||
* MSIs are faster. HOWEVER, currently seem to loose MSIs once in a while. :( */
|
||||
#define USE_MSI 1
|
||||
|
||||
/* Static function declarations */
|
||||
static int __init probe(struct pci_dev *dev, const struct pci_device_id *id);
|
||||
static int __init init_chrdev (struct aclpci_dev *aclpci);
|
||||
static void __exit remove(struct pci_dev *dev);
|
||||
|
||||
static int __init scan_bars(struct aclpci_dev *aclpci, struct pci_dev *dev);
|
||||
static int __init map_bars(struct aclpci_dev *aclpci, struct pci_dev *dev);
|
||||
static void free_bars(struct aclpci_dev *aclpci, struct pci_dev *dev);
|
||||
|
||||
//#define CREATE_CLASS_IN_INIT
|
||||
#ifdef CREATE_CLASS_IN_INIT
|
||||
static struct class *devclass;
|
||||
static dev_t chrdev;
|
||||
#endif
|
||||
|
||||
/* Populating kernel-defined data structures */
|
||||
// static // static is removed to make struct pci_device_id aclpci_ids[] available in acl_init.c via extern
|
||||
struct pci_device_id aclpci_ids[] = {
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_CHAMELEON_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_PREDATOR_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_KOMODO_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_KOMODO_4R4T_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_KOMODO_FIBER_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_COMMON_DEVICE_ID) },
|
||||
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_KOMODO_FIBER_CLHS_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_KOMODO_FIBER_GIGE_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_FNC_DEVICE_ID) },
|
||||
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_CHAMELEON_II_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_PREDATOR_II_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_KOMODO_II_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_KOMODO_II_4R4T_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_KOMODO_II_FIBER_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_KOMODO_FIBER_CLHS_II_DEVICE_ID) },
|
||||
{ PCI_DEVICE(ACL_PCI_ALTERA_VENDOR_ID, ACL_PCI_KOMODO_FIBER_GIGE_II_DEVICE_ID) },
|
||||
|
||||
{ 0 },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, aclpci_ids);
|
||||
|
||||
static struct pci_driver aclpci_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = aclpci_ids,
|
||||
.probe = probe,
|
||||
.remove = remove,
|
||||
/* resume, suspend are optional */
|
||||
};
|
||||
|
||||
|
||||
struct file_operations aclpci_fileops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = aclpci_read,
|
||||
.write = aclpci_write,
|
||||
/* .ioctl = aclpci_ioctl, */
|
||||
.open = aclpci_open,
|
||||
.release = aclpci_close,
|
||||
/* .llseek = aclpci_llseek, */
|
||||
};
|
||||
|
||||
static int deviceNumber = 0;
|
||||
/* Allocate /dev/BOARD_NAME device */
|
||||
static int __init init_chrdev (struct aclpci_dev *aclpci)
|
||||
{
|
||||
|
||||
int dev_minor = 0;
|
||||
int dev_major = 0;
|
||||
int devno = -1;
|
||||
int result;
|
||||
char deviceName[64];
|
||||
|
||||
#ifdef CREATE_CLASS_IN_INIT
|
||||
aclpci->cdev_num=chrdev;
|
||||
#else
|
||||
/* request major number for device */
|
||||
sprintf(deviceName, "%s%d", BOARD_NAME, deviceNumber++);
|
||||
result = alloc_chrdev_region(&aclpci->cdev_num, dev_minor, MAXBOARDS /* one device*/, deviceName);
|
||||
if (result < 0)
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING "can't get major ID %d", MAJOR(aclpci->cdev_num));
|
||||
goto fail_alloc;
|
||||
}
|
||||
#endif
|
||||
dev_major = MAJOR(aclpci->cdev_num);
|
||||
ACL_DEBUG (KERN_WARNING "major ID %d", dev_major);
|
||||
|
||||
devno = MKDEV(dev_major, dev_minor);
|
||||
|
||||
cdev_init (&aclpci->cdev, &aclpci_fileops);
|
||||
aclpci->cdev.owner = THIS_MODULE;
|
||||
aclpci->cdev.ops = &aclpci_fileops;
|
||||
result = cdev_add (&aclpci->cdev, devno, MAXBOARDS);
|
||||
/* Fail gracefully if need be */
|
||||
if (result)
|
||||
{
|
||||
ACL_DEBUG(KERN_NOTICE "Error %d adding aclpci (%d, %d)", result, dev_major, dev_minor);
|
||||
goto fail_add;
|
||||
}
|
||||
ACL_DEBUG (KERN_DEBUG "aclpci = %d:%d", MAJOR(devno), MINOR(devno));
|
||||
/*
|
||||
aclpci->my_class = class_create(THIS_MODULE, "acl");
|
||||
if (IS_ERR(aclpci->my_class))
|
||||
{
|
||||
printk(KERN_NOTICE "Can't create class\n");
|
||||
goto fail_add;
|
||||
}
|
||||
|
||||
aclpci->device = device_create (aclpci->my_class, NULL, aclpci->cdev_num, NULL, "acl");
|
||||
if (IS_ERR(aclpci->device))
|
||||
{
|
||||
printk(KERN_NOTICE "Can't create device\n");
|
||||
goto fail_dev_create;
|
||||
}
|
||||
*/
|
||||
|
||||
return 0;
|
||||
|
||||
/*
|
||||
fail_dev_create:
|
||||
class_unregister(aclpci->my_class);
|
||||
class_destroy(aclpci->my_class); */
|
||||
|
||||
/* ERROR HANDLING */
|
||||
fail_add:
|
||||
/* free the dynamically allocated character device node */
|
||||
unregister_chrdev_region(devno, MAXBOARDS/*count*/);
|
||||
|
||||
fail_alloc:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
static int debug_interrupt_counter = 0;
|
||||
static int debug_interrupt_send_counter = 0;
|
||||
static int debug_interrupt_skip_counter = 0;
|
||||
static unsigned long last_time_signal_sent = 0;
|
||||
*/
|
||||
void get_wd_debug_info(WD_DEBUG_INFO *pWD_DEBUG_INFO){}
|
||||
/*{
|
||||
pWD_DEBUG_INFO->int1 = debug_interrupt_counter;
|
||||
pWD_DEBUG_INFO->int2 = debug_interrupt_send_counter;
|
||||
pWD_DEBUG_INFO->int3 = debug_interrupt_skip_counter;
|
||||
}
|
||||
|
||||
unsigned long get_current_time()
|
||||
{
|
||||
struct timeval time;
|
||||
do_gettimeofday(&time);
|
||||
unsigned long local_time = 1000 * time.tv_sec + time.tv_usec / 1000;
|
||||
|
||||
// start = ktime_get();
|
||||
// //unsigned long local_time = ktime_to_ns(ktime_sub(end, start));
|
||||
// unsigned long local_time = ktime_to_ms(start);
|
||||
|
||||
|
||||
return local_time;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
|
||||
irqreturn_t aclpci_irq (int irq, void *dev_id, struct pt_regs * not_used)
|
||||
#else
|
||||
irqreturn_t aclpci_irq (int irq, void *dev_id)
|
||||
#endif
|
||||
{
|
||||
bool do_log = false;
|
||||
static int do_send_send_sig_info = 1;
|
||||
|
||||
struct aclpci_dev *aclpci = (struct aclpci_dev *)dev_id;
|
||||
irqreturn_t res;
|
||||
|
||||
// Read ISR status register to identify false interrupt
|
||||
if (!readl(aclpci->bar[2] + (unsigned long)0x40))
|
||||
{
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (aclpci == NULL)
|
||||
{
|
||||
if (do_log)
|
||||
ACL_DEBUG (KERN_WARNING "aclpci_irq exiting for 'aclpci == NULL)'");
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* From this point on, this is our interrupt. So return IRQ_HANDLED
|
||||
* no matter what (since nobody else in the system will handle this
|
||||
* interrupt for us). */
|
||||
aclpci->num_handled_interrupts++;
|
||||
|
||||
|
||||
|
||||
#if !POLLING
|
||||
/* Send SIGNAL to user program to notify about the kernel update interrupt. */
|
||||
if (aclpci->user_task != NULL)
|
||||
{
|
||||
int ret = 0;
|
||||
if (do_send_send_sig_info)
|
||||
{
|
||||
ret = send_sig_info(SIG_INT_NOTIFY, &aclpci->signal_info, aclpci->user_task);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Can get to this state if the host is suspended for whatever reason.
|
||||
* Just print a warning message the first few times. The FPGA will keep
|
||||
* the interrupt level high until the kernel done bit is cleared (by the host).
|
||||
* See Case:84460. */
|
||||
|
||||
|
||||
do_send_send_sig_info = 0;
|
||||
//if (do_log)
|
||||
ACL_DEBUG (KERN_DEBUG "Wasn't able to execute send_sig_info(), will not try again\n");
|
||||
|
||||
aclpci->num_undelivered_signals++;
|
||||
if (aclpci->num_undelivered_signals < 5)
|
||||
{
|
||||
//if (do_log)
|
||||
//ACL_DEBUG (KERN_DEBUG "Error sending signal to host! irq_status is 0x%x\n", irq_status);
|
||||
ACL_DEBUG (KERN_DEBUG "Error sending signal to host! \n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (do_log)
|
||||
ACL_DEBUG (KERN_DEBUG "send_sig_info was sent\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (do_log)
|
||||
ACL_DEBUG (KERN_DEBUG "Didn't try to execute send_sig_info()\n");
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
if (do_log)
|
||||
ACL_DEBUG (KERN_DEBUG "aclpci->user_task is NULL\n");
|
||||
}
|
||||
#else
|
||||
ACL_VERBOSE_DEBUG (KERN_WARNING "Kernel update interrupt. Letting host POLL for it.");
|
||||
#endif
|
||||
res = IRQ_HANDLED;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void load_signal_info (struct aclpci_dev *aclpci)
|
||||
{
|
||||
|
||||
/* Setup siginfo struct to send signal to user process. Doing it once here
|
||||
* so don't waste time inside the interrupt handler. */
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,20,0)
|
||||
struct kernel_siginfo *info = &aclpci->signal_info;
|
||||
memset(info, 0, sizeof(struct kernel_siginfo));
|
||||
#else
|
||||
struct siginfo *info = &aclpci->signal_info;
|
||||
memset(info, 0, sizeof(struct siginfo));
|
||||
#endif
|
||||
|
||||
info->si_signo = SIG_INT_NOTIFY;
|
||||
/* this is bit of a trickery: SI_QUEUE is normally used by sigqueue from user
|
||||
* space, and kernel space should use SI_KERNEL. But if SI_KERNEL is used the
|
||||
* real_time data is not delivered to the user space signal handler function. */
|
||||
info->si_code = SI_QUEUE;
|
||||
info->si_int = -1; /* Signal payload. Will be filled later with
|
||||
ACLPCI_CMD_SET_SIGNAL_PAYLOAD cmd from user. */
|
||||
}
|
||||
|
||||
|
||||
int init_irq (struct pci_dev *dev, void *dev_id)
|
||||
{
|
||||
|
||||
u32 irq_type;
|
||||
|
||||
struct aclpci_dev *aclpci = (struct aclpci_dev*)dev_id;
|
||||
int rc;
|
||||
|
||||
if (dev == NULL || aclpci == NULL)
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING "Invalid inputs to init_irq (%p, %p)", dev, dev_id);
|
||||
return -1;
|
||||
}
|
||||
ACL_DEBUG (KERN_WARNING "VALID inputs to init_irq (%p, %p)", dev, dev_id);
|
||||
|
||||
// hold the semaphore and enter critical section but let function be interruptable
|
||||
irq_type = IRQF_SHARED; /* IRQF_SHARED -- allow sharing IRQs with other devices */
|
||||
/* Message Signalled Interrupts. */
|
||||
#if USE_MSI
|
||||
if(pci_enable_msi(dev) != 0)
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING "Could not enable MSI");
|
||||
}
|
||||
else
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING "Could YES enable MSI");
|
||||
irq_type = 0; /* No need to share MSI interrupts since they don't use dedicated wires.*/
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Do NOT use PCI_INTERRUPT_LINE config register. Its value is different
|
||||
* from dev->irq and doesn't work! Why? Who knows! */
|
||||
|
||||
rc = request_irq (dev->irq, aclpci_irq, irq_type, DRIVER_NAME, dev_id);
|
||||
if (rc)
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING "Could not request IRQ #%d, error %d", dev->irq, rc);
|
||||
return -1;
|
||||
}
|
||||
ACL_DEBUG (KERN_WARNING "Could YES request IRQ #%d, error %d", dev->irq, rc);
|
||||
|
||||
aclpci->irq_requested += 1;
|
||||
if (aclpci->irq_requested > 1)
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING "IRQ requested %d times", aclpci->irq_requested);
|
||||
}
|
||||
|
||||
aclpci->num_handled_interrupts = 0;
|
||||
aclpci->num_undelivered_signals = 0;
|
||||
|
||||
load_signal_info (aclpci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int release_irq (struct pci_dev *dev, void *aclpci)
|
||||
{
|
||||
|
||||
int num_usignals;
|
||||
|
||||
/* Disable interrupts before going away. If something bad happened in
|
||||
* user space and the user program crashes, the interrupt assigned to the device
|
||||
* will be freed (on automatic close()) call but the device will continue
|
||||
* generating interrupts. Soon the kernel will notice, complain, and bring down
|
||||
* the whole system. */
|
||||
//mask_irq(aclpci);
|
||||
|
||||
|
||||
if ( ((struct aclpci_dev*)aclpci)->irq_requested > 0 )
|
||||
{
|
||||
((struct aclpci_dev*)aclpci)->irq_requested -= 1;
|
||||
ACL_DEBUG (KERN_WARNING "Freeing IRQ %d, count %d", dev->irq, ((struct aclpci_dev*)aclpci)->irq_requested);
|
||||
}
|
||||
else
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING "NOT Freeing IRQ %d, because it is already freed", dev->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
free_irq (dev->irq, aclpci);
|
||||
|
||||
ACL_VERBOSE_DEBUG (KERN_DEBUG "Handled %d interrupts",
|
||||
((struct aclpci_dev*)aclpci)->num_handled_interrupts);
|
||||
|
||||
num_usignals = ((struct aclpci_dev*)aclpci)->num_undelivered_signals;
|
||||
if (num_usignals > 0)
|
||||
{
|
||||
ACL_DEBUG (KERN_DEBUG "Number undelivered signals is %d", num_usignals);
|
||||
}
|
||||
|
||||
#if USE_MSI
|
||||
pci_disable_msi (dev);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find upstream PCIe root node.
|
||||
* Used for re-training and disabling AER. */
|
||||
static struct pci_dev* find_upstream_dev (struct pci_dev *dev) {
|
||||
struct pci_bus *bus = 0;
|
||||
struct pci_dev *bridge = 0;
|
||||
struct pci_dev *cur = 0;
|
||||
int found_dev = 0;
|
||||
|
||||
bus = dev->bus;
|
||||
if (bus == 0) {
|
||||
ACL_DEBUG (KERN_WARNING "Device doesn't have an associated bus!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bridge = bus->self;
|
||||
if (bridge == 0) {
|
||||
ACL_DEBUG (KERN_WARNING "Can't get the bridge for the bus!\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ACL_DEBUG (KERN_DEBUG "Upstream device %x/%x, bus:slot.func %02x:%02x.%02x",
|
||||
bridge->vendor, bridge->device,
|
||||
bridge->bus->number, PCI_SLOT(bridge->devfn), PCI_FUNC(bridge->devfn));
|
||||
|
||||
ACL_DEBUG (KERN_DEBUG "List of downstream devices:");
|
||||
list_for_each_entry (cur, &bus->devices, bus_list) {
|
||||
if (cur != 0) {
|
||||
ACL_DEBUG (KERN_DEBUG " %x/%x", cur->vendor, cur->device);
|
||||
if (cur == dev) {
|
||||
found_dev = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found_dev) {
|
||||
return bridge;
|
||||
} else {
|
||||
ACL_DEBUG (KERN_WARNING "Couldn't find upstream device!");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int __init probe(struct pci_dev *dev, const struct pci_device_id *id)
|
||||
{
|
||||
|
||||
struct aclpci_dev *aclpci = 0;
|
||||
int res;
|
||||
|
||||
ACL_VERBOSE_DEBUG (KERN_DEBUG " probe (dev = 0x%p, pciid = 0x%p)", dev, id);
|
||||
ACL_DEBUG (KERN_DEBUG " vendor = 0x%x, device = 0x%x, class = 0x%x, bus:slot.func = %02x:%02x.%02x",
|
||||
dev->vendor, dev->device, dev->class,
|
||||
dev->bus->number, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn));
|
||||
|
||||
acl_set_board_id_based_on_device_id (dev->device);
|
||||
if (board_id == -1)
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING "\nNO MAPPING FROM DEVICE ID to board_id!\n");
|
||||
}
|
||||
|
||||
/* Load all board-related constants */
|
||||
//acl_init_board_parameters();
|
||||
|
||||
ACL_DEBUG (KERN_DEBUG "board_id is %d",board_id);
|
||||
|
||||
aclpci = kzalloc(sizeof(struct aclpci_dev), GFP_KERNEL);
|
||||
if (!aclpci)
|
||||
{
|
||||
ACL_DEBUG(KERN_WARNING "Couldn't allocate memory!\n");
|
||||
goto fail_kzalloc;
|
||||
}
|
||||
|
||||
sema_init (&aclpci->sem, 1);
|
||||
aclpci->pci_dev = dev;
|
||||
dev_set_drvdata(&dev->dev, (void*)aclpci);
|
||||
aclpci->user_pid = -1;
|
||||
aclpci->cvp_in_progress = 0;
|
||||
aclpci->pci_gen = 0;
|
||||
aclpci->pci_num_lanes = 0;
|
||||
aclpci->upstream = find_upstream_dev (dev);
|
||||
aclpci->irq_requested = 0;
|
||||
|
||||
get_pcie_params (aclpci);
|
||||
|
||||
aclpci->buffer = kmalloc (BUF_SIZE * sizeof(char), GFP_KERNEL);
|
||||
if (!aclpci->buffer)
|
||||
{
|
||||
ACL_DEBUG(KERN_WARNING "Couldn't allocate memory for buffer!\n");
|
||||
goto fail_kmalloc;
|
||||
}
|
||||
|
||||
res = init_chrdev (aclpci);
|
||||
if (res)
|
||||
{
|
||||
ACL_DEBUG(KERN_WARNING "Couldn't init_chrdev!\n");
|
||||
goto fail_chrdev_init;
|
||||
}
|
||||
|
||||
if (pci_enable_device(dev))
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING "pci_enable_device() failed");
|
||||
goto fail_enable;
|
||||
}
|
||||
|
||||
pci_set_master(dev);
|
||||
|
||||
if (pci_request_regions(dev, DRIVER_NAME))
|
||||
{
|
||||
goto fail_regions;
|
||||
}
|
||||
|
||||
if (!pci_set_dma_mask(dev, DMA_BIT_MASK(64)))
|
||||
{
|
||||
pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(64));
|
||||
/* use 64-bit DMA */
|
||||
ACL_DEBUG(KERN_DEBUG "Using a 64-bit DMA mask.\n");
|
||||
}
|
||||
else
|
||||
if (!pci_set_dma_mask(dev, DMA_BIT_MASK(32)))
|
||||
{
|
||||
ACL_DEBUG(KERN_DEBUG "Could not set 64-bit DMA mask.\n");
|
||||
pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(32));
|
||||
/* use 32-bit DMA */
|
||||
ACL_DEBUG(KERN_DEBUG "Using a 32-bit DMA mask.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
ACL_DEBUG(KERN_DEBUG "No suitable DMA possible.\n");
|
||||
/** @todo Choose proper error return code */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* map BARs */
|
||||
scan_bars(aclpci, dev);
|
||||
if (map_bars(aclpci, dev))
|
||||
{
|
||||
goto fail_map_bars;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
/* ERROR HANDLING */
|
||||
fail_map_bars:
|
||||
pci_release_regions(dev);
|
||||
pci_disable_device (dev);
|
||||
|
||||
fail_regions:
|
||||
|
||||
fail_enable:
|
||||
unregister_chrdev_region (aclpci->cdev_num, MAXBOARDS);
|
||||
|
||||
fail_chrdev_init:
|
||||
kfree (aclpci->buffer);
|
||||
|
||||
fail_kmalloc:
|
||||
kfree (aclpci);
|
||||
|
||||
fail_kzalloc:
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static int __init scan_bars(struct aclpci_dev * UNUSED(aclpci), struct pci_dev *dev)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ACL_PCI_NUM_BARS; i++) {
|
||||
unsigned long bar_start = pci_resource_start(dev, i);
|
||||
if (bar_start) {
|
||||
unsigned long VARIABLE_IS_NOT_USED bar_end = pci_resource_end(dev, i);
|
||||
unsigned long VARIABLE_IS_NOT_USED bar_flags = pci_resource_flags(dev, i);
|
||||
ACL_DEBUG (KERN_DEBUG "BAR[%d] 0x%08lx-0x%08lx flags 0x%08lx",
|
||||
i, bar_start, bar_end, bar_flags);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Map the device memory regions into kernel virtual address space
|
||||
* after verifying their sizes respect the minimum sizes needed, given
|
||||
* by the bar_min_len[] array.
|
||||
*/
|
||||
static int __init map_bars(struct aclpci_dev *aclpci, struct pci_dev *dev)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < ACL_PCI_NUM_BARS; i++){
|
||||
unsigned long bar_start = pci_resource_start(dev, i);
|
||||
unsigned long bar_end = pci_resource_end(dev, i);
|
||||
unsigned long bar_length = bar_end - bar_start + 1;
|
||||
aclpci->bar_length[i] = bar_length;
|
||||
|
||||
if (!bar_start || !bar_end) {
|
||||
aclpci->bar_length[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bar_length < 1) {
|
||||
ACL_DEBUG (KERN_WARNING "BAR #%d length is less than 1 byte", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* map the device memory or IO region into kernel virtual
|
||||
* address space */
|
||||
aclpci->bar[i] = ioremap (bar_start, bar_length);
|
||||
|
||||
if (!aclpci->bar[i]) {
|
||||
ACL_DEBUG (KERN_WARNING "Could not map BAR #%d.", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ACL_DEBUG (KERN_DEBUG "BAR[%d] mapped at 0x%p with length %lu.", i,
|
||||
aclpci->bar[i], bar_length);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void free_bars(struct aclpci_dev *aclpci, struct pci_dev *dev) {
|
||||
|
||||
int i;
|
||||
for (i = 0; i < ACL_PCI_NUM_BARS; i++) {
|
||||
if (aclpci->bar[i]) {
|
||||
pci_iounmap(dev, aclpci->bar[i]);
|
||||
aclpci->bar[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void __exit remove(struct pci_dev *dev)
|
||||
{
|
||||
|
||||
struct aclpci_dev *aclpci = 0;
|
||||
ACL_DEBUG (KERN_DEBUG ": dev is %p", dev);
|
||||
|
||||
if (dev == 0)
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING ": dev is 0");
|
||||
return;
|
||||
}
|
||||
|
||||
aclpci = (struct aclpci_dev*) dev_get_drvdata(&dev->dev);
|
||||
if (aclpci == 0)
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING ": aclpci_dev is 0");
|
||||
return;
|
||||
}
|
||||
|
||||
cdev_del (&aclpci->cdev);
|
||||
unregister_chrdev_region (aclpci->cdev_num, MAXBOARDS);
|
||||
free_bars (aclpci, dev);
|
||||
pci_disable_device(dev);
|
||||
pci_release_regions(dev);
|
||||
|
||||
kfree (aclpci->buffer);
|
||||
kfree (aclpci);
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the driver module (but not any device) and register
|
||||
* the module with the kernel PCI subsystem. */
|
||||
static int __init aclpci_init(void)
|
||||
{
|
||||
int init_ret = 0;
|
||||
#ifdef CREATE_CLASS_IN_INIT
|
||||
int rc;
|
||||
#endif
|
||||
|
||||
ACL_DEBUG (KERN_DEBUG "----------------------------");
|
||||
ACL_DEBUG (KERN_DEBUG "Driver version: %s", ACL_DRIVER_VERSION);
|
||||
|
||||
/* Following XIMEA sample we are performing class creation here - removed from probe*/
|
||||
#ifdef CREATE_CLASS_IN_INIT
|
||||
rc = alloc_chrdev_region(&chrdev, 0, 1, BOARD_NAME);
|
||||
if(rc < 0)
|
||||
{
|
||||
ACL_DEBUG(KERN_DEBUG "Char dev allocation failed\n");
|
||||
return rc;
|
||||
}
|
||||
devclass = class_create(THIS_MODULE, DRIVER_NAME);
|
||||
if(IS_ERR(devclass))
|
||||
{
|
||||
ACL_DEBUG(KERN_DEBUG "Class creation failed\n");
|
||||
rc = PTR_ERR(devclass);
|
||||
unregister_chrdev_region(chrdev, MAXBOARDS);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
// End of class creation
|
||||
|
||||
/* register this driver with the PCI bus driver */
|
||||
init_ret = pci_register_driver(&aclpci_driver);
|
||||
ACL_DEBUG (KERN_DEBUG "pci_register_driver ret: %d", init_ret);
|
||||
return init_ret;
|
||||
}
|
||||
|
||||
static void __exit aclpci_exit(void)
|
||||
{
|
||||
ACL_DEBUG (KERN_DEBUG "aclpci_exit...");
|
||||
|
||||
/* unregister this driver from the PCI bus driver */
|
||||
pci_unregister_driver(&aclpci_driver);
|
||||
}
|
||||
|
||||
|
||||
module_init (aclpci_init);
|
||||
module_exit (aclpci_exit);
|
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Altera Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Global declarations shared by all files of this driver. */
|
||||
|
||||
#ifndef ACLPCI_H
|
||||
#define ACLPCI_H
|
||||
|
||||
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/kdev_t.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
/* includes from opencl/include/pcie */
|
||||
#include "hw_pcie_constants.h"
|
||||
#include "pcie_linux_driver_exports.h"
|
||||
|
||||
/* Local includes */
|
||||
#include "version.h"
|
||||
|
||||
/* For now, disable DMA on big-endian system. */
|
||||
#ifdef ACLPCI_BIGENDIAN
|
||||
#define USE_DMA 0
|
||||
#else
|
||||
#define USE_DMA 1
|
||||
#endif
|
||||
|
||||
#include "aclpci_dma.h"
|
||||
|
||||
|
||||
#define DRIVER_NAME "predator_driver"
|
||||
#define BOARD_NAME "predatorDevice"
|
||||
|
||||
// The following macros should only be applied to code that exists purely for debugging purposes
|
||||
// and otherwise we want to suppress "not used" warnings
|
||||
// (we don't want to apply global compiler switches because we do want to see those warnings where
|
||||
// these macros are not explicitely applied
|
||||
|
||||
#ifdef UNUSED
|
||||
#elif defined(__GNUC__)
|
||||
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
|
||||
#elif defined(__LCLINT__)
|
||||
# define UNUSED(x) /*@unused@*/ x
|
||||
#else
|
||||
# define UNUSED(x) x
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define VARIABLE_IS_NOT_USED __attribute__ ((unused))
|
||||
#else
|
||||
#define VARIABLE_IS_NOT_USED
|
||||
#endif
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
/* Set to 1 to use Polling (instead of interrupts) to communicate
|
||||
* with hal. HAL must be compiled in the same mode */
|
||||
#define POLLING 0
|
||||
|
||||
|
||||
/* Debugging defines */
|
||||
#define DO_DEBUG 0
|
||||
#define VERBOSE_DEBUG 0
|
||||
#if DO_DEBUG
|
||||
#define ACL_DEBUG(...) \
|
||||
do { \
|
||||
printk("%s (%d): ", __func__, __LINE__); \
|
||||
printk(__VA_ARGS__); \
|
||||
printk("-----------------\n"); \
|
||||
} while (0)
|
||||
#else
|
||||
#define ACL_DEBUG(...)
|
||||
#endif
|
||||
#if VERBOSE_DEBUG
|
||||
# define ACL_VERBOSE_DEBUG(...) ACL_DEBUG(__VA_ARGS__)
|
||||
#else
|
||||
# define ACL_VERBOSE_DEBUG(...)
|
||||
#endif
|
||||
|
||||
/* Don't actually bring down the kernel on an error condition */
|
||||
#define assert(expr) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
printk(KERN_ERR "Assertion failed! %s, %s, %s, line %d\n", \
|
||||
#expr, __FILE__, __func__, __LINE__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
/* Maximum size of driver buffer (allocated with kalloc()).
|
||||
* Needed to copy data from user to kernel space, among other
|
||||
* things. */
|
||||
static const size_t BUF_SIZE = PAGE_SIZE;
|
||||
|
||||
|
||||
/* Device data used by this driver. */
|
||||
struct aclpci_dev {
|
||||
/* the kernel pci device data structure */
|
||||
struct pci_dev *pci_dev;
|
||||
|
||||
/* upstream root node */
|
||||
struct pci_dev *upstream;
|
||||
|
||||
/* kernels virtual addr. for the mapped BARs */
|
||||
void * __iomem bar[ACL_PCI_NUM_BARS];
|
||||
|
||||
/* length of each memory region. Used for error checking. */
|
||||
size_t bar_length[ACL_PCI_NUM_BARS];
|
||||
|
||||
/* Controls which section of board's DDR maps to BAR */
|
||||
u64 global_mem_segment;
|
||||
|
||||
/* Location of global_mem_segment value on the board. */
|
||||
void *global_mem_segment_addr;
|
||||
|
||||
/* temporary buffer. If allocated, will be BUF_SIZE. */
|
||||
char *buffer;
|
||||
|
||||
/* Mutex for this device. */
|
||||
struct semaphore sem;
|
||||
|
||||
/* PID of process that called open() */
|
||||
int user_pid;
|
||||
|
||||
/* character device */
|
||||
dev_t cdev_num;
|
||||
struct cdev cdev;
|
||||
struct class *my_class;
|
||||
struct device *device;
|
||||
|
||||
|
||||
/* signal sending structs */
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,20,0)
|
||||
struct kernel_siginfo signal_info;
|
||||
#else
|
||||
struct siginfo signal_info;
|
||||
#endif
|
||||
struct task_struct *user_task;
|
||||
int user_filehandle;
|
||||
|
||||
/* 1 if doing core reconfig via PCIe.
|
||||
* Ignore all interrupts when this is going on. */
|
||||
int cvp_in_progress;
|
||||
|
||||
/* State of uncorrectable error mask register, AER ext capability.
|
||||
* Saved during reprogramming */
|
||||
u32 aer_uerr_mask_reg;
|
||||
|
||||
/* All the DMA data */
|
||||
//struct aclpci_dma dma_data;
|
||||
|
||||
/* IRQ requests 'reference counter' */
|
||||
int irq_requested;
|
||||
|
||||
/* Debug data */
|
||||
/* number of hw interrupts handled. */
|
||||
int num_handled_interrupts;
|
||||
int num_undelivered_signals;
|
||||
int pci_gen;
|
||||
int pci_num_lanes;
|
||||
};
|
||||
|
||||
|
||||
/* aclpci_fileio.c funciton */
|
||||
int aclpci_open(struct inode *inode, struct file *file);
|
||||
int aclpci_close(struct inode *inode, struct file *file);
|
||||
ssize_t aclpci_read(struct file *file, char __user *buf, size_t count, loff_t *pos);
|
||||
ssize_t aclpci_write(struct file *file, const char __user *buf, size_t count, loff_t *pos);
|
||||
void* aclpci_get_checked_addr (int bar_id, void *device_addr, size_t count,
|
||||
struct aclpci_dev *aclpci, ssize_t *errno, int print_error_msg);
|
||||
|
||||
/*loff_t aclpci_llseek(struct file *filp, loff_t off, int whence);*/
|
||||
|
||||
/* aclpci.c functions */
|
||||
void load_signal_info (struct aclpci_dev *aclpci);
|
||||
int init_irq (struct pci_dev *dev, void *dev_id);
|
||||
int release_irq (struct pci_dev *dev, void *aclpci);
|
||||
|
||||
/* aclpci_dma.c functions */
|
||||
void aclpci_dma_init(struct aclpci_dev *aclpci);
|
||||
void aclpci_dma_finish(struct aclpci_dev *aclpci);
|
||||
int aclpci_dma_get_idle_status(struct aclpci_dev *aclpci);
|
||||
irqreturn_t aclpci_dma_service_interrupt (struct aclpci_dev *aclpci);
|
||||
int aclpci_dma_update(struct aclpci_dev *aclpci, int forced);
|
||||
int lock_dma_buffer (struct aclpci_dev *aclpci, WD_DMA *dma);
|
||||
void unlock_dma_buffer (struct aclpci_dev *aclpci, WD_DMA *dma);
|
||||
//void sync_dma_buffer (struct aclpci_dev *aclpci, WD_DMA *dma);
|
||||
void sync_cache_for_memory(struct aclpci_dev *aclpci, WD_DMA *dma, bool forDevice);
|
||||
|
||||
/* aclpci_cmd.c functions */
|
||||
void get_pcie_params (struct aclpci_dev *aclpci);
|
||||
ssize_t aclpci_exec_cmd (struct aclpci_dev *aclpci, struct aclpci_cmd kcmd, size_t count);
|
||||
int aclpci_get_user_pages(unsigned long start_page, size_t num_pages, struct page **p);
|
||||
void aclpci_release_user_pages(struct page **p, size_t num_pages);
|
||||
|
||||
void get_wd_debug_info(WD_DEBUG_INFO* pWD_DEBUG_INFO);
|
||||
|
||||
#endif /* ACLPCI_H */
|
@@ -0,0 +1,635 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Altera Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* Handling of special commands (anything that is not read/write/open/close)
|
||||
* that user may call.
|
||||
* See pcie_linux_driver_exports.h for explanations of each command. */
|
||||
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/aer.h>
|
||||
//#include <linux/pagemap.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "aclpci.h"
|
||||
|
||||
/* RedHat 5.5 doesn't define this */
|
||||
#ifndef PCI_EXP_LNKSTA_NLW_SHIFT
|
||||
#define PCI_EXP_LNKSTA_NLW_SHIFT 4
|
||||
#endif
|
||||
|
||||
|
||||
void get_pcie_params (struct aclpci_dev *aclpci);
|
||||
void disable_aer_on_upstream_dev(struct aclpci_dev *aclpci);
|
||||
void restore_aer_on_upstream_dev(struct aclpci_dev *aclpci);
|
||||
|
||||
|
||||
/* Execute special command */
|
||||
ssize_t aclpci_exec_cmd (struct aclpci_dev *aclpci,
|
||||
struct aclpci_cmd kcmd,
|
||||
size_t count) {
|
||||
ssize_t result = 0;
|
||||
|
||||
switch (kcmd.command) {
|
||||
case ACLPCI_CMD_SAVE_PCI_CONTROL_REGS: {
|
||||
/* Disable interrupts before reprogramming. O/w the board will get into
|
||||
* a funny state and hang the system . */
|
||||
ACL_DEBUG (KERN_DEBUG "Saving PCI control registers");
|
||||
disable_aer_on_upstream_dev(aclpci);
|
||||
release_irq (aclpci->pci_dev, aclpci);
|
||||
result = pci_save_state(aclpci->pci_dev);
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_LOAD_PCI_CONTROL_REGS: {
|
||||
|
||||
pci_set_master(aclpci->pci_dev);
|
||||
init_irq (aclpci->pci_dev, aclpci);
|
||||
/*result = */pci_restore_state(aclpci->pci_dev);
|
||||
restore_aer_on_upstream_dev(aclpci);
|
||||
get_pcie_params(aclpci);
|
||||
ACL_DEBUG (KERN_DEBUG "Restored PCI control registers");
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_INTERRUPT_ENABLE: {
|
||||
result = init_irq(aclpci->pci_dev, aclpci);
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_INTERRUPT_DISABLE: {
|
||||
result = release_irq(aclpci->pci_dev, aclpci);
|
||||
break;
|
||||
}
|
||||
|
||||
// case ACLPCI_CMD_PIN_USER_ADDR:
|
||||
// result = aclpci_pin_user_addr (kcmd.user_addr, count);
|
||||
// break;
|
||||
|
||||
// case ACLPCI_CMD_UNPIN_USER_ADDR:
|
||||
// result = aclpci_unpin_user_addr (kcmd.user_addr, count);
|
||||
// break;
|
||||
|
||||
// case ACLPCI_CMD_GET_DMA_IDLE_STATUS: {
|
||||
// u32 idle = aclpci_dma_get_idle_status(aclpci);
|
||||
// result = copy_to_user ( kcmd.user_addr, &idle, sizeof(idle) );
|
||||
// break;
|
||||
// }
|
||||
|
||||
// case ACLPCI_CMD_DMA_UPDATE: {
|
||||
// aclpci_dma_update(aclpci, 0);
|
||||
// break;
|
||||
// }
|
||||
|
||||
case ACLPCI_CMD_GET_DEVICE_ID: {
|
||||
u32 id = aclpci->pci_dev->device;
|
||||
result = copy_to_user ( kcmd.user_addr, &id, sizeof(id) );
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_GET_VENDOR_ID: {
|
||||
u32 id = aclpci->pci_dev->vendor;
|
||||
result = copy_to_user ( kcmd.user_addr, &id, sizeof(id) );
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_GET_BUS: {
|
||||
u32 id = aclpci->pci_dev->bus->number;
|
||||
result = copy_to_user ( kcmd.user_addr, &id, sizeof(id) );
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_GET_SLOT: {
|
||||
u32 id = PCI_SLOT(aclpci->pci_dev->devfn);
|
||||
result = copy_to_user ( kcmd.user_addr, &id, sizeof(id) );
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_GET_FUNC: {
|
||||
u32 id = PCI_FUNC(aclpci->pci_dev->devfn);
|
||||
result = copy_to_user ( kcmd.user_addr, &id, sizeof(id) );
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_GET_PCI_GEN: {
|
||||
u32 pci_gen = aclpci->pci_gen;
|
||||
result = copy_to_user ( kcmd.user_addr, &pci_gen, sizeof(pci_gen) );
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_GET_PCI_NUM_LANES: {
|
||||
u32 pci_num_lanes = aclpci->pci_num_lanes;
|
||||
result = copy_to_user ( kcmd.user_addr, &pci_num_lanes, sizeof(pci_num_lanes) );
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_SET_SIGNAL_PAYLOAD: {
|
||||
u32 id;
|
||||
result = copy_from_user ( &id, kcmd.user_addr, sizeof(id) );
|
||||
aclpci->signal_info.si_int = id;
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_GET_DRIVER_VERSION: {
|
||||
/* Driver version is a string */
|
||||
result = copy_to_user ( kcmd.user_addr, &ACL_DRIVER_VERSION, strlen(ACL_DRIVER_VERSION)+1 );
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// KAYA Additional commands:
|
||||
//
|
||||
case ACLPCI_CMD_KAYA_DEBUG:
|
||||
{
|
||||
WD_DEBUG_INFO wdi;
|
||||
get_wd_debug_info(&wdi);
|
||||
// int interrupts_counter = aclpci->num_handled_interrupts;
|
||||
if(0 != copy_to_user ( kcmd.user_addr, &wdi, sizeof(wdi)))
|
||||
{
|
||||
result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACLPCI_CMD_DMA_SGLOCK: {
|
||||
//struct aclpci_dma *d = &(aclpci->dma_data);
|
||||
|
||||
//void *addr = kcmd.user_addr;
|
||||
int i;
|
||||
size_t firstPageLength = 0;
|
||||
|
||||
WD_DMA* pUserWdDMA = (WD_DMA*)kcmd.user_addr2;
|
||||
|
||||
WD_DMA kernerWdDMA;
|
||||
WD_DMA_PAGE * pOriginalUserDMAPageArray;
|
||||
if(0 != copy_from_user(&kernerWdDMA, pUserWdDMA, sizeof(WD_DMA)))
|
||||
{
|
||||
result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
// save the original dma page array address
|
||||
pOriginalUserDMAPageArray = kernerWdDMA.Page;
|
||||
|
||||
result = lock_dma_buffer (aclpci, &kernerWdDMA);
|
||||
|
||||
// copy output of lock_dma_buffer() back to user space addressed by kcmd.user_addr2
|
||||
//ACL_DEBUG (KERN_WARNING " pages_rem==%d, first_page_offset=%d, last_page_offset=%d\n", pinned_mem_ptr->pages_rem, pinned_mem_ptr->first_page_offset, pinned_mem_ptr->last_page_offset);
|
||||
// ACL_DEBUG (KERN_WARNING " gma->len==%d, dma->num_pages=%d\n", pinned_mem_ptr->dma.len, pinned_mem_ptr->dma.num_pages);
|
||||
|
||||
for(i = 0; i < kernerWdDMA.dwPages; i++)
|
||||
{
|
||||
kernerWdDMA.Page[i].pPhysicalAddr = kernerWdDMA.dma_addrs[i];
|
||||
kernerWdDMA.Page[i].dwBytes = PAGE_SIZE;
|
||||
}
|
||||
|
||||
firstPageLength = PAGE_SIZE - kernerWdDMA.first_page_offset;
|
||||
|
||||
kernerWdDMA.Page[0].dwBytes = (kernerWdDMA.dwBytes <= firstPageLength) ? kernerWdDMA.dwBytes : firstPageLength;
|
||||
//ACL_DEBUG (KERN_WARNING " first page size =%d\n", wdDMA.Page[0].dwBytes);
|
||||
|
||||
if( (kernerWdDMA.dwPages > 1)
|
||||
&&
|
||||
(kernerWdDMA.last_page_offset > 0))
|
||||
{
|
||||
kernerWdDMA.Page[kernerWdDMA.dwPages - 1].dwBytes = kernerWdDMA.last_page_offset;
|
||||
}
|
||||
//ACL_DEBUG (KERN_WARNING " last page size =%d\n", wdDMA.Page[dma.num_pages - 1].dwBytes);
|
||||
|
||||
// copy newly created WD_DMA_PAGE structure for each created page to user space
|
||||
if(0 != copy_to_user(pOriginalUserDMAPageArray, kernerWdDMA.Page, kernerWdDMA.dwPages * sizeof(WD_DMA_PAGE)))
|
||||
{
|
||||
result = -EFAULT;
|
||||
}
|
||||
kfree(kernerWdDMA.Page);
|
||||
|
||||
// copy newly created WD_DMA structure to user space
|
||||
kernerWdDMA.Page = pOriginalUserDMAPageArray;
|
||||
if(0 != copy_to_user(pUserWdDMA, &kernerWdDMA, sizeof(WD_DMA)))
|
||||
{
|
||||
result = -EFAULT;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_DMA_SGUNLOCK: {
|
||||
|
||||
if(NULL != kcmd.user_addr2)
|
||||
{
|
||||
WD_DMA kernerWdDMA;
|
||||
if(0 != copy_from_user(&kernerWdDMA, kcmd.user_addr2, sizeof(WD_DMA)))
|
||||
{
|
||||
result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
unlock_dma_buffer (aclpci, &kernerWdDMA);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_SYNC_FOR_CPU: {
|
||||
// struct aclpci_dma *d = &(aclpci->dma_data);
|
||||
//void *addr = kcmd.user_addr;
|
||||
//ssize_t len = count;
|
||||
//int i;
|
||||
if(NULL != kcmd.user_addr2)
|
||||
{
|
||||
WD_DMA kernerWdDMA;
|
||||
|
||||
if(0 != copy_from_user(&kernerWdDMA, kcmd.user_addr2, sizeof(WD_DMA)))
|
||||
{
|
||||
result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
sync_cache_for_memory(aclpci, &kernerWdDMA, false);
|
||||
|
||||
//sync_dma_buffer (aclpci, &kernerWdDMA);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ACLPCI_CMD_SYNC_FOR_DEVICE: {
|
||||
// struct aclpci_dma *d = &(aclpci->dma_data);
|
||||
//void *addr = kcmd.user_addr;
|
||||
//ssize_t len = count;
|
||||
//int i;
|
||||
if(NULL != kcmd.user_addr2)
|
||||
{
|
||||
WD_DMA kernerWdDMA;
|
||||
|
||||
if(0 != copy_from_user(&kernerWdDMA, kcmd.user_addr2, sizeof(WD_DMA)))
|
||||
{
|
||||
result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
sync_cache_for_memory(aclpci, &kernerWdDMA, true);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
ACL_DEBUG (KERN_WARNING " Invalid command id %u! Ignoring the call. See aclpci_common.h for list of understood commands", kcmd.command);
|
||||
result = -EFAULT;
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Pinning user pages.
|
||||
*
|
||||
* Taken from <kernel code>/drivers/infiniband/hw/ipath/ipath_user_pages.c
|
||||
*/
|
||||
static void __aclpci_release_user_pages(struct page **p, size_t num_pages, int dirty)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < num_pages; i++)
|
||||
{
|
||||
if (dirty)
|
||||
{
|
||||
set_page_dirty_lock(p[i]);
|
||||
}
|
||||
put_page(p[i]);//page_cache_release(p[i]);//
|
||||
}
|
||||
}
|
||||
|
||||
/* call with current->mm->mmap_sem held */
|
||||
static int __aclpci_get_user_pages(unsigned long start_page, size_t num_pages,
|
||||
struct page **p, struct vm_area_struct **vma)
|
||||
{
|
||||
size_t got;
|
||||
int ret;
|
||||
|
||||
//#define USE_get_user_pages_fast
|
||||
#ifdef USE_get_user_pages_fast
|
||||
// see https://www.kernel.org/doc/htmldocs/kernel-api/API-get-user-pages-fast.html
|
||||
// for get_user_pages_fast documentation
|
||||
for (got = 0; got < num_pages; got += ret)
|
||||
{
|
||||
ret = get_user_pages_fast(
|
||||
start_page + got * PAGE_SIZE, // unsigned long start
|
||||
num_pages - got, // int nr_pages
|
||||
1, // write
|
||||
p + got // struct page ** pages
|
||||
);
|
||||
if (ret < 0)
|
||||
goto bail_release;
|
||||
}
|
||||
#else // #ifdef USE_get_user_pages_fast
|
||||
|
||||
// see http://www.hep.by/gnu/kernel/kernel-api/API-get-user-pages.html
|
||||
// and http://www.makelinux.net/ldd3/chp-15-sect-3
|
||||
// for get_user_pages documentation
|
||||
|
||||
int force = 0; // TODO: this was 1 originaly, changed to 0 following documentation (links above)
|
||||
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,0,0) // TODO: find exact version starting from which 'current' and 'current->tm' should not be used //https://github.com/torvalds/linux/commit/d4edcf0d56958db0aca0196314ca38a5e730ea92
|
||||
#pragma message("Building with old 'get_user_pages'")
|
||||
#else
|
||||
#pragma message("Building with new 'get_user_pages'")
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0) // TODO: find exact version starting from which 'gup_flags' should be used instead of 'write' and 'force'
|
||||
#pragma message("Building with 'write' and 'force' parameters of 'get_user_pages'")
|
||||
#else
|
||||
#pragma message("Building with 'gup_flags' parameter of 'get_user_pages'")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
for (got = 0; got < num_pages; got += ret)
|
||||
{
|
||||
ret = get_user_pages(
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4,0,0) //https://github.com/torvalds/linux/commit/d4edcf0d56958db0aca0196314ca38a5e730ea92
|
||||
current, // struct task_struct * tsk
|
||||
current->mm, // struct mm_struct * mm
|
||||
#endif
|
||||
start_page + got * PAGE_SIZE, // unsigned long start
|
||||
num_pages - got, // int nr_pages
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4,0,0)
|
||||
1, // int write
|
||||
force, // int force
|
||||
#else
|
||||
FOLL_WRITE,
|
||||
#endif
|
||||
p + got, // struct page ** pages
|
||||
vma); // struct vm_area_struct ** vmas
|
||||
if (ret < 0)
|
||||
goto bail_release;
|
||||
}
|
||||
// https://forum.blackmagicdesign.com/viewtopic.php?f=12&t=55075: #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) ...# elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
|
||||
// https://www.linuxquestions.org/questions/linux-kernel-70/direct-i-o-usgin-get_user_pages-4175559449/
|
||||
// https://lwn.net/Articles/753027/
|
||||
#endif // else of #ifdef USE_get_user_pages_fast
|
||||
|
||||
current->mm->locked_vm += num_pages;
|
||||
|
||||
ret = 0;
|
||||
goto bail;
|
||||
|
||||
bail_release:
|
||||
//tickets #2334: we should use 'got' here
|
||||
__aclpci_release_user_pages(p, got, 0);
|
||||
ACL_DEBUG (KERN_DEBUG "'get_user_pages' failed with code %d", ret);
|
||||
bail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* aclpci_get_user_pages - lock user pages into memory
|
||||
* @start_page: the start page
|
||||
* @num_pages: the number of pages
|
||||
* @p: the output page structures
|
||||
*
|
||||
* This function takes a given start page (page aligned user virtual
|
||||
* address) and pins it and the following specified number of pages.
|
||||
*/
|
||||
int aclpci_get_user_pages(unsigned long start_page, size_t num_pages, struct page **p)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
|
||||
mmap_write_lock(current->mm);
|
||||
#else
|
||||
down_write(¤t->mm->mmap_sem);
|
||||
#endif
|
||||
|
||||
ret = __aclpci_get_user_pages(start_page, num_pages, p, NULL);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
|
||||
mmap_write_unlock(current->mm);
|
||||
#else
|
||||
up_write(¤t->mm->mmap_sem);
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void aclpci_release_user_pages(struct page **p, size_t num_pages)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
|
||||
mmap_write_lock(current->mm);
|
||||
#else
|
||||
down_write(¤t->mm->mmap_sem);
|
||||
#endif
|
||||
|
||||
__aclpci_release_user_pages(p, num_pages, 1);
|
||||
|
||||
current->mm->locked_vm -= num_pages;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,8,0)
|
||||
mmap_write_unlock(current->mm);
|
||||
#else
|
||||
up_write(¤t->mm->mmap_sem);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void get_pcie_params (struct aclpci_dev *aclpci)
|
||||
{
|
||||
|
||||
struct pci_dev *dev = aclpci->pci_dev;
|
||||
u16 linkstat, speed, width;
|
||||
struct pci_dev *upstream;
|
||||
int pos, upos;
|
||||
u16 status_reg, control_reg, link_cap_reg;
|
||||
u16 status, control;
|
||||
u32 link_cap;
|
||||
|
||||
/* Defines for some special PCIe control bits */
|
||||
#define DISABLE_LINK_BIT (1 << 4)
|
||||
#define RETRAIN_LINK_BIT (1 << 5)
|
||||
#define TRAINING_IN_PROGRESS_BIT (1 << 11)
|
||||
#define LINKSPEED_2_5_GB (0x1)
|
||||
#define LINKSPEED_5_0_GB (0x2)
|
||||
|
||||
pos = pci_find_capability (dev, PCI_CAP_ID_EXP);
|
||||
if (!pos) {
|
||||
ACL_DEBUG (KERN_WARNING "Can't find PCI Express capability!");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find root node for this bus and tell it to retrain itself. */
|
||||
upstream = aclpci->upstream;
|
||||
if (upstream == NULL) {
|
||||
return;
|
||||
}
|
||||
upos = pci_find_capability (upstream, PCI_CAP_ID_EXP);
|
||||
status_reg = upos + PCI_EXP_LNKSTA;
|
||||
control_reg = upos + PCI_EXP_LNKCTL;
|
||||
link_cap_reg = upos + PCI_EXP_LNKCAP;
|
||||
pci_read_config_word (upstream, status_reg, &status);
|
||||
pci_read_config_word (upstream, control_reg, &control);
|
||||
pci_read_config_dword (upstream, link_cap_reg, &link_cap);
|
||||
|
||||
|
||||
pci_read_config_word (dev, pos + PCI_EXP_LNKSTA, &linkstat);
|
||||
pci_read_config_dword (upstream, link_cap_reg, &link_cap);
|
||||
speed = linkstat & PCI_EXP_LNKSTA_CLS;
|
||||
width = (linkstat & PCI_EXP_LNKSTA_NLW) >> PCI_EXP_LNKSTA_NLW_SHIFT;
|
||||
|
||||
aclpci->pci_gen = speed;
|
||||
aclpci->pci_num_lanes = width;
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* For some reason, pci_find_ext_capability is not resolved
|
||||
* when loading this driver. So copied the implementation here. */
|
||||
#define PCI_CFG_SPACE_SIZE 256
|
||||
#define PCI_CFG_SPACE_EXP_SIZE 4096
|
||||
int my_pci_find_ext_capability(struct pci_dev *dev, int cap)
|
||||
{
|
||||
u32 header;
|
||||
int ttl;
|
||||
int pos = PCI_CFG_SPACE_SIZE;
|
||||
|
||||
/* minimum 8 bytes per capability */
|
||||
ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;
|
||||
|
||||
if (dev->cfg_size <= PCI_CFG_SPACE_SIZE)
|
||||
return 0;
|
||||
|
||||
if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If we have no capabilities, this is indicated by cap ID,
|
||||
* cap version and next pointer all being 0.
|
||||
*/
|
||||
if (header == 0)
|
||||
return 0;
|
||||
|
||||
while (ttl-- > 0) {
|
||||
if (PCI_EXT_CAP_ID(header) == cap)
|
||||
return pos;
|
||||
|
||||
pos = PCI_EXT_CAP_NEXT(header);
|
||||
if (pos < PCI_CFG_SPACE_SIZE)
|
||||
break;
|
||||
|
||||
if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* return value of AER uncorrectable error mask register
|
||||
* for UPSTREAM node of the given device. */
|
||||
u32 get_aer_uerr_mask_reg (struct aclpci_dev *aclpci)
|
||||
{
|
||||
struct pci_dev *dev = aclpci->upstream;
|
||||
u32 reg32 = 0;
|
||||
int pos;
|
||||
|
||||
if (dev == NULL) {
|
||||
ACL_DEBUG (KERN_DEBUG "No upstream device found!");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
//TODO: compilation fails on RedHat
|
||||
// if (dev->__aer_firmware_first) {
|
||||
// return -EIO;
|
||||
// }
|
||||
|
||||
pos = my_pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!pos) {
|
||||
ACL_DEBUG (KERN_DEBUG "Upstream device doesn't have AER extended capability.");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
pci_read_config_dword(dev, pos+0x4, ®32);
|
||||
pci_read_config_dword(dev, pos+0x8, ®32);
|
||||
return reg32;
|
||||
}
|
||||
|
||||
/* Surprise down is the 5th register inside AER uncorrectable error register/mask */
|
||||
#define AER_SURPRISE_DOWN 0x20
|
||||
|
||||
|
||||
/* Set AER uncorrectable error mask register
|
||||
* for UPSTREAM node of the given device. */
|
||||
void set_aer_uerr_mask_reg (struct aclpci_dev *aclpci, u32 val)
|
||||
{
|
||||
int pos;
|
||||
struct pci_dev *dev = aclpci->upstream;
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
|
||||
pos = my_pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
|
||||
if (!pos) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* First, clear the error bit by writing 1 to 5th bit. */
|
||||
pci_write_config_dword(dev, pos+0x4, AER_SURPRISE_DOWN);
|
||||
|
||||
/* Now set the mask register */
|
||||
pci_write_config_dword(dev, pos+0x8, val);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/* Mask off "Surprise Down" error in AER. Note that setting the mask to '1' means
|
||||
* the error is ignored. */
|
||||
void disable_aer_on_upstream_dev(struct aclpci_dev *aclpci) {
|
||||
|
||||
u32 disabled;
|
||||
aclpci->aer_uerr_mask_reg = get_aer_uerr_mask_reg(aclpci);
|
||||
if (aclpci->aer_uerr_mask_reg == -EIO) {
|
||||
return;
|
||||
}
|
||||
|
||||
disabled = aclpci->aer_uerr_mask_reg | AER_SURPRISE_DOWN;
|
||||
|
||||
ACL_DEBUG (KERN_WARNING "Changing AER Uncorrectable error mask register from %x to %x",
|
||||
aclpci->aer_uerr_mask_reg, disabled);
|
||||
set_aer_uerr_mask_reg(aclpci, disabled);
|
||||
}
|
||||
|
||||
|
||||
/* Restore AER uncorrectable error mask register
|
||||
* for UPSTREAM node of the given device. */
|
||||
void restore_aer_on_upstream_dev(struct aclpci_dev *aclpci) {
|
||||
|
||||
if (aclpci->aer_uerr_mask_reg == -EIO)
|
||||
return;
|
||||
ACL_DEBUG (KERN_WARNING "Restoring AER Uncorrectable error mask register to %x", aclpci->aer_uerr_mask_reg);
|
||||
set_aer_uerr_mask_reg(aclpci, aclpci->aer_uerr_mask_reg);
|
||||
}
|
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Altera Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
*
|
||||
* 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 "aclpci.h"
|
||||
#include "hw_pcie_cvp_constants.h"
|
||||
|
||||
|
||||
|
||||
/********************************************/
|
||||
/* Code below is taken from quartus/pgm/cvp_drv/cvp_drv.c
|
||||
* When OpenCL becomes part of Quartus, remove the duplicate code */
|
||||
|
||||
/* Map VSEC_BIT to offset and bitmask.
|
||||
* Taken from pgm/cvp_drv/cvp_drv.c */
|
||||
static void get_mask_and_offset(unsigned char whichBit, unsigned char *bitMask, unsigned char *bitRegOffset)
|
||||
{
|
||||
switch (whichBit) {
|
||||
case DATA_ENCRYPTED:
|
||||
*bitRegOffset = OFFSET_CVP_STATUS;
|
||||
*bitMask = MASK_DATA_ENCRYPTED;
|
||||
break;
|
||||
case DATA_COMPRESSED:
|
||||
*bitRegOffset = OFFSET_CVP_STATUS;
|
||||
*bitMask = MASK_DATA_COMPRESSED;
|
||||
break;
|
||||
case CVP_CONFIG_READY:
|
||||
*bitRegOffset = OFFSET_CVP_STATUS;
|
||||
*bitMask = MASK_CVP_CONFIG_READY;
|
||||
break;
|
||||
case CVP_CONFIG_ERROR:
|
||||
*bitRegOffset = OFFSET_CVP_STATUS;
|
||||
*bitMask = MASK_CVP_CONFIG_ERROR;
|
||||
break;
|
||||
case CVP_EN:
|
||||
*bitRegOffset = OFFSET_CVP_STATUS;
|
||||
*bitMask = MASK_CVP_EN;
|
||||
break;
|
||||
case USER_MODE:
|
||||
*bitRegOffset = OFFSET_CVP_STATUS;
|
||||
*bitMask = MASK_USER_MODE;
|
||||
break;
|
||||
case PLD_CLK_IN_USE:
|
||||
*bitRegOffset = OFFSET_CVP_STATUS+1;
|
||||
*bitMask = MASK_PLD_CLK_IN_USE;
|
||||
break;
|
||||
case CVP_MODE:
|
||||
*bitRegOffset = OFFSET_CVP_MODE_CTRL;
|
||||
*bitMask = MASK_CVP_MODE;
|
||||
break;
|
||||
case HIP_CLK_SEL:
|
||||
*bitRegOffset = OFFSET_CVP_MODE_CTRL;
|
||||
*bitMask = MASK_HIP_CLK_SEL;
|
||||
break;
|
||||
case CVP_CONFIG:
|
||||
*bitRegOffset = OFFSET_CVP_PROG_CTRL;
|
||||
*bitMask = MASK_CVP_CONFIG;
|
||||
break;
|
||||
case START_XFER:
|
||||
*bitRegOffset = OFFSET_CVP_PROG_CTRL;
|
||||
*bitMask = MASK_START_XFER;
|
||||
break;
|
||||
case CVP_CFG_ERR_LATCH:
|
||||
*bitRegOffset = OFFSET_UNC_IE_STATUS;
|
||||
*bitMask = MASK_CVP_CFG_ERR_LATCH;
|
||||
break;
|
||||
default:
|
||||
*bitRegOffset = -1;
|
||||
*bitMask = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned char read_bit (struct pci_dev *dev, unsigned char whichBit)
|
||||
{
|
||||
unsigned char bitMask, bitRegOffset;
|
||||
u8 byteRead;
|
||||
get_mask_and_offset(whichBit, &bitMask, &bitRegOffset);
|
||||
pci_read_config_byte (dev, OFFSET_VSEC + bitRegOffset, &byteRead);
|
||||
return (byteRead & bitMask) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void write_bit (struct pci_dev *dev, unsigned char whichBit, unsigned char bitValue)
|
||||
{
|
||||
unsigned char bitMask, bitRegOffset;
|
||||
u8 byteValue;
|
||||
switch (whichBit) {
|
||||
case CVP_MODE:
|
||||
case HIP_CLK_SEL:
|
||||
case CVP_CONFIG:
|
||||
case START_XFER:
|
||||
case CVP_CFG_ERR_LATCH:
|
||||
get_mask_and_offset (whichBit, &bitMask, &bitRegOffset);
|
||||
pci_read_config_byte (dev, OFFSET_VSEC + bitRegOffset, &byteValue);
|
||||
byteValue = bitValue ? (byteValue | bitMask) : (byteValue & ~bitMask);
|
||||
pci_write_config_byte (dev, OFFSET_VSEC + bitRegOffset, byteValue);
|
||||
break;
|
||||
default:
|
||||
break; // do nothing, the 5 bits above are the only writeable ones
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Dump state of PCIe VSEC region. Useful when detect an error during CvP */
|
||||
static void dump_pcie_vsec_state (struct pci_dev *dev)
|
||||
{
|
||||
#define READ_PRINT16(x) pci_read_config_word (dev, OFFSET_VSEC + x, &result16); ACL_DEBUG (KERN_DEBUG "%s = 0x%x", #x, result16);
|
||||
#define READ_PRINT32(x) pci_read_config_dword (dev, OFFSET_VSEC + x, &result32); ACL_DEBUG (KERN_DEBUG "%s = 0x%x", #x, result32);
|
||||
|
||||
u32 result32;
|
||||
u16 result16;
|
||||
|
||||
ACL_DEBUG (KERN_DEBUG "Dump of PCIe VSEC");
|
||||
READ_PRINT16 (OFFSET_CVP_STATUS);
|
||||
READ_PRINT32 (OFFSET_CVP_MODE_CTRL);
|
||||
READ_PRINT32 (OFFSET_CVP_DATA);
|
||||
READ_PRINT32 (OFFSET_CVP_PROG_CTRL);
|
||||
READ_PRINT32 (OFFSET_UNC_IE_STATUS);
|
||||
|
||||
#undef READ_PRINT16
|
||||
#undef READ_PRINT32
|
||||
}
|
||||
|
||||
|
||||
/* Wait for given bit to be given value, upto maximum value.
|
||||
* Returns 1 if the given value was reached. */
|
||||
static unsigned char wait_for_bit (struct pci_dev *dev, unsigned char whichBit, unsigned char value)
|
||||
{
|
||||
int max_num_tries = 10;
|
||||
int single_delay = 1; /* in milliseconds */
|
||||
int itry = 0;
|
||||
while (read_bit (dev, whichBit) != value && itry < max_num_tries) {
|
||||
itry++;
|
||||
msleep (single_delay);
|
||||
}
|
||||
return (itry < max_num_tries);
|
||||
}
|
||||
|
||||
|
||||
static void send_pgm_data (struct aclpci_dev *aclpci, u32 data)
|
||||
{
|
||||
writel (data, aclpci->bar[0] + 0x0);
|
||||
}
|
||||
|
||||
|
||||
#define OFFSET_CVP_NUMCLKS 0X21
|
||||
static void CVP_DRV_SetNumClks(struct pci_dev *dev, unsigned char numClks)
|
||||
{
|
||||
int write_addr = OFFSET_VSEC+OFFSET_CVP_NUMCLKS;
|
||||
|
||||
if (numClks == 64) {
|
||||
pci_write_config_byte (dev, write_addr, 0x0);
|
||||
} else if (numClks > 0 && numClks < 64) {
|
||||
pci_write_config_byte (dev, write_addr, numClks);
|
||||
} else {
|
||||
// numClks is not a valid number!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void prepare_for_pgm_data(struct pci_dev *dev)
|
||||
{
|
||||
if (read_bit(dev, DATA_COMPRESSED)) {
|
||||
CVP_DRV_SetNumClks(dev, 8);
|
||||
} else if (read_bit(dev, DATA_ENCRYPTED)) {
|
||||
CVP_DRV_SetNumClks(dev, 4);
|
||||
} else {
|
||||
CVP_DRV_SetNumClks(dev, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Issue "dummy" writes to the HIP's memory to make the CB switch between CvP and internal clocks.
|
||||
* The CB needs at least 244 125 MHz clock ticks, so we give it 64*4 = 256. */
|
||||
#define NUM_REG_WRITES 244
|
||||
#define VALUE_DUMMY 0x0
|
||||
#define NUM_CVP_CLKS 1
|
||||
static void switch_clock (struct aclpci_dev *aclpci)
|
||||
{
|
||||
int i;
|
||||
CVP_DRV_SetNumClks(aclpci->pci_dev, NUM_CVP_CLKS);
|
||||
for (i = 0; i < NUM_REG_WRITES; i++) {
|
||||
writel (VALUE_DUMMY, aclpci->bar[0] + 0x0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* check for CRC error that many 32-bit words */
|
||||
#define ERR_CHK_INTERVAL 25000
|
||||
|
||||
|
||||
/* Re-configure FPGA core with given bitstream via PCIe.
|
||||
* Support for Stratix V devices and higher */
|
||||
int aclpci_cvp (struct aclpci_dev *aclpci, void __user* core_bitstream, ssize_t len) {
|
||||
|
||||
struct pci_dev *dev = NULL;
|
||||
u32 dev_id;
|
||||
u32 *data;
|
||||
int i;
|
||||
int cvp_failed = 0;
|
||||
int result = -EFAULT;
|
||||
|
||||
// ACL_DEBUG (KERN_DEBUG "aclpci_cvp (%p, %p, %lu)", aclpci, core_bitstream, len);
|
||||
|
||||
/* Basic error checks */
|
||||
if (aclpci == NULL) {
|
||||
ACL_DEBUG (KERN_WARNING "Need to open device before can do reconfigure!");
|
||||
return result;
|
||||
}
|
||||
if (core_bitstream == NULL) {
|
||||
ACL_DEBUG (KERN_WARNING "Programming bitstream is not provided!");
|
||||
return result;
|
||||
}
|
||||
if (len < 1000000) {
|
||||
ACL_DEBUG (KERN_WARNING "Programming bitstream length is suspiciously small. Not doing CvP!");
|
||||
return result;
|
||||
}
|
||||
|
||||
dev = aclpci->pci_dev;
|
||||
if (dev == NULL) {
|
||||
ACL_DEBUG (KERN_WARNING "Dude, where is PCIe device?!");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* Only do CVP on supported boards */
|
||||
dev_id = dev->device;
|
||||
if (dev_id != ACL_PCI_PCIE385_DEVICE_ID) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!read_bit (dev, CVP_EN)) {
|
||||
ACL_DEBUG (KERN_WARNING "CvP is not enabled in the design on the FPGA!");
|
||||
return result;
|
||||
}
|
||||
|
||||
ACL_DEBUG (KERN_DEBUG "OK to proceed with CvP!");
|
||||
aclpci->cvp_in_progress = 1;
|
||||
|
||||
write_bit(dev, HIP_CLK_SEL, 1);
|
||||
write_bit(dev, CVP_MODE, 1);
|
||||
msleep (2);
|
||||
switch_clock(aclpci);
|
||||
|
||||
write_bit(dev, CVP_CONFIG, 1);
|
||||
msleep (2);
|
||||
switch_clock(aclpci);
|
||||
|
||||
/* wait for the CB to say it's ready for CvP */
|
||||
if (!wait_for_bit (dev, CVP_CONFIG_READY, 1)) {
|
||||
ACL_DEBUG (KERN_WARNING "Timed out waiting for CVP_CONFIG_READY to become 1! CvP has failed");
|
||||
dump_pcie_vsec_state(dev);
|
||||
cvp_failed = 1;
|
||||
goto teardown;
|
||||
}
|
||||
|
||||
switch_clock(aclpci);
|
||||
write_bit(dev, START_XFER, 1);
|
||||
|
||||
if (!wait_for_bit (dev, HIP_CLK_SEL, 1) || !wait_for_bit (dev, CVP_MODE, 1)) {
|
||||
ACL_DEBUG (KERN_WARNING "Timed out waiting for HIP_CLK_SEL and CVP_MODE to be 1! CvP has failed");
|
||||
dump_pcie_vsec_state(dev);
|
||||
cvp_failed = 1;
|
||||
goto teardown;
|
||||
}
|
||||
|
||||
/* Transfer */
|
||||
prepare_for_pgm_data(dev);
|
||||
|
||||
ACL_DEBUG (KERN_WARNING "Setup is done. Starting to write CvP data!");
|
||||
|
||||
data = (u32 __user*)core_bitstream;
|
||||
for (i = 0; i < len; i++) {
|
||||
u32 curData;
|
||||
result = copy_from_user ( &curData, data + i, sizeof(curData));
|
||||
send_pgm_data (aclpci, curData);
|
||||
if ((i % ERR_CHK_INTERVAL == 0) && read_bit(dev, CVP_CONFIG_ERROR)) {
|
||||
ACL_DEBUG (KERN_WARNING "ERROR: CB detected a CRC error between words %d and %d!\n", (i - ERR_CHK_INTERVAL), i);
|
||||
dump_pcie_vsec_state(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == len) {
|
||||
ACL_DEBUG (KERN_DEBUG "INFO: Reached the end of the core programming file.");
|
||||
}
|
||||
|
||||
teardown:
|
||||
// Teardown
|
||||
write_bit(dev, START_XFER, 0);
|
||||
write_bit(dev, CVP_CONFIG, 0);
|
||||
switch_clock(aclpci);
|
||||
|
||||
// wait for the CB to say it's done with CvP
|
||||
if (!wait_for_bit (dev, CVP_CONFIG_READY, 0)) {
|
||||
ACL_DEBUG (KERN_WARNING "Timed out waiting for CVP_CONFIG_READY to become 0! CvP has failed");
|
||||
dump_pcie_vsec_state(dev);
|
||||
}
|
||||
|
||||
if (read_bit(dev, CVP_CFG_ERR_LATCH)) {
|
||||
ACL_DEBUG (KERN_WARNING "ERROR: Configuration error detected!");
|
||||
dump_pcie_vsec_state(dev);
|
||||
cvp_failed = 1;
|
||||
write_bit(dev, CVP_CFG_ERR_LATCH, 1); // write a 1 to clear the config space error bit
|
||||
} else {
|
||||
cvp_failed = 0;
|
||||
}
|
||||
|
||||
write_bit(dev, CVP_MODE, 0);
|
||||
write_bit(dev, HIP_CLK_SEL, 0);
|
||||
|
||||
if (!cvp_failed) {
|
||||
// wait for the Application Layer to be ready for normal operation
|
||||
if (!wait_for_bit (dev, PLD_CLK_IN_USE, 1)) {
|
||||
ACL_DEBUG (KERN_WARNING "Timed out waiting for PLD_CLK_IN_USE to become 1! CvP has failed");
|
||||
dump_pcie_vsec_state(dev);
|
||||
cvp_failed = 1;
|
||||
}
|
||||
if (!wait_for_bit (dev, USER_MODE, 1)) {
|
||||
ACL_DEBUG (KERN_WARNING "Timed out waiting for USER_MODE to become 1! CvP has failed");
|
||||
dump_pcie_vsec_state(dev);
|
||||
cvp_failed = 1;
|
||||
}
|
||||
if (!cvp_failed) {
|
||||
ACL_DEBUG (KERN_DEBUG "SUCCESS: CvP has finished.");
|
||||
ACL_DEBUG (KERN_DEBUG " The Application Layer is ready for normal operation!");
|
||||
msleep (200);
|
||||
result = 0;
|
||||
}
|
||||
}
|
||||
|
||||
aclpci->cvp_in_progress = 0;
|
||||
return result;
|
||||
}
|
||||
|
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Altera Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* DMA logic imlementation.
|
||||
*
|
||||
* The basic flow of DMA transfer is as follows:
|
||||
* 1. Pin user memory (a contiguous set of address in processor address space)
|
||||
* to get a list of physical pages (almost never contiguous list of 4KB
|
||||
* blocks).
|
||||
* 2. Setup ATT (address translation table) entries for physical page address
|
||||
* to "row #" mapping in the FPGA PCIe core (a "row number" is just a
|
||||
* counter that is passed in DMA descriptor as a host address).
|
||||
* 3. Create and send DMA descriptor (src addr, dest addr, and lengh) to the DMA
|
||||
* core to perform the transfer.
|
||||
* 4. Go to step 2 if have not transfered all currently pinned memory yet.
|
||||
* 5. Go to step 1 if need to pin more memory.
|
||||
*
|
||||
* DMA controller sends an interrupt whenever work for a single DMA descriptor
|
||||
* is complete. The driver also checks "done_count" to see how many descriptors
|
||||
* completed. This is in case the driver misses an interrupt.
|
||||
*
|
||||
* To keep interrupt logic simple (i.e. no work-queues), the user has to stall
|
||||
* using the following logic until DMA is done:
|
||||
*
|
||||
* while (!dma->is_idle())
|
||||
* dma->update(0);
|
||||
*
|
||||
* The DMA logic is complicated because there are a number of limits of what can
|
||||
* be done in one shot. Here they are (all are constants in hw_pcie_constants.h):
|
||||
* - How much user memory can be pinned at one time.
|
||||
* - Size of ATT table in hardware
|
||||
* - Number of outstanding descriptors the hardware can have.
|
||||
*
|
||||
* Due to hardware restrictions, can only do DMA for 32-byte aligned start
|
||||
* addresses (on both host and device) AND 32-byte aligned lengths.
|
||||
* Also, need to use a separate descriptor for transfers that do NOT start or
|
||||
* end on page boundary. DMA engine does NOT do DMA for these cases, so these
|
||||
* transfers are very slow. */
|
||||
|
||||
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/sched.h>
|
||||
#include <asm/page.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include "aclpci.h"
|
||||
|
||||
#define MAP_UNMAP_PAGES 1
|
||||
|
||||
__inline int KERNEL_DIRECTION_FROM_USER_DIRECTION(int dmaOptions)
|
||||
{
|
||||
int direction = PCI_DMA_BIDIRECTIONAL;
|
||||
|
||||
if(dmaOptions & WD_DMA_FROM_DEVICE)
|
||||
{
|
||||
if(!(dmaOptions & WD_DMA_TO_DEVICE))
|
||||
{
|
||||
direction = PCI_DMA_FROMDEVICE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(dmaOptions & WD_DMA_TO_DEVICE)
|
||||
{
|
||||
direction = PCI_DMA_TODEVICE;
|
||||
}
|
||||
}
|
||||
return direction;
|
||||
}
|
||||
|
||||
int lock_dma_buffer (struct aclpci_dev *aclpci, WD_DMA *dma)
|
||||
{
|
||||
|
||||
int ret;
|
||||
unsigned int i = 0;
|
||||
//struct aclpci_dma *d = &(aclpci->dma_data);
|
||||
//ssize_t start_page, end_page, num_pages;
|
||||
//u64 ej, startj = get_jiffies_64();
|
||||
|
||||
/*TODO remove debug output*/
|
||||
//ACL_DEBUG (KERN_DEBUG "enter lock_dma_buffer()");
|
||||
/***************************/
|
||||
/* num_pages that [addr, addr+len] map to. */
|
||||
/*
|
||||
ACL_DEBUG (KERN_DEBUG "addr is: %p", dma->pUserAddr);
|
||||
|
||||
start_page = (ssize_t)dma->pUserAddr >> PAGE_SHIFT;
|
||||
ACL_DEBUG (KERN_DEBUG "start_page is: %p, size is %d", start_page, dma->dwBytes);
|
||||
|
||||
end_page = ((ssize_t)dma->pUserAddr + dma->dwBytes - 1) >> PAGE_SHIFT;
|
||||
ACL_DEBUG (KERN_DEBUG "end_page is: %p", end_page);
|
||||
|
||||
num_pages = end_page - start_page + 1;
|
||||
ACL_DEBUG (KERN_DEBUG "num_pages is: %p", num_pages);
|
||||
*/
|
||||
|
||||
dma->Page = (WD_DMA_PAGE*)kzalloc ( sizeof(WD_DMA_PAGE) * dma->dwPages, GFP_KERNEL );
|
||||
if (dma->Page == NULL)
|
||||
{
|
||||
ACL_DEBUG (KERN_DEBUG "Couldn't allocate array of %u page descriptors!", dma->dwPages);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
dma->ppInternal = (void**)kzalloc ( sizeof(struct page*) * dma->dwPages, GFP_KERNEL );
|
||||
if (dma->ppInternal == NULL)
|
||||
{
|
||||
ACL_DEBUG (KERN_DEBUG "Couldn't allocate array of %u 'page' pointers!", dma->dwPages);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
dma->dma_addrs = (dma_addr_t*)kzalloc ( sizeof(dma_addr_t) * dma->dwPages, GFP_KERNEL );
|
||||
if (dma->dma_addrs == NULL)
|
||||
{
|
||||
ACL_DEBUG (KERN_DEBUG "Couldn't allocate array of %u dma_addr_t's!", dma->dwPages);
|
||||
return -EFAULT;
|
||||
}
|
||||
//ACL_VERBOSE_DEBUG (KERN_DEBUG "pages = [%p, %p), dma_addrs = [%p, %p)",
|
||||
// dma->pages, dma->pages+dma->dwPages, dma->dma_addrs, dma->dma_addrs+dma->dwPages);
|
||||
|
||||
/* pin user memory and get set of physical pages back in 'p' ptr. */
|
||||
ret = aclpci_get_user_pages((unsigned long)dma->pUserAddr & PAGE_MASK, (size_t)dma->dwPages, (struct page**)dma->ppInternal);
|
||||
if (ret != 0)
|
||||
{
|
||||
ACL_DEBUG (KERN_DEBUG "Couldn't pin all user pages. %d!\n", ret);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* map pages for PCI access. */
|
||||
#if MAP_UNMAP_PAGES
|
||||
for (i = 0; i < dma->dwPages; i++)
|
||||
{
|
||||
struct page *cur = dma->ppInternal[i];
|
||||
//ACL_DEBUG (KERN_DEBUG "p[%d] = 0x%p", i, cur);
|
||||
if (cur != NULL) {
|
||||
//ACL_DEBUG (KERN_DEBUG " phys_addr = 0x%llx", page_to_phys(cur));
|
||||
|
||||
dma_addr_t phys = pci_map_page (aclpci->pci_dev, cur, 0, PAGE_SIZE, KERNEL_DIRECTION_FROM_USER_DIRECTION(dma->dwOptions));
|
||||
if (phys == 0) {
|
||||
ACL_DEBUG (KERN_DEBUG " Couldn't pci_map_page!");
|
||||
return -EFAULT;
|
||||
}
|
||||
dma->dma_addrs[i] = phys;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
dma->first_page_offset = (unsigned long)dma->pUserAddr & ACL_PCIE_DMA_ATT_PAGE_ADDR_MASK;
|
||||
dma->last_page_offset = (unsigned long)(dma->pUserAddr + dma->dwBytes) & ACL_PCIE_DMA_ATT_PAGE_ADDR_MASK;
|
||||
|
||||
//ej = get_jiffies_64();
|
||||
|
||||
//d->m_pin_time += (ej - startj);
|
||||
// d->m_lock_time += (ej - startj);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void unlock_dma_buffer (struct aclpci_dev *aclpci, WD_DMA *dma)
|
||||
{
|
||||
#if VERBOSE_DEBUG
|
||||
u64 ej, startj = get_jiffies_64();
|
||||
#endif
|
||||
|
||||
#if MAP_UNMAP_PAGES
|
||||
int i;
|
||||
/* Unmap pages to make the data available for CPU */
|
||||
for (i = 0; i < dma->dwPages; i++)
|
||||
{
|
||||
struct page *cur = dma->ppInternal[i];
|
||||
// ACL_DEBUG (KERN_DEBUG "p[%d] = %p", i, cur);
|
||||
if (cur != NULL) {
|
||||
dma_addr_t phys = dma->dma_addrs[i];
|
||||
pci_unmap_page (aclpci->pci_dev, phys, PAGE_SIZE, KERNEL_DIRECTION_FROM_USER_DIRECTION(dma->dwOptions));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Unpin pages */
|
||||
aclpci_release_user_pages ((struct page**)dma->ppInternal, dma->dwPages);
|
||||
|
||||
/* TODO: try to re-use these buffers on future allocs */
|
||||
kfree (dma->ppInternal);
|
||||
kfree (dma->dma_addrs);
|
||||
|
||||
//ej = get_jiffies_64();
|
||||
ACL_VERBOSE_DEBUG (KERN_DEBUG "DMA: Unpinned ? pages in %u usec",
|
||||
//dma->num_pages,
|
||||
jiffies_to_usecs(ej - startj));
|
||||
|
||||
dma->ppInternal = NULL;
|
||||
dma->dma_addrs = NULL;
|
||||
//d->m_pin_time += (ej - startj);
|
||||
//d->m_unlock_time += (ej - startj);
|
||||
}
|
||||
|
||||
|
||||
void sync_cache_for_memory(struct aclpci_dev *aclpci, WD_DMA *dma, bool forDevice)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < dma->dwPages; i++)
|
||||
{
|
||||
struct page *cur = dma->ppInternal[i];
|
||||
if (cur != NULL)
|
||||
{
|
||||
dma_addr_t phys = dma->dma_addrs[i];
|
||||
if(forDevice)
|
||||
{
|
||||
pci_dma_sync_single_for_device (aclpci->pci_dev, phys, PAGE_SIZE, KERNEL_DIRECTION_FROM_USER_DIRECTION(dma->dwOptions));
|
||||
}
|
||||
else
|
||||
{
|
||||
pci_dma_sync_single_for_cpu (aclpci->pci_dev, phys, PAGE_SIZE, KERNEL_DIRECTION_FROM_USER_DIRECTION(dma->dwOptions));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
void sync_memory_for_cpu (struct aclpci_dev *aclpci, WD_DMA *dma)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < dma->dwPages; i++)
|
||||
{
|
||||
struct page *cur = dma->ppInternal[i];
|
||||
if (cur != NULL)
|
||||
{
|
||||
dma_addr_t phys = dma->dma_addrs[i];
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Altera Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Defines used only by aclpci_dma.c. */
|
||||
|
||||
|
||||
#if USE_DMA
|
||||
|
||||
/* Enable Linux-specific defines in the hw_pcie_dma.h file */
|
||||
#define LINUX
|
||||
#include "hw_pcie_dma.h"
|
||||
//#include "aclpci_queue.h"
|
||||
|
||||
struct dma_t_______________________ {
|
||||
void *ptr; /* if ptr is NULL, the whole struct considered invalid */
|
||||
size_t len;
|
||||
enum dma_data_direction dir;
|
||||
struct page **pages; /* one for each struct page */
|
||||
dma_addr_t *dma_addrs; /* one for each struct page */
|
||||
unsigned int num_pages;
|
||||
unsigned int first_page_offset;
|
||||
unsigned int last_page_offset;
|
||||
int m_read;
|
||||
};
|
||||
|
||||
//struct pinned_mem {
|
||||
// struct dma_t dma;
|
||||
// struct page **next_page;
|
||||
// unsigned int pages_rem;
|
||||
// unsigned int first_page_offset;
|
||||
// unsigned int last_page_offset;
|
||||
//};
|
||||
|
||||
|
||||
//struct aclpci_dma {
|
||||
|
||||
// Update information
|
||||
// unsigned int m_descriptors_updated;
|
||||
// unsigned int m_descriptors_acknowledged;
|
||||
// unsigned int m_descriptors_sent;
|
||||
// unsigned int m_old_done_count;
|
||||
// unsigned int m_bytes_acknowledged;
|
||||
|
||||
// // The container of pending memory transactions.
|
||||
// // Contains wd_dma values
|
||||
// struct queue m_dma_pending;
|
||||
|
||||
// // A representation of the hardware's descriptor fifo
|
||||
// // Contains DESCRIPTOR_UPDATE_DATA values
|
||||
// struct queue m_desc_pending;
|
||||
|
||||
// // Pinned memory we're currently building DMA transactions for.
|
||||
// struct pinned_mem m_active_mem;
|
||||
|
||||
// // The transaction we are currently working on
|
||||
// //struct DMA_DESCRIPTOR m_active_descriptor;
|
||||
|
||||
// int m_active_descriptor_valid;
|
||||
// unsigned int m_active_descriptor_size;
|
||||
// // The next ATT table row to write to
|
||||
// unsigned int m_next_att_row;
|
||||
// // The total number of active ATT rows
|
||||
// unsigned int m_att_size;
|
||||
|
||||
//struct pci_dev *m_pci_dev;
|
||||
//struct aclpci_dev *m_aclpci;
|
||||
|
||||
// Transfer information
|
||||
// unsigned int m_device_addr;
|
||||
// void* m_host_addr;
|
||||
// int m_read;
|
||||
// unsigned int m_bytes;
|
||||
// unsigned int m_bytes_sent;
|
||||
// int m_idle;
|
||||
|
||||
// u64 m_update_time, m_pin_time, m_start_time;
|
||||
// u64 m_lock_time, m_unlock_time;
|
||||
//};
|
||||
|
||||
#else
|
||||
//struct aclpci_dma {};
|
||||
#endif
|
@@ -0,0 +1,80 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/vermagic.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
MODULE_INFO(vermagic, VERMAGIC_STRING);
|
||||
|
||||
struct module __this_module
|
||||
__attribute__((section(".gnu.linkonce.this_module"))) = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.init = init_module,
|
||||
#ifdef CONFIG_MODULE_UNLOAD
|
||||
.exit = cleanup_module,
|
||||
#endif
|
||||
.arch = MODULE_ARCH_INIT,
|
||||
};
|
||||
|
||||
static const struct modversion_info ____versions[]
|
||||
__used
|
||||
__attribute__((section("__versions"))) = {
|
||||
{ 0x28950ef1, __VMLINUX_SYMBOL_STR(module_layout) },
|
||||
{ 0x3fa89e8f, __VMLINUX_SYMBOL_STR(cdev_del) },
|
||||
{ 0x98ab5c8d, __VMLINUX_SYMBOL_STR(kmalloc_caches) },
|
||||
{ 0x7f59e59, __VMLINUX_SYMBOL_STR(pci_write_config_dword) },
|
||||
{ 0xd2b09ce5, __VMLINUX_SYMBOL_STR(__kmalloc) },
|
||||
{ 0xdacd8618, __VMLINUX_SYMBOL_STR(cdev_init) },
|
||||
{ 0xf9a482f9, __VMLINUX_SYMBOL_STR(msleep) },
|
||||
{ 0x8bd590db, __VMLINUX_SYMBOL_STR(pci_write_config_word) },
|
||||
{ 0xc2f7c1b1, __VMLINUX_SYMBOL_STR(pci_read_config_byte) },
|
||||
{ 0xc483a55a, __VMLINUX_SYMBOL_STR(dev_set_drvdata) },
|
||||
{ 0x1c3e657e, __VMLINUX_SYMBOL_STR(pci_disable_device) },
|
||||
{ 0x62b1ada0, __VMLINUX_SYMBOL_STR(set_page_dirty_lock) },
|
||||
{ 0x9f6b4c62, __VMLINUX_SYMBOL_STR(down_interruptible) },
|
||||
{ 0xc9426d6d, __VMLINUX_SYMBOL_STR(pci_write_config_byte) },
|
||||
{ 0xcf73ce21, __VMLINUX_SYMBOL_STR(pci_release_regions) },
|
||||
{ 0x7485e15e, __VMLINUX_SYMBOL_STR(unregister_chrdev_region) },
|
||||
{ 0x7d11c268, __VMLINUX_SYMBOL_STR(jiffies) },
|
||||
{ 0x71de9b3f, __VMLINUX_SYMBOL_STR(_copy_to_user) },
|
||||
{ 0xbe4a1520, __VMLINUX_SYMBOL_STR(pci_set_master) },
|
||||
{ 0x5bbdc39c, __VMLINUX_SYMBOL_STR(pci_restore_state) },
|
||||
{ 0x127b8725, __VMLINUX_SYMBOL_STR(pci_iounmap) },
|
||||
{ 0xb8c7ff88, __VMLINUX_SYMBOL_STR(current_task) },
|
||||
{ 0x27e1a049, __VMLINUX_SYMBOL_STR(printk) },
|
||||
{ 0xc2560ac2, __VMLINUX_SYMBOL_STR(pci_read_config_word) },
|
||||
{ 0xd6b8e852, __VMLINUX_SYMBOL_STR(request_threaded_irq) },
|
||||
{ 0x3b4ceb4a, __VMLINUX_SYMBOL_STR(up_write) },
|
||||
{ 0xe6e3b875, __VMLINUX_SYMBOL_STR(down_write) },
|
||||
{ 0x99b0aabc, __VMLINUX_SYMBOL_STR(pci_find_capability) },
|
||||
{ 0x5f675a65, __VMLINUX_SYMBOL_STR(cdev_add) },
|
||||
{ 0x42c8de35, __VMLINUX_SYMBOL_STR(ioremap_nocache) },
|
||||
{ 0xf0fdf6cb, __VMLINUX_SYMBOL_STR(__stack_chk_fail) },
|
||||
{ 0x32f730e3, __VMLINUX_SYMBOL_STR(get_user_pages) },
|
||||
{ 0xebfdcb96, __VMLINUX_SYMBOL_STR(pci_read_config_dword) },
|
||||
{ 0xbdfb6dbb, __VMLINUX_SYMBOL_STR(__fentry__) },
|
||||
{ 0x2cb61da5, __VMLINUX_SYMBOL_STR(pci_unregister_driver) },
|
||||
{ 0x41ec4c1a, __VMLINUX_SYMBOL_STR(kmem_cache_alloc_trace) },
|
||||
{ 0x37a0cba, __VMLINUX_SYMBOL_STR(kfree) },
|
||||
{ 0x69acdf38, __VMLINUX_SYMBOL_STR(memcpy) },
|
||||
{ 0xc3fc2f, __VMLINUX_SYMBOL_STR(pci_request_regions) },
|
||||
{ 0xd9cd486, __VMLINUX_SYMBOL_STR(send_sig_info) },
|
||||
{ 0x71e3cecb, __VMLINUX_SYMBOL_STR(up) },
|
||||
{ 0x99487493, __VMLINUX_SYMBOL_STR(__pci_register_driver) },
|
||||
{ 0x334c1f75, __VMLINUX_SYMBOL_STR(put_page) },
|
||||
{ 0x46734db7, __VMLINUX_SYMBOL_STR(pci_enable_device) },
|
||||
{ 0x77e2f33, __VMLINUX_SYMBOL_STR(_copy_from_user) },
|
||||
{ 0x7cf5b2b3, __VMLINUX_SYMBOL_STR(dev_get_drvdata) },
|
||||
{ 0x584c5b17, __VMLINUX_SYMBOL_STR(dma_ops) },
|
||||
{ 0x29537c9e, __VMLINUX_SYMBOL_STR(alloc_chrdev_region) },
|
||||
{ 0xf20dabd8, __VMLINUX_SYMBOL_STR(free_irq) },
|
||||
{ 0x53108e44, __VMLINUX_SYMBOL_STR(pci_save_state) },
|
||||
};
|
||||
|
||||
static const char __module_depends[]
|
||||
__used
|
||||
__attribute__((section(".modinfo"))) =
|
||||
"depends=";
|
||||
|
||||
MODULE_ALIAS("pci:v00001D2Ad00000200sv*sd*bc*sc*i*");
|
||||
MODULE_ALIAS("pci:v00001D2Ad0000E385sv*sd*bc*sc*i*");
|
||||
|
||||
MODULE_INFO(srcversion, "A71392B52B6EA6074FA72B5");
|
@@ -0,0 +1,452 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Altera Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/* Implementation of all I/O functions except DMA transfers.
|
||||
* See aclpci_dma.c for DMA code.
|
||||
*/
|
||||
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/sched.h>
|
||||
#include "aclpci.h"
|
||||
|
||||
|
||||
static ssize_t aclpci_rw_large (void *dev_addr, void __user* user_addr, ssize_t len, char *buffer, int reading);
|
||||
|
||||
/* Given (bar_id, device_addr) pair, make sure they're valid and return
|
||||
* the resulting address. errno will contain error code, if any. */
|
||||
void* aclpci_get_checked_addr (int bar_id, void *device_addr, size_t count,
|
||||
struct aclpci_dev *aclpci, ssize_t *errno,
|
||||
int print_error_msg)
|
||||
{
|
||||
|
||||
if (bar_id >= ACL_PCI_NUM_BARS)
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING "Requested read/write from BAR #%d. Only have %d BARs!",
|
||||
bar_id, ACL_PCI_NUM_BARS);
|
||||
*errno = -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
/* Make sure the final address is within range */
|
||||
if (((unsigned long)device_addr + count) > aclpci->bar_length[bar_id])
|
||||
{
|
||||
if (print_error_msg)
|
||||
{
|
||||
ACL_DEBUG (KERN_WARNING "Requested read/write from BAR #%d from range (%lu, %lu). Length is %lu. BAR length is only %lu!",
|
||||
bar_id,
|
||||
(unsigned long)device_addr,
|
||||
(unsigned long)device_addr + count,
|
||||
count,
|
||||
aclpci->bar_length[bar_id]);
|
||||
}
|
||||
*errno = -EFAULT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
*errno = 0;
|
||||
return (void*)(aclpci->bar[bar_id] + (unsigned long)device_addr);
|
||||
}
|
||||
|
||||
#define INIT_RELEASE_IRQ 1
|
||||
|
||||
/* Response to user's open() call */
|
||||
int aclpci_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
|
||||
struct aclpci_dev *aclpci = 0;
|
||||
int result = 0;
|
||||
|
||||
/* pointer to containing data structure of the character device inode */
|
||||
aclpci = container_of(inode->i_cdev, struct aclpci_dev, cdev);
|
||||
|
||||
|
||||
/* create a reference to our device state in the opened file */
|
||||
file->private_data = aclpci;
|
||||
ACL_DEBUG (KERN_DEBUG "aclpci = %p, pid = %d (%s)", aclpci, current->pid, current->comm);
|
||||
|
||||
aclpci->user_pid = current->pid;
|
||||
aclpci->user_task = current;
|
||||
|
||||
aclpci->global_mem_segment = 0;
|
||||
|
||||
result = 0;
|
||||
|
||||
//done:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* Response to user's close() call. Will also be called by the kernel
|
||||
* if the user process dies for any reason. */
|
||||
int aclpci_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
ssize_t result = 0;
|
||||
struct aclpci_dev *aclpci = (struct aclpci_dev *)file->private_data;
|
||||
|
||||
#if INIT_RELEASE_IRQ
|
||||
release_irq (aclpci->pci_dev, aclpci);
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/* Read a small number of bytes and put them into user space */
|
||||
ssize_t aclpci_read_small (void *read_addr, void __user* dest_addr, ssize_t len)
|
||||
{
|
||||
ssize_t copy_res = 0;
|
||||
switch (len)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
u8 d = readb ( read_addr );
|
||||
copy_res = copy_to_user ( dest_addr, &d, sizeof(d) );
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
u16 d = readw ( read_addr );
|
||||
copy_res = copy_to_user ( dest_addr, &d, sizeof(d) );
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
u32 d = readl ( read_addr );
|
||||
copy_res = copy_to_user ( dest_addr, &d, sizeof(d) );
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
u64 d = readq ( read_addr );
|
||||
copy_res = copy_to_user ( dest_addr, &d, sizeof(d) );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (copy_res)
|
||||
{
|
||||
return -EFAULT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Write a small number of bytes taken from user space */
|
||||
ssize_t aclpci_write_small (void *write_addr, void __user* src_addr, ssize_t len)
|
||||
{
|
||||
|
||||
ssize_t copy_res = 0;
|
||||
switch (len)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
u8 d;
|
||||
copy_res = copy_from_user ( &d, src_addr, sizeof(d) );
|
||||
writeb ( d, write_addr );
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
u16 d;
|
||||
copy_res = copy_from_user ( &d, src_addr, sizeof(d) );
|
||||
writew ( d, write_addr );
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
u32 d;
|
||||
copy_res = copy_from_user ( &d, src_addr, sizeof(d) );
|
||||
writel ( d, write_addr );
|
||||
break;
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
u64 d;
|
||||
copy_res = copy_from_user ( &d, src_addr, sizeof(d) );
|
||||
writeq ( d, write_addr );
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (copy_res)
|
||||
{
|
||||
return -EFAULT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Read or Write arbitrary length sequency starting at read_addr and put it into
|
||||
* user space at dest_addr. if 'reading' is set to 1, doing the read. If 0, doing
|
||||
* the write. */
|
||||
static ssize_t aclpci_rw_large (void *dev_addr, void __user* user_addr, ssize_t len, char *buffer, int reading)
|
||||
{
|
||||
size_t bytes_left = len;
|
||||
size_t i, num_missed;
|
||||
u32 *ibuffer = (u32*)buffer;
|
||||
char *cbuffer;
|
||||
size_t offset, num_to_read;
|
||||
size_t chunk = BUF_SIZE;
|
||||
#if VERBOSE_DEBUG
|
||||
u64 startj, ej;
|
||||
#endif
|
||||
u64 sj = 0, acc_readj = 0, acc_transfj = 0;
|
||||
|
||||
#if VERBOSE_DEBUG
|
||||
startj = get_jiffies_64();
|
||||
#endif
|
||||
|
||||
/* Reading upto BUF_SIZE values, one int at a time, and then transfer
|
||||
* the buffer at once to user space. Repeat as necessary. */
|
||||
while (bytes_left > 0)
|
||||
{
|
||||
if (bytes_left < BUF_SIZE)
|
||||
{
|
||||
chunk = bytes_left;
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk = BUF_SIZE;
|
||||
}
|
||||
|
||||
if (!reading)
|
||||
{
|
||||
sj = get_jiffies_64();
|
||||
if (copy_from_user (ibuffer, user_addr, chunk))
|
||||
{
|
||||
return -EFAULT;
|
||||
}
|
||||
acc_transfj += get_jiffies_64() - sj;
|
||||
}
|
||||
|
||||
/* Read one u32 at a time until fill the buffer. Then copy the whole
|
||||
* buffer at once to user space. */
|
||||
sj = get_jiffies_64();
|
||||
num_to_read = chunk / sizeof(u32);
|
||||
for (i = 0; i < num_to_read; i++)
|
||||
{
|
||||
if (reading)
|
||||
{
|
||||
ibuffer[i] = readl ( ((u32*)dev_addr) + i);
|
||||
}
|
||||
else
|
||||
{
|
||||
writel ( ibuffer[i], ((u32*)dev_addr) + i );
|
||||
}
|
||||
}
|
||||
|
||||
/* If length is not a multiple of sizeof(u32), will miss last few bytes.
|
||||
* In that case, read it one byte at a time. This can only happen on
|
||||
* last iteration of the while() loop. */
|
||||
offset = num_to_read * sizeof(u32);
|
||||
num_missed = chunk - offset;
|
||||
cbuffer = (char*)(ibuffer + num_to_read);
|
||||
|
||||
for (i = 0; i < num_missed; i++)
|
||||
{
|
||||
if (reading)
|
||||
{
|
||||
cbuffer[i] = readb ( (u8*)(dev_addr) + offset + i );
|
||||
}
|
||||
else
|
||||
{
|
||||
writeb ( cbuffer[i], (u8*)(dev_addr) + offset + i );
|
||||
}
|
||||
}
|
||||
acc_readj += get_jiffies_64() - sj;
|
||||
|
||||
if (reading)
|
||||
{
|
||||
sj = get_jiffies_64();
|
||||
if (copy_to_user (user_addr, ibuffer, chunk))
|
||||
{
|
||||
return -EFAULT;
|
||||
}
|
||||
acc_transfj += get_jiffies_64() - sj;
|
||||
}
|
||||
|
||||
dev_addr += chunk;
|
||||
user_addr += chunk;
|
||||
bytes_left -= chunk;
|
||||
}
|
||||
|
||||
#if VERBOSE_DEBUG
|
||||
ej = get_jiffies_64();
|
||||
#endif
|
||||
// ACL_VERBOSE_DEBUG (KERN_DEBUG "Spent %u msec %sing %lu bytes", jiffies_to_msecs(ej - startj), reading ? "read" : "writ", len);
|
||||
|
||||
// ACL_VERBOSE_DEBUG (KERN_DEBUG " Dev access %u msec. User space transfer %u msec",
|
||||
// jiffies_to_msecs(acc_readj),
|
||||
// jiffies_to_msecs(acc_transfj));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Set CRA window so raw_user_ptr is "visible" to the BAR.
|
||||
* Return pointer to use to access the user memory */
|
||||
//static void* aclpci_set_segment (struct aclpci_dev *aclpci, void * raw_user_ptr) {
|
||||
|
||||
// ssize_t cur_segment = ((ssize_t)raw_user_ptr) / ACL_PCIE_MEMWINDOW_SIZE;
|
||||
// aclpci_set_segment_by_val (aclpci, cur_segment);
|
||||
|
||||
// /* Can use the return value in all read/write functions in this file now */
|
||||
// return (void*)((ssize_t)raw_user_ptr % ACL_PCIE_MEMWINDOW_SIZE);
|
||||
//}
|
||||
|
||||
|
||||
/* Both start and end, user and device addresses must be
|
||||
* 64-byte aligned to use DMA */
|
||||
//int aligned_request (struct aclpci_cmd *cmd, size_t count) {
|
||||
|
||||
// return (( (unsigned long)cmd->user_addr & DMA_ALIGNMENT_BYTE_MASK) |
|
||||
// ( (unsigned long)cmd->device_addr & DMA_ALIGNMENT_BYTE_MASK) |
|
||||
// ( count & DMA_ALIGNMENT_BYTE_MASK)
|
||||
// ) == 0;
|
||||
//}
|
||||
|
||||
ssize_t aclpci_rw(struct file *file,
|
||||
char __user *buf,
|
||||
size_t count, loff_t *pos,
|
||||
int reading);
|
||||
|
||||
|
||||
/* Response to user's read() call */
|
||||
ssize_t aclpci_read(struct file *file,
|
||||
char __user *buf,
|
||||
size_t count,
|
||||
loff_t *pos)
|
||||
{
|
||||
|
||||
return aclpci_rw (file, buf, 0, pos, 1 /* reading */);
|
||||
}
|
||||
|
||||
|
||||
/* Response to user's write() call */
|
||||
ssize_t aclpci_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
|
||||
{
|
||||
return aclpci_rw (file, (char __user *)buf, 0, pos, 0 /* writing */);
|
||||
}
|
||||
|
||||
|
||||
/* High-level read/write dispatcher. */
|
||||
ssize_t aclpci_rw(struct file *file, char __user *buf, size_t count, loff_t *pos, int reading)
|
||||
{
|
||||
|
||||
struct aclpci_dev *aclpci = (struct aclpci_dev *)file->private_data;
|
||||
struct aclpci_cmd __user *ucmd;
|
||||
struct aclpci_cmd kcmd;
|
||||
//u64 old_segment = 0;
|
||||
//int restore_segment = 0;
|
||||
void *addr = 0;
|
||||
//int aligned = 0;
|
||||
int use_dma = 0;
|
||||
ssize_t result = 0;
|
||||
ssize_t errno = 0;
|
||||
|
||||
if (down_interruptible(&aclpci->sem))
|
||||
{
|
||||
return -ERESTARTSYS;
|
||||
}
|
||||
|
||||
ucmd = (struct aclpci_cmd __user *) buf;
|
||||
if (copy_from_user (&kcmd, ucmd, sizeof(*ucmd)))
|
||||
{
|
||||
result = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (kcmd.bar_id == ACLPCI_CMD_BAR)
|
||||
{
|
||||
/* This is not a read but a special command. */
|
||||
result = aclpci_exec_cmd (aclpci, kcmd, kcmd.buff_size);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Only using DMA for large aligned reads/writes on global memory
|
||||
* (due to some assumptions inside the DMA code). */
|
||||
//aligned = aligned_request (&kcmd, kcmd.buff_size);
|
||||
//use_dma = USE_DMA && (kcmd.buff_size >= 1024) &&
|
||||
// aligned && kcmd.bar_id == ACL_PCI_GLOBAL_MEM_BAR;
|
||||
|
||||
// ACL_VERBOSE_DEBUG (KERN_DEBUG " kcmd = {%u, %p, %p}, count = %lu", kcmd.bar_id, (void*)kcmd.device_addr, (void*)kcmd.user_addr, kcmd.buff_size);
|
||||
if (!use_dma)
|
||||
{
|
||||
addr = aclpci_get_checked_addr (kcmd.bar_id, kcmd.device_addr, kcmd.buff_size, aclpci, &errno, 1);
|
||||
/* If not using DMA, need 'addr', which is mem-mapped virtual address. If that's out of
|
||||
* range of the BAR, can't do the operation. For DMA, mem-mapped virtual addresses are not
|
||||
* used. So don't care if they're in the BAR range or not. */
|
||||
if (errno != 0)
|
||||
{
|
||||
result = -EFAULT;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Offset value is always an address offset, not element offset. */
|
||||
/* ACL_DEBUG (KERN_DEBUG "Read address is %p", addr); */
|
||||
|
||||
switch (kcmd.buff_size)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
//case 8:
|
||||
{
|
||||
if (reading)
|
||||
{
|
||||
result = aclpci_read_small (addr, (void __user*) kcmd.user_addr, kcmd.buff_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = aclpci_write_small (addr, (void __user*) kcmd.user_addr, kcmd.buff_size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
result = aclpci_rw_large (addr, (void __user*) kcmd.user_addr, kcmd.buff_size, aclpci->buffer, reading);
|
||||
break;
|
||||
}
|
||||
}//switch (kcmd.buff_size)
|
||||
|
||||
done:
|
||||
up (&aclpci->sem);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Altera Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Queue of fixed size.
|
||||
* Uncomment below to run in user-space unit-test mode.
|
||||
* Otherwise, will compile in kernel mode. */
|
||||
// #define UNIT_TEST_MODE
|
||||
|
||||
#include "aclpci_queue.h"
|
||||
|
||||
#ifdef UNIT_TEST_MODE
|
||||
#include <stdlib.h> // for calloc
|
||||
#include <stdio.h> // for printf
|
||||
#include <string.h> // for memcpy
|
||||
#else
|
||||
#include "aclpci.h"
|
||||
#endif
|
||||
|
||||
|
||||
void queue_init (struct queue *q, unsigned int elem_size, unsigned int size) {
|
||||
// printk ("queue_init %p, elem_size = %u, size = %u\n", q, elem_size, size);
|
||||
if (q == 0) { return; }
|
||||
#ifdef UNIT_TEST_MODE
|
||||
q->buffer = calloc (elem_size, size);
|
||||
#else
|
||||
q->buffer = kzalloc (elem_size * size, GFP_KERNEL);
|
||||
#endif
|
||||
if (q->buffer == 0) {
|
||||
printk ("Couldn't allocate queue buffer!\n");
|
||||
return;
|
||||
}
|
||||
q->size = size;
|
||||
q->elem_size = elem_size;
|
||||
q->count = 0;
|
||||
q->out = 0;
|
||||
}
|
||||
|
||||
|
||||
void queue_fini (struct queue *q) {
|
||||
// printk ("queue_init %p\n", q);
|
||||
if (q == 0) { return; }
|
||||
#ifdef UNIT_TEST_MODE
|
||||
free (q->buffer);
|
||||
#else
|
||||
kfree (q->buffer);
|
||||
#endif
|
||||
q->buffer = NULL;
|
||||
q->size = 0;
|
||||
q->elem_size = 0;
|
||||
q->count = 0;
|
||||
q->out = 0;
|
||||
}
|
||||
|
||||
|
||||
unsigned int queue_size (struct queue *q) {
|
||||
return q->count;
|
||||
}
|
||||
|
||||
int queue_empty(struct queue *q) {
|
||||
return (q->count == 0);
|
||||
}
|
||||
|
||||
/* localize ugly casts */
|
||||
void *queue_addr (struct queue *q, unsigned int offset) {
|
||||
unsigned long buffer_loc = (unsigned long)q->buffer + offset * q->elem_size;
|
||||
return (void*)buffer_loc;
|
||||
}
|
||||
|
||||
/* When working with the circular buffer, values can wrap around
|
||||
* at most once. So instead of doing val % size, can do a simple comparison */
|
||||
unsigned int fast_mod (unsigned int val, unsigned int size) {
|
||||
if (val >= size)
|
||||
return val - size;
|
||||
else
|
||||
return val;
|
||||
}
|
||||
|
||||
void queue_push (struct queue *q, void *e) {
|
||||
unsigned int loc;
|
||||
if (q->count == q->size) {
|
||||
/* queue is full! */
|
||||
return;
|
||||
}
|
||||
loc = fast_mod ( (q->out + q->count), q->size );
|
||||
memcpy (queue_addr(q, loc), e, q->elem_size);
|
||||
q->count++;
|
||||
}
|
||||
|
||||
void queue_pop (struct queue *q) {
|
||||
if (q->count == 0) {
|
||||
return;
|
||||
}
|
||||
q->count--;
|
||||
q->out = fast_mod ( (q->out + 1), q->size );
|
||||
}
|
||||
|
||||
void *queue_front (struct queue *q) {
|
||||
if (q->count == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return queue_addr (q, q->out);
|
||||
}
|
||||
|
||||
void *queue_back (struct queue *q) {
|
||||
if (q->count == 0) {
|
||||
return NULL;
|
||||
}
|
||||
return queue_addr (q, fast_mod( (q->out + q->count - 1), q->size ) );
|
||||
}
|
||||
|
||||
|
||||
/* Unit tests. */
|
||||
#ifdef UNIT_TEST_MODE
|
||||
int main() {
|
||||
struct queue q;
|
||||
int i, j, k;
|
||||
queue_init (&q, sizeof(int), 5);
|
||||
i = 1; queue_push(&q, &i);
|
||||
i = 2; queue_push(&q, &i);
|
||||
i = 3; queue_push(&q, &i);
|
||||
j = *(int*)queue_front(&q); k = *(int*)queue_back(&q);
|
||||
printf ("%d, %d\n", j, k);
|
||||
|
||||
queue_pop(&q);
|
||||
j = *(int*)queue_front(&q); k = *(int*)queue_back(&q);
|
||||
printf ("%d, %d\n", j, k);
|
||||
|
||||
queue_pop(&q);
|
||||
queue_pop(&q);
|
||||
i = 11; queue_push(&q, &i);
|
||||
i = 12; queue_push(&q, &i);
|
||||
i = 13; queue_push(&q, &i);
|
||||
i = 14; queue_push(&q, &i);
|
||||
i = 15; queue_push(&q, &i);
|
||||
i = 16; queue_push(&q, &i);
|
||||
j = *(int*)queue_front(&q);
|
||||
k = *(int*)queue_back(&q);
|
||||
printf ("%d, %d\n", j, k);
|
||||
|
||||
while (!queue_empty(&q)) {
|
||||
int s = *(int*)queue_front(&q); queue_pop(&q);
|
||||
printf ("%d\n", s);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Altera Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef ACLPCI_QUEUE_H
|
||||
#define ACLPCI_QUEUE_H
|
||||
|
||||
/* FIFO for a fixed number of elements. Interface is the same as for
|
||||
* C++ STL queue<> adaptor.
|
||||
*
|
||||
* Implemented as a circular buffer in an array.
|
||||
* Could've used kfifo but its interface changes between kernel
|
||||
* versions. So don't want to bother porting source code just for a fifo. */
|
||||
|
||||
struct queue {
|
||||
void *buffer; /* Buffer to hold the data. Size is >= size * elem_size */
|
||||
unsigned int size; /* number of elements */
|
||||
unsigned int elem_size; /* size of single element */
|
||||
unsigned int count; /* number of valid entries */
|
||||
unsigned int out; /* First valid entry */
|
||||
};
|
||||
|
||||
void queue_init (struct queue *q, unsigned int elem_size, unsigned int size);
|
||||
void queue_fini (struct queue *q);
|
||||
|
||||
unsigned int queue_size (struct queue *q);
|
||||
int queue_empty(struct queue *q);
|
||||
|
||||
void queue_push (struct queue *q, void *e);
|
||||
void queue_pop (struct queue *q);
|
||||
void *queue_front (struct queue *q);
|
||||
void *queue_back (struct queue *q);
|
||||
|
||||
#endif
|
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Altera Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// hw_pcie_constants.h //
|
||||
// Constants to keep in sync with the HW board design //
|
||||
// //
|
||||
// Note: This file *MUST* be kept in sync with any //
|
||||
// changes to the HW board design! //
|
||||
// //
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef HW_PCIE_CONSTANTS_H
|
||||
#define HW_PCIE_CONSTANTS_H
|
||||
|
||||
|
||||
//struct board_parameters {
|
||||
// const char * board_name;
|
||||
// unsigned int acl_pci_global_mem_bar;
|
||||
// unsigned int acl_pci_cra_bar;
|
||||
// unsigned int acl_pci_cra_offset;
|
||||
// unsigned int acl_pci_cra_size;
|
||||
// unsigned int acl_kernel_csr_bar;
|
||||
// unsigned int acl_kernel_csr_offset;
|
||||
// unsigned int acl_kernel_option0_csr_size;
|
||||
// unsigned int acl_configuration_storage_bar;
|
||||
// unsigned int acl_configuration_storage_offset;
|
||||
// unsigned int acl_pcie_dma_bar;
|
||||
// unsigned int acl_pcie_dma_offset;
|
||||
// unsigned int acl_pcie_dma_descriptor_bar;
|
||||
// unsigned int acl_pcie_dma_descriptor_offset;
|
||||
// unsigned int acl_pcie_pio_in_bar;
|
||||
// unsigned int acl_pcie_pio_in_offset;
|
||||
// unsigned int acl_pcie_pio_out_bar;
|
||||
// unsigned int acl_pcie_pio_out_offset;
|
||||
// unsigned int acl_pcie_tx_port;
|
||||
// unsigned int acl_pcie_memwindow_bar;
|
||||
// unsigned int acl_pcie_memwindow_cra;
|
||||
// unsigned int acl_pcie_memwindow_base;
|
||||
// unsigned int acl_pcie_memwindow_size;
|
||||
// unsigned int acl_pcie_kernwindow_bar;
|
||||
// unsigned int acl_pcie_kernwindow_cra;
|
||||
// unsigned int acl_pcie_kernwindow_base;
|
||||
// unsigned int acl_pcie_kernwindow_size;
|
||||
// unsigned int acl_pcie_em_bar;
|
||||
// unsigned int acl_pcie_em_offset;
|
||||
// unsigned int acl_pcie_em2_bar;
|
||||
// unsigned int acl_pcie_em2_offset;
|
||||
// // Byte offsets
|
||||
// unsigned int kernel_offset_csr;
|
||||
// unsigned int kernel_offset_printf_buffer_size;
|
||||
// unsigned int kernel_offset_invocation_image;
|
||||
// unsigned int kernel_csr_go;
|
||||
// unsigned int kernel_csr_done;
|
||||
// unsigned int kernel_csr_stalled;
|
||||
// unsigned int kernel_csr_unstall;
|
||||
// unsigned int kernel_csr_last_status_bit;
|
||||
// unsigned int kernel_csr_status_bits_mask;
|
||||
// unsigned int kernel_csr_lmem_invalid_bank;
|
||||
// unsigned int kernel_csr_lsu_active;
|
||||
// unsigned int kernel_csr_wr_active;
|
||||
// unsigned int kernel_csr_valid_in;
|
||||
// unsigned int kernel_csr_started;
|
||||
// unsigned int kernel_csr_first_version_bit;
|
||||
// unsigned int kernel_csr_last_version_bit;
|
||||
// unsigned int pcie_cra_irq_status;
|
||||
// unsigned int pcie_cra_irq_enable;
|
||||
// unsigned int pcie_cra_addr_trans;
|
||||
// unsigned int acl_pcie_kernel_irq_vec;
|
||||
// unsigned int acl_pcie_dma_irq_vec;
|
||||
// unsigned int use_kernelpll_reconfig;
|
||||
// unsigned int acl_pcie_kernelpll_reconfig_bar;
|
||||
// unsigned int acl_pcie_kernelpll_reconfig_offset;
|
||||
// unsigned int acl_pcie_kernelpll_rom_bar;
|
||||
// unsigned int acl_pcie_kernelpll_rom_offset;
|
||||
// unsigned int acl_pcie_counter_bar;
|
||||
// unsigned int acl_pcie_counter_offset;
|
||||
//#ifndef QSYS_IFACE
|
||||
// unsigned int pcie_cra_irq_rxmirq;
|
||||
// unsigned int pcie_cra_avl_irq_vec_lo;
|
||||
// unsigned int pcie_cra_avl_irq_vec_hi;
|
||||
//#endif
|
||||
// unsigned int dma_alignment_bytes;
|
||||
// unsigned int dma_dc_transfer_complete_irq_mask;
|
||||
// unsigned int dma_dc_early_done_enable;
|
||||
// unsigned int dma_dc_go;
|
||||
// unsigned int dma_csr_status;
|
||||
// unsigned int dma_csr_control;
|
||||
// unsigned int dma_status_busy;
|
||||
// unsigned int dma_status_descriptor_empty;
|
||||
// unsigned int dma_status_resetting;
|
||||
// unsigned int dma_status_irq;
|
||||
// unsigned int dma_status_count_lo;
|
||||
// unsigned int dma_status_count_hi;
|
||||
// unsigned int dma_ctrl_stop;
|
||||
// unsigned int dma_ctrl_reset;
|
||||
// unsigned int dma_ctrl_irq_enable;
|
||||
// unsigned int perfmon_status_reg;
|
||||
// unsigned int perfmon_build_number;
|
||||
// unsigned int perfmon_perf_registers;
|
||||
// unsigned int perfmon_log_start_reg;
|
||||
// unsigned int perfmon_log_end_reg;
|
||||
// unsigned int perfmon_log_registers;
|
||||
// unsigned int perfmon_trigger_fifo_register;
|
||||
// unsigned int perfmon_enable_monitoring;
|
||||
// unsigned int perfmon_enable_logging;
|
||||
// unsigned int perfmon_buffer_is_empty;
|
||||
// unsigned int perfmon_reset;
|
||||
// unsigned int perfmon_trigger_log_on_fifo_activity;
|
||||
// unsigned int pio_data;
|
||||
// unsigned int pio_set;
|
||||
// unsigned int pio_clr;
|
||||
// unsigned int pio_out_swreset;
|
||||
// unsigned int pio_out_pllreset;
|
||||
// unsigned int pio_out_interleave_mode;
|
||||
// unsigned int pio_in_kpll_locked;
|
||||
// unsigned int pio_in_a_init_done;
|
||||
// unsigned int pio_in_a_cal_success;
|
||||
// unsigned int pio_in_b_init_done;
|
||||
// unsigned int pio_in_b_cal_success;
|
||||
// unsigned int has_temp_sensor;
|
||||
// unsigned int temp_sensor_addr;
|
||||
//};
|
||||
|
||||
enum BOARD_ID {
|
||||
DE4 = 0, //DE4 Board
|
||||
DE4_EXPCARD = 1, //DE4 Board on express card extension board for laptops
|
||||
PCIE385 = 2, //Nallatech SV board
|
||||
BSP = 3, //BSP SV board
|
||||
C5DK = 4,
|
||||
NUM_SUPPORTED_BOARDS = 5
|
||||
};
|
||||
|
||||
|
||||
#define ACL_PCIE_READ_BIT( w, b ) (((w) >> (b)) & 1)
|
||||
#define ACL_PCIE_READ_BIT_RANGE( w, h, l ) (((w) >> (l)) & ((1 << ((h) - (l) + 1)) - 1))
|
||||
#define ACL_PCIE_SET_BIT( w, b ) ((w) |= (1 << (b)))
|
||||
#define ACL_PCIE_CLEAR_BIT( w, b ) ((w) &= (~(1 << (b))))
|
||||
#define ACL_PCIE_GET_BIT( b ) (unsigned) (1 << (b))
|
||||
|
||||
//#define QSYS_IFACE 1
|
||||
// Number of Base Address Registers in the PCIe core
|
||||
#define ACL_PCI_NUM_BARS 4
|
||||
|
||||
// PCI Vendor and Device IDs
|
||||
#define ACL_PCI_ALTERA_VENDOR_ID 0x1D2A
|
||||
#define ACL_PCI_CHAMELEON_DEVICE_ID 0x0100
|
||||
#define ACL_PCI_PREDATOR_DEVICE_ID 0x0200
|
||||
#define ACL_PCI_KOMODO_DEVICE_ID 0X0210
|
||||
#define ACL_PCI_KOMODO_4R4T_DEVICE_ID 0x0211
|
||||
#define ACL_PCI_KOMODO_FIBER_DEVICE_ID 0x0220
|
||||
#define ACL_PCI_KOMODO_FIBER_CLHS_DEVICE_ID 0x0221
|
||||
#define ACL_PCI_KOMODO_FIBER_GIGE_DEVICE_ID 0x0222
|
||||
#define ACL_PCI_FNC_DEVICE_ID 0x0240
|
||||
|
||||
#define ACL_PCI_CHAMELEON_II_DEVICE_ID 0x0300
|
||||
#define ACL_PCI_PREDATOR_II_DEVICE_ID 0x0400
|
||||
#define ACL_PCI_KOMODO_II_DEVICE_ID 0X0410
|
||||
#define ACL_PCI_KOMODO_II_4R4T_DEVICE_ID 0x0411
|
||||
#define ACL_PCI_KOMODO_II_FIBER_DEVICE_ID 0x0420
|
||||
#define ACL_PCI_KOMODO_FIBER_CLHS_II_DEVICE_ID 0x0421
|
||||
#define ACL_PCI_KOMODO_FIBER_GIGE_II_DEVICE_ID 0x0422
|
||||
|
||||
#define ACL_PCI_COMMON_DEVICE_ID 0x1000
|
||||
|
||||
|
||||
#define ACL_SOC_C5_DK 0xC5DE
|
||||
#define ACL_CONFIGURATION_STORAGE_SIZE 4096
|
||||
|
||||
//#define ACL_BOARD_NAME boards[board_id].board_name
|
||||
|
||||
// Global memory
|
||||
/*#define ACL_PCI_GLOBAL_MEM_BAR boards[board_id].acl_pci_global_mem_bar
|
||||
|
||||
// PCIe control register addresses
|
||||
#define ACL_PCI_CRA_BAR boards[board_id].acl_pci_cra_bar
|
||||
#define ACL_PCI_CRA_OFFSET boards[board_id].acl_pci_cra_offset
|
||||
#define ACL_PCI_CRA_SIZE boards[board_id].acl_pci_cra_size
|
||||
|
||||
// Kernel control/status register addresses
|
||||
#define ACL_KERNEL_CSR_BAR boards[board_id].acl_kernel_csr_bar
|
||||
#define ACL_KERNEL_CSR_OFFSET boards[board_id].acl_kernel_csr_offset
|
||||
#define ACL_KERNEL_OPTION0_CSR_SIZE boards[board_id].acl_kernel_option0_csr_size
|
||||
|
||||
// Location of the auto-discover configuration storage information
|
||||
#define ACL_CONFIGURATION_STORAGE_BAR boards[board_id].acl_configuration_storage_bar
|
||||
#define ACL_CONFIGURATION_STORAGE_OFFSET boards[board_id].acl_configuration_storage_offset
|
||||
|
||||
// DMA control/status register address
|
||||
#define ACL_PCIE_DMA_BAR boards[board_id].acl_pcie_dma_bar
|
||||
#define ACL_PCIE_DMA_OFFSET boards[board_id].acl_pcie_dma_offset
|
||||
|
||||
// DMA descriptor slave address
|
||||
#define ACL_PCIE_DMA_DESCRIPTOR_BAR boards[board_id].acl_pcie_dma_descriptor_bar
|
||||
#define ACL_PCIE_DMA_DESCRIPTOR_OFFSET boards[board_id].acl_pcie_dma_descriptor_offset
|
||||
|
||||
// PIO input slave address
|
||||
#define ACL_PCIE_PIO_IN_BAR boards[board_id].acl_pcie_pio_in_bar
|
||||
#define ACL_PCIE_PIO_IN_OFFSET boards[board_id].acl_pcie_pio_in_offset
|
||||
|
||||
// PIO output slave address
|
||||
#define ACL_PCIE_PIO_OUT_BAR boards[board_id].acl_pcie_pio_out_bar
|
||||
#define ACL_PCIE_PIO_OUT_OFFSET boards[board_id].acl_pcie_pio_out_offset
|
||||
|
||||
// Avalon Tx port address as seen by the DMA read/write masters
|
||||
#define ACL_PCIE_TX_PORT boards[board_id].acl_pcie_tx_port
|
||||
|
||||
// Global memory window slave address. The host has different "view" of global
|
||||
// memory: it sees only 512megs segments of memory at a time for non-DMA xfers
|
||||
#define ACL_PCIE_MEMWINDOW_BAR boards[board_id].acl_pcie_memwindow_bar
|
||||
#define ACL_PCIE_MEMWINDOW_CRA boards[board_id].acl_pcie_memwindow_cra
|
||||
#define ACL_PCIE_MEMWINDOW_BASE boards[board_id].acl_pcie_memwindow_base
|
||||
#define ACL_PCIE_MEMWINDOW_SIZE boards[board_id].acl_pcie_memwindow_size
|
||||
|
||||
// Kernel window slave address. A 4K window is also used for the kernel
|
||||
// CRA addresses. This lets us scale # of kernels and debug ports without
|
||||
// requiring more address bits out of the iface
|
||||
#define ACL_PCIE_KERNWINDOW_BAR boards[board_id].acl_pcie_kernwindow_bar
|
||||
#define ACL_PCIE_KERNWINDOW_CRA boards[board_id].acl_pcie_kernwindow_cra
|
||||
#define ACL_PCIE_KERNWINDOW_BASE boards[board_id].acl_pcie_kernwindow_base
|
||||
#define ACL_PCIE_KERNWINDOW_SIZE boards[board_id].acl_pcie_kernwindow_size
|
||||
|
||||
// Efficiency Monitor address
|
||||
#define ACL_PCIE_EM_BAR boards[board_id].acl_pcie_em_bar
|
||||
#define ACL_PCIE_EM_OFFSET boards[board_id].acl_pcie_em_offset
|
||||
|
||||
// Efficiency Monitor address for 2nd DIMM
|
||||
#define ACL_PCIE_EM2_BAR boards[board_id].acl_pcie_em2_bar
|
||||
#define ACL_PCIE_EM2_OFFSET boards[board_id].acl_pcie_em2_offset
|
||||
|
||||
// Byte offsets
|
||||
#define KERNEL_OFFSET_CSR boards[board_id].kernel_offset_csr
|
||||
#define KERNEL_OFFSET_PRINTF_BUFFER_SIZE boards[board_id].kernel_offset_printf_buffer_size
|
||||
#define KERNEL_OFFSET_INVOCATION_IMAGE boards[board_id].kernel_offset_invocation_image
|
||||
|
||||
// Bits in the kernel CSR register
|
||||
// Bits 16-31 contain a version code that can be read;writes to those bits are ignored.
|
||||
// Option 3 wrappers have version 1.
|
||||
// Prior to Option 3, bits 16-31 always read as 0.
|
||||
#define KERNEL_CSR_GO boards[board_id].kernel_csr_go
|
||||
#define KERNEL_CSR_DONE boards[board_id].kernel_csr_done
|
||||
#define KERNEL_CSR_STALLED boards[board_id].kernel_csr_stalled
|
||||
#define KERNEL_CSR_UNSTALL boards[board_id].kernel_csr_unstall
|
||||
#define KERNEL_CSR_LAST_STATUS_BIT boards[board_id].kernel_csr_last_status_bit
|
||||
#define KERNEL_CSR_STATUS_BITS_MASK boards[board_id].kernel_csr_status_bits_mask
|
||||
#define KERNEL_CSR_LMEM_INVALID_BANK boards[board_id].kernel_csr_lmem_invalid_bank
|
||||
#define KERNEL_CSR_LSU_ACTIVE boards[board_id].kernel_csr_lsu_active
|
||||
#define KERNEL_CSR_WR_ACTIVE boards[board_id].kernel_csr_wr_active
|
||||
#define KERNEL_CSR_VALID_IN boards[board_id].kernel_csr_valid_in
|
||||
#define KERNEL_CSR_STARTED boards[board_id].kernel_csr_started
|
||||
#define KERNEL_CSR_FIRST_VERSION_BIT boards[board_id].kernel_csr_first_version_bit
|
||||
#define KERNEL_CSR_LAST_VERSION_BIT boards[board_id].kernel_csr_last_version_bit
|
||||
|
||||
// PCI express control-register offsets
|
||||
#define PCIE_CRA_IRQ_STATUS boards[board_id].pcie_cra_irq_status
|
||||
#define PCIE_CRA_IRQ_ENABLE boards[board_id].pcie_cra_irq_enable
|
||||
#define PCIE_CRA_ADDR_TRANS boards[board_id].pcie_cra_addr_trans
|
||||
|
||||
// IRQ vector mappings (as seen by the PCIe RxIRQ port)
|
||||
#define ACL_PCIE_KERNEL_IRQ_VEC boards[board_id].acl_pcie_kernel_irq_vec
|
||||
#define ACL_PCIE_DMA_IRQ_VEC boards[board_id].acl_pcie_dma_irq_vec
|
||||
|
||||
// PLL related
|
||||
#define USE_KERNELPLL_RECONFIG boards[board_id].use_kernelpll_reconfig
|
||||
#define ACL_PCIE_KERNELPLL_RECONFIG_BAR boards[board_id].acl_pcie_kernelpll_reconfig_bar
|
||||
#define ACL_PCIE_KERNELPLL_RECONFIG_OFFSET boards[board_id].acl_pcie_kernelpll_reconfig_offset
|
||||
#define ACL_PCIE_KERNELPLL_ROM_BAR boards[board_id].acl_pcie_kernelpll_rom_bar
|
||||
#define ACL_PCIE_KERNELPLL_ROM_OFFSET boards[board_id].acl_pcie_kernelpll_rom_offset
|
||||
#define ACL_PCIE_COUNTER_BAR boards[board_id].acl_pcie_counter_bar
|
||||
#define ACL_PCIE_COUNTER_OFFSET boards[board_id].acl_pcie_counter_offset
|
||||
|
||||
#ifndef QSYS_IFACE
|
||||
// PCI express IRQ register bits
|
||||
#define PCIE_CRA_IRQ_RXMIRQ boards[board_id].pcie_cra_irq_rxmirq
|
||||
#define PCIE_CRA_AVL_IRQ_VEC_LO boards[board_id].pcie_cra_avl_irq_vec_lo
|
||||
#define PCIE_CRA_AVL_IRQ_VEC_HI boards[board_id].pcie_cra_avl_irq_vec_hi
|
||||
#endif
|
||||
|
||||
// DMA descriptor control bits
|
||||
#define DMA_ALIGNMENT_BYTES boards[board_id].dma_alignment_bytes
|
||||
#define DMA_ALIGNMENT_BYTE_MASK (DMA_ALIGNMENT_BYTES-1)
|
||||
#define DMA_DC_TRANSFER_COMPLETE_IRQ_MASK boards[board_id].dma_dc_transfer_complete_irq_mask
|
||||
#define DMA_DC_EARLY_DONE_ENABLE boards[board_id].dma_dc_early_done_enable
|
||||
#define DMA_DC_GO boards[board_id].dma_dc_go
|
||||
// DMA controller control/status registers
|
||||
#define DMA_CSR_STATUS boards[board_id].dma_csr_status
|
||||
#define DMA_CSR_CONTROL boards[board_id].dma_csr_control
|
||||
// DMA CSR status bits
|
||||
#define DMA_STATUS_BUSY boards[board_id].dma_status_busy
|
||||
#define DMA_STATUS_DESCRIPTOR_EMPTY boards[board_id].dma_status_descriptor_empty
|
||||
#define DMA_STATUS_RESETTING boards[board_id].dma_status_resetting
|
||||
#define DMA_STATUS_IRQ boards[board_id].dma_status_irq
|
||||
#define DMA_STATUS_COUNT_LO boards[board_id].dma_status_count_lo
|
||||
#define DMA_STATUS_COUNT_HI boards[board_id].dma_status_count_hi
|
||||
// DMA CSR control bits
|
||||
#define DMA_CTRL_STOP boards[board_id].dma_ctrl_stop
|
||||
#define DMA_CTRL_RESET boards[board_id].dma_ctrl_reset
|
||||
#define DMA_CTRL_IRQ_ENABLE boards[board_id].dma_ctrl_irq_enable
|
||||
// Kernel performance monitor control/status registers
|
||||
// The Avalon slave data is 32-bits while PCI address is w.r.t. bytes, so *4 to translate
|
||||
#define PERFMON_STATUS_REG boards[board_id].perfmon_status_reg
|
||||
#define PERFMON_BUILD_NUMBER boards[board_id].perfmon_build_number
|
||||
#define PERFMON_PERF_REGISTERS boards[board_id].perfmon_perf_registers
|
||||
#define PERFMON_LOG_START_REG boards[board_id].perfmon_log_start_reg
|
||||
#define PERFMON_LOG_END_REG boards[board_id].perfmon_log_end_reg
|
||||
#define PERFMON_LOG_REGISTERS boards[board_id].perfmon_log_registers
|
||||
#define PERFMON_TRIGGER_FIFO_REGISTER boards[board_id].perfmon_trigger_fifo_register
|
||||
// Kernel performance monitor status/control bits
|
||||
#define PERFMON_ENABLE_MONITORING boards[board_id].perfmon_enable_monitoring
|
||||
#define PERFMON_ENABLE_LOGGING boards[board_id].perfmon_enable_logging
|
||||
#define PERFMON_BUFFER_IS_EMPTY boards[board_id].perfmon_buffer_is_empty
|
||||
#define PERFMON_RESET boards[board_id].perfmon_reset
|
||||
#define PERFMON_TRIGGER_LOG_ON_FIFO_ACTIVITY boards[board_id].perfmon_trigger_log_on_fifo_activity
|
||||
#define PIO_DATA boards[board_id].pio_data
|
||||
#define PIO_SET boards[board_id].pio_set
|
||||
#define PIO_CLR boards[board_id].pio_clr
|
||||
#define PIO_OUT_SWRESET boards[board_id].pio_out_swreset
|
||||
#define PIO_OUT_PLLRESET boards[board_id].pio_out_pllreset
|
||||
#define PIO_OUT_INTERLEAVE_MODE boards[board_id].pio_out_interleave_mode
|
||||
#define PIO_IN_KPLL_LOCKED boards[board_id].pio_in_kpll_locked
|
||||
#define PIO_IN_A_INIT_DONE boards[board_id].pio_in_a_init_done
|
||||
#define PIO_IN_A_CAL_SUCCESS boards[board_id].pio_in_a_cal_success
|
||||
#define PIO_IN_B_INIT_DONE boards[board_id].pio_in_b_init_done
|
||||
#define PIO_IN_B_CAL_SUCCESS boards[board_id].pio_in_b_cal_success
|
||||
|
||||
// Temperature sensor presence and base address macros
|
||||
#define ACL_PCIE_HAS_TEMP_SENSOR boards[board_id].has_temp_sensor
|
||||
#define ACL_PCIE_TEMP_SENSOR_ADDRESS boards[board_id].temp_sensor_addr */
|
||||
|
||||
extern int board_id;
|
||||
//extern struct board_parameters boards[NUM_SUPPORTED_BOARDS];
|
||||
|
||||
#endif // HW_PCIE_CONSTANTS_H
|
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Altera Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HW_PCIE_DMA_H
|
||||
#define HW_PCIE_DMA_H
|
||||
|
||||
|
||||
// DMA parameters to tweak
|
||||
//static const unsigned int ACL_PCIE_DMA_MAX_PINNED_MEM_SIZE = 1024*1024;
|
||||
//static const unsigned int ACL_PCIE_DMA_MAX_PINNED_MEM = 64; // x PINNED_MEM_SIZE above
|
||||
//static const unsigned int ACL_PCIE_DMA_MAX_ATT_PER_DESCRIPTOR = 128;
|
||||
|
||||
//// Constants matched to the HW
|
||||
//static const unsigned int ACL_PCIE_DMA_MAX_DONE_COUNT = (1 << 16);
|
||||
//static const unsigned int ACL_PCIE_DMA_MAX_ATT_PAGE_SIZE = 4*1024;
|
||||
//static const unsigned int ACL_PCIE_DMA_MAX_ATT_SIZE = 256;
|
||||
//static const unsigned int ACL_PCIE_DMA_MAX_DESCRIPTORS = 128;
|
||||
|
||||
static const unsigned int ACL_PCIE_DMA_ATT_PAGE_ADDR_MASK = 4*1024-1; // (ACL_PCIE_DMA_MAX_ATT_PAGE_SIZE-1);
|
||||
|
||||
#ifdef LINUX
|
||||
# define cl_ulong unsigned long
|
||||
#endif
|
||||
|
||||
/*
|
||||
struct DMA_DESCRIPTOR {
|
||||
unsigned int read_address;
|
||||
unsigned int write_address;
|
||||
unsigned int bytes;
|
||||
unsigned int control;
|
||||
};
|
||||
|
||||
struct DESCRIPTOR_UPDATE_DATA {
|
||||
unsigned int bytes;
|
||||
unsigned int att_entries;
|
||||
cl_ulong start;
|
||||
};
|
||||
*/
|
||||
|
||||
#endif // HW_PCIE_DMA_H
|
@@ -0,0 +1,12 @@
|
||||
# /etc/systemd/system/kaya_driver.service
|
||||
[Unit]
|
||||
Description=KAYA Instruments PCI devices kernel driver
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStart=/etc/kaya_driver.d/kaya_driver_load.sh --load
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
WantedBy=default.target
|
@@ -0,0 +1,63 @@
|
||||
DRIVER_FILE_NAME='predator_driver.ko'
|
||||
if [ ! -e "$DRIVER_FILE_NAME" ]
|
||||
then
|
||||
echo Building driver locally..
|
||||
sh make_all.sh
|
||||
fi
|
||||
|
||||
#copy driver to designated system folder
|
||||
sudo mkdir -p /lib/modules/$(uname -r)/kernel/drivers/kyfg
|
||||
if [ -e "$DRIVER_FILE_NAME" ]
|
||||
then
|
||||
echo Locally built driver was found and will be installed
|
||||
sudo cp $DRIVER_FILE_NAME /lib/modules/$(uname -r)/kernel/drivers/kyfg
|
||||
else
|
||||
echo No locally built driver was found, looking for suitable pre-built driver...
|
||||
prebuilt_driver_file_name="predator_driver-$(uname -r).ko"
|
||||
if [ -e "$prebuilt_driver_file_name" ]
|
||||
then
|
||||
echo "$prebuilt_driver_file_name will be installed as /lib/modules/$(uname -r)/kernel/drivers/kyfg/$DRIVER_FILE_NAME"
|
||||
sudo cp $prebuilt_driver_file_name /lib/modules/$(uname -r)/kernel/drivers/kyfg/$DRIVER_FILE_NAME
|
||||
else
|
||||
echo "No suitable pre-built driver was found for your current kernel $(uname -r)"
|
||||
echo "Please run \"make_all.sh\" to build one and then run this script again"
|
||||
exit 2 #No such file or directory https://www.cyberciti.biz/faq/linux-bash-exit-status-set-exit-statusin-bash/
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
#remove old driver
|
||||
echo "Remove old driver installation"
|
||||
#remove "predator_driver" from /etc/modules
|
||||
sudo sed -i '/predator_driver/d' /etc/modules
|
||||
|
||||
#remove legacy predator_driver load script from /etc/rc.local
|
||||
DRIVER_LOAD_VALUE_ALL="$(grep -n predator_driver /etc/rc.local | cut -d : -f 1)"
|
||||
echo $DRIVER_LOAD_VALUE_ALL
|
||||
|
||||
if [ ! -z "$DRIVER_LOAD_VALUE_ALL" ]
|
||||
then
|
||||
DRIVER_LOAD_VALUE_LINE_FIRST="$(grep -n predator_driver /etc/rc.local | cut -d : -f 1 | head -n 1)"
|
||||
DRIVER_LOAD_VALUE_LINE_FIRST="$(($DRIVER_LOAD_VALUE_LINE_FIRST-1))"
|
||||
#echo $DRIVER_LOAD_VALUE_LINE_FIRST
|
||||
|
||||
DRIVER_LOAD_VALUE_LINE_LAST="$(grep -n predator_driver /etc/rc.local | cut -d : -f 1 | tail -n 1)"
|
||||
DRIVER_LOAD_VALUE_LINE_LAST="$(($DRIVER_LOAD_VALUE_LINE_LAST+1))"
|
||||
#echo $DRIVER_LOAD_VALUE_LINE_LAST
|
||||
|
||||
sudo sed -i "$DRIVER_LOAD_VALUE_LINE_FIRST"','"$DRIVER_LOAD_VALUE_LINE_LAST"'d' /etc/rc.local
|
||||
fi
|
||||
|
||||
|
||||
#install new driver
|
||||
echo "Installing Persistent Module Loading"
|
||||
sudo mkdir -p /etc/kaya_driver.d
|
||||
sudo cp kaya_driver_load.sh /etc/kaya_driver.d
|
||||
sudo chmod +x /etc/kaya_driver.d/kaya_driver_load.sh
|
||||
sudo cp kaya_driver.service /etc/systemd/system
|
||||
sudo ln -s /lib/modules/$(uname -r)/kernel/drivers/kyfg/$DRIVER_FILE_NAME /lib/modules/`uname -r`
|
||||
sudo depmod
|
||||
sudo systemctl enable kaya_driver
|
||||
sudo systemctl start kaya_driver
|
||||
|
||||
exit 0
|
@@ -0,0 +1,139 @@
|
||||
#!/bin/sh -e
|
||||
|
||||
##### Constants
|
||||
device="predatorDevice"
|
||||
mode="666"
|
||||
|
||||
##### Variables
|
||||
module="predator_driver"
|
||||
current_directory=0
|
||||
operation=x
|
||||
|
||||
# https://linux.die.net/man/8/modprobe:
|
||||
#-q --quiet
|
||||
# Normally modprobe will report an error if you try to remove or insert a module it can't find (and isn't an alias or install/remove command).
|
||||
# With this flag, modprobe will simply ignore any bogus names (the kernel uses this to opportunistically probe for modules which might exist).
|
||||
#
|
||||
#--first-time
|
||||
# Normally, modprobe will succeed (and do nothing) if told to insert a module which is already present or to remove a module which isn't present.
|
||||
# This is ideal for simple scripts; however, more complicated scripts often want to know whether modprobe really did something:
|
||||
# this option makes modprobe fail for that case.
|
||||
|
||||
##### Functions
|
||||
usage()
|
||||
{
|
||||
echo "usage: kaya_driver_load.sh [options] ([-l|--load] | [-u|--unload] )"
|
||||
echo "options:"
|
||||
echo "-c, --current Look for kernel module in current directory instead of /lib/modules/$(uname -r)/kernel/drivers/kyfg"
|
||||
echo "-m <module_name>, --module <module_name> Name of kernel module (.ko file) to be loaded [default: 'predator_driver']"
|
||||
}
|
||||
|
||||
unload_driver()
|
||||
{
|
||||
###### Remove stale nodes (Ubuntu: /dev)
|
||||
|
||||
echo "rmmod..."
|
||||
/sbin/rmmod $module.ko
|
||||
if [ $? != 0 ]; then
|
||||
echo "rmmod failed"
|
||||
else
|
||||
echo "rmmod done."
|
||||
fi
|
||||
|
||||
echo "rm(s)..."
|
||||
#remove legacy single node
|
||||
rm -f /dev/${device}
|
||||
#remove numbered nodes
|
||||
for c in $(seq 0 15)
|
||||
do
|
||||
rm -f /dev/"${device}$c"
|
||||
done
|
||||
echo "rm(s) done."
|
||||
}
|
||||
|
||||
load_driver()
|
||||
{
|
||||
echo "insmod..."
|
||||
|
||||
#
|
||||
#note: if script launched with parameter "current_directory" then module is loaded from current directory
|
||||
#
|
||||
if [ "$current_directory" = "1" ]; then
|
||||
echo "looking for $module.ko in current directory"
|
||||
/sbin/insmod $module.ko $*
|
||||
else
|
||||
echo "looking for $module.ko in directory /lib/modules/$(uname -r)/kernel/drivers/kyfg"
|
||||
# /sbin/insmod /lib/modules/$(uname -r)/kernel/drivers/kyfg/$module.ko
|
||||
#sudo ln -s /lib/modules/$(uname -r)/kernel/drivers/kyfg/$module.ko /lib/modules/`uname -r`
|
||||
# sudo depmod
|
||||
sudo modprobe -i $module
|
||||
fi
|
||||
|
||||
if [ $? != 0 ]; then
|
||||
echo "modprobe failed (File exists?)."
|
||||
else
|
||||
echo "modprobe succeeded."
|
||||
fi
|
||||
|
||||
# Install devices (Ubuntu: /dev)
|
||||
echo "mknod(s)..."
|
||||
for c in $(seq 0 15)
|
||||
do
|
||||
# retrieve major number
|
||||
major=`cat /proc/devices | grep predatorDevice$c | awk '{print $1}' `
|
||||
echo "major: "$major""
|
||||
if [ -z "$major" ]; then
|
||||
echo "Breaking when trying device id $c"
|
||||
break
|
||||
fi
|
||||
|
||||
rm -f /dev/"${device}$c"
|
||||
mknod -m "$mode" /dev/"${device}$c" c "$major" 0
|
||||
done
|
||||
echo "mknod(s) done."
|
||||
|
||||
echo "kaya_driver loading done"
|
||||
}
|
||||
|
||||
##### Main
|
||||
|
||||
|
||||
# Processing arguments
|
||||
while [ "$1" != "" ]; do
|
||||
case $1 in
|
||||
-c | --current ) current_directory=1
|
||||
;;
|
||||
-m | --module ) shift
|
||||
module=$1
|
||||
;;
|
||||
-l | --load ) operation=load
|
||||
;;
|
||||
-u | --unload ) operation=unload
|
||||
;;
|
||||
-h | --help ) usage
|
||||
exit 0
|
||||
;;
|
||||
* ) usage
|
||||
exit 1
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
echo "operation: $operation"
|
||||
echo "module: $module"
|
||||
echo "use current directory (0 - No, 1 - Yes): $current_directory"
|
||||
|
||||
if [ "$operation" = "load" ]; then
|
||||
echo "Loading driver..."
|
||||
load_driver
|
||||
exit 0
|
||||
fi
|
||||
if [ "$operation" = "unload" ]; then
|
||||
echo "Unoading driver..."
|
||||
unload_driver
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Valid operation('load' or 'unload' was not specified"
|
||||
usage
|
||||
exit 1
|
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
|
||||
KAYA_XAVIER_BUILD=""
|
||||
|
||||
|
||||
# Need to use the exact version of gcc that was used to compile the kernel
|
||||
# It's in /usr/bin, so put it at the front of the path
|
||||
export PATH=/usr/bin:$PATH
|
||||
|
||||
#TODO: CONFIG_MODULE_SIG=n
|
||||
|
||||
rm *.o
|
||||
rm predator_driver.ko
|
||||
|
||||
if [ ! "$1" = "" ]; then
|
||||
#example of parameter: /usr/src/linux-headers-4.9.140-tegra-ubuntu18.04_aarch64/kernel-4.9
|
||||
echo "SRC_PATH from parameter: '$1'"
|
||||
SRC_PATH="$1"
|
||||
else
|
||||
kernelpath=""
|
||||
if [ -f /etc/redhat-release ] ; then
|
||||
kernelpath=kernels/
|
||||
elif [ -f /etc/lsb-release ] ; then
|
||||
kernelpath="linux-headers-"
|
||||
fi
|
||||
echo "kernelpath: '$kernelpath'"
|
||||
|
||||
kernelversion=`uname -r`
|
||||
echo "kernelversion from uname: '$kernelversion'"
|
||||
if [ "$KAYA_XAVIER_BUILD" = "1" ]; then
|
||||
echo "building for xavier..."
|
||||
#uname_r=`uname -r` #4.9.140-tegra
|
||||
#uname_m=`uname -m` #aarch64
|
||||
#SRC_PATH=/usr/src/"${kernelpath}${uname_r}-ubuntu18.04_${uname_m}/kernel-4.9"
|
||||
SRC_PATH=/usr/src/${kernelpath}4.9.140-tegra-ubuntu18.04_aarch64/kernel-4.9
|
||||
else
|
||||
SRC_PATH=/usr/src/"$kernelpath$kernelversion"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Checking SRC_PATH '$SRC_PATH'"
|
||||
if [ ! -d "$SRC_PATH" ]; then
|
||||
echo "specified SRC_PATH '$SRC_PATH' was not found"
|
||||
exit
|
||||
else
|
||||
echo specified SRC_PATH found
|
||||
fi
|
||||
|
||||
echo "Using kernel source files from '$SRC_PATH'"
|
||||
#read -p "Line ${LINENO}, Press any key to continue or Ctrl-C to stop here... " -n1 -s
|
||||
|
||||
|
||||
if echo ["$kernelversion"=~ .*"$xavier".*];then
|
||||
make -C $SRC_PATH M=`pwd` modules
|
||||
# with gcc 4.8 the above make fails with gcc: error: unrecognized command line option '-fstack-protector-strong'
|
||||
#-fstack-protector-strong was merged into GCC version 4.9; 4.8.x does not support it.
|
||||
# upgrade GCC or use -fstack-protector-all or -fstack-protector
|
||||
else
|
||||
make CONFIG_CC_STACKPROTECTOR_REGULAR=1 -C $SRC_PATH M=`pwd` modules
|
||||
fi
|
||||
|
||||
if [ ! "$1" = "" ]; then
|
||||
echo copying predator_driver.ko to predator_driver-$1.ko...
|
||||
cp predator_driver.ko predator_driver-$1.ko
|
||||
fi
|
||||
|
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Altera Corporation.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* - 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.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* All defines necessary to communicate with the Linux PCIe driver.
|
||||
* The actual communication functions are open()/close()/read()/write().
|
||||
*
|
||||
* Example read call (read single ushort from BAR 0, device address 0x2):
|
||||
* ssize_t f = open ("/dev/de4", O_RDWR);
|
||||
* unsigned short val;
|
||||
* struct aclpci_cmd read_cmd = { 0, ACLPCI_CMD_DEFAULT, 0x2, &val };
|
||||
* read (f, &read_cmd, sizeof(val));
|
||||
*
|
||||
* See user.c for a tester of all functions and more elaborate examples.
|
||||
*/
|
||||
|
||||
#ifndef PCIE_LINUX_DRIVER_EXPORTS_H
|
||||
#define PCIE_LINUX_DRIVER_EXPORTS_H
|
||||
|
||||
|
||||
/* if bar_id in aclpci_cmd is set to this, this is a special command,
|
||||
* not a usual read/write request. So the command field is used. Otherwise,
|
||||
* command field is ignored. */
|
||||
#define ACLPCI_CMD_BAR 23
|
||||
|
||||
/* Values for 'command' field of aclpci_cmd. */
|
||||
|
||||
/* Default value -- noop. */
|
||||
#define ACLPCI_CMD_DEFAULT 0
|
||||
|
||||
/* Save/Restore all board PCI control registers to user_addr.
|
||||
* Allows user program to reprogram the board without having root
|
||||
* priviliges (which is required to change PCI control registers). */
|
||||
#define ACLPCI_CMD_SAVE_PCI_CONTROL_REGS 1
|
||||
#define ACLPCI_CMD_LOAD_PCI_CONTROL_REGS 2
|
||||
|
||||
/* Lock/Unlock user_addr memory to physical RAM ("pin" it) */
|
||||
//#define ACLPCI_CMD_PIN_USER_ADDR 3
|
||||
//#define ACLPCI_CMD_UNPIN_USER_ADDR 4
|
||||
|
||||
/* Get m_idle status of DMA */
|
||||
//#define ACLPCI_CMD_GET_DMA_IDLE_STATUS 5
|
||||
//#define ACLPCI_CMD_DMA_UPDATE 6
|
||||
|
||||
#define ACLPCI_CMD_INTERRUPT_ENABLE 3
|
||||
#define ACLPCI_CMD_INTERRUPT_DISABLE 4
|
||||
|
||||
/* Get vendor_id and device_id of loaded PCIe device */
|
||||
#define ACLPCI_CMD_GET_DEVICE_ID 7
|
||||
#define ACLPCI_CMD_GET_VENDOR_ID 8
|
||||
|
||||
/* PCIe link status queries (PCIe gen and number of lanes) */
|
||||
#define ACLPCI_CMD_GET_PCI_GEN 10
|
||||
#define ACLPCI_CMD_GET_PCI_NUM_LANES 11
|
||||
|
||||
/* Set id to receive back on signal from kernel */
|
||||
#define ACLPCI_CMD_SET_SIGNAL_PAYLOAD 12
|
||||
|
||||
/* Get full driver version, as string */
|
||||
#define ACLPCI_CMD_GET_DRIVER_VERSION 13
|
||||
|
||||
//
|
||||
// KAYA Additional commands:
|
||||
//
|
||||
#define ACLPCI_CMD_KAYA_DEBUG 14
|
||||
#define ACLPCI_CMD_DMA_SGLOCK 15
|
||||
#define ACLPCI_CMD_DMA_SGUNLOCK 16
|
||||
|
||||
/* sync memory cache */
|
||||
#define ACLPCI_CMD_SYNC_FOR_CPU 17
|
||||
#define ACLPCI_CMD_SYNC_FOR_DEVICE 18
|
||||
|
||||
/* Get vendor_id and device_id of loaded PCIe device */
|
||||
#define ACLPCI_CMD_GET_BUS 19
|
||||
#define ACLPCI_CMD_GET_SLOT 20
|
||||
#define ACLPCI_CMD_GET_FUNC 21
|
||||
|
||||
|
||||
#define ACLPCI_CMD_MAX_CMD 22
|
||||
|
||||
|
||||
/* Signal from driver to user (hal) to notify about hw interrupt */
|
||||
#define SIG_INT_NOTIFY 44
|
||||
|
||||
/* Main structure to communicate any command (including read/write)
|
||||
* from user space to the driver. */
|
||||
struct aclpci_cmd {
|
||||
|
||||
/* base address register of PCIe device. device_addr is interpreted
|
||||
* as an offset from this BAR's start address. */
|
||||
unsigned int bar_id;
|
||||
|
||||
/* Special command to execute. Only used if bar_id is set
|
||||
* to ACLPCI_CMD_BAR. */
|
||||
unsigned int command;
|
||||
|
||||
/* Address in device space where to read/write data. */
|
||||
void* device_addr;
|
||||
|
||||
/* Address in user space where to write/read data.
|
||||
* Always virtual address. */
|
||||
void* user_addr;
|
||||
void* user_addr2;
|
||||
unsigned int buff_size;
|
||||
};
|
||||
|
||||
#ifndef KYDRIVER_BUILD
|
||||
#include "ky_defines.h" // TODO: causes compilation error while building driver
|
||||
#endif
|
||||
|
||||
typedef unsigned int DWORD;
|
||||
typedef unsigned long long KPTR;
|
||||
typedef uint64_t DMA_ADDR;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DMA_ADDR pPhysicalAddr; // Physical address of page.
|
||||
DWORD dwBytes; // Size of page.
|
||||
} WD_DMA_PAGE, WD_DMA_PAGE_V80;
|
||||
|
||||
enum {
|
||||
WD_DMA_KERNEL_BUFFER_ALLOC = 0x1, // The system allocates a contiguous buffer.
|
||||
// The uWD_Openser does not need to supply linear address.
|
||||
|
||||
WD_DMA_KBUF_BELOW_16M = 0x2, // If DMA_KERNEL_BUFFER_ALLOC is used,
|
||||
// this will make sure it is under 16M.
|
||||
|
||||
WD_DMA_LARGE_BUFFER = 0x4, // If DMA_LARGE_BUFFER is used,
|
||||
// the maximum number of pages are dwPages, and not
|
||||
// WD_DMA_PAGES. If you lock a user buffer (not a kernel
|
||||
// allocated buffer) that is larger than 1MB, then use this
|
||||
// option and allocate memory for pages.
|
||||
|
||||
WD_DMA_ALLOW_CACHE = 0x8, // Allow caching of contiguous memory.
|
||||
|
||||
WD_DMA_KERNEL_ONLY_MAP = 0x10, // Only map to kernel, dont map to user-mode.
|
||||
// relevant with DMA_KERNEL_BUFFER_ALLOC flag only
|
||||
|
||||
WD_DMA_FROM_DEVICE = 0x20, // memory pages are locked to be written by device
|
||||
|
||||
WD_DMA_TO_DEVICE = 0x40, // memory pages are locked to be read by device
|
||||
|
||||
WD_DMA_TO_FROM_DEVICE = (WD_DMA_FROM_DEVICE | WD_DMA_TO_DEVICE), // memory pages are
|
||||
// locked for both read and write
|
||||
|
||||
WD_DMA_ALLOW_64BIT_ADDRESS = 0x80, // Use this value for devices that support
|
||||
// 64-bit DMA addressing.
|
||||
|
||||
WD_DMA_ALLOW_NO_HCARD = 0x100, // allow memory lock without hCard
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
DWORD hDma; // Handle of dma buffer.
|
||||
void* pUserAddr; // Beginning of buffer.
|
||||
KPTR pKernelAddr; // Kernel mapping of kernel allocated buffer
|
||||
DWORD dwBytes; // Size of buffer.
|
||||
DWORD dwOptions; // Allocation options:
|
||||
// DMA_KERNEL_BUFFER_ALLOC, DMA_KBUF_BELOW_16M,
|
||||
// DMA_LARGE_BUFFER
|
||||
// DMA_ALLOW_CACHE, DMA_KERNEL_ONLY_MAP,
|
||||
// DMA_FROM_DEVICE,
|
||||
// DMA_TO_DEVICE, DMA_ALLOW_64BIT_ADDRESS
|
||||
DWORD dwPages; // Number of pages in buffer.
|
||||
DWORD hCard; // Handle of relevant card as received from
|
||||
// WD_CardRegister()
|
||||
WD_DMA_PAGE* Page;
|
||||
|
||||
// Former 'dma_t' members (that are not covered above):
|
||||
void** ppInternal; //
|
||||
KPTR *dma_addrs; /* one for each struct page */
|
||||
unsigned int first_page_offset;
|
||||
unsigned int last_page_offset;
|
||||
} WD_DMA, WD_DMA_V80;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int int1;
|
||||
int int2;
|
||||
int int3;
|
||||
int int4;
|
||||
int int5;
|
||||
int int6;
|
||||
int int7;
|
||||
int int8;
|
||||
int int9;
|
||||
int int10;
|
||||
int int11;
|
||||
int int12;
|
||||
} WD_DEBUG_INFO;
|
||||
|
||||
#define MAXBOARDS 16
|
||||
|
||||
#endif /* PCIE_LINUX_DRIVER_EXPORTS_H */
|
@@ -0,0 +1,24 @@
|
||||
|
||||
if [ -f /etc/redhat-release ] ; then
|
||||
|
||||
echo building drivers for RedHat/CentOS...
|
||||
kernelpath=kernels/
|
||||
|
||||
sh make_all.sh 2.6.32-431.el6.x86_64
|
||||
sh make_all.sh 2.6.32-573.26.1.el6.centos.plus.x86_64
|
||||
|
||||
elif [ -f /etc/lsb-release ] ; then
|
||||
|
||||
echo building drivers for Ubuntu...
|
||||
kernelpath="linux-headers-"
|
||||
|
||||
sh make_all.sh 3.19.0-25-generic
|
||||
sh make_all.sh 3.19.0-26-generic
|
||||
sh make_all.sh 3.19.0-28-generic
|
||||
sh make_all.sh 3.19.0-30-generic
|
||||
sh make_all.sh 3.19.0-31-generic
|
||||
|
||||
fi
|
||||
|
||||
|
||||
|
@@ -0,0 +1 @@
|
||||
#define ACL_DRIVER_VERSION "13.0.176"
|
Reference in New Issue
Block a user