diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2024-07-16 11:35:27 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2024-07-16 11:35:27 -0700 | 
| commit | cc0f7c3f97bc6e888bf4be28a9da9dbd3735d2b4 (patch) | |
| tree | ec328d17e2b9ddff11579e8759b9922b4e7a7f48 /drivers/firmware/microchip/mpfs-auto-update.c | |
| parent | 99298eb615debd41c1fccff05b163d0a29091904 (diff) | |
| parent | 49e24c80d3c81c43e2a56101449e1eea32fcf292 (diff) | |
Merge tag 'soc-drivers-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull SoC driver updates from Arnd Bergmann:
 "The updates to the mediatek, allwinner, ti, tegra, microchip, stm32,
  samsung, imx, zynq and amlogic platoforms are fairly small maintenance
  changes, either addressing minor mistakes or enabling additional
  hardware.
  The qualcomm platform changes add a number of features and are larger
  than the other ones combined, introducing the use of linux/cleanup.h
  across several drivers, adding support for Snapdragon X1E and other
  SoCs in platform drivers, a new "protection domain mapper" driver, and
  a "shared memory bridge" driver.
  The cznic "turris omnia" router based on Marvell Armada gets a
  platform driver that talks to the board specific microcontroller.
  The reset and cache subsystems get a few minor updates to SoC specific
  drivers, while the ff-a, scmi and optee firmware drivers get some code
  refactoring and new features"
* tag 'soc-drivers-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (122 commits)
  firmware: turris-mox-rwtm: Initialize completion before mailbox
  firmware: turris-mox-rwtm: Fix checking return value of wait_for_completion_timeout()
  firmware: turris-mox-rwtm: Do not complete if there are no waiters
  MAINTAINERS: drop riscv list from cache controllers
  platform: cznic: turris-omnia-mcu: fix Kconfig dependencies
  bus: sunxi-rsb: Constify struct regmap_bus
  soc: sunxi: sram: Constify struct regmap_config
  platform: cznic: turris-omnia-mcu: Depend on WATCHDOG
  platform: cznic: turris-omnia-mcu: Depend on OF
  soc: samsung: exynos-pmu: add support for PMU_ALIVE non atomic registers
  arm64: stm32: enable scmi regulator for stm32
  firmware: qcom: tzmem: blacklist more platforms for SHM Bridge
  soc: qcom: wcnss: simplify with cleanup.h
  soc: qcom: pdr: simplify with cleanup.h
  soc: qcom: ocmem: simplify with cleanup.h
  soc: qcom: mdt_loader: simplify with cleanup.h
  soc: qcom: llcc: simplify with cleanup.h
  firmware: qcom: tzmem: simplify returning pointer without cleanup
  soc: qcom: socinfo: Add PM6350 PMIC
  arm64: dts: renesas: rz-smarc: Replace fixed regulator for USB VBUS
  ...
