Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 1 | /* |
| 2 | * This program is free software; you can redistribute it and/or modify |
| 3 | * it under the terms of the GNU General Public License as published by |
| 4 | * the Free Software Foundation; either version 2 of the License, or |
| 5 | * (at your option) any later version. |
| 6 | * |
| 7 | * This program is distributed in the hope that it will be useful, |
| 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | * GNU General Public License for more details. |
| 11 | * |
| 12 | * You should have received a copy of the GNU General Public License |
| 13 | * along with this program; if not, write to the Free Software |
| 14 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
| 15 | */ |
| 16 | |
| 17 | #include <linux/kernel.h> |
| 18 | #include <linux/init.h> |
| 19 | #include <linux/module.h> |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 20 | #include <linux/types.h> |
| 21 | #include <linux/device.h> |
| 22 | #include <linux/string.h> |
| 23 | #include <linux/slab.h> |
| 24 | #include <linux/fs.h> |
| 25 | #include <linux/platform_device.h> |
| 26 | #include <linux/of.h> |
| 27 | #include <linux/delay.h> |
| 28 | #include <linux/io.h> |
| 29 | #include <linux/firmware.h> |
| 30 | |
| 31 | #include "gs_fpgaboot.h" |
| 32 | #include "io.h" |
| 33 | |
| 34 | #define DEVICE_NAME "device" |
| 35 | #define CLASS_NAME "fpgaboot" |
| 36 | |
| 37 | static uint8_t bits_magic[] = { |
| 38 | 0x0, 0x9, 0xf, 0xf0, 0xf, 0xf0, |
| 39 | 0xf, 0xf0, 0xf, 0xf0, 0x0, 0x0, 0x1}; |
| 40 | |
| 41 | /* fake device for request_firmware */ |
| 42 | static struct platform_device *firmware_pdev; |
| 43 | |
| 44 | static char *file = "xlinx_fpga_firmware.bit"; |
| 45 | module_param(file, charp, S_IRUGO); |
| 46 | MODULE_PARM_DESC(file, "Xilinx FPGA firmware file."); |
| 47 | |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 48 | static void read_bitstream(char *bitdata, char *buf, int *offset, int rdsize) |
| 49 | { |
| 50 | memcpy(buf, bitdata + *offset, rdsize); |
| 51 | *offset += rdsize; |
| 52 | } |
| 53 | |
| 54 | static void readinfo_bitstream(char *bitdata, char *buf, int *offset) |
| 55 | { |
| 56 | char tbuf[64]; |
| 57 | int32_t len; |
| 58 | |
| 59 | /* read section char */ |
| 60 | read_bitstream(bitdata, tbuf, offset, 1); |
| 61 | |
| 62 | /* read length */ |
| 63 | read_bitstream(bitdata, tbuf, offset, 2); |
| 64 | |
| 65 | len = tbuf[0] << 8 | tbuf[1]; |
| 66 | |
| 67 | read_bitstream(bitdata, buf, offset, len); |
| 68 | buf[len] = '\0'; |
| 69 | } |
| 70 | |
| 71 | /* |
| 72 | * read bitdata length |
| 73 | */ |
| 74 | static int readlength_bitstream(char *bitdata, int *lendata, int *offset) |
| 75 | { |
| 76 | char tbuf[64]; |
| 77 | |
| 78 | /* read section char */ |
| 79 | read_bitstream(bitdata, tbuf, offset, 1); |
| 80 | |
| 81 | /* make sure it is section 'e' */ |
| 82 | if (tbuf[0] != 'e') { |
| 83 | pr_err("error: length section is not 'e', but %c\n", tbuf[0]); |
| 84 | return -1; |
| 85 | } |
| 86 | |
| 87 | /* read 4bytes length */ |
| 88 | read_bitstream(bitdata, tbuf, offset, 4); |
| 89 | |
| 90 | *lendata = tbuf[0] << 24 | tbuf[1] << 16 | |
| 91 | tbuf[2] << 8 | tbuf[3]; |
| 92 | |
| 93 | return 0; |
| 94 | } |
| 95 | |
| 96 | |
| 97 | /* |
| 98 | * read first 13 bytes to check bitstream magic number |
| 99 | */ |
| 100 | static int readmagic_bitstream(char *bitdata, int *offset) |
| 101 | { |
| 102 | char buf[13]; |
| 103 | int r; |
| 104 | |
| 105 | read_bitstream(bitdata, buf, offset, 13); |
| 106 | r = memcmp(buf, bits_magic, 13); |
| 107 | if (r) { |
| 108 | pr_err("error: corrupted header"); |
| 109 | return -1; |
| 110 | } |
| 111 | pr_info("bitstream file magic number Ok\n"); |
| 112 | |
| 113 | *offset = 13; /* magic length */ |
| 114 | |
| 115 | return 0; |
| 116 | } |
| 117 | |
| 118 | /* |
| 119 | * NOTE: supports only bitstream format |
| 120 | */ |
| 121 | static enum fmt_image get_imageformat(struct fpgaimage *fimage) |
| 122 | { |
| 123 | return f_bit; |
| 124 | } |
| 125 | |
| 126 | static void gs_print_header(struct fpgaimage *fimage) |
| 127 | { |
| 128 | pr_info("file: %s\n", fimage->filename); |
| 129 | pr_info("part: %s\n", fimage->part); |
| 130 | pr_info("date: %s\n", fimage->date); |
| 131 | pr_info("time: %s\n", fimage->time); |
| 132 | pr_info("lendata: %d\n", fimage->lendata); |
| 133 | } |
| 134 | |
| 135 | static void gs_read_bitstream(struct fpgaimage *fimage) |
| 136 | { |
| 137 | char *bitdata; |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 138 | int offset; |
| 139 | |
| 140 | offset = 0; |
| 141 | bitdata = (char *)fimage->fw_entry->data; |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 142 | |
| 143 | readmagic_bitstream(bitdata, &offset); |
| 144 | readinfo_bitstream(bitdata, fimage->filename, &offset); |
| 145 | readinfo_bitstream(bitdata, fimage->part, &offset); |
| 146 | readinfo_bitstream(bitdata, fimage->date, &offset); |
| 147 | readinfo_bitstream(bitdata, fimage->time, &offset); |
| 148 | readlength_bitstream(bitdata, &fimage->lendata, &offset); |
| 149 | |
| 150 | fimage->fpgadata = bitdata + offset; |
| 151 | } |
| 152 | |
| 153 | static int gs_read_image(struct fpgaimage *fimage) |
| 154 | { |
| 155 | int img_fmt; |
| 156 | |
| 157 | img_fmt = get_imageformat(fimage); |
| 158 | |
| 159 | switch (img_fmt) { |
| 160 | case f_bit: |
| 161 | pr_info("image is bitstream format\n"); |
| 162 | gs_read_bitstream(fimage); |
| 163 | break; |
| 164 | default: |
| 165 | pr_err("unsupported fpga image format\n"); |
| 166 | return -1; |
Fengguang Wu | c39e9c8 | 2014-02-14 09:26:16 -0800 | [diff] [blame] | 167 | } |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 168 | |
| 169 | gs_print_header(fimage); |
| 170 | |
| 171 | return 0; |
| 172 | } |
| 173 | |
Devendra Naga | 84e9cd6 | 2014-10-25 02:01:56 +0530 | [diff] [blame] | 174 | static int gs_load_image(struct fpgaimage *fimage, char *fw_file) |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 175 | { |
| 176 | int err; |
| 177 | |
Devendra Naga | 84e9cd6 | 2014-10-25 02:01:56 +0530 | [diff] [blame] | 178 | pr_info("load fpgaimage %s\n", fw_file); |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 179 | |
Devendra Naga | 84e9cd6 | 2014-10-25 02:01:56 +0530 | [diff] [blame] | 180 | err = request_firmware(&fimage->fw_entry, fw_file, &firmware_pdev->dev); |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 181 | if (err != 0) { |
Devendra Naga | 84e9cd6 | 2014-10-25 02:01:56 +0530 | [diff] [blame] | 182 | pr_err("firmware %s is missing, cannot continue.\n", fw_file); |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 183 | return err; |
| 184 | } |
| 185 | |
| 186 | return 0; |
| 187 | } |
| 188 | |
| 189 | static int gs_download_image(struct fpgaimage *fimage, enum wbus bus_bytes) |
| 190 | { |
| 191 | char *bitdata; |
| 192 | int size, i, cnt; |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 193 | |
Rocco Folino | 5535c4d | 2014-04-03 00:47:46 +0200 | [diff] [blame] | 194 | cnt = 0; |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 195 | bitdata = (char *)fimage->fpgadata; |
| 196 | size = fimage->lendata; |
| 197 | |
| 198 | #ifdef DEBUG_FPGA |
Joe Perches | 6232876 | 2014-10-07 17:53:20 +0200 | [diff] [blame] | 199 | print_hex_dump_bytes("bitfile sample: ", DUMP_PREFIX_OFFSET, |
| 200 | bitdata, 0x100); |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 201 | #endif /* DEBUG_FPGA */ |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 202 | if (!xl_supported_prog_bus_width(bus_bytes)) { |
| 203 | pr_err("unsupported program bus width %d\n", |
| 204 | bus_bytes); |
| 205 | return -1; |
| 206 | } |
| 207 | |
| 208 | /* Bring csi_b, rdwr_b Low and program_b High */ |
| 209 | xl_program_b(1); |
| 210 | xl_rdwr_b(0); |
| 211 | xl_csi_b(0); |
| 212 | |
| 213 | /* Configuration reset */ |
| 214 | xl_program_b(0); |
| 215 | msleep(20); |
| 216 | xl_program_b(1); |
| 217 | |
| 218 | /* Wait for Device Initialization */ |
| 219 | while (xl_get_init_b() == 0) |
| 220 | ; |
| 221 | |
| 222 | pr_info("device init done\n"); |
| 223 | |
| 224 | for (i = 0; i < size; i += bus_bytes) |
| 225 | xl_shift_bytes_out(bus_bytes, bitdata+i); |
| 226 | |
| 227 | pr_info("program done\n"); |
| 228 | |
| 229 | /* Check INIT_B */ |
| 230 | if (xl_get_init_b() == 0) { |
| 231 | pr_err("init_b 0\n"); |
| 232 | return -1; |
| 233 | } |
| 234 | |
| 235 | while (xl_get_done_b() == 0) { |
| 236 | if (cnt++ > MAX_WAIT_DONE) { |
| 237 | pr_err("init_B %d\n", xl_get_init_b()); |
| 238 | break; |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | if (cnt > MAX_WAIT_DONE) { |
| 243 | pr_err("fpga download fail\n"); |
| 244 | return -1; |
| 245 | } |
| 246 | |
| 247 | pr_info("download fpgaimage\n"); |
| 248 | |
| 249 | /* Compensate for Special Startup Conditions */ |
| 250 | xl_shift_cclk(8); |
| 251 | |
| 252 | return 0; |
| 253 | } |
| 254 | |
| 255 | static int gs_release_image(struct fpgaimage *fimage) |
| 256 | { |
| 257 | release_firmware(fimage->fw_entry); |
| 258 | pr_info("release fpgaimage\n"); |
| 259 | |
| 260 | return 0; |
| 261 | } |
| 262 | |
| 263 | /* |
| 264 | * NOTE: supports systemmap parallel programming |
| 265 | */ |
| 266 | static int gs_set_download_method(struct fpgaimage *fimage) |
| 267 | { |
| 268 | pr_info("set program method\n"); |
| 269 | |
| 270 | fimage->dmethod = m_systemmap; |
| 271 | |
| 272 | pr_info("systemmap program method\n"); |
| 273 | |
| 274 | return 0; |
| 275 | } |
| 276 | |
| 277 | static int init_driver(void) |
| 278 | { |
| 279 | firmware_pdev = platform_device_register_simple("fpgaboot", -1, |
| 280 | NULL, 0); |
Fengguang Wu | 00e0d3c | 2014-02-14 09:26:07 -0800 | [diff] [blame] | 281 | return PTR_ERR_OR_ZERO(firmware_pdev); |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 282 | } |
| 283 | |
| 284 | static void finish_driver(void) |
| 285 | { |
| 286 | platform_device_unregister(firmware_pdev); |
| 287 | } |
| 288 | |
| 289 | static int gs_fpgaboot(void) |
| 290 | { |
| 291 | int err; |
| 292 | struct fpgaimage *fimage; |
| 293 | |
| 294 | fimage = kmalloc(sizeof(struct fpgaimage), GFP_KERNEL); |
Dzmitry Sledneu | 02c2d43 | 2014-10-09 09:22:43 +0200 | [diff] [blame] | 295 | if (!fimage) |
| 296 | return -ENOMEM; |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 297 | |
| 298 | err = gs_load_image(fimage, file); |
| 299 | if (err) { |
| 300 | pr_err("gs_load_image error\n"); |
| 301 | goto err_out1; |
| 302 | } |
| 303 | |
| 304 | err = gs_read_image(fimage); |
| 305 | if (err) { |
| 306 | pr_err("gs_read_image error\n"); |
| 307 | goto err_out2; |
| 308 | } |
| 309 | |
| 310 | err = gs_set_download_method(fimage); |
| 311 | if (err) { |
| 312 | pr_err("gs_set_download_method error\n"); |
| 313 | goto err_out2; |
| 314 | } |
| 315 | |
| 316 | err = gs_download_image(fimage, bus_2byte); |
| 317 | if (err) { |
| 318 | pr_err("gs_download_image error\n"); |
| 319 | goto err_out2; |
| 320 | } |
| 321 | |
| 322 | err = gs_release_image(fimage); |
| 323 | if (err) { |
| 324 | pr_err("gs_release_image error\n"); |
| 325 | goto err_out1; |
| 326 | } |
| 327 | |
| 328 | kfree(fimage); |
| 329 | return 0; |
| 330 | |
| 331 | err_out2: |
| 332 | err = gs_release_image(fimage); |
| 333 | if (err) |
| 334 | pr_err("gs_release_image error\n"); |
| 335 | err_out1: |
| 336 | kfree(fimage); |
| 337 | |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 338 | return -1; |
| 339 | |
| 340 | } |
| 341 | |
| 342 | static int __init gs_fpgaboot_init(void) |
| 343 | { |
Devendra Naga | 92bf93f | 2014-10-25 02:01:54 +0530 | [diff] [blame] | 344 | int err; |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 345 | |
| 346 | pr_info("FPGA DOWNLOAD --->\n"); |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 347 | |
| 348 | pr_info("FPGA image file name: %s\n", file); |
| 349 | |
| 350 | err = init_driver(); |
Devendra Naga | 92bf93f | 2014-10-25 02:01:54 +0530 | [diff] [blame] | 351 | if (err) { |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 352 | pr_err("FPGA DRIVER INIT FAIL!!\n"); |
Devendra Naga | 92bf93f | 2014-10-25 02:01:54 +0530 | [diff] [blame] | 353 | return err; |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 354 | } |
| 355 | |
| 356 | err = xl_init_io(); |
| 357 | if (err) { |
| 358 | pr_err("GPIO INIT FAIL!!\n"); |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 359 | goto errout; |
| 360 | } |
| 361 | |
| 362 | err = gs_fpgaboot(); |
| 363 | if (err) { |
| 364 | pr_err("FPGA DOWNLOAD FAIL!!\n"); |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 365 | goto errout; |
| 366 | } |
| 367 | |
| 368 | pr_info("FPGA DOWNLOAD DONE <---\n"); |
| 369 | |
Devendra Naga | 92bf93f | 2014-10-25 02:01:54 +0530 | [diff] [blame] | 370 | return 0; |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 371 | |
| 372 | errout: |
| 373 | finish_driver(); |
| 374 | |
Devendra Naga | 92bf93f | 2014-10-25 02:01:54 +0530 | [diff] [blame] | 375 | return err; |
Insop Song | e7185c69 | 2014-01-20 23:47:06 -0800 | [diff] [blame] | 376 | } |
| 377 | |
| 378 | static void __exit gs_fpgaboot_exit(void) |
| 379 | { |
| 380 | finish_driver(); |
| 381 | pr_info("FPGA image download module removed\n"); |
| 382 | } |
| 383 | |
| 384 | module_init(gs_fpgaboot_init); |
| 385 | module_exit(gs_fpgaboot_exit); |
| 386 | |
| 387 | MODULE_AUTHOR("Insop Song"); |
| 388 | MODULE_DESCRIPTION("Xlinix FPGA firmware download"); |
| 389 | MODULE_LICENSE("GPL"); |