1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2019-2020 Linaro Ltd.
4*4882a593Smuzhiyun */
5*4882a593Smuzhiyun #include <linux/kernel.h>
6*4882a593Smuzhiyun #include <linux/module.h>
7*4882a593Smuzhiyun #include <linux/mutex.h>
8*4882a593Smuzhiyun #include <linux/of_address.h>
9*4882a593Smuzhiyun #include "qcom_pil_info.h"
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun /*
12*4882a593Smuzhiyun * The PIL relocation information region is used to communicate memory regions
13*4882a593Smuzhiyun * occupied by co-processor firmware for post mortem crash analysis.
14*4882a593Smuzhiyun *
15*4882a593Smuzhiyun * It consists of an array of entries with an 8 byte textual identifier of the
16*4882a593Smuzhiyun * region followed by a 64 bit base address and 32 bit size, both little
17*4882a593Smuzhiyun * endian.
18*4882a593Smuzhiyun */
19*4882a593Smuzhiyun #define PIL_RELOC_NAME_LEN 8
20*4882a593Smuzhiyun #define PIL_RELOC_ENTRY_SIZE (PIL_RELOC_NAME_LEN + sizeof(__le64) + sizeof(__le32))
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun struct pil_reloc {
23*4882a593Smuzhiyun void __iomem *base;
24*4882a593Smuzhiyun size_t num_entries;
25*4882a593Smuzhiyun };
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun static struct pil_reloc _reloc __read_mostly;
28*4882a593Smuzhiyun static DEFINE_MUTEX(pil_reloc_lock);
29*4882a593Smuzhiyun
qcom_pil_info_init(void)30*4882a593Smuzhiyun static int qcom_pil_info_init(void)
31*4882a593Smuzhiyun {
32*4882a593Smuzhiyun struct device_node *np;
33*4882a593Smuzhiyun struct resource imem;
34*4882a593Smuzhiyun void __iomem *base;
35*4882a593Smuzhiyun int ret;
36*4882a593Smuzhiyun
37*4882a593Smuzhiyun /* Already initialized? */
38*4882a593Smuzhiyun if (_reloc.base)
39*4882a593Smuzhiyun return 0;
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun np = of_find_compatible_node(NULL, NULL, "qcom,pil-reloc-info");
42*4882a593Smuzhiyun if (!np)
43*4882a593Smuzhiyun return -ENOENT;
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun ret = of_address_to_resource(np, 0, &imem);
46*4882a593Smuzhiyun of_node_put(np);
47*4882a593Smuzhiyun if (ret < 0)
48*4882a593Smuzhiyun return ret;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun base = ioremap(imem.start, resource_size(&imem));
51*4882a593Smuzhiyun if (!base) {
52*4882a593Smuzhiyun pr_err("failed to map PIL relocation info region\n");
53*4882a593Smuzhiyun return -ENOMEM;
54*4882a593Smuzhiyun }
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun memset_io(base, 0, resource_size(&imem));
57*4882a593Smuzhiyun
58*4882a593Smuzhiyun _reloc.base = base;
59*4882a593Smuzhiyun _reloc.num_entries = (u32)resource_size(&imem) / PIL_RELOC_ENTRY_SIZE;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun return 0;
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun /**
65*4882a593Smuzhiyun * qcom_pil_info_store() - store PIL information of image in IMEM
66*4882a593Smuzhiyun * @image: name of the image
67*4882a593Smuzhiyun * @base: base address of the loaded image
68*4882a593Smuzhiyun * @size: size of the loaded image
69*4882a593Smuzhiyun *
70*4882a593Smuzhiyun * Return: 0 on success, negative errno on failure
71*4882a593Smuzhiyun */
qcom_pil_info_store(const char * image,phys_addr_t base,size_t size)72*4882a593Smuzhiyun int qcom_pil_info_store(const char *image, phys_addr_t base, size_t size)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun char buf[PIL_RELOC_NAME_LEN];
75*4882a593Smuzhiyun void __iomem *entry;
76*4882a593Smuzhiyun int ret;
77*4882a593Smuzhiyun int i;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun mutex_lock(&pil_reloc_lock);
80*4882a593Smuzhiyun ret = qcom_pil_info_init();
81*4882a593Smuzhiyun if (ret < 0) {
82*4882a593Smuzhiyun mutex_unlock(&pil_reloc_lock);
83*4882a593Smuzhiyun return ret;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun for (i = 0; i < _reloc.num_entries; i++) {
87*4882a593Smuzhiyun entry = _reloc.base + i * PIL_RELOC_ENTRY_SIZE;
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun memcpy_fromio(buf, entry, PIL_RELOC_NAME_LEN);
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun /*
92*4882a593Smuzhiyun * An empty record means we didn't find it, given that the
93*4882a593Smuzhiyun * records are packed.
94*4882a593Smuzhiyun */
95*4882a593Smuzhiyun if (!buf[0])
96*4882a593Smuzhiyun goto found_unused;
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun if (!strncmp(buf, image, PIL_RELOC_NAME_LEN))
99*4882a593Smuzhiyun goto found_existing;
100*4882a593Smuzhiyun }
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun pr_warn("insufficient PIL info slots\n");
103*4882a593Smuzhiyun mutex_unlock(&pil_reloc_lock);
104*4882a593Smuzhiyun return -ENOMEM;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun found_unused:
107*4882a593Smuzhiyun memcpy_toio(entry, image, strnlen(image, PIL_RELOC_NAME_LEN));
108*4882a593Smuzhiyun found_existing:
109*4882a593Smuzhiyun /* Use two writel() as base is only aligned to 4 bytes on odd entries */
110*4882a593Smuzhiyun writel(base, entry + PIL_RELOC_NAME_LEN);
111*4882a593Smuzhiyun writel((u64)base >> 32, entry + PIL_RELOC_NAME_LEN + 4);
112*4882a593Smuzhiyun writel(size, entry + PIL_RELOC_NAME_LEN + sizeof(__le64));
113*4882a593Smuzhiyun mutex_unlock(&pil_reloc_lock);
114*4882a593Smuzhiyun
115*4882a593Smuzhiyun return 0;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(qcom_pil_info_store);
118*4882a593Smuzhiyun
pil_reloc_exit(void)119*4882a593Smuzhiyun static void __exit pil_reloc_exit(void)
120*4882a593Smuzhiyun {
121*4882a593Smuzhiyun mutex_lock(&pil_reloc_lock);
122*4882a593Smuzhiyun iounmap(_reloc.base);
123*4882a593Smuzhiyun _reloc.base = NULL;
124*4882a593Smuzhiyun mutex_unlock(&pil_reloc_lock);
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun module_exit(pil_reloc_exit);
127*4882a593Smuzhiyun
128*4882a593Smuzhiyun MODULE_DESCRIPTION("Qualcomm PIL relocation info");
129*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
130