Diffstat (limited to 'drivers/firmware/microchip/mpfs-auto-update.c')
| -rw-r--r-- | drivers/firmware/microchip/mpfs-auto-update.c | 136 | 
1 files changed, 67 insertions, 69 deletions
diff --git a/drivers/firmware/microchip/mpfs-auto-update.c b/drivers/firmware/microchip/mpfs-auto-update.c index 835a19a7a3a0..30de47895b1c 100644 --- a/drivers/firmware/microchip/mpfs-auto-update.c +++ b/drivers/firmware/microchip/mpfs-auto-update.c @@ -9,6 +9,7 @@   *   * Author: Conor Dooley <conor.dooley@microchip.com>   */ +#include <linux/cleanup.h>  #include <linux/debugfs.h>  #include <linux/firmware.h>  #include <linux/math.h> @@ -71,8 +72,9 @@  #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_INFO_BASE		AUTO_UPDATE_DIRECTORY_SIZE +#define AUTO_UPDATE_INFO_SIZE		SZ_1M +#define AUTO_UPDATE_BITSTREAM_BASE	(AUTO_UPDATE_DIRECTORY_SIZE + AUTO_UPDATE_INFO_SIZE)  #define AUTO_UPDATE_TIMEOUT_MS		60000 @@ -86,6 +88,17 @@ struct mpfs_auto_update_priv {  	bool cancel_request;  }; +static bool mpfs_auto_update_is_bitstream_info(const u8 *data, u32 size) +{ +	if (size < 4) +		return false; + +	if (data[0] == 0x4d && data[1] == 0x43 && data[2] == 0x48 && data[3] == 0x50) +		return true; + +	return false; +} +  static enum fw_upload_err mpfs_auto_update_prepare(struct fw_upload *fw_uploader, const u8 *data,  						   u32 size)  { @@ -162,28 +175,17 @@ static enum fw_upload_err mpfs_auto_update_poll_complete(struct fw_upload *fw_up  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; +	u32 *response_msg __free(kfree) = +		kzalloc(AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(*response_msg), GFP_KERNEL); +	struct mpfs_mss_response *response __free(kfree) = +		kzalloc(sizeof(struct mpfs_mss_response), GFP_KERNEL); +	struct mpfs_mss_msg *message __free(kfree) = +		kzalloc(sizeof(struct mpfs_mss_msg), GFP_KERNEL);  	int ret; -	response_msg = devm_kzalloc(priv->dev, AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(*response_msg), -				    GFP_KERNEL); -	if (!response_msg) +	if (!response_msg || !response || !message)  		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 @@ -205,31 +207,25 @@ static int mpfs_auto_update_verify_image(struct fw_upload *fw_uploader)  	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; -		goto free_message; +		return ret ? ret : -EBADMSG;  	}  	dev_info(priv->dev, "Verification of Upgrade Image passed!\n"); -free_message: -	devm_kfree(priv->dev, message); -free_response: -	devm_kfree(priv->dev, response); -free_response_msg: -	devm_kfree(priv->dev, response_msg); - -	return ret; +	return 0;  } -static int mpfs_auto_update_set_image_address(struct mpfs_auto_update_priv *priv, char *buffer, +static int mpfs_auto_update_set_image_address(struct mpfs_auto_update_priv *priv,  					      u32 image_address, loff_t directory_address)  {  	struct erase_info erase; -	size_t erase_size = AUTO_UPDATE_DIRECTORY_SIZE; +	size_t erase_size = round_up(AUTO_UPDATE_DIRECTORY_SIZE, (u64)priv->flash->erasesize);  	size_t bytes_written = 0, bytes_read = 0; +	char *buffer __free(kfree) = kzalloc(erase_size, GFP_KERNEL);  	int ret; -	erase_size = round_up(erase_size, (u64)priv->flash->erasesize); +	if (!buffer) +		return -ENOMEM;  	erase.addr = AUTO_UPDATE_DIRECTORY_BASE;  	erase.len = erase_size; @@ -275,7 +271,7 @@ static int mpfs_auto_update_set_image_address(struct mpfs_auto_update_priv *priv  		return ret;  	if (bytes_written != erase_size) -		return ret; +		return -EIO;  	return 0;  } @@ -285,26 +281,36 @@ static int mpfs_auto_update_write_bitstream(struct fw_upload *fw_uploader, const  {  	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; +	bool is_info = mpfs_auto_update_is_bitstream_info(data, size);  	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; +	if (is_info) +		image_address = AUTO_UPDATE_INFO_BASE; +	else +		image_address = AUTO_UPDATE_BITSTREAM_BASE + +				AUTO_UPDATE_UPGRADE_INDEX * priv->size_per_bitstream; -	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; +	/* +	 * For bitstream info, the descriptor is written to a fixed offset, +	 * so there is no need to set the image address. +	 */ +	if (!is_info) { +		ret = mpfs_auto_update_set_image_address(priv, image_address, directory_address); +		if (ret) { +			dev_err(priv->dev, "failed to set image address in the SPI directory: %d\n", ret); +			return ret; +		} +	} else { +		if (size > AUTO_UPDATE_INFO_SIZE) { +			dev_err(priv->dev, "bitstream info exceeds permitted size\n"); +			return -ENOSPC; +		}  	}  	/* @@ -318,7 +324,7 @@ static int mpfs_auto_update_write_bitstream(struct fw_upload *fw_uploader, const  	dev_info(priv->dev, "Erasing the flash at address (0x%x)\n", image_address);  	ret = mtd_erase(priv->flash, &erase);  	if (ret) -		goto out; +		return ret;  	/*  	 * No parsing etc of the bitstream is required. The system controller @@ -328,18 +334,15 @@ static int mpfs_auto_update_write_bitstream(struct fw_upload *fw_uploader, const  	dev_info(priv->dev, "Writing the image to the flash at address (0x%x)\n", image_address);  	ret = mtd_write(priv->flash, (loff_t)image_address, size, &bytes_written, data);  	if (ret) -		goto out; +		return ret; -	if (bytes_written != size) { -		ret = -EIO; -		goto out; -	} +	if (bytes_written != size) +		return -EIO;  	*written = bytes_written; +	dev_info(priv->dev, "Wrote 0x%zx bytes to the flash\n", bytes_written); -out: -	devm_kfree(priv->dev, buffer); -	return ret; +	return 0;  }  static enum fw_upload_err mpfs_auto_update_write(struct fw_upload *fw_uploader, const u8 *data, @@ -362,6 +365,9 @@ static enum fw_upload_err mpfs_auto_update_write(struct fw_upload *fw_uploader,  		goto out;  	} +	if (mpfs_auto_update_is_bitstream_info(data, size)) +		goto out; +  	ret = mpfs_auto_update_verify_image(fw_uploader);  	if (ret)  		err = FW_UPLOAD_ERR_FW_INVALID; @@ -381,23 +387,15 @@ static const struct fw_upload_ops mpfs_auto_update_ops = {  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; +	u32 *response_msg __free(kfree) = +		kzalloc(AUTO_UPDATE_FEATURE_RESP_SIZE * sizeof(*response_msg), GFP_KERNEL); +	struct mpfs_mss_response *response __free(kfree) = +		kzalloc(sizeof(struct mpfs_mss_response), GFP_KERNEL); +	struct mpfs_mss_msg *message __free(kfree) = +		kzalloc(sizeof(struct mpfs_mss_msg), GFP_KERNEL);  	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) +	if (!response_msg || !response || !message)  		return -ENOMEM;  	/*  | 
