/* SPDX-License-Identifier: GPL-2.0-only OR MIT */


#ifndef _LINUX_MFD_MACSMC_H
#define _LINUX_MFD_MACSMC_H

#include <linux/soc/apple/rtkit.h>


typedef u32 smc_key;


#define SMC_KEY(s) (smc_key)(_SMC_KEY(#s))
#define _SMC_KEY(s) (((s)[0] << 24) | ((s)[1] << 16) | ((s)[2] << 8) | (s)[3])
#define __SMC_KEY(a, b, c, d) (((u32)(a) << 24) | ((u32)(b) << 16) | ((u32)(c) << 8) | ((u32)(d)))

#define APPLE_SMC_READABLE BIT(7)
#define APPLE_SMC_WRITABLE BIT(6)
#define APPLE_SMC_FUNCTION BIT(4)


struct apple_smc_key_info {
	u32 type_code;
	u8 size;
	u8 flags;
};


enum apple_smc_boot_stage {
	APPLE_SMC_BOOTING,
	APPLE_SMC_INITIALIZED,
	APPLE_SMC_ERROR_NO_SHMEM,
	APPLE_SMC_ERROR_CRASHED
};


struct apple_smc {
	struct device *dev;

	u32 key_count;
	smc_key first_key;
	smc_key last_key;

	struct blocking_notifier_head event_handlers;

	struct apple_rtkit *rtk;

	struct completion init_done;
	enum apple_smc_boot_stage boot_stage;

	struct resource *sram;
	void __iomem *sram_base;
	struct apple_rtkit_shmem shmem;

	unsigned int msg_id;

	bool atomic_mode;
	bool atomic_pending;
	struct completion cmd_done;
	u64 cmd_ret;

	struct mutex mutex;
	spinlock_t lock;
};


int apple_smc_read(struct apple_smc *smc, smc_key key, void *buf, size_t size);


int apple_smc_write(struct apple_smc *smc, smc_key key, const void *buf, size_t size);


int apple_smc_enter_atomic(struct apple_smc *smc);


int apple_smc_write_atomic(struct apple_smc *smc, smc_key key, const void *buf, size_t size);


int apple_smc_rw(struct apple_smc *smc, smc_key key, const void *wbuf, size_t wsize,
		 void *rbuf, size_t rsize);


int apple_smc_get_key_by_index(struct apple_smc *smc, int index, smc_key *key);


int apple_smc_get_key_info(struct apple_smc *smc, smc_key key, struct apple_smc_key_info *info);


static inline bool apple_smc_key_exists(struct apple_smc *smc, smc_key key)
{
	return apple_smc_get_key_info(smc, key, NULL) >= 0;
}

#define APPLE_SMC_TYPE_OPS(type) \
	static inline int apple_smc_read_##type(struct apple_smc *smc, smc_key key, type *p) \
	{ \
		int ret = apple_smc_read(smc, key, p, sizeof(*p)); \
		return (ret < 0) ? ret : ((ret != sizeof(*p)) ? -EINVAL : 0); \
	} \
	static inline int apple_smc_write_##type(struct apple_smc *smc, smc_key key, type p) \
	{ \
		return apple_smc_write(smc, key, &p, sizeof(p)); \
	} \
	static inline int apple_smc_write_##type##_atomic(struct apple_smc *smc, smc_key key, type p) \
	{ \
		return apple_smc_write_atomic(smc, key, &p, sizeof(p)); \
	} \
	static inline int apple_smc_rw_##type(struct apple_smc *smc, smc_key key, \
					      type w, type *r) \
	{ \
		int ret = apple_smc_rw(smc, key, &w, sizeof(w), r, sizeof(*r)); \
		return (ret < 0) ? ret : ((ret != sizeof(*r)) ? -EINVAL : 0); \
	}

APPLE_SMC_TYPE_OPS(u64)
APPLE_SMC_TYPE_OPS(u32)
APPLE_SMC_TYPE_OPS(u16)
APPLE_SMC_TYPE_OPS(u8)
APPLE_SMC_TYPE_OPS(s64)
APPLE_SMC_TYPE_OPS(s32)
APPLE_SMC_TYPE_OPS(s16)
APPLE_SMC_TYPE_OPS(s8)

static inline int apple_smc_read_flag(struct apple_smc *smc, smc_key key, bool *flag)
{
	u8 val;
	int ret = apple_smc_read_u8(smc, key, &val);

	if (ret < 0)
		return ret;

	*flag = val ? true : false;
	return ret;
}

static inline int apple_smc_write_flag(struct apple_smc *smc, smc_key key, bool state)
{
	return apple_smc_write_u8(smc, key, state ? 1 : 0);
}

static inline int apple_smc_write_flag_atomic(struct apple_smc *smc, smc_key key, bool state)
{
	return apple_smc_write_u8_atomic(smc, key, state ? 1 : 0);
}

#endif
