embeddedsw/mcap/linux/mcap_lib.c

729 lines
17 KiB
C
Raw Permalink Normal View History

/******************************************************************************
* Copyright (C) 2014-2015 Xilinx, Inc. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* Use of the Software is limited solely to applications:
* (a) running on a Xilinx device, or
* (b) that interact with a Xilinx device through a bus or interconnect.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* XILINX CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Except as contained in this notice, the name of the Xilinx shall not be used
* in advertising or otherwise to promote the sale, use or other dealings in
* this Software without prior written authorization from Xilinx.
*
******************************************************************************/
/*****************************************************************************/
/**
*
* @file mcap_lib.c
* MCAP Interface Library functions
*
******************************************************************************/
#include "mcap_lib.h"
/* Library Specific Definitions */
#define MCAP_VENDOR_ID 0x10EE
#define MCAP_LOOP_COUNT 1000000
#define MCAP_SYNC_DWORD 0xFFFFFFFF
#define MCAP_SYNC_BYTE0 ((MCAP_SYNC_DWORD & 0xFF000000) >> 24)
#define MCAP_SYNC_BYTE1 ((MCAP_SYNC_DWORD & 0x00FF0000) >> 16)
#define MCAP_SYNC_BYTE2 ((MCAP_SYNC_DWORD & 0x0000FF00) >> 8)
#define MCAP_SYNC_BYTE3 ((MCAP_SYNC_DWORD & 0x000000FF) >> 0)
#define MCAP_RBT_FILE ".rbt"
#define MCAP_BIT_FILE ".bit"
#define MCAP_BIN_FILE ".bin"
static char *MCapFindTypeofFile(const char *s1, const char *s2)
{
size_t l1, l2;
l2 = strlen(s2);
if (!l2)
return (char *)s1;
l1 = strlen(s1);
while (l1 >= l2) {
l1--;
if (!strncasecmp(s1, s2, l2))
return (char *)s1;
s1++;
}
return NULL;
}
static u32 MCapProcessRBT(FILE *fptr, u32 *buf)
{
char *raw = NULL;
int i, read;
size_t linelen;
u32 count = 0, len = 0, result = 0;
while ((read = getline(&raw, &linelen, fptr)) != -1) {
if (raw[0] != '1' && raw[1] != '0')
continue;
for (i = 0; i < read - 1; i++) {
if (raw[i] == '1' || raw[i] == '0') {
result = (result << 1) | (raw[i] - 0x30);
count++;
if (count == 32) {
*buf++ = result;
len ++;
result = count = 0;
break;
}
}
}
}
return len;
}
static u32 MCapProcessBIT(FILE *fptr, u32 *buf, int sz)
{
int err;
u8 value, len = 0;
/*
* .bit files are not guaranteed to be aligned with
* the bitstream sync word on a 32-bit boundary. So,
* we need to check every byte here.
*/
while ((err = fread(&value, 1, 1, fptr)) == 1) {
len++; if (value == MCAP_SYNC_BYTE0)
if ((err = fread(&value, 1, 1, fptr)) == 1) {
len++; if (value == MCAP_SYNC_BYTE1)
if ((err = fread(&value, 1, 1, fptr)) == 1) {
len++; if (value == MCAP_SYNC_BYTE2)
if ((err = fread(&value, 1, 1, fptr)) == 1) {
len++; if (value == MCAP_SYNC_BYTE3)
break;
}
}
}
}
if (err != 1 && !feof(fptr)) {
pr_err("Failed to Read BIT file\n");
return 0;
}
if (err != 1 && feof(fptr)) {
pr_err("Failed to find SYNC Word in BIT file\n");
return 0;
}
*buf++ = __bswap_32(MCAP_SYNC_DWORD);
while ((err = fread(buf, sz - len, 1, fptr)) == 1)
;
if (err != 1 && !feof(fptr)) {
pr_err("Failed to Read BIT file\n");
return 0;
}
return (sz - len)/4 + 1;
}
static u32 MCapProcessBIN(FILE *fptr, u32 *buf, int sz)
{
int err;
err = fread(buf, sz, 1, fptr);
if (err != 1 && !feof(fptr)) {
pr_err("Failed to Read BIN file\n");
return 0;
}
return sz/4;
}
static int MCapDoBusWalk(struct mcap_dev *mdev)
{
struct pci_cap *c;
c = pci_find_cap(mdev->pdev, MCAP_EXT_CAP_ID, PCI_CAP_EXTENDED);
if (!c)
return -EMCAPBUSWALK;
mdev->reg_base = c->addr;
return 0;
}
static int MCapClearRequestByConfigure(struct mcap_dev *mdev, u32 *restore)
{
u32 set;
int loop = MCAP_LOOP_COUNT;
set = *restore = MCapRegRead(mdev, MCAP_CONTROL);
if (IsConfigureMCapReqSet(mdev)) {
/* Set 'Mode' and 'In Use by PCIe' bits */
set |= (MCAP_CTRL_MODE_MASK | MCAP_CTRL_IN_USE_MASK);
MCapRegWrite(mdev, MCAP_CONTROL, set);
do {
if (!(IsConfigureMCapReqSet(mdev)))
break;
} while (loop--);
if (!loop) {
pr_err("Failed to clear MCAP Request by config bit\n");
MCapRegWrite(mdev, MCAP_CONTROL, *restore);
return -EMCAPREQ;
}
}
pr_dbg("Request by Configure bit cleared!!\n");
return 0;
}
static int Checkforcompletion(struct mcap_dev *mdev)
{
unsigned long retry_count = 0;
u32 delay;
int sr, i;
sr = MCapRegRead(mdev, MCAP_STATUS);
while (!(sr & MCAP_STS_EOS_MASK)) {
usleep(2);
for (i=0 ; i < EMCAP_EOS_LOOP_COUNT; i++) {
MCapRegWrite(mdev, MCAP_DATA, EMCAP_NOOP_VAL);
}
sr = MCapRegRead(mdev, MCAP_STATUS);
retry_count++;
if (retry_count > EMCAP_EOS_RETRY_COUNT) {
pr_err("Error: The MCAP EOS bit did not assert after");
pr_err(" programming the specified programming file\n");
return -EMCAPREQ;
}
}
return 0;
}
static int MCapWritePartialBitStream(struct mcap_dev *mdev, u32 *data,
int len, u8 bswap)
{
u32 set, restore;
int err, count = 0, i;
if (!data || !len) {
pr_err("Invalid Arguments\n");
return -EMCAPWRITE;
}
err = MCapClearRequestByConfigure(mdev, &restore);
if (err)
return err;
if (IsErrSet(mdev) || IsRegReadComplete(mdev) ||
IsFifoOverflow(mdev)) {
pr_err("Failed to initialize configuring FPGA\n");
MCapRegWrite(mdev, MCAP_CONTROL, restore);
return -EMCAPWRITE;
}
/* Set 'Mode', 'In Use by PCIe' and 'Data Reg Protect' bits */
set = MCapRegRead(mdev, MCAP_CONTROL);
set |= MCAP_CTRL_MODE_MASK | MCAP_CTRL_IN_USE_MASK |
MCAP_CTRL_DATA_REG_PROT_MASK;
/* Clear 'Reset', 'Module Reset' and 'Register Read' bits */
set &= ~(MCAP_CTRL_RESET_MASK | MCAP_CTRL_MOD_RESET_MASK |
MCAP_CTRL_REG_READ_MASK | MCAP_CTRL_DESIGN_SWITCH_MASK);
MCapRegWrite(mdev, MCAP_CONTROL, set);
/* Write Data */
if (!bswap) {
for (count = 0; count < len; count++)
MCapRegWrite(mdev, MCAP_DATA, data[count]);
} else {
for (count = 0; count < len; count++)
MCapRegWrite(mdev, MCAP_DATA, __bswap_32(data[count]));
}
for (i = 0 ; i < EMCAP_EOS_LOOP_COUNT; i++) {
MCapRegWrite(mdev, MCAP_DATA, EMCAP_NOOP_VAL);
}
if (IsErrSet(mdev) || IsFifoOverflow(mdev)) {
pr_err("Failed to Write Bitstream\n");
MCapRegWrite(mdev, MCAP_CONTROL, restore);
MCapFullReset(mdev);
return -EMCAPWRITE;
}
if (!mdev->is_multiplebit) {
pr_info("Info: A partial reconfiguration clear file (-C) was");
pr_info(" loaded without a partial reconfiguration file (-p)");
pr_info(" as result the MCAP Control register was not restored");
pr_info(" to its original value\n\r");
}
return 0;
}
static int MCapWriteBitStream(struct mcap_dev *mdev, u32 *data,
int len, u8 bswap)
{
u32 set, restore;
int err, count = 0;
if (!data || !len) {
pr_err("Invalid Arguments\n");
return -EMCAPWRITE;
}
err = MCapClearRequestByConfigure(mdev, &restore);
if (err)
return err;
if (IsErrSet(mdev) || IsRegReadComplete(mdev) ||
IsFifoOverflow(mdev)) {
pr_err("Failed to initialize configuring FPGA\n");
MCapRegWrite(mdev, MCAP_CONTROL, restore);
return -EMCAPWRITE;
}
if (!mdev->is_multiplebit) {
/* Set 'Mode', 'In Use by PCIe' and 'Data Reg Protect' bits */
set = MCapRegRead(mdev, MCAP_CONTROL);
set |= MCAP_CTRL_MODE_MASK | MCAP_CTRL_IN_USE_MASK |
MCAP_CTRL_DATA_REG_PROT_MASK;
/* Clear 'Reset', 'Module Reset' and 'Register Read' bits */
set &= ~(MCAP_CTRL_RESET_MASK | MCAP_CTRL_MOD_RESET_MASK |
MCAP_CTRL_REG_READ_MASK | MCAP_CTRL_DESIGN_SWITCH_MASK);
MCapRegWrite(mdev, MCAP_CONTROL, set);
}
/* Write Data */
if (!bswap) {
for (count = 0; count < len; count++)
MCapRegWrite(mdev, MCAP_DATA, data[count]);
} else {
for (count = 0; count < len; count++)
MCapRegWrite(mdev, MCAP_DATA, __bswap_32(data[count]));
}
/* Check for Completion */
err = Checkforcompletion(mdev);
if (err)
return -EMCAPCFG;
if (IsErrSet(mdev) || IsFifoOverflow(mdev)) {
pr_err("Failed to Write Bitstream\n");
MCapRegWrite(mdev, MCAP_CONTROL, restore);
MCapFullReset(mdev);
return -EMCAPWRITE;
}
/* Enable PCIe BAR reads/writes in the PCIe hardblock */
restore |= MCAP_CTRL_DESIGN_SWITCH_MASK;
MCapRegWrite(mdev, MCAP_CONTROL, restore);
return 0;
}
void MCapLibFree(struct mcap_dev *mdev)
{
if (mdev) {
pci_cleanup(mdev->pacc);
free(mdev);
}
}
struct mcap_dev *MCapLibInit(int device_id)
{
struct pci_dev *dev;
struct mcap_dev *mdev;
/* Allocate MCAP device */
mdev = malloc(sizeof(struct mcap_dev));
if (!mdev)
return NULL;
/* Get the pci_access structure */
mdev->pacc = pci_alloc();
mdev->is_multiplebit = 0;
/* Initialize the PCI library */
pci_init(mdev->pacc);
/* Get the list of devices */
pci_scan_bus(mdev->pacc);
for (dev = mdev->pacc->devices; dev; dev = dev->next) {
/* Fill in header info we need */
pci_fill_info(dev, PCI_FILL_IDENT | PCI_FILL_BASES |
PCI_FILL_CLASS);
if (dev->vendor_id == MCAP_VENDOR_ID &&
dev->device_id == device_id) {
pr_info("Xilinx MCAP device found\n");
mdev->pdev = dev;
} else {
continue;
}
}
if (!mdev->pdev) {
pr_err("Xilinx MCAP device not found .. Exiting ...\n");
goto free_resources;
}
/* Get the MCAP Register base */
if (MCapDoBusWalk(mdev)) {
pr_err("Unable to get the Register Base\n");
goto free_resources;
}
return mdev;
free_resources:
MCapLibFree(mdev);
return NULL;
}
int MCapReset(struct mcap_dev *mdev)
{
u32 set, restore;
int err;
err = MCapClearRequestByConfigure(mdev, &restore);
if (err)
return err;
/* Set 'Mode', 'In Use by PCIe' and 'Reset' bits */
set = MCapRegRead(mdev, MCAP_CONTROL);
set |= MCAP_CTRL_MODE_MASK | MCAP_CTRL_IN_USE_MASK |
MCAP_CTRL_RESET_MASK;
MCapRegWrite(mdev, MCAP_CONTROL, set);
if (IsErrSet(mdev) || !(IsResetSet(mdev))) {
pr_err("Failed to Reset\n");
MCapRegWrite(mdev, MCAP_CONTROL, restore);
return -EMCAPRESET;
}
MCapRegWrite(mdev, MCAP_CONTROL, restore);
pr_info("Reset Done!!\n");
return 0;
}
int MCapModuleReset(struct mcap_dev *mdev)
{
u32 set, restore;
int err;
err = MCapClearRequestByConfigure(mdev, &restore);
if (err)
return err;
/* Set 'Mode', 'In Use by PCIe' and 'Module Reset' bits */
set = MCapRegRead(mdev, MCAP_CONTROL);
set |= MCAP_CTRL_MODE_MASK | MCAP_CTRL_IN_USE_MASK |
MCAP_CTRL_MOD_RESET_MASK;
MCapRegWrite(mdev, MCAP_CONTROL, set);
if (IsErrSet(mdev) || !(IsModuleResetSet(mdev))) {
pr_err("Failed to Reset Module\n");
MCapRegWrite(mdev, MCAP_CONTROL, restore);
return -EMCAPMODRESET;
}
MCapRegWrite(mdev, MCAP_CONTROL, restore);
pr_info("Module Reset Done!!\n");
return 0;
}
int MCapFullReset(struct mcap_dev *mdev)
{
u32 set, restore;
int err;
err = MCapClearRequestByConfigure(mdev, &restore);
if (err)
return err;
/* Set 'Mode', 'In Use by PCIe' and 'Module Reset' bits */
set = MCapRegRead(mdev, MCAP_CONTROL);
set |= MCAP_CTRL_MODE_MASK | MCAP_CTRL_IN_USE_MASK |
MCAP_CTRL_RESET_MASK | MCAP_CTRL_MOD_RESET_MASK;
MCapRegWrite(mdev, MCAP_CONTROL, set);
if (IsErrSet(mdev) || !(IsModuleResetSet(mdev)) ||
!(IsResetSet(mdev))) {
pr_err("Failed to Full Reset\n");
MCapRegWrite(mdev, MCAP_CONTROL, restore);
return -EMCAPFULLRESET;
}
MCapRegWrite(mdev, MCAP_CONTROL, restore);
pr_info("Full Reset Done!!\n");
return 0;
}
static int MCapReadDataRegisters(struct mcap_dev *mdev, u32 *data)
{
u32 set, restore, read_cnt;
int err;
if (!data) {
pr_err("Invalid Arguments\n");
return -EMCAPREAD;
}
err = MCapClearRequestByConfigure(mdev, &restore);
if (err)
return err;
/* Set 'Mode', 'In Use by PCIe' and 'Data Reg Protect' bits */
set = MCapRegRead(mdev, MCAP_CONTROL);
set |= MCAP_CTRL_MODE_MASK | MCAP_CTRL_IN_USE_MASK |
MCAP_CTRL_REG_READ_MASK;
/* Clear 'Reset', 'Module Reset' and 'Register Read' bits */
set &= ~(MCAP_CTRL_RESET_MASK | MCAP_CTRL_MOD_RESET_MASK);
MCapRegWrite(mdev, MCAP_CONTROL, set);
read_cnt = GetRegReadCount(mdev);
if (!(read_cnt) || !(IsRegReadComplete(mdev))) {
MCapRegWrite(mdev, MCAP_CONTROL, restore);
return EMCAPREAD;
}
if (IsErrSet(mdev) || IsFifoOverflow(mdev)) {
pr_err("Read Register Set Configuration Failed\n");
MCapRegWrite(mdev, MCAP_CONTROL, restore);
return -EMCAPREAD;
}
switch (read_cnt) {
case 7: case 6: case 5: case 4:
data[3] = MCapRegRead(mdev, MCAP_READ_DATA_3);
/* Fall-through */
case 3:
data[2] = MCapRegRead(mdev, MCAP_READ_DATA_2);
/* Fall-through */
case 2:
data[1] = MCapRegRead(mdev, MCAP_READ_DATA_1);
/* Fall-through */
case 1:
data[0] = MCapRegRead(mdev, MCAP_READ_DATA_0);
break;
}
MCapRegWrite(mdev, MCAP_CONTROL, restore);
pr_dbg("Read Data Registers Complete!\n");
return 0;
}
void MCapDumpReadRegs(struct mcap_dev *mdev)
{
u32 data[4];
u32 status;
status = MCapReadDataRegisters(mdev, data);
if (status == EMCAPREAD)
return;
if (status) {
pr_err("Failed Reading Registers.. This may be");
pr_err(" due to inappropriate FPGA configuration.");
pr_err(" Make sure you downloaded the correct bitstream\n");
return;
}
pr_info("Register Read Data 0:\t0x%08x\n", data[0]);
pr_info("Register Read Data 1:\t0x%08x\n", data[1]);
pr_info("Register Read Data 2:\t0x%08x\n", data[2]);
pr_info("Register Read Data 3:\t0x%08x\n", data[3]);
}
void MCapDumpRegs(struct mcap_dev *mdev)
{
pr_info("Extended Capability:\t0x%08x\n",
MCapRegRead(mdev, MCAP_EXT_CAP_HEADER));
pr_info("Vendor Specific Header:\t0x%08x\n",
MCapRegRead(mdev, MCAP_VEND_SPEC_HEADER));
pr_info("FPGA JTAG ID:\t\t0x%08x\n",
MCapRegRead(mdev, MCAP_FPGA_JTAG_ID));
pr_info("FPGA Bit-Stream Version:0x%08x\n",
MCapRegRead(mdev, MCAP_FPGA_BIT_VERSION));
pr_info("Status:\t\t\t0x%08x\n",
MCapRegRead(mdev, MCAP_STATUS));
pr_info("Control:\t\t0x%08x\n",
MCapRegRead(mdev, MCAP_CONTROL));
pr_info("Data:\t\t\t0x%08x\n",
MCapRegRead(mdev, MCAP_DATA));
MCapDumpReadRegs(mdev);
}
int MCapConfigureFPGA(struct mcap_dev *mdev, char *file_path, u32 bitfile_type)
{
FILE *fptr;
u32 *data;
u32 binsz, wrdatasz;
int err = 0;
u8 bswap = 0;
/* Get the size */
fptr = fopen(file_path, "rb");
if (fptr == NULL)
return -EMCAPCFG;
fseek(fptr, 0L, SEEK_END);
binsz = ftell(fptr);
fseek(fptr, 0L, SEEK_SET);
/* Allocate the buffer */
data = malloc(binsz);
if (data == NULL)
return -EMCAPCFG;
/* Process files and Read the data */
if (MCapFindTypeofFile(file_path, MCAP_RBT_FILE)) {
/* Read the RBT file */
wrdatasz = MCapProcessRBT(fptr, data);
} else if (MCapFindTypeofFile(file_path, MCAP_BIT_FILE)) {
/* Read the BIT file */
wrdatasz = MCapProcessBIT(fptr, data, binsz);
bswap = 1;
} else if (MCapFindTypeofFile(file_path, MCAP_BIN_FILE)) {
/* Read the BIN file */
wrdatasz = MCapProcessBIN(fptr, data, binsz);
bswap = 1;
} else {
pr_err("Unknown File Format.. This may be");
pr_err(" due to .bit/.bin/.rbt files does not exist at the.");
pr_err(" specified location, Please cross check the");
pr_err(" path is correct or not\n");
goto free_resources;
}
/* Program FPGA */
if (bitfile_type == EMCAP_PARTIALCONFIG_FILE) {
err = MCapWritePartialBitStream(mdev, data, wrdatasz, bswap);
if (err)
return -EMCAPCFG;
pr_info("FPGA Partial Configuration Done!!\n");
} else if (bitfile_type == EMCAP_CONFIG_FILE) {
err = MCapWriteBitStream(mdev, data, wrdatasz, bswap);
if (err)
return -EMCAPCFG;
pr_info("FPGA Configuration Done!!\n");
}
free_resources:
if (data)
free(data);
fclose(fptr);
return err;
}
int MCapAccessConfigSpace(struct mcap_dev *mdev, int argc, char **argv)
{
unsigned long wrval, rdval;
int pos, access_type;
pos = (int) strtol(argv[4], NULL, 16);
access_type = tolower(argv[5][0]);
if (argc == 6) {
switch (access_type) {
case 'b':
rdval = pci_read_byte(mdev->pdev, pos);
break;
case 'h':
rdval = pci_read_word(mdev->pdev, pos);
break;
case 'w':
rdval = pci_read_long(mdev->pdev, pos);
break;
default:
return -EMCAPCFGACC;
}
pr_info("Read 0x%08lx @ 0x%x\n", rdval, pos);
}
if (argc > 6) {
wrval = strtoul(argv[6], 0, 0);
switch (access_type) {
case 'b':
pci_write_byte(mdev->pdev, pos, wrval);
break;
case 'h':
pci_write_word(mdev->pdev, pos, wrval);
break;
case 'w':
pci_write_long(mdev->pdev, pos, wrval);
break;
default:
return -EMCAPCFGACC;
}
pr_info("Written 0x%08lx @ 0x%x\n", wrval, pos);
}
return 0;
}
int MCapShowDevice(struct mcap_dev *mdev, int verbose)
{
char command[80];
u16 vendor_id, device_id;
vendor_id = mdev->pdev->vendor_id;
device_id = mdev->pdev->device_id;
if (verbose == 1)
sprintf(command, "lspci -vd %x:%x", vendor_id, device_id);
if (verbose >= 2)
sprintf(command, "lspci -vvd %x:%x", vendor_id, device_id);
if (!verbose)
sprintf(command, "lspci -d %x:%x", vendor_id, device_id);
return system(command);
}