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