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 <sys/cdefs.h>
27__FBSDID("$FreeBSD$");
28
29#include <openfirm.h>
30#include <stand.h>
31
32/* PVR */
33#define PVR_CPU_P8E		0x004b0000
34#define PVR_CPU_P8NVL		0x004c0000
35#define PVR_CPU_P8		0x004d0000
36#define PVR_CPU_P9		0x004e0000
37#define PVR_CPU_MASK		0xffff0000
38
39#define PVR_ISA_207		0x0f000004
40#define PVR_ISA_300		0x0f000005
41#define PVR_ISA_MASK		0xffffffff
42
43/* loader version of kernel's CPU_MAXSIZE */
44#define MAX_CPUS		((uint32_t)256u)
45
46/* Option Vectors' settings */
47
48/* length of ignored OV */
49#define OV_IGN_LEN		0
50
51/* byte 1 (of any OV) */
52#define OV_IGN			0x80
53
54/* Option Vector 5 */
55
56/* byte 2 */
57#define OV5_LPAR		0x80
58#define OV5_SPLPAR		0x40
59#define OV5_DRMEM		0x20
60#define OV5_LP			0x10
61#define OV5_ALPHA_PART		0x08
62#define OV5_DMA_DELAY		0x04
63#define OV5_DONATE_CPU		0x02
64#define OV5_MSI			0x01
65
66/* 9-12: max cpus */
67#define OV5_MAX_CPUS(n)		((MAX_CPUS >> (3*8 - (n)*8)) & 0xff)
68
69/* 13-14: LoPAPR Level */
70#define LOPAPR_LEVEL		0x0101	/* 1.1 */
71#define OV5_LOPAPR_LEVEL(n)	((LOPAPR_LEVEL >> (8 - (n)*8)) & 0xff)
72
73/* byte 17: Platform Facilities */
74#define OV5_RNG			0x80
75#define OV5_COMP_ENG		0x40
76#define OV5_ENC_ENG		0x20
77
78/* byte 21: Sub-Processors */
79#define OV5_NO_SUBPROCS		0
80#define OV5_SUBPROCS		1
81
82/* byte 23: interrupt controller */
83#define OV5_INTC_XICS		0
84
85/* byte 24: MMU */
86#define OV5_MMU_HPT		0
87
88/* byte 25: HPT MMU Extensions */
89#define OV5_HPT_EXT_NONE	0
90
91/* byte 26: Radix MMU Extensions */
92#define OV5_RPT_EXT_NONE	0
93
94
95struct pvr {
96	uint32_t	mask;
97	uint32_t	val;
98};
99
100struct opt_vec_ignore {
101	char	data[2];
102} __packed;
103
104struct opt_vec4 {
105	char data[3];
106} __packed;
107
108struct opt_vec5 {
109	char data[27];
110} __packed;
111
112static struct ibm_arch_vec {
113	struct pvr		pvr_list[7];
114	uint8_t			num_opts;
115	struct opt_vec_ignore	vec1;
116	struct opt_vec_ignore	vec2;
117	struct opt_vec_ignore	vec3;
118	struct opt_vec4		vec4;
119	struct opt_vec5		vec5;
120} __packed ibm_arch_vec = {
121	/* pvr_list */ {
122		{ PVR_CPU_MASK, PVR_CPU_P8 },		/* POWER8 */
123		{ PVR_CPU_MASK, PVR_CPU_P8E },		/* POWER8E */
124		{ PVR_CPU_MASK, PVR_CPU_P8NVL },	/* POWER8NVL */
125		{ PVR_CPU_MASK, PVR_CPU_P9 },		/* POWER9 */
126		{ PVR_ISA_MASK, PVR_ISA_207 },		/* All ISA 2.07 */
127		{ PVR_ISA_MASK, PVR_ISA_300 },		/* All ISA 3.00 */
128		{ 0, 0xffffffffu }			/* terminator */
129	},
130	4,	/* num_opts (4 actually means 5 option vectors) */
131	{ OV_IGN_LEN, OV_IGN },		/* OV1 */
132	{ OV_IGN_LEN, OV_IGN },		/* OV2 */
133	{ OV_IGN_LEN, OV_IGN },		/* OV3 */
134	/* OV4 (can't be ignored) */ {
135		sizeof(struct opt_vec4) - 2,	/* length (n-2) */
136		0,
137		10 /* Minimum VP entitled capacity percentage * 100
138		    * (if absent assume 10%) */
139	},
140	/* OV5 */ {
141		sizeof(struct opt_vec5) - 2,	/* length (n-2) */
142		0,				/* don't ignore */
143		OV5_LPAR | OV5_SPLPAR | OV5_LP | OV5_MSI,
144		0,
145		0,	/* Cooperative Memory Over-commitment */
146		0,	/* Associativity Information Option */
147		0,	/* Binary Option Controls */
148		0,	/* Reserved */
149		0,	/* Reserved */
150		OV5_MAX_CPUS(0),
151		OV5_MAX_CPUS(1),		/* 10 */
152		OV5_MAX_CPUS(2),
153		OV5_MAX_CPUS(3),
154		OV5_LOPAPR_LEVEL(0),
155		OV5_LOPAPR_LEVEL(1),
156		0,	/* Reserved */
157		0,	/* Reserved */
158		0,	/* Platform Facilities */
159		0,	/* Reserved */
160		0,	/* Reserved */
161		0,	/* Reserved */		/* 20 */
162		OV5_NO_SUBPROCS,
163		0,	/* DRMEM_V2 */
164		OV5_INTC_XICS,
165		OV5_MMU_HPT,
166		OV5_HPT_EXT_NONE,
167		OV5_RPT_EXT_NONE
168	}
169};
170
171static __inline register_t
172mfpvr(void)
173{
174	register_t value;
175
176	__asm __volatile ("mfpvr %0" : "=r"(value));
177
178	return (value);
179}
180
181static __inline int
182ppc64_hv(void)
183{
184	int hv;
185
186	/* PSL_HV is bit 3 of 64-bit MSR */
187	__asm __volatile ("mfmsr %0\n\t"
188		"rldicl %0,%0,4,63" : "=r"(hv));
189
190	return (hv);
191}
192
193int
194ppc64_cas(void)
195{
196	int rc;
197	ihandle_t ihandle;
198	cell_t err;
199
200	/* Perform CAS only for POWER8 and later cores */
201	switch (mfpvr() & PVR_CPU_MASK) {
202		case PVR_CPU_P8:
203		case PVR_CPU_P8E:
204		case PVR_CPU_P8NVL:
205		case PVR_CPU_P9:
206			break;
207		default:
208			return (0);
209	}
210
211	/* Skip CAS when running on PowerNV */
212	if (ppc64_hv())
213		return (0);
214
215	ihandle = OF_open("/");
216	if (ihandle == -1) {
217		printf("cas: failed to open / node\n");
218		return (-1);
219	}
220
221	if (rc = OF_call_method("ibm,client-architecture-support",
222	    ihandle, 1, 1, &ibm_arch_vec, &err))
223		printf("cas: failed to call CAS method\n");
224	else if (err) {
225		printf("cas: error: 0x%08lX\n", err);
226		rc = -1;
227	}
228
229	OF_close(ihandle);
230	return (rc);
231}
232