summaryrefslogtreecommitdiffstats
path: root/hw/capp.c
blob: 7287ed826748c53169ca0de6c625ecf7374c4db1 (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/* Copyright 2013-2017 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include <skiboot.h>
#include <io.h>
#include <opal.h>
#include <chip.h>
#include <xscom.h>
#include <capp.h>

#define PHBERR(opal_id, chip_id, index, fmt, a...) \
	       prlog(PR_ERR, "PHB#%04x[%d:%d]: " fmt, \
		     opal_id, chip_id, \
		     index,  ## a)

static struct {
	uint32_t			ec_level;
	struct capp_lid_hdr		*lid;
	size_t size;
	int load_result;
} capp_ucode_info = { 0, NULL, 0, false };

#define CAPP_UCODE_MAX_SIZE 0x20000

struct lock capi_lock = LOCK_UNLOCKED;
struct capp_ops capi_ops = { NULL };

bool capp_ucode_loaded(struct proc_chip *chip, unsigned int index)
{
	return (chip->capp_ucode_loaded & (1 << index));
}

int preload_capp_ucode(void)
{
	struct dt_node *p;
	struct proc_chip *chip;
	uint32_t index;
	uint64_t rc;
	int ret;

	p = dt_find_compatible_node(dt_root, NULL, "ibm,power8-pbcq");

	if (!p) {
		p = dt_find_compatible_node(dt_root, NULL, "ibm,power9-pbcq");
		if (!p) {
			prlog(PR_INFO, "CAPI: WARNING: no compat thing found\n");
			return OPAL_SUCCESS;
		}
	}

	chip = get_chip(dt_get_chip_id(p));

	rc = xscom_read_cfam_chipid(chip->id, &index);
	if (rc) {
		prerror("CAPP: Error reading cfam chip-id\n");
		ret = OPAL_HARDWARE;
		return ret;
	}
	/* Keep ChipID and Major/Minor EC.  Mask out the Location Code. */
	index = index & 0xf0fff;

	/* Assert that we're preloading */
	assert(capp_ucode_info.lid == NULL);
	capp_ucode_info.load_result = OPAL_EMPTY;

	capp_ucode_info.ec_level = index;

	/* Is the ucode preloaded like for BML? */
	if (dt_has_node_property(p, "ibm,capp-ucode", NULL)) {
		capp_ucode_info.lid = (struct capp_lid_hdr *)(u64)
			dt_prop_get_u32(p, "ibm,capp-ucode");
		capp_ucode_info.load_result = OPAL_SUCCESS;
		ret = OPAL_SUCCESS;
		goto end;
	}
	/* If we successfully download the ucode, we leave it around forever */
	capp_ucode_info.size = CAPP_UCODE_MAX_SIZE;
	capp_ucode_info.lid = malloc(CAPP_UCODE_MAX_SIZE);
	if (!capp_ucode_info.lid) {
		prerror("CAPP: Can't allocate space for ucode lid\n");
		ret = OPAL_NO_MEM;
		goto end;
	}

	prlog(PR_INFO, "CAPI: Preloading ucode %x\n", capp_ucode_info.ec_level);

	ret = start_preload_resource(RESOURCE_ID_CAPP, index,
				     capp_ucode_info.lid,
				     &capp_ucode_info.size);

	if (ret != OPAL_SUCCESS) {
		prerror("CAPI: Failed to preload resource %d\n", ret);
		capp_ucode_info.load_result = ret;
	}

end:
	return ret;
}

static int64_t capp_lid_download(void)
{
	int64_t ret;

	if (capp_ucode_info.load_result != OPAL_EMPTY)
		return capp_ucode_info.load_result;

	capp_ucode_info.load_result = wait_for_resource_loaded(
		RESOURCE_ID_CAPP,
		capp_ucode_info.ec_level);

	if (capp_ucode_info.load_result != OPAL_SUCCESS) {
		prerror("CAPP: Error loading ucode lid. index=%x\n",
			capp_ucode_info.ec_level);
		ret = OPAL_RESOURCE;
		free(capp_ucode_info.lid);
		capp_ucode_info.lid = NULL;
		goto end;
	}

	ret = OPAL_SUCCESS;
end:
	return ret;
}

