/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright IBM Corp. 2024
 */

#ifndef __ASM_S390_MACHINE_H
#define __ASM_S390_MACHINE_H

#include <linux/const.h>

#define MFEATURE_LOWCORE	0
#define MFEATURE_PCI_MIO	1
#define MFEATURE_SCC		2
#define MFEATURE_TLB_GUEST	3
#define MFEATURE_TX		4
#define MFEATURE_ESOP		5
#define MFEATURE_DIAG9C		6
#define MFEATURE_VM		7
#define MFEATURE_KVM		8
#define MFEATURE_LPAR		9
#define MFEATURE_DIAG288	10

#ifndef __ASSEMBLER__

#include <linux/bitops.h>
#include <asm/alternative.h>

extern unsigned long machine_features[1];

#define MAX_MFEATURE_BIT (sizeof(machine_features) * BITS_PER_BYTE)

static inline void __set_machine_feature(unsigned int nr, unsigned long *mfeatures)
{
	if (nr >= MAX_MFEATURE_BIT)
		return;
	__set_bit(nr, mfeatures);
}

static inline void set_machine_feature(unsigned int nr)
{
	__set_machine_feature(nr, machine_features);
}

static inline void __clear_machine_feature(unsigned int nr, unsigned long *mfeatures)
{
	if (nr >= MAX_MFEATURE_BIT)
		return;
	__clear_bit(nr, mfeatures);
}

static inline void clear_machine_feature(unsigned int nr)
{
	__clear_machine_feature(nr, machine_features);
}

static bool __test_machine_feature(unsigned int nr, unsigned long *mfeatures)
{
	if (nr >= MAX_MFEATURE_BIT)
		return false;
	return test_bit(nr, mfeatures);
}

static bool test_machine_feature(unsigned int nr)
{
	return __test_machine_feature(nr, machine_features);
}

static __always_inline bool __test_machine_feature_constant(unsigned int nr)
{
	asm goto(
		ALTERNATIVE("brcl 15,%l[l_no]", "brcl 0,0", ALT_FEATURE(%[nr]))
		:
		: [nr] "i" (nr)
		:
		: l_no);
	return true;
l_no:
	return false;
}

#define DEFINE_MACHINE_HAS_FEATURE(name, feature)				\
static __always_inline bool machine_has_##name(void)				\
{										\
	if (!__is_defined(__DECOMPRESSOR) && __builtin_constant_p(feature))	\
		return __test_machine_feature_constant(feature);		\
	return test_machine_feature(feature);					\
}

DEFINE_MACHINE_HAS_FEATURE(relocated_lowcore, MFEATURE_LOWCORE)
DEFINE_MACHINE_HAS_FEATURE(scc, MFEATURE_SCC)
DEFINE_MACHINE_HAS_FEATURE(tlb_guest, MFEATURE_TLB_GUEST)
DEFINE_MACHINE_HAS_FEATURE(tx, MFEATURE_TX)
DEFINE_MACHINE_HAS_FEATURE(esop, MFEATURE_ESOP)
DEFINE_MACHINE_HAS_FEATURE(diag9c, MFEATURE_DIAG9C)
DEFINE_MACHINE_HAS_FEATURE(vm, MFEATURE_VM)
DEFINE_MACHINE_HAS_FEATURE(kvm, MFEATURE_KVM)
DEFINE_MACHINE_HAS_FEATURE(lpar, MFEATURE_LPAR)

#define machine_is_vm	machine_has_vm
#define machine_is_kvm	machine_has_kvm
#define machine_is_lpar	machine_has_lpar

#endif /* __ASSEMBLER__ */
#endif /* __ASM_S390_MACHINE_H */
