1/*-
2 * Copyright (c) 2019 Leandro Lupori
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <openfirm.h>
27#include <stand.h>
28
29#include <sys/endian.h>
30
31/* #define CAS_DEBUG */
32#ifdef CAS_DEBUG
33#define DPRINTF(fmt, ...)	printf(fmt, ## __VA_ARGS__)
34#else
35#define DPRINTF(fmt, ...)	do { ; } while (0)
36#endif
37
38/* PVR */
39#define PVR_CPU_P8E		0x004b0000
40#define PVR_CPU_P8NVL		0x004c0000
41#define PVR_CPU_P8		0x004d0000
42#define PVR_CPU_P9		0x004e0000
43#define PVR_CPU_MASK		0xffff0000
44
45#define PVR_ISA_207		0x0f000004
46#define PVR_ISA_300		0x0f000005
47#define PVR_ISA_MASK		0xffffffff
48
49/* loader version of kernel's CPU_MAXSIZE */
50#define MAX_CPUS		((uint32_t)256u)
51
52/* Option Vectors' settings */
53
54/* length of ignored OV */
55#define OV_IGN_LEN		0
56
57/* byte 1 (of any OV) */
58#define OV_IGN			0x80
59
60/* Option Vector 5 */
61
62/* byte 2 */
63#define OV5_LPAR		0x80
64#define OV5_SPLPAR		0x40
65#define OV5_DRMEM		0x20
66#define OV5_LP			0x10
67#define OV5_ALPHA_PART		0x08
68#define OV5_DMA_DELAY		0x04
69#define OV5_DONATE_CPU		0x02
70#define OV5_MSI			0x01
71
72/* 9-12: max cpus */
73#define OV5_MAX_CPUS(n)		((MAX_CPUS >> (3*8 - (n)*8)) & 0xff)
74
75/* 13-14: LoPAPR Level */
76#define LOPAPR_LEVEL		0x0101	/* 1.1 */
77#define OV5_LOPAPR_LEVEL(n)	((LOPAPR_LEVEL >> (8 - (n)*8)) & 0xff)
78
79/* byte 17: Platform Facilities */
80#define OV5_RNG			0x80
81#define OV5_COMP_ENG		0x40
82#define OV5_ENC_ENG		0x20
83
84/* byte 21: Sub-Processors */
85#define OV5_NO_SUBPROCS		0
86#define OV5_SUBPROCS		1
87
88/* byte 23: interrupt controller */
89#define OV5_INTC_XICS		0
90
91/* byte 24: MMU */
92#define OV5_MMU_INDEX		24
93#define OV5_MMU_HPT		0
94#define OV5_MMU_RADIX		0x40
95#define OV5_MMU_EITHER		0x80
96#define OV5_MMU_DYNAMIC		0xc0
97
98/* byte 25: HPT MMU Extensions */
99#define OV5_HPT_EXT_INDEX	25
100#define OV5_HPT_GTSE		0x40
101
102/* byte 26: Radix MMU Extensions */
103#define OV5_RADIX_EXT_INDEX	26
104#define OV5_RADIX_GTSE		0x40
105
106
107struct pvr {
108	uint32_t	mask;
109	uint32_t	val;
110};
111
112struct opt_vec_ignore {
113	char	data[2];
114} __packed;
115
116struct opt_vec4 {
117	char data[3];
118} __packed;
119
120struct opt_vec5 {
121	char data[27];
122} __packed;
123
124static struct ibm_arch_vec {
125	struct pvr		pvr_list[7];
126	uint8_t			num_opts;
127	struct opt_vec_ignore	vec1;
128	struct opt_vec_ignore	vec2;
129	struct opt_vec_ignore	vec3;
130	struct opt_vec4		vec4;
131	struct opt_vec5		vec5;
132} __packed ibm_arch_vec = {
133	/* pvr_list */ {
134		{ htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8) },
135		{ htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8E) },
136		{ htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P8NVL) },
137		{ htobe32(PVR_CPU_MASK), htobe32(PVR_CPU_P9) },
138		{ htobe32(PVR_ISA_MASK), htobe32(PVR_ISA_207) },
139		{ htobe32(PVR_ISA_MASK), htobe32(PVR_ISA_300) },
140		{ 0, 0xffffffffu }			/* terminator */
141	},
142	4,	/* num_opts (4 actually means 5 option vectors) */
143	{ OV_IGN_LEN, OV_IGN },		/* OV1 */
144	{ OV_IGN_LEN, OV_IGN },		/* OV2 */
145	{ OV_IGN_LEN, OV_IGN },		/* OV3 */
146	/* OV4 (can't be ignored) */ {
147		sizeof(struct opt_vec4) - 2,	/* length (n-2) */
148		0,
149		10 /* Minimum VP entitled capacity percentage * 100
150		    * (if absent assume 10%) */
151	},
152	/* OV5 */ {
153		sizeof(struct opt_vec5) - 2,	/* length (n-2) */
154		0,				/* don't ignore */
155		OV5_LPAR | OV5_SPLPAR | OV5_LP | OV5_MSI,
156		0,
157		0,	/* Cooperative Memory Over-commitment */
158		0,	/* Associativity Information Option */
159		0,	/* Binary Option Controls */
160		0,	/* Reserved */
161		0,	/* Reserved */
162		OV5_MAX_CPUS(0),
163		OV5_MAX_CPUS(1),		/* 10 */
164		OV5_MAX_CPUS(2),
165		OV5_MAX_CPUS(3),
166		OV5_LOPAPR_LEVEL(0),
167		OV5_LOPAPR_LEVEL(1),
168		0,	/* Reserved */
169		0,	/* Reserved */
170		0,	/* Platform Facilities */
171		0,	/* Reserved */
172		0,	/* Reserved */
173		0,	/* Reserved */		/* 20 */
174		OV5_NO_SUBPROCS,
175		0,	/* DRMEM_V2 */
176		OV5_INTC_XICS,
177		OV5_MMU_HPT,
178		0,
179		0
180	}
181};
182
183int
184ppc64_cas(void)
185{
186	phandle_t pkg;
187	ihandle_t inst;
188	cell_t err;
189	uint8_t buf[16], idx, val;
190	int i, len, rc, radix_mmu;
191	const char *var;
192	char *ov5;
193
194	pkg = OF_finddevice("/chosen");
195	if (pkg == -1) {
196		printf("cas: couldn't find /chosen\n");
197		return (-1);
198	}
199
200	len = OF_getprop(pkg, "ibm,arch-vec-5-platform-support", buf,
201	    sizeof(buf));
202	if (len == -1)
203		/* CAS not supported */
204		return (0);
205
206	radix_mmu = 0;
207	ov5 = ibm_arch_vec.vec5.data;
208	for (i = 0; i < len; i += 2) {
209		idx = buf[i];
210		val = buf[i + 1];
211		DPRINTF("idx 0x%02x val 0x%02x\n", idx, val);
212
213		switch (idx) {
214		case OV5_MMU_INDEX:
215			/*
216			 * Note that testing for OV5_MMU_RADIX/OV5_MMU_EITHER
217			 * also covers OV5_MMU_DYNAMIC.
218			 */
219			if ((val & OV5_MMU_RADIX) || (val & OV5_MMU_EITHER))
220				radix_mmu = 1;
221			break;
222
223		case OV5_RADIX_EXT_INDEX:
224			if (val & OV5_RADIX_GTSE)
225				ov5[idx] = OV5_RADIX_GTSE;
226			break;
227
228		case OV5_HPT_EXT_INDEX:
229		default:
230			break;
231		}
232	}
233
234	if (!radix_mmu)
235		/*
236		 * If radix is not supported, set radix_mmu to 0 to avoid
237		 * the kernel trying to use it and panic.
238		 */
239		setenv("radix_mmu", "0", 1);
240	else if ((var = getenv("radix_mmu")) != NULL && var[0] == '0')
241		radix_mmu = 0;
242	else
243		ov5[OV5_MMU_INDEX] = OV5_MMU_RADIX;
244
245	inst = OF_open("/");
246	if (inst == -1) {
247		printf("cas: failed to open / node\n");
248		return (-1);
249	}
250
251	DPRINTF("MMU 0x%02x RADIX_EXT 0x%02x\n",
252	    ov5[OV5_MMU_INDEX], ov5[OV5_RADIX_EXT_INDEX]);
253	rc = OF_call_method("ibm,client-architecture-support",
254	    inst, 1, 1, &ibm_arch_vec, &err);
255	if (rc != 0 || err) {
256		printf("cas: CAS method returned an error: rc %d err %jd\n",
257		    rc, (intmax_t)err);
258		rc = -1;
259	}
260
261	OF_close(inst);
262	printf("cas: selected %s MMU\n", radix_mmu ? "radix" : "hash");
263	return (rc);
264}
265