int64_t capp_load_ucode(unsigned int chip_id, uint32_t opal_id,
			unsigned int index, u64 lid_eyecatcher,
			uint32_t reg_offset,
			uint64_t apc_master_addr, uint64_t apc_master_write,
			uint64_t snp_array_addr, uint64_t snp_array_write)
{
	struct proc_chip *chip = get_chip(chip_id);
	struct capp_ucode_lid *ucode;
	struct capp_ucode_data *data;
	struct capp_lid_hdr *lid;
	uint64_t rc, val, addr;
	uint32_t chunk_count, offset;
	int i;

	if (capp_ucode_loaded(chip, index))
		return OPAL_SUCCESS;

	rc = capp_lid_download();
	if (rc)
		return rc;

	prlog(PR_INFO, "CHIP%i: CAPP ucode lid loaded at %p\n",
	      chip_id, capp_ucode_info.lid);

	lid = capp_ucode_info.lid;
	/*
	 * If lid header is present (on FSP machines), it'll tell us where to
	 * find the ucode.  Otherwise this is the ucode.
	 */
	ucode = (struct capp_ucode_lid *)lid;
	if (be64_to_cpu(lid->eyecatcher) == lid_eyecatcher) {
		if (be64_to_cpu(lid->version) != 0x1) {
			PHBERR(opal_id, chip_id, index,
			       "capi ucode lid header invalid\n");
			return OPAL_HARDWARE;
		}
		ucode = (struct capp_ucode_lid *)
			((char *)ucode + be64_to_cpu(lid->ucode_offset));
	}

	/* 'CAPPULID' in ASCII */
	if ((be64_to_cpu(ucode->eyecatcher) != 0x43415050554C4944UL) ||
	    (be64_to_cpu(ucode->version != 1))) {
		PHBERR(opal_id, chip_id, index,
		       "CAPP: ucode header invalid\n");
		return OPAL_HARDWARE;
	}

	offset = 0;
	while (offset < be64_to_cpu(ucode->data_size)) {
		data = (struct capp_ucode_data *)
			((char *)&ucode->data + offset);
		chunk_count = be32_to_cpu(data->hdr.chunk_count);
		offset += sizeof(struct capp_ucode_data_hdr) + chunk_count * 8;

		/* 'CAPPUCOD' in ASCII */
		if (be64_to_cpu(data->hdr.eyecatcher) != 0x4341505055434F44UL) {
			PHBERR(opal_id, chip_id, index,
			       "CAPP: ucode data header invalid:%i\n",
			       offset);
			return OPAL_HARDWARE;
		}

		switch (data->hdr.reg) {
		case apc_master_cresp:
			xscom_write(chip_id, apc_master_addr + reg_offset,
				    0);
			addr = apc_master_write;
			break;
		case apc_master_uop_table:
			xscom_write(chip_id, apc_master_addr + reg_offset,
				    0x180ULL << 52);
			addr = apc_master_write;
			break;
		case snp_ttype:
			xscom_write(chip_id, snp_array_addr + reg_offset,
				    0x5000ULL << 48);
			addr = snp_array_write;
			break;
		case snp_uop_table:
			xscom_write(chip_id, snp_array_addr + reg_offset,
				    0x4000ULL << 48);
			addr = snp_array_write;
			break;
		default:
			continue;
		}

		for (i = 0; i < chunk_count; i++) {
			val = be64_to_cpu(data->data[i]);
			xscom_write(chip_id, addr + reg_offset, val);
		}
	}

	chip->capp_ucode_loaded |= (1 << index);

	return OPAL_SUCCESS;
}

int64_t capp_get_info(int chip_id, struct phb *phb, struct capp_info *info)
{
	if (capi_ops.get_capp_info)
		return capi_ops.get_capp_info(chip_id, phb, info);

	return OPAL_PARAMETER;
}

int64_t capp_xscom_read(struct capp *capp, int64_t off, uint64_t *val)
{
	return capp == NULL ? OPAL_PARAMETER :
		xscom_read(capp->chip_id, off + capp->capp_xscom_offset, val);
}

int64_t capp_xscom_write(struct capp *capp, int64_t off, uint64_t val)
{
	return capp == NULL ? OPAL_PARAMETER :
		xscom_write(capp->chip_id, off + capp->capp_xscom_offset, val);
}
OpenPOWER on IntegriCloud