summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml10
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/base/firmware_loader/sysfs_upload.c1
-rw-r--r--drivers/firmware/Kconfig1
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/microchip/Kconfig12
-rw-r--r--drivers/firmware/microchip/Makefile3
-rw-r--r--drivers/firmware/microchip/mpfs-auto-update.c494
-rw-r--r--drivers/soc/microchip/Kconfig1
-rw-r--r--drivers/soc/microchip/mpfs-sys-controller.c33
-rw-r--r--include/linux/firmware.h2
-rw-r--r--include/soc/microchip/mpfs.h2
-rw-r--r--lib/test_firmware.c1
13 files changed, 559 insertions, 3 deletions
diff --git a/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml b/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml
index 365a9fed5914..a3fa04f3a1bd 100644
--- a/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml
+++ b/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml
@@ -26,6 +26,16 @@ properties:
compatible:
const: microchip,mpfs-sys-controller
+ microchip,bitstream-flash:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ The SPI flash connected to the system controller's QSPI controller.
+ The system controller may retrieve FPGA bitstreams from this flash to
+ perform In-Application Programming (IAP) or during device initialisation
+ for Auto Update. The MSS and system controller have separate QSPI
+ controllers and this flash is connected to both. Software running in the
+ MSS can write bitstreams to the flash.
+
required:
- compatible
- mboxes
diff --git a/MAINTAINERS b/MAINTAINERS
index 71315544f1d8..089b8183e43c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18638,6 +18638,7 @@ F: Documentation/devicetree/bindings/usb/microchip,mpfs-musb.yaml
F: arch/riscv/boot/dts/microchip/
F: drivers/char/hw_random/mpfs-rng.c
F: drivers/clk/microchip/clk-mpfs*.c
+F: drivers/firmware/microchip/mpfs-auto-update.c
F: drivers/i2c/busses/i2c-microchip-corei2c.c
F: drivers/mailbox/mailbox-mpfs.c
F: drivers/pci/controller/pcie-microchip-host.c
diff --git a/drivers/base/firmware_loader/sysfs_upload.c b/drivers/base/firmware_loader/sysfs_upload.c
index a0af8f5f13d8..829270067d16 100644
--- a/drivers/base/firmware_loader/sysfs_upload.c
+++ b/drivers/base/firmware_loader/sysfs_upload.c
@@ -27,6 +27,7 @@ static const char * const fw_upload_err_str[] = {
[FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size",
[FW_UPLOAD_ERR_RW_ERROR] = "read-write-error",
[FW_UPLOAD_ERR_WEAROUT] = "flash-wearout",
+ [FW_UPLOAD_ERR_FW_INVALID] = "firmware-invalid",
};
static const char *fw_upload_progress(struct device *dev,
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 4a98a859d44d..61eba7646edc 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -272,6 +272,7 @@ source "drivers/firmware/google/Kconfig"
source "drivers/firmware/efi/Kconfig"
source "drivers/firmware/imx/Kconfig"
source "drivers/firmware/meson/Kconfig"
+source "drivers/firmware/microchip/Kconfig"
source "drivers/firmware/psci/Kconfig"
source "drivers/firmware/qcom/Kconfig"
source "drivers/firmware/smccc/Kconfig"
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 5f9dab82e1a0..d305d66bc820 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -28,6 +28,7 @@ obj-y += arm_scmi/
obj-y += broadcom/
obj-y += cirrus/
obj-y += meson/
+obj-y += microchip/
obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
obj-y += efi/
obj-y += imx/
diff --git a/drivers/firmware/microchip/Kconfig b/drivers/firmware/microchip/Kconfig
new file mode 100644
index 000000000000..434b923e08c2
--- /dev/null
+++ b/drivers/firmware/microchip/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config POLARFIRE_SOC_AUTO_UPDATE
+ tristate "Microchip PolarFire SoC AUTO UPDATE"
+ depends on POLARFIRE_SOC_SYS_CTRL
+ select FW_LOADER
+ select FW_UPLOAD
+ help
+ Support for reprogramming PolarFire SoC from within Linux, using the
+ Auto Upgrade feature of the system controller.
+
+ If built as a module, it will be called mpfs-auto-update.
diff --git a/drivers/firmware/microchip/Makefile b/drivers/firmware/microchip/Makefile
new file mode 100644
index 000000000000..38796fd82893
--- /dev/null
+++ b/drivers/firmware/microchip/Makefile
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_POLARFIRE_SOC_AUTO_UPDATE) += mpfs-auto-update.o
diff --git a/drivers/firmware/microchip/mpfs-auto-update.c b/drivers/firmware/microchip/mpfs-auto-update.c
new file mode 100644
index 000000000000..81f5f62e34fc
--- /dev/null
+++ b/drivers/firmware/microchip/mpfs-auto-update.c
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Microchip Polarfire SoC "Auto Update" FPGA reprogramming.
+ *
+ * Documentation of this functionality is available in the "PolarFire® FPGA and
+ * PolarFire SoC FPGA Programming" User Guide.
+ *
+ * Copyright (c) 2022-2023 Microchip Corporation. All rights reserved.
+ *
+ * Author: Conor Dooley <conor.dooley@microchip.com>
+ */
+#include <linux/debugfs.h>
+#include <linux/firmware.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/platform_device.h>
+#include <linux/sizes.h>
+
+#include <soc/microchip/mpfs.h>
+
+#define AUTO_UPDATE_DEFAULT_MBOX_OFFSET 0u
+#define AUTO_UPDATE_DEFAULT_RESP_OFFSET 0u
+
+#define AUTO_UPDATE_FEATURE_CMD_OPCODE 0x05u
+#define AUTO_UPDATE_FEATURE_CMD_DATA_SIZE 0u
+#define AUTO_UPDATE_FEATURE_RESP_SIZE 33u
+#define AUTO_UPDATE_FEATURE_CMD_DATA NULL
+#define AUTO_UPDATE_FEATURE_ENABLED BIT(5)
+
+#define AUTO_UPDATE_AUTHENTICATE_CMD_OPCODE 0x22u
+#define AUTO_UPDATE_AUTHENTICATE_CMD_DATA_SIZE 0u
+#define AUTO_UPDATE_AUTHENTICATE_RESP_SIZE 1u
+#define AUTO_UPDATE_AUTHENTICATE_CMD_DATA NULL
+
+#define AUTO_UPDATE_PROGRAM_CMD_OPCODE 0x46u
+#define AUTO_UPDATE_PROGRAM_CMD_DATA_SIZE 0u
+#define AUTO_UPDATE_PROGRAM_RESP_SIZE 1u
+#define AUTO_UPDATE_PROGRAM_CMD_DATA NULL
+
+/*
+ * SPI Flash layout example:
+ * |------------------------------| 0x0000000
+ * | 1 KiB |
+ * | SPI "directories" |
+ * |------------------------------| 0x0000400
+ * | 1 MiB |
+ * | Reserved area |
+ * | Used for bitstream info |
+ * |------------------------------| 0x0100400
+ * | 20 MiB |
+ * | Golden Image |
+ * |------------------------------| 0x1500400
+ * | 20 MiB |
+ * | Auto Upgrade Image |
+ * |------------------------------| 0x2900400
+ * | 20 MiB |
+ * | Reserved for multi-image IAP |
+ * | Unused for Auto Upgrade |
+ * |------------------------------| 0x3D00400
+ * | ? B |
+ * | Unused |
+ * |------------------------------| 0x?
+ */
+#define AUTO_UPDATE_DIRECTORY_BASE 0u
+#define AUTO_UPDATE_DIRECTORY_WIDTH 4u
+#define AUTO_UPDATE_GOLDEN_INDEX 0u
+#define AUTO_UPDATE_UPGRADE_INDEX 1u
+#define AUTO_UPDATE_BLANK_INDEX 2u
+#define AUTO_UPDATE_GOLDEN_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_GOLDEN_INDEX)
+#define AUTO_UPDATE_UPGRADE_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_UPGRADE_INDEX)
+#define AUTO_UPDATE_BLANK_DIRECTORY (AUTO_UPDATE_DIRECTORY_WIDTH * AUTO_UPDATE_BLANK_INDEX)
+#define AUTO_UPDATE_DIRECTORY_SIZE SZ_1K
+#define AUTO_UPDATE_RESERVED_SIZE SZ_1M
+#define AUTO_UPDATE_BITSTREAM_BASE (AUTO_UPDATE_DIRECTORY_SIZE + AUTO_UPDATE_RESERVED_SIZE)
+
+#define AUTO_UPDATE_TIMEOUT_MS 60000
+
+struct mpfs_auto_update_priv {
+ struct mpfs_sys_controller *sys_controller;
+ struct device *dev;
+ struct mtd_info *flash;
+ struct fw_upload *fw_uploader;
+ struct completion programming_complete;
+ size_t size_per_bitstream;
+ bool cancel_request;
+};
+
+static enum fw_upload_err mpfs_auto_update_prepare(struct fw_upload *fw_uploader, const u8 *data,
+ u32 size)
+{
+ struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle;
+ size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE;
+
+ /*
+ * Verifying the Golden Image is idealistic. It will be evaluated
+ * against the currently programmed image and thus may fail - due to
+ * either rollback protection (if its an older version than that in use)
+ * or if the version is the same as that of the in-use image.
+ * Extracting the information as to why a failure occurred is not
+ * currently possible due to limitations of the system controller
+ * driver. If those are fixed, verification of the Golden Image should
+ * be added here.
+ */
+
+ priv->flash = mpfs_sys_controller_get_flash(priv->sys_controller);
+ if (!priv->flash)
+ return FW_UPLOAD_ERR_HW_ERROR;
+
+ erase_size = round_up(erase_size, (u64)priv->flash->erasesize);
+
+ /*
+ * We need to calculate if we have enough space in the flash for the
+ * new image.
+ * First, chop off the first 1 KiB as it's reserved for the directory.
+ * The 1 MiB reserved for design info needs to be ignored also.
+ * All that remains is carved into 3 & rounded down to the erasesize.
+ * If this is smaller than the image size, we abort.
+ * There's also no need to consume more than 20 MiB per image.
+ */
+ priv->size_per_bitstream = priv->flash->size - SZ_1K - SZ_1M;
+ priv->size_per_bitstream = round_down(priv->size_per_bitstream / 3, erase_size);
+ if (priv->size_per_bitstream > 20 * SZ_1M)
+ priv->size_per_bitstream = 20 * SZ_1M;
+
+ if (priv->size_per_bitstream < size) {
+ dev_err(priv->dev,
+ "flash device has insufficient capacity to store this bitstream\n");
+ return FW_UPLOAD_ERR_INVALID_SIZE;
+ }
+
+ priv->cancel_request = false;
+
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static void mpfs_auto_update_cancel(struct fw_upload *fw_uploader)
+{
+ struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle;
+
+ priv->cancel_request = true;
+}
+
+static enum fw_upload_err mpfs_auto_update_poll_complete(struct fw_upload *fw_uploader)
+{
+ struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle;
+ int ret;
+
+ /*
+ * There is no meaningful way to get the status of the programming while
+ * it is in progress, so attempting anything other than waiting for it
+ * to complete would be misplaced.
+ */
+ ret = wait_for_completion_timeout(&priv->programming_complete,
+ msecs_to_jiffies(AUTO_UPDATE_TIMEOUT_MS));
+ if (ret)
+ return FW_UPLOAD_ERR_TIMEOUT;
+
+ return FW_UPLOAD_ERR_NONE;
+}
+
+static int mpfs_auto_update_verify_image(struct fw_upload *fw_uploader)
+{
+ struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle;
+ struct mpfs_mss_response *response;
+ struct mpfs_mss_msg *message;
+ u32 *response_msg;
+ int ret;
+
+ response_msg = devm_kzalloc(priv->dev, AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(response_msg),
+ GFP_KERNEL);
+ if (!response_msg)
+ return -ENOMEM;
+
+ response = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_response), GFP_KERNEL);
+ if (!response) {
+ ret = -ENOMEM;
+ goto free_response_msg;
+ }
+
+ message = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_msg), GFP_KERNEL);
+ if (!message) {
+ ret = -ENOMEM;
+ goto free_response;
+ }
+
+ /*
+ * The system controller can verify that an image in the flash is valid.
+ * Rather than duplicate the check in this driver, call the relevant
+ * service from the system controller instead.
+ * This service has no command data and no response data. It overloads
+ * mbox_offset with the image index in the flash's SPI directory where
+ * the bitstream is located.
+ */
+ response->resp_msg = response_msg;
+ response->resp_size = AUTO_UPDATE_AUTHENTICATE_RESP_SIZE;
+ message->cmd_opcode = AUTO_UPDATE_AUTHENTICATE_CMD_OPCODE;
+ message->cmd_data_size = AUTO_UPDATE_AUTHENTICATE_CMD_DATA_SIZE;
+ message->response = response;
+ message->cmd_data = AUTO_UPDATE_AUTHENTICATE_CMD_DATA;
+ message->mbox_offset = AUTO_UPDATE_UPGRADE_INDEX;
+ message->resp_offset = AUTO_UPDATE_DEFAULT_RESP_OFFSET;
+
+ dev_info(priv->dev, "Running verification of Upgrade Image\n");
+ ret = mpfs_blocking_transaction(priv->sys_controller, message);
+ if (ret | response->resp_status) {
+ dev_warn(priv->dev, "Verification of Upgrade Image failed!\n");
+ ret = ret ? ret : -EBADMSG;
+ }
+
+ dev_info(priv->dev, "Verification of Upgrade Image passed!\n");
+
+ devm_kfree(priv->dev, message);
+free_response:
+ devm_kfree(priv->dev, response);
+free_response_msg:
+ devm_kfree(priv->dev, response_msg);
+
+ return ret;
+}
+
+static int mpfs_auto_update_set_image_address(struct mpfs_auto_update_priv *priv, char *buffer,
+ u32 image_address, loff_t directory_address)
+{
+ struct erase_info erase;
+ size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE;
+ size_t bytes_written = 0, bytes_read = 0;
+ int ret;
+
+ erase_size = round_up(erase_size, (u64)priv->flash->erasesize);
+
+ erase.addr = AUTO_UPDATE_DIRECTORY_BASE;
+ erase.len = erase_size;
+
+ /*
+ * We need to write the "SPI DIRECTORY" to the first 1 KiB, telling
+ * the system controller where to find the actual bitstream. Since
+ * this is spi-nor, we have to read the first eraseblock, erase that
+ * portion of the flash, modify the data and then write it back.
+ * There's no need to do this though if things are already the way they
+ * should be, so check and save the write in that case.
+ */
+ ret = mtd_read(priv->flash, AUTO_UPDATE_DIRECTORY_BASE, erase_size, &bytes_read,
+ (u_char *)buffer);
+ if (ret)
+ return ret;
+
+ if (bytes_read != erase_size)
+ return -EIO;
+
+ if ((*(u32 *)(buffer + AUTO_UPDATE_UPGRADE_DIRECTORY) == image_address) &&
+ !(*(u32 *)(buffer + AUTO_UPDATE_BLANK_DIRECTORY)))
+ return 0;
+
+ ret = mtd_erase(priv->flash, &erase);
+ if (ret)
+ return ret;
+
+ /*
+ * Populate the image address and then zero out the next directory so
+ * that the system controller doesn't complain if in "Single Image"
+ * mode.
+ */
+ memcpy(buffer + AUTO_UPDATE_UPGRADE_DIRECTORY, &image_address,
+ AUTO_UPDATE_DIRECTORY_WIDTH);
+ memset(buffer + AUTO_UPDATE_BLANK_DIRECTORY, 0x0, AUTO_UPDATE_DIRECTORY_WIDTH);
+
+ dev_info(priv->dev, "Writing the image address (%x) to the flash directory (%llx)\n",
+ image_address, directory_address);
+
+ ret = mtd_write(priv->flash, 0x0, erase_size, &bytes_written, (u_char *)buffer);
+ if (ret)
+ return ret;
+
+ if (bytes_written != erase_size)
+ return ret;
+
+ return 0;
+}
+
+static int mpfs_auto_update_write_bitstream(struct fw_upload *fw_uploader, const u8 *data,
+ u32 offset, u32 size, u32 *written)
+{
+ struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle;
+ struct erase_info erase;
+ char *buffer;
+ loff_t directory_address = AUTO_UPDATE_UPGRADE_DIRECTORY;
+ size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE;
+ size_t bytes_written = 0;
+ u32 image_address;
+ int ret;
+
+ erase_size = round_up(erase_size, (u64)priv->flash->erasesize);
+
+ image_address = AUTO_UPDATE_BITSTREAM_BASE +
+ AUTO_UPDATE_UPGRADE_INDEX * priv->size_per_bitstream;
+
+ buffer = devm_kzalloc(priv->dev, erase_size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+
+ ret = mpfs_auto_update_set_image_address(priv, buffer, image_address, directory_address);
+ if (ret) {
+ dev_err(priv->dev, "failed to set image address in the SPI directory: %d\n", ret);
+ goto out;
+ }
+
+ /*
+ * Now the .spi image itself can be written to the flash. Preservation
+ * of contents here is not important here, unlike the spi "directory"
+ * which must be RMWed.
+ */
+ erase.len = round_up(size, (size_t)priv->flash->erasesize);
+ erase.addr = image_address;
+
+ dev_info(priv->dev, "Erasing the flash at address (%x)\n", image_address);
+ ret = mtd_erase(priv->flash, &erase);
+ if (ret)
+ goto out;
+
+ /*
+ * No parsing etc of the bitstream is required. The system controller
+ * will do all of that itself - including verifying that the bitstream
+ * is valid.
+ */
+ dev_info(priv->dev, "Writing the image to the flash at address (%x)\n", image_address);
+ ret = mtd_write(priv->flash, (loff_t)image_address, size, &bytes_written, data);
+ if (ret)
+ goto out;
+
+ if (bytes_written != size) {
+ ret = -EIO;
+ goto out;
+ }
+
+ *written = bytes_written;
+
+out:
+ devm_kfree(priv->dev, buffer);
+ return ret;
+}
+
+static enum fw_upload_err mpfs_auto_update_write(struct fw_upload *fw_uploader, const u8 *data,
+ u32 offset, u32 size, u32 *written)
+{
+ struct mpfs_auto_update_priv *priv = fw_uploader->dd_handle;
+ enum fw_upload_err err = FW_UPLOAD_ERR_NONE;
+ int ret;
+
+ reinit_completion(&priv->programming_complete);
+
+ ret = mpfs_auto_update_write_bitstream(fw_uploader, data, offset, size, written);
+ if (ret) {
+ err = FW_UPLOAD_ERR_RW_ERROR;
+ goto out;
+ }
+
+ if (priv->cancel_request) {
+ err = FW_UPLOAD_ERR_CANCELED;
+ goto out;
+ }
+
+ ret = mpfs_auto_update_verify_image(fw_uploader);
+ if (ret)
+ err = FW_UPLOAD_ERR_FW_INVALID;
+
+out:
+ complete(&priv->programming_complete);
+
+ return err;
+}
+
+static const struct fw_upload_ops mpfs_auto_update_ops = {
+ .prepare = mpfs_auto_update_prepare,
+ .write = mpfs_auto_update_write,
+ .poll_complete = mpfs_auto_update_poll_complete,
+ .cancel = mpfs_auto_update_cancel,
+};
+
+static int mpfs_auto_update_available(struct mpfs_auto_update_priv *priv)
+{
+ struct mpfs_mss_response *response;
+ struct mpfs_mss_msg *message;
+ u32 *response_msg;
+ int ret;
+
+ response_msg = devm_kzalloc(priv->dev, AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(response_msg),
+ GFP_KERNEL);
+ if (!response_msg)
+ return -ENOMEM;
+
+ response = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_response), GFP_KERNEL);
+ if (!response)
+ return -ENOMEM;
+
+ message = devm_kzalloc(priv->dev, sizeof(struct mpfs_mss_msg), GFP_KERNEL);
+ if (!message)
+ return -ENOMEM;
+
+ /*
+ * To verify that Auto Update is possible, the "Query Security Service
+ * Request" is performed.
+ * This service has no command data & does not overload mbox_offset.
+ */
+ response->resp_msg = response_msg;
+ response->resp_size = AUTO_UPDATE_FEATURE_RESP_SIZE;
+ message->cmd_opcode = AUTO_UPDATE_FEATURE_CMD_OPCODE;
+ message->cmd_data_size = AUTO_UPDATE_FEATURE_CMD_DATA_SIZE;
+ message->response = response;
+ message->cmd_data = AUTO_UPDATE_FEATURE_CMD_DATA;
+ message->mbox_offset = AUTO_UPDATE_DEFAULT_MBOX_OFFSET;
+ message->resp_offset = AUTO_UPDATE_DEFAULT_RESP_OFFSET;
+
+ ret = mpfs_blocking_transaction(priv->sys_controller, message);
+ if (ret)
+ return ret;
+
+ /*
+ * Currently, the system controller's firmware does not generate any
+ * interrupts for failed services, so mpfs_blocking_transaction() should
+ * time out & therefore return an error.
+ * Hitting this check is highly unlikely at present, but if the system
+ * controller's behaviour changes so that it does generate interrupts
+ * for failed services, it will be required.
+ */
+ if (response->resp_status)
+ return -EIO;
+
+ /*
+ * Bit 5 of byte 1 is "UL_Auto Update" & if it is set, Auto Update is
+ * not possible.
+ */
+ if (response_msg[1] & AUTO_UPDATE_FEATURE_ENABLED)
+ return -EPERM;
+
+ return 0;
+}
+
+static int mpfs_auto_update_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mpfs_auto_update_priv *priv;
+ struct fw_upload *fw_uploader;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->sys_controller = mpfs_sys_controller_get(dev);
+ if (IS_ERR(priv->sys_controller))
+ return dev_err_probe(dev, PTR_ERR(priv->sys_controller),
+ "Could not register as a sub device of the system controller\n");
+
+ priv->dev = dev;
+ platform_set_drvdata(pdev, priv);
+
+ ret = mpfs_auto_update_available(priv);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "The current bitstream does not support auto-update\n");
+
+ init_completion(&priv->programming_complete);
+
+ fw_uploader = firmware_upload_register(THIS_MODULE, dev, "mpfs-auto-update",
+ &mpfs_auto_update_ops, priv);
+ if (IS_ERR(fw_uploader))
+ return dev_err_probe(dev, PTR_ERR(fw_uploader),
+ "Failed to register the bitstream uploader\n");
+
+ priv->fw_uploader = fw_uploader;
+
+ return 0;
+}
+
+static void mpfs_auto_update_remove(struct platform_device *pdev)
+{
+ struct mpfs_auto_update_priv *priv = platform_get_drvdata(pdev);
+
+ firmware_upload_unregister(priv->fw_uploader);
+}
+
+static struct platform_driver mpfs_auto_update_driver = {
+ .driver = {
+ .name = "mpfs-auto-update",
+ },
+ .probe = mpfs_auto_update_probe,
+ .remove_new = mpfs_auto_update_remove,
+};
+module_platform_driver(mpfs_auto_update_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
+MODULE_DESCRIPTION("PolarFire SoC Auto Update FPGA reprogramming");
diff --git a/drivers/soc/microchip/Kconfig b/drivers/soc/microchip/Kconfig
index eb656b33156b..9b0fdd95276e 100644
--- a/drivers/soc/microchip/Kconfig
+++ b/drivers/soc/microchip/Kconfig
@@ -1,6 +1,7 @@
config POLARFIRE_SOC_SYS_CTRL
tristate "POLARFIRE_SOC_SYS_CTRL"
depends on POLARFIRE_SOC_MAILBOX
+ depends on MTD
help
This driver adds support for the PolarFire SoC (MPFS) system controller.
diff --git a/drivers/soc/microchip/mpfs-sys-controller.c b/drivers/soc/microchip/mpfs-sys-controller.c
index 0935e9e94172..7a4936019329 100644
--- a/drivers/soc/microchip/mpfs-sys-controller.c
+++ b/drivers/soc/microchip/mpfs-sys-controller.c
@@ -12,6 +12,8 @@
#include <linux/kref.h>
#include <linux/module.h>
#include <linux/jiffies.h>
+#include <linux/mtd/mtd.h>
+#include <linux/spi/spi.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/mailbox_client.h>
@@ -30,6 +32,7 @@ struct mpfs_sys_controller {
struct mbox_client client;
struct mbox_chan *chan;
struct completion c;
+ struct mtd_info *flash;
struct kref consumers;
};
@@ -63,7 +66,9 @@ int mpfs_blocking_transaction(struct mpfs_sys_controller *sys_controller, struct
*/
if (!wait_for_completion_timeout(&sys_controller->c, timeout)) {
ret = -EBADMSG;
- dev_warn(sys_controller->client.dev, "MPFS sys controller service failed\n");
+ dev_warn(sys_controller->client.dev,
+ "MPFS sys controller service failed with status: %d\n",
+ msg->response->resp_status);
} else {
ret = 0;
}
@@ -99,6 +104,12 @@ static void mpfs_sys_controller_put(void *data)
kref_put(&sys_controller->consumers, mpfs_sys_controller_delete);
}
+struct mtd_info *mpfs_sys_controller_get_flash(struct mpfs_sys_controller *mpfs_client)
+{
+ return mpfs_client->flash;
+}
+EXPORT_SYMBOL(mpfs_sys_controller_get_flash);
+
static struct platform_device subdevs[] = {
{
.name = "mpfs-rng",
@@ -107,19 +118,34 @@ static struct platform_device subdevs[] = {
{
.name = "mpfs-generic-service",
.id = -1,
- }
+ },
+ {
+ .name = "mpfs-auto-update",
+ .id = -1,
+ },
};
static int mpfs_sys_controller_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mpfs_sys_controller *sys_controller;
+ struct device_node *np;
int i, ret;
sys_controller = kzalloc(sizeof(*sys_controller), GFP_KERNEL);
if (!sys_controller)
return -ENOMEM;
+ np = of_parse_phandle(dev->of_node, "microchip,bitstream-flash", 0);
+ if (!np)
+ goto no_flash;
+
+ sys_controller->flash = of_get_mtd_device_by_node(np);
+ of_node_put(np);
+ if (IS_ERR(sys_controller->flash))
+ return dev_err_probe(dev, PTR_ERR(sys_controller->flash), "Failed to get flash\n");
+
+no_flash:
sys_controller->client.dev = dev;
sys_controller->client.rx_callback = mpfs_sys_controller_rx_callback;
sys_controller->client.tx_block = 1U;
@@ -138,7 +164,6 @@ static int mpfs_sys_controller_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, sys_controller);
- dev_info(&pdev->dev, "Registered MPFS system controller\n");
for (i = 0; i < ARRAY_SIZE(subdevs); i++) {
subdevs[i].dev.parent = dev;
@@ -146,6 +171,8 @@ static int mpfs_sys_controller_probe(struct platform_device *pdev)
dev_warn(dev, "Error registering sub device %s\n", subdevs[i].name);
}
+ dev_info(&pdev->dev, "Registered MPFS system controller\n");
+
return 0;
}
diff --git a/include/linux/firmware.h b/include/linux/firmware.h
index de7fea3bca51..0311858b46ce 100644
--- a/include/linux/firmware.h
+++ b/include/linux/firmware.h
@@ -27,6 +27,7 @@ struct firmware {
* @FW_UPLOAD_ERR_INVALID_SIZE: invalid firmware image size
* @FW_UPLOAD_ERR_RW_ERROR: read or write to HW failed, see kernel log
* @FW_UPLOAD_ERR_WEAROUT: FLASH device is approaching wear-out, wait & retry
+ * @FW_UPLOAD_ERR_FW_INVALID: invalid firmware file
* @FW_UPLOAD_ERR_MAX: Maximum error code marker
*/
enum fw_upload_err {
@@ -38,6 +39,7 @@ enum fw_upload_err {
FW_UPLOAD_ERR_INVALID_SIZE,
FW_UPLOAD_ERR_RW_ERROR,
FW_UPLOAD_ERR_WEAROUT,
+ FW_UPLOAD_ERR_FW_INVALID,
FW_UPLOAD_ERR_MAX
};
diff --git a/include/soc/microchip/mpfs.h b/include/soc/microchip/mpfs.h
index f916dcde457f..09722f83b0ca 100644
--- a/include/soc/microchip/mpfs.h
+++ b/include/soc/microchip/mpfs.h
@@ -38,6 +38,8 @@ int mpfs_blocking_transaction(struct mpfs_sys_controller *mpfs_client, struct mp
struct mpfs_sys_controller *mpfs_sys_controller_get(struct device *dev);
+struct mtd_info *mpfs_sys_controller_get_flash(struct mpfs_sys_controller *mpfs_client);
+
#endif /* if IS_ENABLED(CONFIG_POLARFIRE_SOC_SYS_CTRL) */
#if IS_ENABLED(CONFIG_MCHP_CLK_MPFS)
diff --git a/lib/test_firmware.c b/lib/test_firmware.c
index add4699fc6cd..9cfdcd6d21db 100644
--- a/lib/test_firmware.c
+++ b/lib/test_firmware.c
@@ -1132,6 +1132,7 @@ static const char * const fw_upload_err_str[] = {
[FW_UPLOAD_ERR_INVALID_SIZE] = "invalid-file-size",
[FW_UPLOAD_ERR_RW_ERROR] = "read-write-error",
[FW_UPLOAD_ERR_WEAROUT] = "flash-wearout",
+ [FW_UPLOAD_ERR_FW_INVALID] = "firmware-invalid",
};
static void upload_err_inject_error(struct test_firmware_upload *tst,