diff options
-rw-r--r-- | Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-sys-controller.yaml | 10 | ||||
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | drivers/base/firmware_loader/sysfs_upload.c | 1 | ||||
-rw-r--r-- | drivers/firmware/Kconfig | 1 | ||||
-rw-r--r-- | drivers/firmware/Makefile | 1 | ||||
-rw-r--r-- | drivers/firmware/microchip/Kconfig | 12 | ||||
-rw-r--r-- | drivers/firmware/microchip/Makefile | 3 | ||||
-rw-r--r-- | drivers/firmware/microchip/mpfs-auto-update.c | 494 | ||||
-rw-r--r-- | drivers/soc/microchip/Kconfig | 1 | ||||
-rw-r--r-- | drivers/soc/microchip/mpfs-sys-controller.c | 33 | ||||
-rw-r--r-- | include/linux/firmware.h | 2 | ||||
-rw-r--r-- | include/soc/microchip/mpfs.h | 2 | ||||
-rw-r--r-- | lib/test_firmware.c | 1 |
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, |