1549b67daSBjorn Andersson // SPDX-License-Identifier: GPL-2.0-only 2549b67daSBjorn Andersson /* 3549b67daSBjorn Andersson * Copyright (c) 2019-2020 Linaro Ltd. 4549b67daSBjorn Andersson */ 5549b67daSBjorn Andersson #include <linux/kernel.h> 6549b67daSBjorn Andersson #include <linux/module.h> 7549b67daSBjorn Andersson #include <linux/mutex.h> 8549b67daSBjorn Andersson #include <linux/of_address.h> 9549b67daSBjorn Andersson #include "qcom_pil_info.h" 10549b67daSBjorn Andersson 11549b67daSBjorn Andersson /* 12549b67daSBjorn Andersson * The PIL relocation information region is used to communicate memory regions 13549b67daSBjorn Andersson * occupied by co-processor firmware for post mortem crash analysis. 14549b67daSBjorn Andersson * 15549b67daSBjorn Andersson * It consists of an array of entries with an 8 byte textual identifier of the 16549b67daSBjorn Andersson * region followed by a 64 bit base address and 32 bit size, both little 17549b67daSBjorn Andersson * endian. 18549b67daSBjorn Andersson */ 19549b67daSBjorn Andersson #define PIL_RELOC_NAME_LEN 8 20549b67daSBjorn Andersson #define PIL_RELOC_ENTRY_SIZE (PIL_RELOC_NAME_LEN + sizeof(__le64) + sizeof(__le32)) 21549b67daSBjorn Andersson 22549b67daSBjorn Andersson struct pil_reloc { 23549b67daSBjorn Andersson void __iomem *base; 24549b67daSBjorn Andersson size_t num_entries; 25549b67daSBjorn Andersson }; 26549b67daSBjorn Andersson 27549b67daSBjorn Andersson static struct pil_reloc _reloc __read_mostly; 28549b67daSBjorn Andersson static DEFINE_MUTEX(pil_reloc_lock); 29549b67daSBjorn Andersson 30549b67daSBjorn Andersson static int qcom_pil_info_init(void) 31549b67daSBjorn Andersson { 32549b67daSBjorn Andersson struct device_node *np; 33549b67daSBjorn Andersson struct resource imem; 34549b67daSBjorn Andersson void __iomem *base; 35549b67daSBjorn Andersson int ret; 36549b67daSBjorn Andersson 37549b67daSBjorn Andersson /* Already initialized? */ 38549b67daSBjorn Andersson if (_reloc.base) 39549b67daSBjorn Andersson return 0; 40549b67daSBjorn Andersson 41549b67daSBjorn Andersson np = of_find_compatible_node(NULL, NULL, "qcom,pil-reloc-info"); 42549b67daSBjorn Andersson if (!np) 43549b67daSBjorn Andersson return -ENOENT; 44549b67daSBjorn Andersson 45549b67daSBjorn Andersson ret = of_address_to_resource(np, 0, &imem); 46549b67daSBjorn Andersson of_node_put(np); 47549b67daSBjorn Andersson if (ret < 0) 48549b67daSBjorn Andersson return ret; 49549b67daSBjorn Andersson 50549b67daSBjorn Andersson base = ioremap(imem.start, resource_size(&imem)); 51549b67daSBjorn Andersson if (!base) { 52549b67daSBjorn Andersson pr_err("failed to map PIL relocation info region\n"); 53549b67daSBjorn Andersson return -ENOMEM; 54549b67daSBjorn Andersson } 55549b67daSBjorn Andersson 56549b67daSBjorn Andersson memset_io(base, 0, resource_size(&imem)); 57549b67daSBjorn Andersson 58549b67daSBjorn Andersson _reloc.base = base; 59*7029e783SArnd Bergmann _reloc.num_entries = (u32)resource_size(&imem) / PIL_RELOC_ENTRY_SIZE; 60549b67daSBjorn Andersson 61549b67daSBjorn Andersson return 0; 62549b67daSBjorn Andersson } 63549b67daSBjorn Andersson 64549b67daSBjorn Andersson /** 65549b67daSBjorn Andersson * qcom_pil_info_store() - store PIL information of image in IMEM 66549b67daSBjorn Andersson * @image: name of the image 67549b67daSBjorn Andersson * @base: base address of the loaded image 68549b67daSBjorn Andersson * @size: size of the loaded image 69549b67daSBjorn Andersson * 70549b67daSBjorn Andersson * Return: 0 on success, negative errno on failure 71549b67daSBjorn Andersson */ 72549b67daSBjorn Andersson int qcom_pil_info_store(const char *image, phys_addr_t base, size_t size) 73549b67daSBjorn Andersson { 74549b67daSBjorn Andersson char buf[PIL_RELOC_NAME_LEN]; 75549b67daSBjorn Andersson void __iomem *entry; 76549b67daSBjorn Andersson int ret; 77549b67daSBjorn Andersson int i; 78549b67daSBjorn Andersson 79549b67daSBjorn Andersson mutex_lock(&pil_reloc_lock); 80549b67daSBjorn Andersson ret = qcom_pil_info_init(); 81549b67daSBjorn Andersson if (ret < 0) { 82549b67daSBjorn Andersson mutex_unlock(&pil_reloc_lock); 83549b67daSBjorn Andersson return ret; 84549b67daSBjorn Andersson } 85549b67daSBjorn Andersson 86549b67daSBjorn Andersson for (i = 0; i < _reloc.num_entries; i++) { 87549b67daSBjorn Andersson entry = _reloc.base + i * PIL_RELOC_ENTRY_SIZE; 88549b67daSBjorn Andersson 89549b67daSBjorn Andersson memcpy_fromio(buf, entry, PIL_RELOC_NAME_LEN); 90549b67daSBjorn Andersson 91549b67daSBjorn Andersson /* 92549b67daSBjorn Andersson * An empty record means we didn't find it, given that the 93549b67daSBjorn Andersson * records are packed. 94549b67daSBjorn Andersson */ 95549b67daSBjorn Andersson if (!buf[0]) 96549b67daSBjorn Andersson goto found_unused; 97549b67daSBjorn Andersson 98549b67daSBjorn Andersson if (!strncmp(buf, image, PIL_RELOC_NAME_LEN)) 99549b67daSBjorn Andersson goto found_existing; 100549b67daSBjorn Andersson } 101549b67daSBjorn Andersson 102549b67daSBjorn Andersson pr_warn("insufficient PIL info slots\n"); 103549b67daSBjorn Andersson mutex_unlock(&pil_reloc_lock); 104549b67daSBjorn Andersson return -ENOMEM; 105549b67daSBjorn Andersson 106549b67daSBjorn Andersson found_unused: 107549b67daSBjorn Andersson memcpy_toio(entry, image, PIL_RELOC_NAME_LEN); 108549b67daSBjorn Andersson found_existing: 109549b67daSBjorn Andersson /* Use two writel() as base is only aligned to 4 bytes on odd entries */ 110549b67daSBjorn Andersson writel(base, entry + PIL_RELOC_NAME_LEN); 11190ec257cSBjorn Andersson writel((u64)base >> 32, entry + PIL_RELOC_NAME_LEN + 4); 112549b67daSBjorn Andersson writel(size, entry + PIL_RELOC_NAME_LEN + sizeof(__le64)); 113549b67daSBjorn Andersson mutex_unlock(&pil_reloc_lock); 114549b67daSBjorn Andersson 115549b67daSBjorn Andersson return 0; 116549b67daSBjorn Andersson } 117549b67daSBjorn Andersson EXPORT_SYMBOL_GPL(qcom_pil_info_store); 118549b67daSBjorn Andersson 119549b67daSBjorn Andersson static void __exit pil_reloc_exit(void) 120549b67daSBjorn Andersson { 121549b67daSBjorn Andersson mutex_lock(&pil_reloc_lock); 122549b67daSBjorn Andersson iounmap(_reloc.base); 123549b67daSBjorn Andersson _reloc.base = NULL; 124549b67daSBjorn Andersson mutex_unlock(&pil_reloc_lock); 125549b67daSBjorn Andersson } 126549b67daSBjorn Andersson module_exit(pil_reloc_exit); 127549b67daSBjorn Andersson 128549b67daSBjorn Andersson MODULE_DESCRIPTION("Qualcomm PIL relocation info"); 129549b67daSBjorn Andersson MODULE_LICENSE("GPL v2"); 130