Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

firmware: microchip: support writing bitstream info to flash

Updating the FPGA image might bring with it changes visible to Linux,
so it is helpful to also co-locate dt-overlays that describe the new
image contents. If these are packaged in a specific format [1] (detected
by first 4 bytes being MCHP, since FPGA images have no magic), load
the file to the reserved 1 MiB region immediately after the directory in
flash. The Beagle-V Fire's "gateware" already creates these files and
puts them in flash [2].

Link: https://github.com/polarfire-soc/polarfire-soc-documentation/blob/master/how-to/re-programming-the-fpga-from-linux.md#main-header-format
Link: https://openbeagle.org/beaglev-fire/gateware/-/blob/main/gateware_scripts/generate_gateware_overlays.py?ref_type=heads [2]
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>

+39 -8
+39 -8
drivers/firmware/microchip/mpfs-auto-update.c
··· 71 71 #define AUTO_UPDATE_UPGRADE_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_UPGRADE_INDEX) 72 72 #define AUTO_UPDATE_BLANK_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_BLANK_INDEX) 73 73 #define AUTO_UPDATE_DIRECTORY_SIZE SZ_1K 74 - #define AUTO_UPDATE_RESERVED_SIZE SZ_1M 75 - #define AUTO_UPDATE_BITSTREAM_BASE (AUTO_UPDATE_DIRECTORY_SIZE + AUTO_UPDATE_RESERVED_SIZE) 74 + #define AUTO_UPDATE_INFO_BASE AUTO_UPDATE_DIRECTORY_SIZE 75 + #define AUTO_UPDATE_INFO_SIZE SZ_1M 76 + #define AUTO_UPDATE_BITSTREAM_BASE (AUTO_UPDATE_DIRECTORY_SIZE + AUTO_UPDATE_INFO_SIZE) 76 77 77 78 #define AUTO_UPDATE_TIMEOUT_MS 60000 78 79 ··· 86 85 size_t size_per_bitstream; 87 86 bool cancel_request; 88 87 }; 88 + 89 + static bool mpfs_auto_update_is_bitstream_info(const u8 *data, u32 size) 90 + { 91 + if (size < 4) 92 + return false; 93 + 94 + if (data[0] == 0x4d && data[1] == 0x43 && data[2] == 0x48 && data[3] == 0x50) 95 + return true; 96 + 97 + return false; 98 + } 89 99 90 100 static enum fw_upload_err mpfs_auto_update_prepare(struct fw_upload *fw_uploader, const u8 *data, 91 101 u32 size) ··· 301 289 loff_t directory_address = AUTO_UPDATE_UPGRADE_DIRECTORY; 302 290 size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE; 303 291 size_t bytes_written = 0; 292 + bool is_info = mpfs_auto_update_is_bitstream_info(data, size); 304 293 u32 image_address; 305 294 int ret; 306 295 307 296 erase_size = round_up(erase_size, (u64)priv->flash->erasesize); 308 297 309 - image_address = AUTO_UPDATE_BITSTREAM_BASE + 310 - AUTO_UPDATE_UPGRADE_INDEX * priv->size_per_bitstream; 298 + if (is_info) 299 + image_address = AUTO_UPDATE_INFO_BASE; 300 + else 301 + image_address = AUTO_UPDATE_BITSTREAM_BASE + 302 + AUTO_UPDATE_UPGRADE_INDEX * priv->size_per_bitstream; 311 303 312 304 buffer = devm_kzalloc(priv->dev, erase_size, GFP_KERNEL); 313 305 if (!buffer) 314 306 return -ENOMEM; 315 307 316 - ret = mpfs_auto_update_set_image_address(priv, buffer, image_address, directory_address); 317 - if (ret) { 318 - dev_err(priv->dev, "failed to set image address in the SPI directory: %d\n", ret); 319 - goto out; 308 + /* 309 + * For bitstream info, the descriptor is written to a fixed offset, 310 + * so there is no need to set the image address. 311 + */ 312 + if (!is_info) { 313 + ret = mpfs_auto_update_set_image_address(priv, buffer, image_address, directory_address); 314 + if (ret) { 315 + dev_err(priv->dev, "failed to set image address in the SPI directory: %d\n", ret); 316 + return ret; 317 + } 318 + } else { 319 + if (size > AUTO_UPDATE_INFO_SIZE) { 320 + dev_err(priv->dev, "bitstream info exceeds permitted size\n"); 321 + return -ENOSPC; 322 + } 320 323 } 321 324 322 325 /* ··· 363 336 } 364 337 365 338 *written = bytes_written; 339 + dev_info(priv->dev, "Wrote 0x%zx bytes to the flash\n", bytes_written); 366 340 367 341 out: 368 342 devm_kfree(priv->dev, buffer); ··· 389 361 err = FW_UPLOAD_ERR_CANCELED; 390 362 goto out; 391 363 } 364 + 365 + if (mpfs_auto_update_is_bitstream_info(data, size)) 366 + goto out; 392 367 393 368 ret = mpfs_auto_update_verify_image(fw_uploader); 394 369 if (ret)