// SPDX-License-Identifier: GPL-2.0+ #include #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 u8 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, 0444); MODULE_PARM_DESC(file, "Xilinx FPGA firmware file."); static void read_bitstream(u8 *bitdata, u8 *buf, int *offset, int rdsize) { memcpy(buf, bitdata + *offset, rdsize); *offset += rdsize; } static int readinfo_bitstream(u8 *bitdata, u8 *buf, int size, int *offset) { u8 tbuf[2]; u16 len; /* read section char */ read_bitstream(bitdata, tbuf, offset, 1); /* read length */ read_bitstream(bitdata, tbuf, offset, 2); len = get_unaligned_be16(tbuf); if (len >= size) { pr_err("error: readinfo buffer too small\n"); return -EINVAL; } read_bitstream(bitdata, buf, offset, len); buf[len] = '\0'; return 0; } /* * read bitdata length */ static int readlength_bitstream(u8 *bitdata, int *lendata, int *offset) { u8 tbuf[4]; /* 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 -EINVAL; } /* read 4bytes length */ read_bitstream(bitdata, tbuf, offset, 4); *lendata = get_unaligned_be32(tbuf); return 0; } /* * read first 13 bytes to check bitstream magic number */ static int readmagic_bitstream(u8 *bitdata, int *offset) { u8 buf[13]; int r; read_bitstream(bitdata, buf, offset, 13); r = memcmp(buf, bits_magic, 13); if (r) { pr_err("error: corrupted header\n"); return -EINVAL; } 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(void) { 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 int gs_read_bitstream(struct fpgaimage *fimage) { u8 *bitdata; int offset; int err; offset = 0; bitdata = (u8 *)fimage->fw_entry->data; err = readmagic_bitstream(bitdata, &offset); if (err) return err; err = readinfo_bitstream(bitdata, fimage->filename, MAX_STR, &offset); if (err) return err; err = readinfo_bitstream(bitdata, fimage->part, MAX_STR, &offset); if (err) return err; err = readinfo_bitstream(bitdata, fimage->date, MAX_STR, &offset); if (err) return err; err = readinfo_bitstream(bitdata, fimage->time, MAX_STR, &offset); if (err) return err; err = readlength_bitstream(bitdata, &fimage->lendata, &offset); if (err) return err; fimage->fpgadata = bitdata + offset; return 0; } static int gs_read_image(struct fpgaimage *fimage) { int img_fmt; int err; img_fmt = get_imageformat(); switch (img_fmt) { case f_bit: pr_info("image is bitstream format\n"); err = gs_read_bitstream(fimage); if (err) return err; break; default: pr_err("unsupported fpga image format\n"); return -EINVAL; } 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) { u8 *bitdata; int size, i, cnt; cnt = 0; bitdata = (u8 *)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 -EINVAL; } /* 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 -EIO; } 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 -EIO; } 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 int gs_fpgaboot(void) { int err; struct fpgaimage *fimage; fimage = kmalloc(sizeof(*fimage), 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 err; } 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: platform_device_unregister(firmware_pdev); return err; } static void __exit gs_fpgaboot_exit(void) { platform_device_unregister(firmware_pdev); 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");