18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2014 Advanced Micro Devices, Inc.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
58c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
68c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
78c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
88c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
98c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
128c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
158c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
168c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
178c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
188c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
198c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
208c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
218c2ecf20Sopenharmony_ci *
228c2ecf20Sopenharmony_ci */
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <linux/firmware.h>
258c2ecf20Sopenharmony_ci#include <linux/slab.h>
268c2ecf20Sopenharmony_ci#include <linux/module.h>
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include "radeon.h"
298c2ecf20Sopenharmony_ci#include "radeon_ucode.h"
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic void radeon_ucode_print_common_hdr(const struct common_firmware_header *hdr)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	DRM_DEBUG("size_bytes: %u\n", le32_to_cpu(hdr->size_bytes));
348c2ecf20Sopenharmony_ci	DRM_DEBUG("header_size_bytes: %u\n", le32_to_cpu(hdr->header_size_bytes));
358c2ecf20Sopenharmony_ci	DRM_DEBUG("header_version_major: %u\n", le16_to_cpu(hdr->header_version_major));
368c2ecf20Sopenharmony_ci	DRM_DEBUG("header_version_minor: %u\n", le16_to_cpu(hdr->header_version_minor));
378c2ecf20Sopenharmony_ci	DRM_DEBUG("ip_version_major: %u\n", le16_to_cpu(hdr->ip_version_major));
388c2ecf20Sopenharmony_ci	DRM_DEBUG("ip_version_minor: %u\n", le16_to_cpu(hdr->ip_version_minor));
398c2ecf20Sopenharmony_ci	DRM_DEBUG("ucode_version: 0x%08x\n", le32_to_cpu(hdr->ucode_version));
408c2ecf20Sopenharmony_ci	DRM_DEBUG("ucode_size_bytes: %u\n", le32_to_cpu(hdr->ucode_size_bytes));
418c2ecf20Sopenharmony_ci	DRM_DEBUG("ucode_array_offset_bytes: %u\n",
428c2ecf20Sopenharmony_ci		  le32_to_cpu(hdr->ucode_array_offset_bytes));
438c2ecf20Sopenharmony_ci	DRM_DEBUG("crc32: 0x%08x\n", le32_to_cpu(hdr->crc32));
448c2ecf20Sopenharmony_ci}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_civoid radeon_ucode_print_mc_hdr(const struct common_firmware_header *hdr)
478c2ecf20Sopenharmony_ci{
488c2ecf20Sopenharmony_ci	uint16_t version_major = le16_to_cpu(hdr->header_version_major);
498c2ecf20Sopenharmony_ci	uint16_t version_minor = le16_to_cpu(hdr->header_version_minor);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	DRM_DEBUG("MC\n");
528c2ecf20Sopenharmony_ci	radeon_ucode_print_common_hdr(hdr);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (version_major == 1) {
558c2ecf20Sopenharmony_ci		const struct mc_firmware_header_v1_0 *mc_hdr =
568c2ecf20Sopenharmony_ci			container_of(hdr, struct mc_firmware_header_v1_0, header);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci		DRM_DEBUG("io_debug_size_bytes: %u\n",
598c2ecf20Sopenharmony_ci			  le32_to_cpu(mc_hdr->io_debug_size_bytes));
608c2ecf20Sopenharmony_ci		DRM_DEBUG("io_debug_array_offset_bytes: %u\n",
618c2ecf20Sopenharmony_ci			  le32_to_cpu(mc_hdr->io_debug_array_offset_bytes));
628c2ecf20Sopenharmony_ci	} else {
638c2ecf20Sopenharmony_ci		DRM_ERROR("Unknown MC ucode version: %u.%u\n", version_major, version_minor);
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_civoid radeon_ucode_print_smc_hdr(const struct common_firmware_header *hdr)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	uint16_t version_major = le16_to_cpu(hdr->header_version_major);
708c2ecf20Sopenharmony_ci	uint16_t version_minor = le16_to_cpu(hdr->header_version_minor);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	DRM_DEBUG("SMC\n");
738c2ecf20Sopenharmony_ci	radeon_ucode_print_common_hdr(hdr);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	if (version_major == 1) {
768c2ecf20Sopenharmony_ci		const struct smc_firmware_header_v1_0 *smc_hdr =
778c2ecf20Sopenharmony_ci			container_of(hdr, struct smc_firmware_header_v1_0, header);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		DRM_DEBUG("ucode_start_addr: %u\n", le32_to_cpu(smc_hdr->ucode_start_addr));
808c2ecf20Sopenharmony_ci	} else {
818c2ecf20Sopenharmony_ci		DRM_ERROR("Unknown SMC ucode version: %u.%u\n", version_major, version_minor);
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_civoid radeon_ucode_print_gfx_hdr(const struct common_firmware_header *hdr)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	uint16_t version_major = le16_to_cpu(hdr->header_version_major);
888c2ecf20Sopenharmony_ci	uint16_t version_minor = le16_to_cpu(hdr->header_version_minor);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	DRM_DEBUG("GFX\n");
918c2ecf20Sopenharmony_ci	radeon_ucode_print_common_hdr(hdr);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (version_major == 1) {
948c2ecf20Sopenharmony_ci		const struct gfx_firmware_header_v1_0 *gfx_hdr =
958c2ecf20Sopenharmony_ci			container_of(hdr, struct gfx_firmware_header_v1_0, header);
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci		DRM_DEBUG("ucode_feature_version: %u\n",
988c2ecf20Sopenharmony_ci			  le32_to_cpu(gfx_hdr->ucode_feature_version));
998c2ecf20Sopenharmony_ci		DRM_DEBUG("jt_offset: %u\n", le32_to_cpu(gfx_hdr->jt_offset));
1008c2ecf20Sopenharmony_ci		DRM_DEBUG("jt_size: %u\n", le32_to_cpu(gfx_hdr->jt_size));
1018c2ecf20Sopenharmony_ci	} else {
1028c2ecf20Sopenharmony_ci		DRM_ERROR("Unknown GFX ucode version: %u.%u\n", version_major, version_minor);
1038c2ecf20Sopenharmony_ci	}
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_civoid radeon_ucode_print_rlc_hdr(const struct common_firmware_header *hdr)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	uint16_t version_major = le16_to_cpu(hdr->header_version_major);
1098c2ecf20Sopenharmony_ci	uint16_t version_minor = le16_to_cpu(hdr->header_version_minor);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	DRM_DEBUG("RLC\n");
1128c2ecf20Sopenharmony_ci	radeon_ucode_print_common_hdr(hdr);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (version_major == 1) {
1158c2ecf20Sopenharmony_ci		const struct rlc_firmware_header_v1_0 *rlc_hdr =
1168c2ecf20Sopenharmony_ci			container_of(hdr, struct rlc_firmware_header_v1_0, header);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		DRM_DEBUG("ucode_feature_version: %u\n",
1198c2ecf20Sopenharmony_ci			  le32_to_cpu(rlc_hdr->ucode_feature_version));
1208c2ecf20Sopenharmony_ci		DRM_DEBUG("save_and_restore_offset: %u\n",
1218c2ecf20Sopenharmony_ci			  le32_to_cpu(rlc_hdr->save_and_restore_offset));
1228c2ecf20Sopenharmony_ci		DRM_DEBUG("clear_state_descriptor_offset: %u\n",
1238c2ecf20Sopenharmony_ci			  le32_to_cpu(rlc_hdr->clear_state_descriptor_offset));
1248c2ecf20Sopenharmony_ci		DRM_DEBUG("avail_scratch_ram_locations: %u\n",
1258c2ecf20Sopenharmony_ci			  le32_to_cpu(rlc_hdr->avail_scratch_ram_locations));
1268c2ecf20Sopenharmony_ci		DRM_DEBUG("master_pkt_description_offset: %u\n",
1278c2ecf20Sopenharmony_ci			  le32_to_cpu(rlc_hdr->master_pkt_description_offset));
1288c2ecf20Sopenharmony_ci	} else {
1298c2ecf20Sopenharmony_ci		DRM_ERROR("Unknown RLC ucode version: %u.%u\n", version_major, version_minor);
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_civoid radeon_ucode_print_sdma_hdr(const struct common_firmware_header *hdr)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	uint16_t version_major = le16_to_cpu(hdr->header_version_major);
1368c2ecf20Sopenharmony_ci	uint16_t version_minor = le16_to_cpu(hdr->header_version_minor);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	DRM_DEBUG("SDMA\n");
1398c2ecf20Sopenharmony_ci	radeon_ucode_print_common_hdr(hdr);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if (version_major == 1) {
1428c2ecf20Sopenharmony_ci		const struct sdma_firmware_header_v1_0 *sdma_hdr =
1438c2ecf20Sopenharmony_ci			container_of(hdr, struct sdma_firmware_header_v1_0, header);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		DRM_DEBUG("ucode_feature_version: %u\n",
1468c2ecf20Sopenharmony_ci			  le32_to_cpu(sdma_hdr->ucode_feature_version));
1478c2ecf20Sopenharmony_ci		DRM_DEBUG("ucode_change_version: %u\n",
1488c2ecf20Sopenharmony_ci			  le32_to_cpu(sdma_hdr->ucode_change_version));
1498c2ecf20Sopenharmony_ci		DRM_DEBUG("jt_offset: %u\n", le32_to_cpu(sdma_hdr->jt_offset));
1508c2ecf20Sopenharmony_ci		DRM_DEBUG("jt_size: %u\n", le32_to_cpu(sdma_hdr->jt_size));
1518c2ecf20Sopenharmony_ci	} else {
1528c2ecf20Sopenharmony_ci		DRM_ERROR("Unknown SDMA ucode version: %u.%u\n",
1538c2ecf20Sopenharmony_ci			  version_major, version_minor);
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ciint radeon_ucode_validate(const struct firmware *fw)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	const struct common_firmware_header *hdr =
1608c2ecf20Sopenharmony_ci		(const struct common_firmware_header *)fw->data;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (fw->size == le32_to_cpu(hdr->size_bytes))
1638c2ecf20Sopenharmony_ci		return 0;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	return -EINVAL;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
168