summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-layerscape/ppa.c
blob: c5eba35b330033dd4e3a61f34d553a814adb6ac3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// SPDX-License-Identifier: GPL-2.0

#define pr_fmt(fmt) "ppa: " fmt

#include <common.h>
#include <init.h>
#include <firmware.h>
#include <memory.h>
#include <linux/sizes.h>
#include <linux/arm-smccc.h>
#include <asm/mmu.h>
#include <soc/fsl/immap_lsch2.h>
#include <asm/system.h>
#include <image-fit.h>
#include <asm/psci.h>
#include <mach/layerscape.h>

int ppa_entry(const void *, u32 *, u32 *);
void dma_flush_range(void *ptr, size_t size);

#define SEC_JR3_OFFSET                     0x40000

static int of_psci_do_fixup(struct device_node *root, void *unused)
{
	unsigned long psci_version;
	struct device_node *np;
	struct arm_smccc_res res = {};

	arm_smccc_smc(ARM_PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0, 0, 0, 0, 0, &res);
	psci_version = res.a0;

	for_each_compatible_node_from(np, root, NULL, "fsl,sec-v4.0-job-ring") {
		const void *reg;
		int na = of_n_addr_cells(np);
		u64 offset;

		reg = of_get_property(np, "reg", NULL);
		if (!reg)
			continue;

		offset = of_read_number(reg, na);
		if (offset != SEC_JR3_OFFSET)
			continue;

		of_delete_node(np);
	}

	return of_psci_fixup(root, psci_version);
}

static int ppa_init(void *ppa, size_t ppa_size, void *sec_firmware_addr)
{
	int ret;
	u32 *boot_loc_ptr_l, *boot_loc_ptr_h;
	struct ccsr_scfg __iomem *scfg = (void *)(LSCH2_SCFG_ADDR);
	int el = current_el();
	struct fit_handle *fit;
	void *conf;
	const void *buf;
	unsigned long firmware_size;

	if (el < 3) {
		printf("EL%d, skip ppa init\n", el);
		return 0;
	}

	boot_loc_ptr_l = &scfg->scratchrw[1];
	boot_loc_ptr_h = &scfg->scratchrw[0];

	fit = fit_open_buf(ppa, ppa_size, false, BOOTM_VERIFY_AVAILABLE);
	if (IS_ERR(fit)) {
		pr_err("Cannot open ppa FIT image: %s\n", strerrorp(fit));
		return PTR_ERR(fit);
	}

	conf = fit_open_configuration(fit, NULL);
	if (IS_ERR(conf)) {
		pr_err("Cannot open default config in ppa FIT image: %s\n",
		       strerrorp(conf));
		fit_close(fit);
		return PTR_ERR(fit);
	}


	ret = fit_open_image(fit, conf, "firmware", &buf, &firmware_size);
	if (ret) {
		pr_err("Cannot open firmware image in ppa FIT image: %s\n",
		       strerror(-ret));
		ret = PTR_ERR(fit);
		goto err;
	}

	/* Copy the secure firmware to secure memory */
	memcpy(sec_firmware_addr, buf, firmware_size);
	dma_flush_range(sec_firmware_addr, ppa_size);

	ret = ppa_entry(sec_firmware_addr, boot_loc_ptr_l, boot_loc_ptr_h);
	if (ret) {
		pr_err("Couldn't install PPA firmware: %s\n", strerror(-ret));
		goto err;
	}

	pr_info("PPA firmware installed at 0x%p, now in EL%d\n",
		sec_firmware_addr, current_el());

	of_register_fixup(of_psci_do_fixup, NULL);
err:
	fit_close(fit);

	return 0;
}

int ls1046a_ppa_init(resource_size_t ppa_start, resource_size_t ppa_size)
{
	resource_size_t ppa_end = ppa_start + ppa_size - 1;
	struct resource *res;
	void *ppa_fw;
	size_t ppa_fw_size;
	int ret;

	res = request_sdram_region("ppa", ppa_start, ppa_size);
	if (!res) {
		pr_err("Cannot request SDRAM region %pa - %pa\n",
		       &ppa_start, &ppa_end);
		return -EINVAL;
	}

	get_builtin_firmware(ppa_ls1046a_bin, &ppa_fw, &ppa_fw_size);

	ret = ppa_init(ppa_fw, ppa_fw_size, (void *)ppa_start);
	if (ret)
		return ret;

	of_add_reserve_entry(ppa_start, ppa_end);

	return 0;
}