xref: /OK3568_Linux_fs/kernel/drivers/remoteproc/qcom_pil_info.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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