Commit e7185c69 authored by Insop Song's avatar Insop Song Committed by Greg Kroah-Hartman

staging: fpgaboot: Xilinx FPGA firmware download driver

This driver downloads Xilinx FPGA firmware using gpio pins.
It loads Xilinx FPGA bitstream format firmware image and
program the Xilinx FPGA using SelectMAP (parallel) mode.
Signed-off-by: default avatarInsop Song <insop.song@gainspeed.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 86f78b8b
......@@ -146,4 +146,6 @@ source "drivers/staging/dgnc/Kconfig"
source "drivers/staging/dgap/Kconfig"
source "drivers/staging/gs_fpgaboot/Kconfig"
endif # STAGING
......@@ -65,3 +65,4 @@ obj-$(CONFIG_XILLYBUS) += xillybus/
obj-$(CONFIG_DGNC) += dgnc/
obj-$(CONFIG_DGAP) += dgap/
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/
#
# "xilinx FPGA firmware download, fpgaboot"
#
config GS_FPGABOOT
tristate "Xilinx FPGA firmware download module"
default n
help
Xilinx FPGA firmware download module
gs_fpga-y += gs_fpgaboot.o io.o
obj-$(CONFIG_GS_FPGABOOT) += gs_fpga.o
ccflags-$(CONFIG_GS_FPGA_DEBUG) := -DDEBUG
==============================================================================
Linux Driver Source for Xilinx FPGA firmware download
==============================================================================
TABLE OF CONTENTS.
1. SUMMARY
2. BACKGROUND
3. DESIGN
4. HOW TO USE
5. REFERENCE
1. SUMMARY
- Download Xilinx FPGA firmware
- This module downloads Xilinx FPGA firmware using gpio pins.
2. BACKGROUND
An FPGA (Field Programmable Gate Array) is a programmable hardware that is
used in various applications. Hardware design needs to programmed through
a dedicated device or CPU assisted way (serial or parallel).
This driver provides a way to download FPGA firmware.
3. DESIGN
- load Xilinx FPGA bitstream format[1] firmware image file using
kernel firmware framework, request_firmware()
- program the Xilinx FPGA using SelectMAP (parallel) mode [2]
- FPGA prgram is done by gpio based bit-banging, as an example
- platform independent file: gs_fpgaboot.c
- platform dependent file: io.c
4. HOW TO USE
$ insmod gs_fpga.ko file="xlinx_fpga_top_bitstream.bit"
$ rmmod gs_fpga
5. USE CASE (from a mailing list discussion with Greg)
a. As a FPGA development support tool,
During FPGA firmware development, you need to download a new FPGA
image frequently.
You would do that with a dedicated JTAG, which usually a limited
resource in the lab.
However, if you use my driver, you don't have to have a dedicated JTAG.
This is a real gain :)
b. For the FPGA that runs without config after the download, which
doesn't talk to any of Linux interfaces (such as PCIE).
We download FPGA firmware from user triggered or some other way, and that's it.
Since that FPGA runs on its own, it doesn't require a linux driver
after the download.
c. For the FPGA that requires config after the download, which talk to
any of linux interfaces (such as PCIE)
Then, this type of FPGA config can be put into device tree and have a
separate driver (pcie or others), then THAT driver calls my driver to
download FPGA firmware during the Linux boot, the take over the device
through the interface.
6. REFERENCE
1. Xilinx APP NOTE XAPP583:
http://www.xilinx.com/support/documentation/application_notes/xapp583-fpga-configuration.pdf
2. bitstream file info:
http://home.earthlink.net/~davesullins/software/bitinfo.html
TODO:
- get bus width input instead of hardcoded bus width
- get it reviewed
Please send any patches for this driver to Insop Song<insop.song@gainspeed.com>
and Greg Kroah-Hartman <gregkh@linuxfoundation.org>.
And please CC to "Staging subsystem" mail list <devel@driverdev.osuosl.org> too.
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/firmware.h>
#include "gs_fpgaboot.h"
#include "io.h"
#define DEVICE_NAME "device"
#define CLASS_NAME "fpgaboot"
static uint8_t bits_magic[] = {
0x0, 0x9, 0xf, 0xf0, 0xf, 0xf0,
0xf, 0xf0, 0xf, 0xf0, 0x0, 0x0, 0x1};
/* fake device for request_firmware */
static struct platform_device *firmware_pdev;
static char *file = "xlinx_fpga_firmware.bit";
module_param(file, charp, S_IRUGO);
MODULE_PARM_DESC(file, "Xilinx FPGA firmware file.");
#ifdef DEBUG_FPGA
static void datadump(char *msg, void *m, int n)
{
int i;
unsigned char *c;
pr_info("=== %s ===\n", msg);
c = m;
for (i = 0; i < n; i++) {
if ((i&0xf) == 0)
pr_info(KERN_INFO "\n 0x%4x: ", i);
pr_info("%02X ", c[i]);
}
pr_info("\n");
}
#endif /* DEBUG_FPGA */
static void read_bitstream(char *bitdata, char *buf, int *offset, int rdsize)
{
memcpy(buf, bitdata + *offset, rdsize);
*offset += rdsize;
}
static void readinfo_bitstream(char *bitdata, char *buf, int *offset)
{
char tbuf[64];
int32_t len;
/* read section char */
read_bitstream(bitdata, tbuf, offset, 1);
/* read length */
read_bitstream(bitdata, tbuf, offset, 2);
len = tbuf[0] << 8 | tbuf[1];
read_bitstream(bitdata, buf, offset, len);
buf[len] = '\0';
}
/*
* read bitdata length
*/
static int readlength_bitstream(char *bitdata, int *lendata, int *offset)
{
char tbuf[64];
/* read section char */
read_bitstream(bitdata, tbuf, offset, 1);
/* make sure it is section 'e' */
if (tbuf[0] != 'e') {
pr_err("error: length section is not 'e', but %c\n", tbuf[0]);
return -1;
}
/* read 4bytes length */
read_bitstream(bitdata, tbuf, offset, 4);
*lendata = tbuf[0] << 24 | tbuf[1] << 16 |
tbuf[2] << 8 | tbuf[3];
return 0;
}
/*
* read first 13 bytes to check bitstream magic number
*/
static int readmagic_bitstream(char *bitdata, int *offset)
{
char buf[13];
int r;
read_bitstream(bitdata, buf, offset, 13);
r = memcmp(buf, bits_magic, 13);
if (r) {
pr_err("error: corrupted header");
return -1;
}
pr_info("bitstream file magic number Ok\n");
*offset = 13; /* magic length */
return 0;
}
/*
* NOTE: supports only bitstream format
*/
static enum fmt_image get_imageformat(struct fpgaimage *fimage)
{
return f_bit;
}
static void gs_print_header(struct fpgaimage *fimage)
{
pr_info("file: %s\n", fimage->filename);
pr_info("part: %s\n", fimage->part);
pr_info("date: %s\n", fimage->date);
pr_info("time: %s\n", fimage->time);
pr_info("lendata: %d\n", fimage->lendata);
}
static void gs_read_bitstream(struct fpgaimage *fimage)
{
char *bitdata;
int size;
int offset;
offset = 0;
bitdata = (char *)fimage->fw_entry->data;
size = fimage->fw_entry->size;
readmagic_bitstream(bitdata, &offset);
readinfo_bitstream(bitdata, fimage->filename, &offset);
readinfo_bitstream(bitdata, fimage->part, &offset);
readinfo_bitstream(bitdata, fimage->date, &offset);
readinfo_bitstream(bitdata, fimage->time, &offset);
readlength_bitstream(bitdata, &fimage->lendata, &offset);
fimage->fpgadata = bitdata + offset;
}
static int gs_read_image(struct fpgaimage *fimage)
{
int img_fmt;
img_fmt = get_imageformat(fimage);
switch (img_fmt) {
case f_bit:
pr_info("image is bitstream format\n");
gs_read_bitstream(fimage);
break;
default:
pr_err("unsupported fpga image format\n");
return -1;
};
gs_print_header(fimage);
return 0;
}
static int gs_load_image(struct fpgaimage *fimage, char *file)
{
int err;
pr_info("load fpgaimage %s\n", file);
err = request_firmware(&fimage->fw_entry, file, &firmware_pdev->dev);
if (err != 0) {
pr_err("firmware %s is missing, cannot continue.\n", file);
return err;
}
return 0;
}
static int gs_download_image(struct fpgaimage *fimage, enum wbus bus_bytes)
{
char *bitdata;
int size, i, cnt;
cnt = 0;
bitdata = (char *)fimage->fpgadata;
size = fimage->lendata;
#ifdef DEBUG_FPGA
datadump("bitfile sample", bitdata, 0x100);
#endif /* DEBUG_FPGA */
if (!xl_supported_prog_bus_width(bus_bytes)) {
pr_err("unsupported program bus width %d\n",
bus_bytes);
return -1;
}
/* Bring csi_b, rdwr_b Low and program_b High */
xl_program_b(1);
xl_rdwr_b(0);
xl_csi_b(0);
/* Configuration reset */
xl_program_b(0);
msleep(20);
xl_program_b(1);
/* Wait for Device Initialization */
while (xl_get_init_b() == 0)
;
pr_info("device init done\n");
for (i = 0; i < size; i += bus_bytes)
xl_shift_bytes_out(bus_bytes, bitdata+i);
pr_info("program done\n");
/* Check INIT_B */
if (xl_get_init_b() == 0) {
pr_err("init_b 0\n");
return -1;
}
while (xl_get_done_b() == 0) {
if (cnt++ > MAX_WAIT_DONE) {
pr_err("init_B %d\n", xl_get_init_b());
break;
}
}
if (cnt > MAX_WAIT_DONE) {
pr_err("fpga download fail\n");
return -1;
}
pr_info("download fpgaimage\n");
/* Compensate for Special Startup Conditions */
xl_shift_cclk(8);
return 0;
}
static int gs_release_image(struct fpgaimage *fimage)
{
release_firmware(fimage->fw_entry);
pr_info("release fpgaimage\n");
return 0;
}
/*
* NOTE: supports systemmap parallel programming
*/
static int gs_set_download_method(struct fpgaimage *fimage)
{
pr_info("set program method\n");
fimage->dmethod = m_systemmap;
pr_info("systemmap program method\n");
return 0;
}
static int init_driver(void)
{
firmware_pdev = platform_device_register_simple("fpgaboot", -1,
NULL, 0);
if (IS_ERR(firmware_pdev))
return PTR_ERR(firmware_pdev);
return 0;
}
static void finish_driver(void)
{
platform_device_unregister(firmware_pdev);
}
static int gs_fpgaboot(void)
{
int err;
struct fpgaimage *fimage;
fimage = kmalloc(sizeof(struct fpgaimage), GFP_KERNEL);
if (fimage == NULL) {
pr_err("No memory is available\n");
goto err_out;
}
err = gs_load_image(fimage, file);
if (err) {
pr_err("gs_load_image error\n");
goto err_out1;
}
err = gs_read_image(fimage);
if (err) {
pr_err("gs_read_image error\n");
goto err_out2;
}
err = gs_set_download_method(fimage);
if (err) {
pr_err("gs_set_download_method error\n");
goto err_out2;
}
err = gs_download_image(fimage, bus_2byte);
if (err) {
pr_err("gs_download_image error\n");
goto err_out2;
}
err = gs_release_image(fimage);
if (err) {
pr_err("gs_release_image error\n");
goto err_out1;
}
kfree(fimage);
return 0;
err_out2:
err = gs_release_image(fimage);
if (err)
pr_err("gs_release_image error\n");
err_out1:
kfree(fimage);
err_out:
return -1;
}
static int __init gs_fpgaboot_init(void)
{
int err, r;
r = -1;
pr_info("FPGA DOWNLOAD --->\n");
pr_info("built at %s UTC\n", __TIMESTAMP__);
pr_info("FPGA image file name: %s\n", file);
err = init_driver();
if (err != 0) {
pr_err("FPGA DRIVER INIT FAIL!!\n");
return r;
}
err = xl_init_io();
if (err) {
pr_err("GPIO INIT FAIL!!\n");
r = -1;
goto errout;
}
err = gs_fpgaboot();
if (err) {
pr_err("FPGA DOWNLOAD FAIL!!\n");
r = -1;
goto errout;
}
pr_info("FPGA DOWNLOAD DONE <---\n");
r = 0;
return r;
errout:
finish_driver();
return r;
}
static void __exit gs_fpgaboot_exit(void)
{
finish_driver();
pr_info("FPGA image download module removed\n");
}
module_init(gs_fpgaboot_init);
module_exit(gs_fpgaboot_exit);
MODULE_AUTHOR("Insop Song");
MODULE_DESCRIPTION("Xlinix FPGA firmware download");
MODULE_LICENSE("GPL");
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/firmware.h>
#define MAX_STR 256
enum fmt_image {
f_bit, /* only bitstream is supported */
f_rbt,
f_bin,
f_mcs,
f_hex,
};
enum mdownload {
m_systemmap, /* only system map is supported */
m_serial,
m_jtag,
};
/*
* xilinx fpgaimage information
* NOTE: use MAX_STR instead of dynamic alloc for simplicity
*/
struct fpgaimage {
enum fmt_image fmt_img;
enum mdownload dmethod;
const struct firmware *fw_entry;
/*
* the followings can be read from bitstream,
* but other image format should have as well
*/
char filename[MAX_STR];
char part[MAX_STR];
char date[MAX_STR];
char time[MAX_STR];
int32_t lendata;
char *fpgadata;
};
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/firmware.h>
#include <linux/io.h>
#include "io.h"
#ifdef CONFIG_B4860G100
static struct gpiobus gbus;
#endif /* CONFIG_B4860G100 */
static inline void byte0_out(unsigned char data);
static inline void byte1_out(unsigned char data);
static inline void xl_cclk_b(int32_t i);
/* Assert and Deassert CCLK */
void xl_shift_cclk(int count)
{
int i;
for (i = 0; i < count; i++) {
xl_cclk_b(1);
xl_cclk_b(0);
}
}
int xl_supported_prog_bus_width(enum wbus bus_bytes)
{
switch (bus_bytes) {
case bus_1byte:
break;
case bus_2byte:
break;
default:
pr_err("unsupported program bus width %d\n",
bus_bytes);
return 0;
}
return 1;
}
/* Serialize byte and clock each bit on target's DIN and CCLK pins */
void xl_shift_bytes_out(enum wbus bus_byte, unsigned char *pdata)
{
/*
* supports 1 and 2 bytes programming mode
*/
if (likely(bus_byte == bus_2byte))
byte0_out(pdata[0]);
byte1_out(pdata[1]);
xl_shift_cclk(1);
}
/*
* generic bit swap for xilinx SYSTEMMAP FPGA programming
*/
static inline unsigned char bitswap(unsigned char s)
{
unsigned char d;
d = (((s&0x80)>>7) | ((s&0x40)>>5) | ((s&0x20)>>3) | ((s&0x10)>>1) |
((s&0x08)<<1) | ((s&0x04)<<3) | ((s&0x02)<<5) | ((s&0x01)<<7));
return d;
}
#ifdef CONFIG_B4860G100
/*
* ======================================================================
* board specific configuration
*/
static inline void mpc85xx_gpio_set_dir(
int32_t port,
uint32_t mask,
uint32_t dir)
{
dir |= (in_be32(gbus.r[port]+GPDIR) & ~mask);
out_be32(gbus.r[port]+GPDIR, dir);
}
static inline void mpc85xx_gpio_set(int32_t port, uint32_t mask, uint32_t val)
{
/* First mask off the unwanted parts of "dir" and "val" */
val &= mask;
/* Now read in the values we're supposed to preserve */
val |= (in_be32(gbus.r[port]+GPDAT) & ~mask);
out_be32(gbus.r[port]+GPDAT, val);
}
static inline uint32_t mpc85xx_gpio_get(int32_t port, uint32_t mask)
{
/* Read the requested values */
return in_be32(gbus.r[port]+GPDAT) & mask;
}
static inline void mpc85xx_gpio_set_low(int32_t port, uint32_t gpios)
{
mpc85xx_gpio_set(port, gpios, 0x00000000);
}
static inline void mpc85xx_gpio_set_high(int32_t port, uint32_t gpios)
{
mpc85xx_gpio_set(port, gpios, 0xFFFFFFFF);
}
static inline void gpio_set_value(int32_t port, uint32_t gpio, uint32_t value)
{
int32_t g;
g = 31 - gpio;
if (value)
mpc85xx_gpio_set_high(port, 1U << g);
else
mpc85xx_gpio_set_low(port, 1U << g);
}
static inline int gpio_get_value(int32_t port, uint32_t gpio)
{
int32_t g;
g = 31 - gpio;
return !!mpc85xx_gpio_get(port, 1U << g);
}
static inline void xl_cclk_b(int32_t i)
{
gpio_set_value(XL_CCLK_PORT, XL_CCLK_PIN, i);
}
void xl_program_b(int32_t i)
{
gpio_set_value(XL_PROGN_PORT, XL_PROGN_PIN, i);
}
void xl_rdwr_b(int32_t i)
{
gpio_set_value(XL_RDWRN_PORT, XL_RDWRN_PIN, i);
}
void xl_csi_b(int32_t i)
{
gpio_set_value(XL_CSIN_PORT, XL_CSIN_PIN, i);
}
int xl_get_init_b(void)
{
return gpio_get_value(XL_INITN_PORT, XL_INITN_PIN);
}
int xl_get_done_b(void)
{
return gpio_get_value(XL_DONE_PORT, XL_DONE_PIN);
}
/* G100 specific bit swap and remmap (to gpio pins) for byte 0 */
static inline uint32_t bit_remap_byte0(uint32_t s)
{
uint32_t d;
d = (((s&0x80)>>7) | ((s&0x40)>>5) | ((s&0x20)>>3) | ((s&0x10)>>1) |
((s&0x08)<<1) | ((s&0x04)<<3) | ((s&0x02)<<6) | ((s&0x01)<<9));
return d;
}
/*
* G100 specific MSB, in this order [byte0 | byte1], out
*/
static inline void byte0_out(unsigned char data)
{
uint32_t swap32;
swap32 = bit_remap_byte0((uint32_t) data) << 8;
mpc85xx_gpio_set(0, 0x0002BF00, (uint32_t) swap32);
}
/*
* G100 specific LSB, in this order [byte0 | byte1], out
*/
static inline void byte1_out(unsigned char data)
{
mpc85xx_gpio_set(0, 0x000000FF, (uint32_t) bitswap(data));
}
/*
* configurable per device type for different I/O config
*/
int xl_init_io()
{
struct device_node *np;
const u32 *p_reg;
int reg, cnt;
cnt = 0;
memset(&gbus, 0, sizeof(struct gpiobus));
for_each_compatible_node(np, NULL, "fsl,qoriq-gpio") {
p_reg = of_get_property(np, "reg", NULL);
if (p_reg == NULL)
break;
reg = (int) *p_reg;
gbus.r[cnt] = of_iomap(np, 0);
if (!gbus.r[cnt]) {
pr_err("not findding gpio cell-index %d\n", cnt);
return -ENODEV;
}
cnt++;
}
mpc85xx_gpio_set_dir(0, 0x0002BFFF, 0x0002BFFF);
mpc85xx_gpio_set_dir(1, 0x00240060, 0x00240060);
gbus.ngpio = cnt;
return 0;
}
#else /* placeholder for boards with different config */
void xl_program_b(int32_t i)
{
return;
}
void xl_rdwr_b(int32_t i)
{
return;
}
void xl_csi_b(int32_t i)
{
return;
}
int xl_get_init_b(void)
{
return -1;
}
int xl_get_done_b(void)
{
return -1;
}
static inline void byte0_out(unsigned char data)
{
return;
}
static inline void byte1_out(unsigned char data)
{
return;
}
static inline void xl_cclk_b(int32_t i)
{
return;
}
/*
* configurable per device type for different I/O config
*/
int xl_init_io()
{
return -1;
}
#endif /* CONFIG_B4860G100 */
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define GPDIR 0
#define GPCFG 4 /* open drain or not */
#define GPDAT 8
/*
* gpio port and pin definitions
* NOTE: port number starts from 0
*/
#define XL_INITN_PORT 1
#define XL_INITN_PIN 14
#define XL_RDWRN_PORT 1
#define XL_RDWRN_PIN 13
#define XL_CCLK_PORT 1
#define XL_CCLK_PIN 10
#define XL_PROGN_PORT 1
#define XL_PROGN_PIN 25
#define XL_CSIN_PORT 1
#define XL_CSIN_PIN 26
#define XL_DONE_PORT 1
#define XL_DONE_PIN 27
/*
* gpio mapping
*
XL_config_D0 – gpio1_31
Xl_config_d1 – gpio1_30
Xl_config_d2 – gpio1_29
Xl_config_d3 – gpio1_28
Xl_config_d4 – gpio1_27
Xl_config_d5 – gpio1_26
Xl_config_d6 – gpio1_25
Xl_config_d7 – gpio1_24
Xl_config_d8 – gpio1_23
Xl_config_d9 – gpio1_22
Xl_config_d10 – gpio1_21
Xl_config_d11 – gpio1_20
Xl_config_d12 – gpio1_19
Xl_config_d13 – gpio1_18
Xl_config_d14 – gpio1_16
Xl_config_d15 – gpio1_14
*
*/
/*
* program bus width in bytes
*/
enum wbus {
bus_1byte = 1,
bus_2byte = 2,
};
#define MAX_WAIT_DONE 10000
struct gpiobus {
int ngpio;
void __iomem *r[4];
};
int xl_supported_prog_bus_width(enum wbus bus_bytes);
void xl_program_b(int32_t i);
void xl_rdwr_b(int32_t i);
void xl_csi_b(int32_t i);
int xl_get_init_b(void);
int xl_get_done_b(void);
void xl_shift_cclk(int count);
void xl_shift_bytes_out(enum wbus bus_byte, unsigned char *pdata);
int xl_init_io(void);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment