18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2019-2020 Linaro Ltd.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci#include <linux/kernel.h>
68c2ecf20Sopenharmony_ci#include <linux/module.h>
78c2ecf20Sopenharmony_ci#include <linux/mutex.h>
88c2ecf20Sopenharmony_ci#include <linux/of_address.h>
98c2ecf20Sopenharmony_ci#include "qcom_pil_info.h"
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci/*
128c2ecf20Sopenharmony_ci * The PIL relocation information region is used to communicate memory regions
138c2ecf20Sopenharmony_ci * occupied by co-processor firmware for post mortem crash analysis.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * It consists of an array of entries with an 8 byte textual identifier of the
168c2ecf20Sopenharmony_ci * region followed by a 64 bit base address and 32 bit size, both little
178c2ecf20Sopenharmony_ci * endian.
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci#define PIL_RELOC_NAME_LEN	8
208c2ecf20Sopenharmony_ci#define PIL_RELOC_ENTRY_SIZE	(PIL_RELOC_NAME_LEN + sizeof(__le64) + sizeof(__le32))
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct pil_reloc {
238c2ecf20Sopenharmony_ci	void __iomem *base;
248c2ecf20Sopenharmony_ci	size_t num_entries;
258c2ecf20Sopenharmony_ci};
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic struct pil_reloc _reloc __read_mostly;
288c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(pil_reloc_lock);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic int qcom_pil_info_init(void)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	struct device_node *np;
338c2ecf20Sopenharmony_ci	struct resource imem;
348c2ecf20Sopenharmony_ci	void __iomem *base;
358c2ecf20Sopenharmony_ci	int ret;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	/* Already initialized? */
388c2ecf20Sopenharmony_ci	if (_reloc.base)
398c2ecf20Sopenharmony_ci		return 0;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	np = of_find_compatible_node(NULL, NULL, "qcom,pil-reloc-info");
428c2ecf20Sopenharmony_ci	if (!np)
438c2ecf20Sopenharmony_ci		return -ENOENT;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	ret = of_address_to_resource(np, 0, &imem);
468c2ecf20Sopenharmony_ci	of_node_put(np);
478c2ecf20Sopenharmony_ci	if (ret < 0)
488c2ecf20Sopenharmony_ci		return ret;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	base = ioremap(imem.start, resource_size(&imem));
518c2ecf20Sopenharmony_ci	if (!base) {
528c2ecf20Sopenharmony_ci		pr_err("failed to map PIL relocation info region\n");
538c2ecf20Sopenharmony_ci		return -ENOMEM;
548c2ecf20Sopenharmony_ci	}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	memset_io(base, 0, resource_size(&imem));
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	_reloc.base = base;
598c2ecf20Sopenharmony_ci	_reloc.num_entries = (u32)resource_size(&imem) / PIL_RELOC_ENTRY_SIZE;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	return 0;
628c2ecf20Sopenharmony_ci}
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci/**
658c2ecf20Sopenharmony_ci * qcom_pil_info_store() - store PIL information of image in IMEM
668c2ecf20Sopenharmony_ci * @image:	name of the image
678c2ecf20Sopenharmony_ci * @base:	base address of the loaded image
688c2ecf20Sopenharmony_ci * @size:	size of the loaded image
698c2ecf20Sopenharmony_ci *
708c2ecf20Sopenharmony_ci * Return: 0 on success, negative errno on failure
718c2ecf20Sopenharmony_ci */
728c2ecf20Sopenharmony_ciint qcom_pil_info_store(const char *image, phys_addr_t base, size_t size)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	char buf[PIL_RELOC_NAME_LEN];
758c2ecf20Sopenharmony_ci	void __iomem *entry;
768c2ecf20Sopenharmony_ci	int ret;
778c2ecf20Sopenharmony_ci	int i;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	mutex_lock(&pil_reloc_lock);
808c2ecf20Sopenharmony_ci	ret = qcom_pil_info_init();
818c2ecf20Sopenharmony_ci	if (ret < 0) {
828c2ecf20Sopenharmony_ci		mutex_unlock(&pil_reloc_lock);
838c2ecf20Sopenharmony_ci		return ret;
848c2ecf20Sopenharmony_ci	}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	for (i = 0; i < _reloc.num_entries; i++) {
878c2ecf20Sopenharmony_ci		entry = _reloc.base + i * PIL_RELOC_ENTRY_SIZE;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		memcpy_fromio(buf, entry, PIL_RELOC_NAME_LEN);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci		/*
928c2ecf20Sopenharmony_ci		 * An empty record means we didn't find it, given that the
938c2ecf20Sopenharmony_ci		 * records are packed.
948c2ecf20Sopenharmony_ci		 */
958c2ecf20Sopenharmony_ci		if (!buf[0])
968c2ecf20Sopenharmony_ci			goto found_unused;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci		if (!strncmp(buf, image, PIL_RELOC_NAME_LEN))
998c2ecf20Sopenharmony_ci			goto found_existing;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	pr_warn("insufficient PIL info slots\n");
1038c2ecf20Sopenharmony_ci	mutex_unlock(&pil_reloc_lock);
1048c2ecf20Sopenharmony_ci	return -ENOMEM;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_cifound_unused:
1078c2ecf20Sopenharmony_ci	memcpy_toio(entry, image, strnlen(image, PIL_RELOC_NAME_LEN));
1088c2ecf20Sopenharmony_cifound_existing:
1098c2ecf20Sopenharmony_ci	/* Use two writel() as base is only aligned to 4 bytes on odd entries */
1108c2ecf20Sopenharmony_ci	writel(base, entry + PIL_RELOC_NAME_LEN);
1118c2ecf20Sopenharmony_ci	writel((u64)base >> 32, entry + PIL_RELOC_NAME_LEN + 4);
1128c2ecf20Sopenharmony_ci	writel(size, entry + PIL_RELOC_NAME_LEN + sizeof(__le64));
1138c2ecf20Sopenharmony_ci	mutex_unlock(&pil_reloc_lock);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return 0;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_pil_info_store);
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic void __exit pil_reloc_exit(void)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	mutex_lock(&pil_reloc_lock);
1228c2ecf20Sopenharmony_ci	iounmap(_reloc.base);
1238c2ecf20Sopenharmony_ci	_reloc.base = NULL;
1248c2ecf20Sopenharmony_ci	mutex_unlock(&pil_reloc_lock);
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_cimodule_exit(pil_reloc_exit);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm PIL relocation info");
1298c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
130