162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2019-2020 Linaro Ltd. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/kernel.h> 662306a36Sopenharmony_ci#include <linux/module.h> 762306a36Sopenharmony_ci#include <linux/mutex.h> 862306a36Sopenharmony_ci#include <linux/of_address.h> 962306a36Sopenharmony_ci#include "qcom_pil_info.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/* 1262306a36Sopenharmony_ci * The PIL relocation information region is used to communicate memory regions 1362306a36Sopenharmony_ci * occupied by co-processor firmware for post mortem crash analysis. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * It consists of an array of entries with an 8 byte textual identifier of the 1662306a36Sopenharmony_ci * region followed by a 64 bit base address and 32 bit size, both little 1762306a36Sopenharmony_ci * endian. 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci#define PIL_RELOC_NAME_LEN 8 2062306a36Sopenharmony_ci#define PIL_RELOC_ENTRY_SIZE (PIL_RELOC_NAME_LEN + sizeof(__le64) + sizeof(__le32)) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct pil_reloc { 2362306a36Sopenharmony_ci void __iomem *base; 2462306a36Sopenharmony_ci size_t num_entries; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic struct pil_reloc _reloc __read_mostly; 2862306a36Sopenharmony_cistatic DEFINE_MUTEX(pil_reloc_lock); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic int qcom_pil_info_init(void) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci struct device_node *np; 3362306a36Sopenharmony_ci struct resource imem; 3462306a36Sopenharmony_ci void __iomem *base; 3562306a36Sopenharmony_ci int ret; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci /* Already initialized? */ 3862306a36Sopenharmony_ci if (_reloc.base) 3962306a36Sopenharmony_ci return 0; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, "qcom,pil-reloc-info"); 4262306a36Sopenharmony_ci if (!np) 4362306a36Sopenharmony_ci return -ENOENT; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci ret = of_address_to_resource(np, 0, &imem); 4662306a36Sopenharmony_ci of_node_put(np); 4762306a36Sopenharmony_ci if (ret < 0) 4862306a36Sopenharmony_ci return ret; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci base = ioremap(imem.start, resource_size(&imem)); 5162306a36Sopenharmony_ci if (!base) { 5262306a36Sopenharmony_ci pr_err("failed to map PIL relocation info region\n"); 5362306a36Sopenharmony_ci return -ENOMEM; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci memset_io(base, 0, resource_size(&imem)); 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci _reloc.base = base; 5962306a36Sopenharmony_ci _reloc.num_entries = (u32)resource_size(&imem) / PIL_RELOC_ENTRY_SIZE; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/** 6562306a36Sopenharmony_ci * qcom_pil_info_store() - store PIL information of image in IMEM 6662306a36Sopenharmony_ci * @image: name of the image 6762306a36Sopenharmony_ci * @base: base address of the loaded image 6862306a36Sopenharmony_ci * @size: size of the loaded image 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * Return: 0 on success, negative errno on failure 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ciint qcom_pil_info_store(const char *image, phys_addr_t base, size_t size) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci char buf[PIL_RELOC_NAME_LEN]; 7562306a36Sopenharmony_ci void __iomem *entry; 7662306a36Sopenharmony_ci int ret; 7762306a36Sopenharmony_ci int i; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci mutex_lock(&pil_reloc_lock); 8062306a36Sopenharmony_ci ret = qcom_pil_info_init(); 8162306a36Sopenharmony_ci if (ret < 0) { 8262306a36Sopenharmony_ci mutex_unlock(&pil_reloc_lock); 8362306a36Sopenharmony_ci return ret; 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci for (i = 0; i < _reloc.num_entries; i++) { 8762306a36Sopenharmony_ci entry = _reloc.base + i * PIL_RELOC_ENTRY_SIZE; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci memcpy_fromio(buf, entry, PIL_RELOC_NAME_LEN); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * An empty record means we didn't find it, given that the 9362306a36Sopenharmony_ci * records are packed. 9462306a36Sopenharmony_ci */ 9562306a36Sopenharmony_ci if (!buf[0]) 9662306a36Sopenharmony_ci goto found_unused; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (!strncmp(buf, image, PIL_RELOC_NAME_LEN)) 9962306a36Sopenharmony_ci goto found_existing; 10062306a36Sopenharmony_ci } 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci pr_warn("insufficient PIL info slots\n"); 10362306a36Sopenharmony_ci mutex_unlock(&pil_reloc_lock); 10462306a36Sopenharmony_ci return -ENOMEM; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cifound_unused: 10762306a36Sopenharmony_ci memcpy_toio(entry, image, strnlen(image, PIL_RELOC_NAME_LEN)); 10862306a36Sopenharmony_cifound_existing: 10962306a36Sopenharmony_ci /* Use two writel() as base is only aligned to 4 bytes on odd entries */ 11062306a36Sopenharmony_ci writel(base, entry + PIL_RELOC_NAME_LEN); 11162306a36Sopenharmony_ci writel((u64)base >> 32, entry + PIL_RELOC_NAME_LEN + 4); 11262306a36Sopenharmony_ci writel(size, entry + PIL_RELOC_NAME_LEN + sizeof(__le64)); 11362306a36Sopenharmony_ci mutex_unlock(&pil_reloc_lock); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(qcom_pil_info_store); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic void __exit pil_reloc_exit(void) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci mutex_lock(&pil_reloc_lock); 12262306a36Sopenharmony_ci iounmap(_reloc.base); 12362306a36Sopenharmony_ci _reloc.base = NULL; 12462306a36Sopenharmony_ci mutex_unlock(&pil_reloc_lock); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_cimodule_exit(pil_reloc_exit); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm PIL relocation info"); 12962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 130