/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #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."); 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 offset; offset = 0; bitdata = (char *)fimage->fw_entry->data; 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 *fw_file) { int err; pr_info("load fpgaimage %s\n", fw_file); err = request_firmware(&fimage->fw_entry, fw_file, &firmware_pdev->dev); if (err != 0) { pr_err("firmware %s is missing, cannot continue.\n", fw_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 print_hex_dump_bytes("bitfile sample: ", DUMP_PREFIX_OFFSET, 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); return PTR_ERR_OR_ZERO(firmware_pdev); } 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) return -ENOMEM; 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); return -1; } static int __init gs_fpgaboot_init(void) { int err; pr_info("FPGA DOWNLOAD --->\n"); pr_info("FPGA image file name: %s\n", file); err = init_driver(); if (err) { pr_err("FPGA DRIVER INIT FAIL!!\n"); return err; } err = xl_init_io(); if (err) { pr_err("GPIO INIT FAIL!!\n"); goto errout; } err = gs_fpgaboot(); if (err) { pr_err("FPGA DOWNLOAD FAIL!!\n"); goto errout; } pr_info("FPGA DOWNLOAD DONE <---\n"); return 0; errout: finish_driver(); return err; } 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");