162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <asm/byteorder.h>
362306a36Sopenharmony_ci#include <asm/vphn.h>
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci/*
662306a36Sopenharmony_ci * The associativity domain numbers are returned from the hypervisor as a
762306a36Sopenharmony_ci * stream of mixed 16-bit and 32-bit fields. The stream is terminated by the
862306a36Sopenharmony_ci * special value of "all ones" (aka. 0xffff) and its size may not exceed 48
962306a36Sopenharmony_ci * bytes.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci *    --- 16-bit fields -->
1262306a36Sopenharmony_ci *  _________________________
1362306a36Sopenharmony_ci *  |  0  |  1  |  2  |  3  |   be_packed[0]
1462306a36Sopenharmony_ci *  ------+-----+-----+------
1562306a36Sopenharmony_ci *  _________________________
1662306a36Sopenharmony_ci *  |  4  |  5  |  6  |  7  |   be_packed[1]
1762306a36Sopenharmony_ci *  -------------------------
1862306a36Sopenharmony_ci *            ...
1962306a36Sopenharmony_ci *  _________________________
2062306a36Sopenharmony_ci *  | 20  | 21  | 22  | 23  |   be_packed[5]
2162306a36Sopenharmony_ci *  -------------------------
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * Convert to the sequence they would appear in the ibm,associativity property.
2462306a36Sopenharmony_ci */
2562306a36Sopenharmony_cistatic int vphn_unpack_associativity(const long *packed, __be32 *unpacked)
2662306a36Sopenharmony_ci{
2762306a36Sopenharmony_ci	__be64 be_packed[VPHN_REGISTER_COUNT];
2862306a36Sopenharmony_ci	int i, nr_assoc_doms = 0;
2962306a36Sopenharmony_ci	const __be16 *field = (const __be16 *) be_packed;
3062306a36Sopenharmony_ci	u16 last = 0;
3162306a36Sopenharmony_ci	bool is_32bit = false;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define VPHN_FIELD_UNUSED	(0xffff)
3462306a36Sopenharmony_ci#define VPHN_FIELD_MSB		(0x8000)
3562306a36Sopenharmony_ci#define VPHN_FIELD_MASK		(~VPHN_FIELD_MSB)
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	/* Let's fix the values returned by plpar_hcall9() */
3862306a36Sopenharmony_ci	for (i = 0; i < VPHN_REGISTER_COUNT; i++)
3962306a36Sopenharmony_ci		be_packed[i] = cpu_to_be64(packed[i]);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	for (i = 1; i < VPHN_ASSOC_BUFSIZE; i++) {
4262306a36Sopenharmony_ci		u16 new = be16_to_cpup(field++);
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci		if (is_32bit) {
4562306a36Sopenharmony_ci			/*
4662306a36Sopenharmony_ci			 * Let's concatenate the 16 bits of this field to the
4762306a36Sopenharmony_ci			 * 15 lower bits of the previous field
4862306a36Sopenharmony_ci			 */
4962306a36Sopenharmony_ci			unpacked[++nr_assoc_doms] =
5062306a36Sopenharmony_ci				cpu_to_be32(last << 16 | new);
5162306a36Sopenharmony_ci			is_32bit = false;
5262306a36Sopenharmony_ci		} else if (new == VPHN_FIELD_UNUSED)
5362306a36Sopenharmony_ci			/* This is the list terminator */
5462306a36Sopenharmony_ci			break;
5562306a36Sopenharmony_ci		else if (new & VPHN_FIELD_MSB) {
5662306a36Sopenharmony_ci			/* Data is in the lower 15 bits of this field */
5762306a36Sopenharmony_ci			unpacked[++nr_assoc_doms] =
5862306a36Sopenharmony_ci				cpu_to_be32(new & VPHN_FIELD_MASK);
5962306a36Sopenharmony_ci		} else {
6062306a36Sopenharmony_ci			/*
6162306a36Sopenharmony_ci			 * Data is in the lower 15 bits of this field
6262306a36Sopenharmony_ci			 * concatenated with the next 16 bit field
6362306a36Sopenharmony_ci			 */
6462306a36Sopenharmony_ci			last = new;
6562306a36Sopenharmony_ci			is_32bit = true;
6662306a36Sopenharmony_ci		}
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	/* The first cell contains the length of the property */
7062306a36Sopenharmony_ci	unpacked[0] = cpu_to_be32(nr_assoc_doms);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return nr_assoc_doms;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/* NOTE: This file is included by a selftest and built in userspace. */
7662306a36Sopenharmony_ci#ifdef __KERNEL__
7762306a36Sopenharmony_ci#include <asm/hvcall.h>
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cilong hcall_vphn(unsigned long cpu, u64 flags, __be32 *associativity)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	long rc;
8262306a36Sopenharmony_ci	long retbuf[PLPAR_HCALL9_BUFSIZE] = {0};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	rc = plpar_hcall9(H_HOME_NODE_ASSOCIATIVITY, retbuf, flags, cpu);
8562306a36Sopenharmony_ci	if (rc == H_SUCCESS)
8662306a36Sopenharmony_ci		vphn_unpack_associativity(retbuf, associativity);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	return rc;
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci#endif
91