/*
* kernel: 4.5.2
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/interrupt.h> // needed in 4.3.3
#define TEST_PCIE_DEV_NAME "test_pcie"
//#define PCI_VENDOR_ID_XILINX 0x10EE /* already defined in <linux/pci_ids.h> The default value, 10EEh, is the Vendor ID for Xilinx. */
#define TEST_PCI_DEVICE_ID_XILINX 0x7033 // !!!check here! /* the default value is 70<link speed><link width>h */
#define TEST_SSD_DEV_NAME "test_ssd"
#define TEST_SSD_PARTITONS 1
#define TEST_SSD_MINORS 1
#define TEST_DEV_MEM_SZ (1 << 22) // 4MB
#define KTHREAD_NAME "test_kthread_fn"
#define COHERENT_DMA_BUF_SZ (1 << 22) // !!!4MB; no less than 1MB
#define TEST_DMA_BUF_MAX_NUM 320 // !!!
/* ZC706 AXI Address */
#define DDR3_BASE_ADDR (0x0 + 0x400000) // !!!!!!
#define AXI_BAR0 0x80800000
#define AXI_BAR1 0x80000000
#define TRANS_BRAM_BASE_ADDR 0x81000000
#define AXI_BAR2 0x40000000 // wb
/* offset from TRANS_BRAM_BASE_ADDR */
#define AXI_PCIE_CTL_OFFSET 0x8000
#define AXI_CDMA_LITE_OFFSET 0xc000
/* AXI to PCIe Base Address Translation Configuration Registers
* offset from AXI_PCIE_CTL_OFFSET
*/
#define AXIBAR2PCIEBAR_0U_OFFSET 0x208
#define AXIBAR2PCIEBAR_0L_OFFSET 0x20c
#define AXIBAR2PCIEBAR_1U_OFFSET 0x210
#define AXIBAR2PCIEBAR_1L_OFFSET 0x214
#define AXIBAR2PCIEBAR_2U_OFFSET 0x218
#define AXIBAR2PCIEBAR_2L_OFFSET 0x21c
/* trans_desc_offset */
/*
#define NXTDESC_PNTR_OFFSET 0x00
#define DESC_SA_OFFSET 0x08
#define DESC_DA_OFFSET 0x10
#define DESC_CTL_OFFSET 0x18
#define DESC_STAT_OFFSET 0x1c
*/
/* cdma_reg_offset
* offset from AXI_CDMA_LITE_OFFSET
*/
#define CDMACR_OFFSET 0x00
#define CDMASR_OFFSET 0x04
#define CURDESC_PNTR_OFFSET 0x08
#define TAILDESC_PNTR_OFFSET 0x10
#define CDMA_SA_OFFSET 0x18
#define CDMA_DA_OFFSET 0x20
#define CDMA_BTT_OFFSET 0x28
struct io_cmd {
struct bio *bio; // !!!
struct scatterlist *scatlist;
dma_addr_t dma_addr; // used by DMA controller of the device
void *kvaddr; // kernel virtual address, used by kernel and driver, especially to deal with data from userspace(__bio_kmap_atomic)
uint32_t len;
};
struct io_que {
struct bio_list bio_lst; // !!!composed of bio, a singly-linked list of bios
struct task_struct *task_s;
struct io_cmd *io_cmd; // above
struct ssd_dev *ssd_dev; // !!!below
spinlock_t lock;
uint8_t volatile is_busy; // origin: unsigned int, DMA busy flag
};
struct ssd_dev {
struct pci_dev *pci_dev;
struct gendisk *disk; // linux/genhd.h
void __iomem *pci_bar; // !!!!!!above, __iomem is needed
struct io_que *dev_que; // !!!above
};
#define TRANS_DESC_ALIGN 0x40 // !!!!!!64
struct trans_desc { // transfer descriptor, according to xapp1171
uint32_t nxt_ptr;
uint32_t reserved0;
uint32_t src_addr;
uint32_t reserved1;
uint32_t dest_addr;
uint32_t reserved2;
uint32_t ctrl;
uint32_t stat;
};
#define __MIN(a, b) ((a) < (b) ? (a) : (b))
/*
static void setup_cmd(struct io_cmd *io_cmd, struct bio *bio, struct io_que *dev_que)
{
io_cmd->bio = bio; // !!!!!!save it until test_bio_complete
}
*/
/*
static int setup_scatter_map(struct ssd_dev *ssd_dev, struct io_cmd *io_cmd, unsigned int const phys_segs)
{
void *kvaddr; // volatile struct scatter_region *
dma_addr_t dma_addr;
// !!!!!!return two params! set here!
kvaddr = dma_alloc_coherent(&ssd_dev->pci_dev->dev, PAGE_SIZE, &dma_addr, GFP_ATOMIC | GFP_DMA);
if (kvaddr == NULL) {
printk("err_dma_pool_alloc\n");
return -ENOMEM;
}
io_cmd->kvaddr = kvaddr;
io_cmd->dma_addr = dma_addr;
io_cmd->len = phys_segs;
return 0;
}
*/
/*
static int setup_scatter_list(struct io_que *dev_que, struct io_cmd *io_cmd, struct bio *bio)
{
//struct ssd_dev *ssd_dev;
struct bio_vec prev_bv, cur_bv; // !!!
struct bvec_iter bvec_iter; // !!!
struct scatterlist *cur_scatlist = NULL;
unsigned int phys_segs, bytes_len = 0;
unsigned char isnt_first_bio_vec = 0u; // !!!
int result = -ENOMEM;
phys_segs = bio_phys_segments(dev_que->ssd_dev->disk->queue, bio); // !!!
io_cmd->scatlist = (struct scatterlist *)kmalloc(sizeof(struct scatterlist) * phys_segs, GFP_ATOMIC | GFP_DMA); // !!!
if (io_cmd->scatlist == NULL) {
printk("err_alloc_scatterlist\n");
goto err_alloc_scatterlist;
}
sg_init_table(io_cmd->scatlist, phys_segs); // !!!lib/scatterlist.c
phys_segs = 0;
memset(&prev_bv, 0, sizeof(struct bio_vec)); // !!!!!!prev_bv need to be initialized
bio_for_each_segment(cur_bv, bio, bvec_iter) { // !!!
if (isnt_first_bio_vec && BIOVEC_PHYS_MERGEABLE(&prev_bv, &cur_bv)) { // !!!BIOVEC_PHYS_MERGEABLE is defined in bio.h
cur_scatlist->length += cur_bv.bv_len; // !!!
} else {
if (isnt_first_bio_vec)
cur_scatlist++;
else
cur_scatlist = io_cmd->scatlist;
sg_set_page(cur_scatlist, cur_bv.bv_page, cur_bv.bv_len, cur_bv.bv_offset); // !!!in <linux/scatterlist.h>
phys_segs++;
}
bytes_len += cur_bv.bv_len; // !!!
prev_bv = cur_bv;
isnt_first_bio_vec = 1u;
}
sg_mark_end(cur_scatlist); // !!!<linux/scatterlist.h>
//ssd_dev = dev_que->ssd_dev;
result = dma_map_sg(&dev_que->ssd_dev->pci_dev->dev, io_cmd->scatlist, phys_segs,
bio_data_dir(io_cmd->bio) == READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE); // !!!???What's its use?
if (result == 0) {
printk("err_dma_map_sg\n");
goto err_dma_map_sg;
}
result = setup_scatter_map(dev_que->ssd_dev, io_cmd, phys_segs); // above
if (result)
goto err_setup_scatter_map;
bio->bi_iter.bi_sector += (sector_t)(bytes_len >> 9); // !!!it will not be set by the kernel?
bio->bi_iter.bi_idx = bvec_iter.bi_idx; // !!!
return 0;
err_setup_scatter_map:
dma_unmap_sg(&dev_que->ssd_dev->pci_dev->dev, io_cmd->scatlist, phys_segs,
bio_data_dir(io_cmd->bio) == READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
printk("err_setup_scatt