179e29cb8SLiming Sun // SPDX-License-Identifier: GPL-2.0+
279e29cb8SLiming Sun /*
379e29cb8SLiming Sun  * Mellanox boot control driver
479e29cb8SLiming Sun  *
579e29cb8SLiming Sun  * This driver provides a sysfs interface for systems management
679e29cb8SLiming Sun  * software to manage reset-time actions.
779e29cb8SLiming Sun  *
879e29cb8SLiming Sun  * Copyright (C) 2019 Mellanox Technologies
979e29cb8SLiming Sun  */
1079e29cb8SLiming Sun 
1179e29cb8SLiming Sun #include <linux/acpi.h>
1279e29cb8SLiming Sun #include <linux/arm-smccc.h>
1382c3a0b7SLiming Sun #include <linux/delay.h>
147e38a742SDavid Thompson #include <linux/if_ether.h>
15e9d1b2d0SLiming Sun #include <linux/iopoll.h>
1679e29cb8SLiming Sun #include <linux/module.h>
1779e29cb8SLiming Sun #include <linux/platform_device.h>
1879e29cb8SLiming Sun 
1979e29cb8SLiming Sun #include "mlxbf-bootctl.h"
2079e29cb8SLiming Sun 
2179e29cb8SLiming Sun #define MLXBF_BOOTCTL_SB_SECURE_MASK		0x03
2279e29cb8SLiming Sun #define MLXBF_BOOTCTL_SB_TEST_MASK		0x0c
23d4eef752SDavid Thompson #define MLXBF_BOOTCTL_SB_DEV_MASK		BIT(4)
2479e29cb8SLiming Sun 
2579e29cb8SLiming Sun #define MLXBF_SB_KEY_NUM			4
2679e29cb8SLiming Sun 
2779e29cb8SLiming Sun /* UUID used to probe ATF service. */
2879e29cb8SLiming Sun static const char *mlxbf_bootctl_svc_uuid_str =
2979e29cb8SLiming Sun 	"89c036b4-e7d7-11e6-8797-001aca00bfc4";
3079e29cb8SLiming Sun 
3179e29cb8SLiming Sun struct mlxbf_bootctl_name {
3279e29cb8SLiming Sun 	u32 value;
3379e29cb8SLiming Sun 	const char *name;
3479e29cb8SLiming Sun };
3579e29cb8SLiming Sun 
3679e29cb8SLiming Sun static struct mlxbf_bootctl_name boot_names[] = {
3779e29cb8SLiming Sun 	{ MLXBF_BOOTCTL_EXTERNAL, "external" },
3879e29cb8SLiming Sun 	{ MLXBF_BOOTCTL_EMMC, "emmc" },
3979e29cb8SLiming Sun 	{ MLNX_BOOTCTL_SWAP_EMMC, "swap_emmc" },
4079e29cb8SLiming Sun 	{ MLXBF_BOOTCTL_EMMC_LEGACY, "emmc_legacy" },
4179e29cb8SLiming Sun 	{ MLXBF_BOOTCTL_NONE, "none" },
4279e29cb8SLiming Sun };
4379e29cb8SLiming Sun 
44d4eef752SDavid Thompson enum {
45d4eef752SDavid Thompson 	MLXBF_BOOTCTL_SB_LIFECYCLE_PRODUCTION = 0,
46d4eef752SDavid Thompson 	MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE = 1,
47d4eef752SDavid Thompson 	MLXBF_BOOTCTL_SB_LIFECYCLE_GA_NON_SECURE = 2,
48d4eef752SDavid Thompson 	MLXBF_BOOTCTL_SB_LIFECYCLE_RMA = 3
49d4eef752SDavid Thompson };
50d4eef752SDavid Thompson 
5179e29cb8SLiming Sun static const char * const mlxbf_bootctl_lifecycle_states[] = {
52d4eef752SDavid Thompson 	[MLXBF_BOOTCTL_SB_LIFECYCLE_PRODUCTION] = "Production",
53d4eef752SDavid Thompson 	[MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE] = "GA Secured",
54d4eef752SDavid Thompson 	[MLXBF_BOOTCTL_SB_LIFECYCLE_GA_NON_SECURE] = "GA Non-Secured",
55d4eef752SDavid Thompson 	[MLXBF_BOOTCTL_SB_LIFECYCLE_RMA] = "RMA",
5679e29cb8SLiming Sun };
5779e29cb8SLiming Sun 
58e9d1b2d0SLiming Sun /* Log header format. */
59e9d1b2d0SLiming Sun #define MLXBF_RSH_LOG_TYPE_MASK		GENMASK_ULL(59, 56)
60e9d1b2d0SLiming Sun #define MLXBF_RSH_LOG_LEN_MASK		GENMASK_ULL(54, 48)
61e9d1b2d0SLiming Sun #define MLXBF_RSH_LOG_LEVEL_MASK	GENMASK_ULL(7, 0)
62e9d1b2d0SLiming Sun 
63e9d1b2d0SLiming Sun /* Log module ID and type (only MSG type in Linux driver for now). */
64e9d1b2d0SLiming Sun #define MLXBF_RSH_LOG_TYPE_MSG		0x04ULL
65e9d1b2d0SLiming Sun 
66e9d1b2d0SLiming Sun /* Log ctl/data register offset. */
67e9d1b2d0SLiming Sun #define MLXBF_RSH_SCRATCH_BUF_CTL_OFF	0
68e9d1b2d0SLiming Sun #define MLXBF_RSH_SCRATCH_BUF_DATA_OFF	0x10
69e9d1b2d0SLiming Sun 
70e9d1b2d0SLiming Sun /* Log message levels. */
71e9d1b2d0SLiming Sun enum {
72e9d1b2d0SLiming Sun 	MLXBF_RSH_LOG_INFO,
73e9d1b2d0SLiming Sun 	MLXBF_RSH_LOG_WARN,
74e9d1b2d0SLiming Sun 	MLXBF_RSH_LOG_ERR,
75e9d1b2d0SLiming Sun 	MLXBF_RSH_LOG_ASSERT
76e9d1b2d0SLiming Sun };
77e9d1b2d0SLiming Sun 
7882c3a0b7SLiming Sun /* Mapped pointer for RSH_BOOT_FIFO_DATA and RSH_BOOT_FIFO_COUNT register. */
7982c3a0b7SLiming Sun static void __iomem *mlxbf_rsh_boot_data;
8082c3a0b7SLiming Sun static void __iomem *mlxbf_rsh_boot_cnt;
8182c3a0b7SLiming Sun 
82e9d1b2d0SLiming Sun /* Mapped pointer for rsh log semaphore/ctrl/data register. */
83e9d1b2d0SLiming Sun static void __iomem *mlxbf_rsh_semaphore;
84e9d1b2d0SLiming Sun static void __iomem *mlxbf_rsh_scratch_buf_ctl;
85e9d1b2d0SLiming Sun static void __iomem *mlxbf_rsh_scratch_buf_data;
86e9d1b2d0SLiming Sun 
87e9d1b2d0SLiming Sun /* Rsh log levels. */
88e9d1b2d0SLiming Sun static const char * const mlxbf_rsh_log_level[] = {
89e9d1b2d0SLiming Sun 	"INFO", "WARN", "ERR", "ASSERT"};
90e9d1b2d0SLiming Sun 
91b18a97edSAsmaa Mnebhi static DEFINE_MUTEX(icm_ops_lock);
92e3205d41SAsmaa Mnebhi static DEFINE_MUTEX(os_up_lock);
937e38a742SDavid Thompson static DEFINE_MUTEX(mfg_ops_lock);
947e38a742SDavid Thompson 
957e38a742SDavid Thompson /*
967e38a742SDavid Thompson  * Objects are stored within the MFG partition per type.
977e38a742SDavid Thompson  * Type 0 is not supported.
987e38a742SDavid Thompson  */
997e38a742SDavid Thompson enum {
1007e38a742SDavid Thompson 	MLNX_MFG_TYPE_OOB_MAC = 1,
1017e38a742SDavid Thompson 	MLNX_MFG_TYPE_OPN_0,
1027e38a742SDavid Thompson 	MLNX_MFG_TYPE_OPN_1,
1037e38a742SDavid Thompson 	MLNX_MFG_TYPE_OPN_2,
1047e38a742SDavid Thompson 	MLNX_MFG_TYPE_SKU_0,
1057e38a742SDavid Thompson 	MLNX_MFG_TYPE_SKU_1,
1067e38a742SDavid Thompson 	MLNX_MFG_TYPE_SKU_2,
1077e38a742SDavid Thompson 	MLNX_MFG_TYPE_MODL_0,
1087e38a742SDavid Thompson 	MLNX_MFG_TYPE_MODL_1,
1097e38a742SDavid Thompson 	MLNX_MFG_TYPE_MODL_2,
1107e38a742SDavid Thompson 	MLNX_MFG_TYPE_SN_0,
1117e38a742SDavid Thompson 	MLNX_MFG_TYPE_SN_1,
1127e38a742SDavid Thompson 	MLNX_MFG_TYPE_SN_2,
1137e38a742SDavid Thompson 	MLNX_MFG_TYPE_UUID_0,
1147e38a742SDavid Thompson 	MLNX_MFG_TYPE_UUID_1,
1157e38a742SDavid Thompson 	MLNX_MFG_TYPE_UUID_2,
1167e38a742SDavid Thompson 	MLNX_MFG_TYPE_UUID_3,
1177e38a742SDavid Thompson 	MLNX_MFG_TYPE_UUID_4,
1187e38a742SDavid Thompson 	MLNX_MFG_TYPE_REV,
1197e38a742SDavid Thompson };
1207e38a742SDavid Thompson 
1217e38a742SDavid Thompson #define MLNX_MFG_OPN_VAL_LEN         24
1227e38a742SDavid Thompson #define MLNX_MFG_SKU_VAL_LEN         24
1237e38a742SDavid Thompson #define MLNX_MFG_MODL_VAL_LEN        24
1247e38a742SDavid Thompson #define MLNX_MFG_SN_VAL_LEN          24
1257e38a742SDavid Thompson #define MLNX_MFG_UUID_VAL_LEN        40
1267e38a742SDavid Thompson #define MLNX_MFG_REV_VAL_LEN         8
1277e38a742SDavid Thompson #define MLNX_MFG_VAL_QWORD_CNT(type) \
1287e38a742SDavid Thompson 	(MLNX_MFG_##type##_VAL_LEN / sizeof(u64))
1297e38a742SDavid Thompson 
1307e38a742SDavid Thompson /*
1317e38a742SDavid Thompson  * The MAC address consists of 6 bytes (2 digits each) separated by ':'.
1327e38a742SDavid Thompson  * The expected format is: "XX:XX:XX:XX:XX:XX"
1337e38a742SDavid Thompson  */
1347e38a742SDavid Thompson #define MLNX_MFG_OOB_MAC_FORMAT_LEN \
1357e38a742SDavid Thompson 	((ETH_ALEN * 2) + (ETH_ALEN - 1))
136b18a97edSAsmaa Mnebhi 
13779e29cb8SLiming Sun /* ARM SMC call which is atomic and no need for lock. */
mlxbf_bootctl_smc(unsigned int smc_op,int smc_arg)13879e29cb8SLiming Sun static int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
13979e29cb8SLiming Sun {
14079e29cb8SLiming Sun 	struct arm_smccc_res res;
14179e29cb8SLiming Sun 
14279e29cb8SLiming Sun 	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
14379e29cb8SLiming Sun 
14479e29cb8SLiming Sun 	return res.a0;
14579e29cb8SLiming Sun }
14679e29cb8SLiming Sun 
14779e29cb8SLiming Sun /* Return the action in integer or an error code. */
mlxbf_bootctl_reset_action_to_val(const char * action)14879e29cb8SLiming Sun static int mlxbf_bootctl_reset_action_to_val(const char *action)
14979e29cb8SLiming Sun {
15079e29cb8SLiming Sun 	int i;
15179e29cb8SLiming Sun 
15279e29cb8SLiming Sun 	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
15379e29cb8SLiming Sun 		if (sysfs_streq(boot_names[i].name, action))
15479e29cb8SLiming Sun 			return boot_names[i].value;
15579e29cb8SLiming Sun 
15679e29cb8SLiming Sun 	return -EINVAL;
15779e29cb8SLiming Sun }
15879e29cb8SLiming Sun 
15979e29cb8SLiming Sun /* Return the action in string. */
mlxbf_bootctl_action_to_string(int action)16079e29cb8SLiming Sun static const char *mlxbf_bootctl_action_to_string(int action)
16179e29cb8SLiming Sun {
16279e29cb8SLiming Sun 	int i;
16379e29cb8SLiming Sun 
16479e29cb8SLiming Sun 	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
16579e29cb8SLiming Sun 		if (boot_names[i].value == action)
16679e29cb8SLiming Sun 			return boot_names[i].name;
16779e29cb8SLiming Sun 
16879e29cb8SLiming Sun 	return "invalid action";
16979e29cb8SLiming Sun }
17079e29cb8SLiming Sun 
post_reset_wdog_show(struct device * dev,struct device_attribute * attr,char * buf)17179e29cb8SLiming Sun static ssize_t post_reset_wdog_show(struct device *dev,
17279e29cb8SLiming Sun 				    struct device_attribute *attr, char *buf)
17379e29cb8SLiming Sun {
17479e29cb8SLiming Sun 	int ret;
17579e29cb8SLiming Sun 
17679e29cb8SLiming Sun 	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0);
17779e29cb8SLiming Sun 	if (ret < 0)
17879e29cb8SLiming Sun 		return ret;
17979e29cb8SLiming Sun 
18079e29cb8SLiming Sun 	return sprintf(buf, "%d\n", ret);
18179e29cb8SLiming Sun }
18279e29cb8SLiming Sun 
post_reset_wdog_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)18379e29cb8SLiming Sun static ssize_t post_reset_wdog_store(struct device *dev,
18479e29cb8SLiming Sun 				     struct device_attribute *attr,
18579e29cb8SLiming Sun 				     const char *buf, size_t count)
18679e29cb8SLiming Sun {
18779e29cb8SLiming Sun 	unsigned long value;
18879e29cb8SLiming Sun 	int ret;
18979e29cb8SLiming Sun 
19079e29cb8SLiming Sun 	ret = kstrtoul(buf, 10, &value);
19179e29cb8SLiming Sun 	if (ret)
19279e29cb8SLiming Sun 		return ret;
19379e29cb8SLiming Sun 
19479e29cb8SLiming Sun 	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value);
19579e29cb8SLiming Sun 	if (ret < 0)
19679e29cb8SLiming Sun 		return ret;
19779e29cb8SLiming Sun 
19879e29cb8SLiming Sun 	return count;
19979e29cb8SLiming Sun }
20079e29cb8SLiming Sun 
mlxbf_bootctl_show(int smc_op,char * buf)20179e29cb8SLiming Sun static ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
20279e29cb8SLiming Sun {
20379e29cb8SLiming Sun 	int action;
20479e29cb8SLiming Sun 
20579e29cb8SLiming Sun 	action = mlxbf_bootctl_smc(smc_op, 0);
20679e29cb8SLiming Sun 	if (action < 0)
20779e29cb8SLiming Sun 		return action;
20879e29cb8SLiming Sun 
20979e29cb8SLiming Sun 	return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
21079e29cb8SLiming Sun }
21179e29cb8SLiming Sun 
mlxbf_bootctl_store(int smc_op,const char * buf,size_t count)21279e29cb8SLiming Sun static int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
21379e29cb8SLiming Sun {
21479e29cb8SLiming Sun 	int ret, action;
21579e29cb8SLiming Sun 
21679e29cb8SLiming Sun 	action = mlxbf_bootctl_reset_action_to_val(buf);
21779e29cb8SLiming Sun 	if (action < 0)
21879e29cb8SLiming Sun 		return action;
21979e29cb8SLiming Sun 
22079e29cb8SLiming Sun 	ret = mlxbf_bootctl_smc(smc_op, action);
22179e29cb8SLiming Sun 	if (ret < 0)
22279e29cb8SLiming Sun 		return ret;
22379e29cb8SLiming Sun 
22479e29cb8SLiming Sun 	return count;
22579e29cb8SLiming Sun }
22679e29cb8SLiming Sun 
reset_action_show(struct device * dev,struct device_attribute * attr,char * buf)22779e29cb8SLiming Sun static ssize_t reset_action_show(struct device *dev,
22879e29cb8SLiming Sun 				 struct device_attribute *attr, char *buf)
22979e29cb8SLiming Sun {
23079e29cb8SLiming Sun 	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf);
23179e29cb8SLiming Sun }
23279e29cb8SLiming Sun 
reset_action_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)23379e29cb8SLiming Sun static ssize_t reset_action_store(struct device *dev,
23479e29cb8SLiming Sun 				  struct device_attribute *attr,
23579e29cb8SLiming Sun 				  const char *buf, size_t count)
23679e29cb8SLiming Sun {
23779e29cb8SLiming Sun 	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count);
23879e29cb8SLiming Sun }
23979e29cb8SLiming Sun 
second_reset_action_show(struct device * dev,struct device_attribute * attr,char * buf)24079e29cb8SLiming Sun static ssize_t second_reset_action_show(struct device *dev,
24179e29cb8SLiming Sun 					struct device_attribute *attr,
24279e29cb8SLiming Sun 					char *buf)
24379e29cb8SLiming Sun {
24479e29cb8SLiming Sun 	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf);
24579e29cb8SLiming Sun }
24679e29cb8SLiming Sun 
second_reset_action_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)24779e29cb8SLiming Sun static ssize_t second_reset_action_store(struct device *dev,
24879e29cb8SLiming Sun 					 struct device_attribute *attr,
24979e29cb8SLiming Sun 					 const char *buf, size_t count)
25079e29cb8SLiming Sun {
25179e29cb8SLiming Sun 	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf,
25279e29cb8SLiming Sun 				   count);
25379e29cb8SLiming Sun }
25479e29cb8SLiming Sun 
lifecycle_state_show(struct device * dev,struct device_attribute * attr,char * buf)25579e29cb8SLiming Sun static ssize_t lifecycle_state_show(struct device *dev,
25679e29cb8SLiming Sun 				    struct device_attribute *attr, char *buf)
25779e29cb8SLiming Sun {
258d4eef752SDavid Thompson 	int status_bits;
259d4eef752SDavid Thompson 	int use_dev_key;
260d4eef752SDavid Thompson 	int test_state;
26179e29cb8SLiming Sun 	int lc_state;
26279e29cb8SLiming Sun 
263d4eef752SDavid Thompson 	status_bits = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
26479e29cb8SLiming Sun 					MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
265d4eef752SDavid Thompson 	if (status_bits < 0)
266d4eef752SDavid Thompson 		return status_bits;
26779e29cb8SLiming Sun 
268d4eef752SDavid Thompson 	use_dev_key = status_bits & MLXBF_BOOTCTL_SB_DEV_MASK;
269d4eef752SDavid Thompson 	test_state = status_bits & MLXBF_BOOTCTL_SB_TEST_MASK;
270d4eef752SDavid Thompson 	lc_state = status_bits & MLXBF_BOOTCTL_SB_SECURE_MASK;
27179e29cb8SLiming Sun 
27279e29cb8SLiming Sun 	/*
27379e29cb8SLiming Sun 	 * If the test bits are set, we specify that the current state may be
27479e29cb8SLiming Sun 	 * due to using the test bits.
27579e29cb8SLiming Sun 	 */
276d4eef752SDavid Thompson 	if (test_state) {
27779e29cb8SLiming Sun 		return sprintf(buf, "%s(test)\n",
27879e29cb8SLiming Sun 			       mlxbf_bootctl_lifecycle_states[lc_state]);
279d4eef752SDavid Thompson 	} else if (use_dev_key &&
280d4eef752SDavid Thompson 		   (lc_state == MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE)) {
281d4eef752SDavid Thompson 		return sprintf(buf, "Secured (development)\n");
28279e29cb8SLiming Sun 	}
28379e29cb8SLiming Sun 
28479e29cb8SLiming Sun 	return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
28579e29cb8SLiming Sun }
28679e29cb8SLiming Sun 
secure_boot_fuse_state_show(struct device * dev,struct device_attribute * attr,char * buf)28779e29cb8SLiming Sun static ssize_t secure_boot_fuse_state_show(struct device *dev,
28879e29cb8SLiming Sun 					   struct device_attribute *attr,
28979e29cb8SLiming Sun 					   char *buf)
29079e29cb8SLiming Sun {
29179e29cb8SLiming Sun 	int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0;
29279e29cb8SLiming Sun 	const char *status;
29379e29cb8SLiming Sun 
29479e29cb8SLiming Sun 	key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
29579e29cb8SLiming Sun 				      MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
29679e29cb8SLiming Sun 	if (key_state < 0)
29779e29cb8SLiming Sun 		return key_state;
29879e29cb8SLiming Sun 
29979e29cb8SLiming Sun 	/*
30079e29cb8SLiming Sun 	 * key_state contains the bits for 4 Key versions, loaded from eFuses
30179e29cb8SLiming Sun 	 * after a hard reset. Lower 4 bits are a thermometer code indicating
30279e29cb8SLiming Sun 	 * key programming has started for key n (0000 = none, 0001 = version 0,
30379e29cb8SLiming Sun 	 * 0011 = version 1, 0111 = version 2, 1111 = version 3). Upper 4 bits
30479e29cb8SLiming Sun 	 * are a thermometer code indicating key programming has completed for
30579e29cb8SLiming Sun 	 * key n (same encodings as the start bits). This allows for detection
30604cdaf6dSBhaskar Chowdhury 	 * of an interruption in the programming process which has left the key
30779e29cb8SLiming Sun 	 * partially programmed (and thus invalid). The process is to burn the
30879e29cb8SLiming Sun 	 * eFuse for the new key start bit, burn the key eFuses, then burn the
30979e29cb8SLiming Sun 	 * eFuse for the new key complete bit.
31079e29cb8SLiming Sun 	 *
31179e29cb8SLiming Sun 	 * For example 0000_0000: no key valid, 0001_0001: key version 0 valid,
31279e29cb8SLiming Sun 	 * 0011_0011: key 1 version valid, 0011_0111: key version 2 started
31379e29cb8SLiming Sun 	 * programming but did not complete, etc. The most recent key for which
31479e29cb8SLiming Sun 	 * both start and complete bit is set is loaded. On soft reset, this
31579e29cb8SLiming Sun 	 * register is not modified.
31679e29cb8SLiming Sun 	 */
31779e29cb8SLiming Sun 	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
31879e29cb8SLiming Sun 		burnt = key_state & BIT(key);
31979e29cb8SLiming Sun 		valid = key_state & BIT(key + MLXBF_SB_KEY_NUM);
32079e29cb8SLiming Sun 
32179e29cb8SLiming Sun 		if (burnt && valid)
32279e29cb8SLiming Sun 			upper_key_used = 1;
32379e29cb8SLiming Sun 
32479e29cb8SLiming Sun 		if (upper_key_used) {
32579e29cb8SLiming Sun 			if (burnt)
32679e29cb8SLiming Sun 				status = valid ? "Used" : "Wasted";
32779e29cb8SLiming Sun 			else
32879e29cb8SLiming Sun 				status = valid ? "Invalid" : "Skipped";
32979e29cb8SLiming Sun 		} else {
33079e29cb8SLiming Sun 			if (burnt)
33179e29cb8SLiming Sun 				status = valid ? "InUse" : "Incomplete";
33279e29cb8SLiming Sun 			else
33379e29cb8SLiming Sun 				status = valid ? "Invalid" : "Free";
33479e29cb8SLiming Sun 		}
33579e29cb8SLiming Sun 		buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
33679e29cb8SLiming Sun 	}
33779e29cb8SLiming Sun 	buf_len += sprintf(buf + buf_len, "\n");
33879e29cb8SLiming Sun 
33979e29cb8SLiming Sun 	return buf_len;
34079e29cb8SLiming Sun }
34179e29cb8SLiming Sun 
fw_reset_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)3427bef7eeaSKhalil Blaiech static ssize_t fw_reset_store(struct device *dev,
3437bef7eeaSKhalil Blaiech 			      struct device_attribute *attr,
3447bef7eeaSKhalil Blaiech 			      const char *buf, size_t count)
3457bef7eeaSKhalil Blaiech {
3467bef7eeaSKhalil Blaiech 	unsigned long key;
3477bef7eeaSKhalil Blaiech 	int err;
3487bef7eeaSKhalil Blaiech 
3497bef7eeaSKhalil Blaiech 	err = kstrtoul(buf, 16, &key);
3507bef7eeaSKhalil Blaiech 	if (err)
3517bef7eeaSKhalil Blaiech 		return err;
3527bef7eeaSKhalil Blaiech 
3537bef7eeaSKhalil Blaiech 	if (mlxbf_bootctl_smc(MLXBF_BOOTCTL_FW_RESET, key) < 0)
3547bef7eeaSKhalil Blaiech 		return -EINVAL;
3557bef7eeaSKhalil Blaiech 
3567bef7eeaSKhalil Blaiech 	return count;
3577bef7eeaSKhalil Blaiech }
3587bef7eeaSKhalil Blaiech 
359e9d1b2d0SLiming Sun /* Size(8-byte words) of the log buffer. */
360e9d1b2d0SLiming Sun #define RSH_SCRATCH_BUF_CTL_IDX_MASK	0x7f
361e9d1b2d0SLiming Sun 
362e9d1b2d0SLiming Sun /* 100ms timeout */
363e9d1b2d0SLiming Sun #define RSH_SCRATCH_BUF_POLL_TIMEOUT	100000
364e9d1b2d0SLiming Sun 
mlxbf_rsh_log_sem_lock(void)365e9d1b2d0SLiming Sun static int mlxbf_rsh_log_sem_lock(void)
366e9d1b2d0SLiming Sun {
367e9d1b2d0SLiming Sun 	unsigned long reg;
368e9d1b2d0SLiming Sun 
369e9d1b2d0SLiming Sun 	return readq_poll_timeout(mlxbf_rsh_semaphore, reg, !reg, 0,
370e9d1b2d0SLiming Sun 				  RSH_SCRATCH_BUF_POLL_TIMEOUT);
371e9d1b2d0SLiming Sun }
372e9d1b2d0SLiming Sun 
mlxbf_rsh_log_sem_unlock(void)373e9d1b2d0SLiming Sun static void mlxbf_rsh_log_sem_unlock(void)
374e9d1b2d0SLiming Sun {
375e9d1b2d0SLiming Sun 	writeq(0, mlxbf_rsh_semaphore);
376e9d1b2d0SLiming Sun }
377e9d1b2d0SLiming Sun 
rsh_log_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)378e9d1b2d0SLiming Sun static ssize_t rsh_log_store(struct device *dev,
379e9d1b2d0SLiming Sun 			     struct device_attribute *attr,
380e9d1b2d0SLiming Sun 			     const char *buf, size_t count)
381e9d1b2d0SLiming Sun {
382e9d1b2d0SLiming Sun 	int rc, idx, num, len, level = MLXBF_RSH_LOG_INFO;
383e9d1b2d0SLiming Sun 	size_t size = count;
384e9d1b2d0SLiming Sun 	u64 data;
385e9d1b2d0SLiming Sun 
386e9d1b2d0SLiming Sun 	if (!size)
387e9d1b2d0SLiming Sun 		return -EINVAL;
388e9d1b2d0SLiming Sun 
389e9d1b2d0SLiming Sun 	if (!mlxbf_rsh_semaphore || !mlxbf_rsh_scratch_buf_ctl)
390e9d1b2d0SLiming Sun 		return -EOPNOTSUPP;
391e9d1b2d0SLiming Sun 
392e9d1b2d0SLiming Sun 	/* Ignore line break at the end. */
393e9d1b2d0SLiming Sun 	if (buf[size - 1] == '\n')
394e9d1b2d0SLiming Sun 		size--;
395e9d1b2d0SLiming Sun 
396e9d1b2d0SLiming Sun 	/* Check the message prefix. */
397e9d1b2d0SLiming Sun 	for (idx = 0; idx < ARRAY_SIZE(mlxbf_rsh_log_level); idx++) {
398e9d1b2d0SLiming Sun 		len = strlen(mlxbf_rsh_log_level[idx]);
399e9d1b2d0SLiming Sun 		if (len + 1 < size &&
400e9d1b2d0SLiming Sun 		    !strncmp(buf, mlxbf_rsh_log_level[idx], len)) {
401e9d1b2d0SLiming Sun 			buf += len;
402e9d1b2d0SLiming Sun 			size -= len;
403e9d1b2d0SLiming Sun 			level = idx;
404e9d1b2d0SLiming Sun 			break;
405e9d1b2d0SLiming Sun 		}
406e9d1b2d0SLiming Sun 	}
407e9d1b2d0SLiming Sun 
408e9d1b2d0SLiming Sun 	/* Ignore leading spaces. */
409e9d1b2d0SLiming Sun 	while (size > 0 && buf[0] == ' ') {
410e9d1b2d0SLiming Sun 		size--;
411e9d1b2d0SLiming Sun 		buf++;
412e9d1b2d0SLiming Sun 	}
413e9d1b2d0SLiming Sun 
414e9d1b2d0SLiming Sun 	/* Take the semaphore. */
415e9d1b2d0SLiming Sun 	rc = mlxbf_rsh_log_sem_lock();
416e9d1b2d0SLiming Sun 	if (rc)
417e9d1b2d0SLiming Sun 		return rc;
418e9d1b2d0SLiming Sun 
419e9d1b2d0SLiming Sun 	/* Calculate how many words are available. */
420e9d1b2d0SLiming Sun 	idx = readq(mlxbf_rsh_scratch_buf_ctl);
421e9d1b2d0SLiming Sun 	num = min((int)DIV_ROUND_UP(size, sizeof(u64)),
422e9d1b2d0SLiming Sun 		  RSH_SCRATCH_BUF_CTL_IDX_MASK - idx - 1);
423e9d1b2d0SLiming Sun 	if (num <= 0)
424e9d1b2d0SLiming Sun 		goto done;
425e9d1b2d0SLiming Sun 
426e9d1b2d0SLiming Sun 	/* Write Header. */
427e9d1b2d0SLiming Sun 	data = FIELD_PREP(MLXBF_RSH_LOG_TYPE_MASK, MLXBF_RSH_LOG_TYPE_MSG);
428e9d1b2d0SLiming Sun 	data |= FIELD_PREP(MLXBF_RSH_LOG_LEN_MASK, num);
429e9d1b2d0SLiming Sun 	data |= FIELD_PREP(MLXBF_RSH_LOG_LEVEL_MASK, level);
430e9d1b2d0SLiming Sun 	writeq(data, mlxbf_rsh_scratch_buf_data);
431e9d1b2d0SLiming Sun 
432e9d1b2d0SLiming Sun 	/* Write message. */
433e9d1b2d0SLiming Sun 	for (idx = 0; idx < num && size > 0; idx++) {
434e9d1b2d0SLiming Sun 		if (size < sizeof(u64)) {
435e9d1b2d0SLiming Sun 			data = 0;
436e9d1b2d0SLiming Sun 			memcpy(&data, buf, size);
437e9d1b2d0SLiming Sun 			size = 0;
438e9d1b2d0SLiming Sun 		} else {
439e9d1b2d0SLiming Sun 			memcpy(&data, buf, sizeof(u64));
440e9d1b2d0SLiming Sun 			size -= sizeof(u64);
441e9d1b2d0SLiming Sun 			buf += sizeof(u64);
442e9d1b2d0SLiming Sun 		}
443e9d1b2d0SLiming Sun 		writeq(data, mlxbf_rsh_scratch_buf_data);
444e9d1b2d0SLiming Sun 	}
445e9d1b2d0SLiming Sun 
446e9d1b2d0SLiming Sun done:
447e9d1b2d0SLiming Sun 	/* Release the semaphore. */
448e9d1b2d0SLiming Sun 	mlxbf_rsh_log_sem_unlock();
449e9d1b2d0SLiming Sun 
450e9d1b2d0SLiming Sun 	/* Ignore the rest if no more space. */
451e9d1b2d0SLiming Sun 	return count;
452e9d1b2d0SLiming Sun }
453e9d1b2d0SLiming Sun 
large_icm_show(struct device * dev,struct device_attribute * attr,char * buf)454b18a97edSAsmaa Mnebhi static ssize_t large_icm_show(struct device *dev,
455b18a97edSAsmaa Mnebhi 				struct device_attribute *attr, char *buf)
456b18a97edSAsmaa Mnebhi {
457b18a97edSAsmaa Mnebhi 	struct arm_smccc_res res;
458b18a97edSAsmaa Mnebhi 
459b18a97edSAsmaa Mnebhi 	mutex_lock(&icm_ops_lock);
460b18a97edSAsmaa Mnebhi 	arm_smccc_smc(MLNX_HANDLE_GET_ICM_INFO, 0, 0, 0, 0,
461b18a97edSAsmaa Mnebhi 		      0, 0, 0, &res);
462b18a97edSAsmaa Mnebhi 	mutex_unlock(&icm_ops_lock);
463b18a97edSAsmaa Mnebhi 	if (res.a0)
464b18a97edSAsmaa Mnebhi 		return -EPERM;
465b18a97edSAsmaa Mnebhi 
466*e5afa3d7SLi Zhijian 	return sysfs_emit(buf, "0x%lx", res.a1);
467b18a97edSAsmaa Mnebhi }
468b18a97edSAsmaa Mnebhi 
large_icm_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)469b18a97edSAsmaa Mnebhi static ssize_t large_icm_store(struct device *dev,
470b18a97edSAsmaa Mnebhi 			       struct device_attribute *attr,
471b18a97edSAsmaa Mnebhi 			       const char *buf, size_t count)
472b18a97edSAsmaa Mnebhi {
473b18a97edSAsmaa Mnebhi 	struct arm_smccc_res res;
474b18a97edSAsmaa Mnebhi 	unsigned long icm_data;
475b18a97edSAsmaa Mnebhi 	int err;
476b18a97edSAsmaa Mnebhi 
477b18a97edSAsmaa Mnebhi 	err = kstrtoul(buf, MLXBF_LARGE_ICMC_MAX_STRING_SIZE, &icm_data);
478b18a97edSAsmaa Mnebhi 	if (err)
479b18a97edSAsmaa Mnebhi 		return err;
480b18a97edSAsmaa Mnebhi 
481b18a97edSAsmaa Mnebhi 	if ((icm_data != 0 && icm_data < MLXBF_LARGE_ICMC_SIZE_MIN) ||
482b18a97edSAsmaa Mnebhi 	    icm_data > MLXBF_LARGE_ICMC_SIZE_MAX || icm_data % MLXBF_LARGE_ICMC_GRANULARITY)
483b18a97edSAsmaa Mnebhi 		return -EPERM;
484b18a97edSAsmaa Mnebhi 
485b18a97edSAsmaa Mnebhi 	mutex_lock(&icm_ops_lock);
486b18a97edSAsmaa Mnebhi 	arm_smccc_smc(MLNX_HANDLE_SET_ICM_INFO, icm_data, 0, 0, 0, 0, 0, 0, &res);
487b18a97edSAsmaa Mnebhi 	mutex_unlock(&icm_ops_lock);
488b18a97edSAsmaa Mnebhi 
489b18a97edSAsmaa Mnebhi 	return res.a0 ? -EPERM : count;
490b18a97edSAsmaa Mnebhi }
491b18a97edSAsmaa Mnebhi 
os_up_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)492e3205d41SAsmaa Mnebhi static ssize_t os_up_store(struct device *dev,
493e3205d41SAsmaa Mnebhi 			   struct device_attribute *attr,
494e3205d41SAsmaa Mnebhi 			   const char *buf, size_t count)
495e3205d41SAsmaa Mnebhi {
496e3205d41SAsmaa Mnebhi 	struct arm_smccc_res res;
497e3205d41SAsmaa Mnebhi 	unsigned long val;
498e3205d41SAsmaa Mnebhi 	int err;
499e3205d41SAsmaa Mnebhi 
500e3205d41SAsmaa Mnebhi 	err = kstrtoul(buf, 10, &val);
501e3205d41SAsmaa Mnebhi 	if (err)
502e3205d41SAsmaa Mnebhi 		return err;
503e3205d41SAsmaa Mnebhi 
504e3205d41SAsmaa Mnebhi 	if (val != 1)
505e3205d41SAsmaa Mnebhi 		return -EINVAL;
506e3205d41SAsmaa Mnebhi 
507e3205d41SAsmaa Mnebhi 	mutex_lock(&os_up_lock);
508e3205d41SAsmaa Mnebhi 	arm_smccc_smc(MLNX_HANDLE_OS_UP, 0, 0, 0, 0, 0, 0, 0, &res);
509e3205d41SAsmaa Mnebhi 	mutex_unlock(&os_up_lock);
510e3205d41SAsmaa Mnebhi 
511e3205d41SAsmaa Mnebhi 	return count;
512e3205d41SAsmaa Mnebhi }
513e3205d41SAsmaa Mnebhi 
oob_mac_show(struct device * dev,struct device_attribute * attr,char * buf)5147e38a742SDavid Thompson static ssize_t oob_mac_show(struct device *dev,
5157e38a742SDavid Thompson 			    struct device_attribute *attr, char *buf)
5167e38a742SDavid Thompson {
5177e38a742SDavid Thompson 	struct arm_smccc_res res;
5187e38a742SDavid Thompson 	u8 *mac_byte_ptr;
5197e38a742SDavid Thompson 
5207e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
5217e38a742SDavid Thompson 	arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, MLNX_MFG_TYPE_OOB_MAC, 0, 0, 0,
5227e38a742SDavid Thompson 		      0, 0, 0, &res);
5237e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
5247e38a742SDavid Thompson 	if (res.a0)
5257e38a742SDavid Thompson 		return -EPERM;
5267e38a742SDavid Thompson 
5277e38a742SDavid Thompson 	mac_byte_ptr = (u8 *)&res.a1;
5287e38a742SDavid Thompson 
5297e38a742SDavid Thompson 	return sysfs_format_mac(buf, mac_byte_ptr, ETH_ALEN);
5307e38a742SDavid Thompson }
5317e38a742SDavid Thompson 
oob_mac_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)5327e38a742SDavid Thompson static ssize_t oob_mac_store(struct device *dev,
5337e38a742SDavid Thompson 			     struct device_attribute *attr,
5347e38a742SDavid Thompson 			     const char *buf, size_t count)
5357e38a742SDavid Thompson {
5367e38a742SDavid Thompson 	unsigned int byte[MLNX_MFG_OOB_MAC_FORMAT_LEN] = { 0 };
5377e38a742SDavid Thompson 	struct arm_smccc_res res;
5387e38a742SDavid Thompson 	int byte_idx, len;
5397e38a742SDavid Thompson 	u64 mac_addr = 0;
5407e38a742SDavid Thompson 	u8 *mac_byte_ptr;
5417e38a742SDavid Thompson 
5427e38a742SDavid Thompson 	if ((count - 1) != MLNX_MFG_OOB_MAC_FORMAT_LEN)
5437e38a742SDavid Thompson 		return -EINVAL;
5447e38a742SDavid Thompson 
5457e38a742SDavid Thompson 	len = sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x",
5467e38a742SDavid Thompson 		     &byte[0], &byte[1], &byte[2],
5477e38a742SDavid Thompson 		     &byte[3], &byte[4], &byte[5]);
5487e38a742SDavid Thompson 	if (len != ETH_ALEN)
5497e38a742SDavid Thompson 		return -EINVAL;
5507e38a742SDavid Thompson 
5517e38a742SDavid Thompson 	mac_byte_ptr = (u8 *)&mac_addr;
5527e38a742SDavid Thompson 
5537e38a742SDavid Thompson 	for (byte_idx = 0; byte_idx < ETH_ALEN; byte_idx++)
5547e38a742SDavid Thompson 		mac_byte_ptr[byte_idx] = (u8)byte[byte_idx];
5557e38a742SDavid Thompson 
5567e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
5577e38a742SDavid Thompson 	arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, MLNX_MFG_TYPE_OOB_MAC,
5587e38a742SDavid Thompson 		      ETH_ALEN, mac_addr, 0, 0, 0, 0, &res);
5597e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
5607e38a742SDavid Thompson 
5617e38a742SDavid Thompson 	return res.a0 ? -EPERM : count;
5627e38a742SDavid Thompson }
5637e38a742SDavid Thompson 
opn_show(struct device * dev,struct device_attribute * attr,char * buf)5647e38a742SDavid Thompson static ssize_t opn_show(struct device *dev,
5657e38a742SDavid Thompson 			struct device_attribute *attr, char *buf)
5667e38a742SDavid Thompson {
5677e38a742SDavid Thompson 	u64 opn_data[MLNX_MFG_VAL_QWORD_CNT(OPN) + 1] = { 0 };
5687e38a742SDavid Thompson 	struct arm_smccc_res res;
5697e38a742SDavid Thompson 	int word;
5707e38a742SDavid Thompson 
5717e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
5727e38a742SDavid Thompson 	for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(OPN); word++) {
5737e38a742SDavid Thompson 		arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO,
5747e38a742SDavid Thompson 			      MLNX_MFG_TYPE_OPN_0 + word,
5757e38a742SDavid Thompson 			      0, 0, 0, 0, 0, 0, &res);
5767e38a742SDavid Thompson 		if (res.a0) {
5777e38a742SDavid Thompson 			mutex_unlock(&mfg_ops_lock);
5787e38a742SDavid Thompson 			return -EPERM;
5797e38a742SDavid Thompson 		}
5807e38a742SDavid Thompson 		opn_data[word] = res.a1;
5817e38a742SDavid Thompson 	}
5827e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
5837e38a742SDavid Thompson 
584*e5afa3d7SLi Zhijian 	return sysfs_emit(buf, "%s", (char *)opn_data);
5857e38a742SDavid Thompson }
5867e38a742SDavid Thompson 
opn_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)5877e38a742SDavid Thompson static ssize_t opn_store(struct device *dev,
5887e38a742SDavid Thompson 			 struct device_attribute *attr,
5897e38a742SDavid Thompson 			 const char *buf, size_t count)
5907e38a742SDavid Thompson {
5917e38a742SDavid Thompson 	u64 opn[MLNX_MFG_VAL_QWORD_CNT(OPN)] = { 0 };
5927e38a742SDavid Thompson 	struct arm_smccc_res res;
5937e38a742SDavid Thompson 	int word;
5947e38a742SDavid Thompson 
5957e38a742SDavid Thompson 	if (count > MLNX_MFG_OPN_VAL_LEN)
5967e38a742SDavid Thompson 		return -EINVAL;
5977e38a742SDavid Thompson 
5987e38a742SDavid Thompson 	memcpy(opn, buf, count);
5997e38a742SDavid Thompson 
6007e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
6017e38a742SDavid Thompson 	for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(OPN); word++) {
6027e38a742SDavid Thompson 		arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO,
6037e38a742SDavid Thompson 			      MLNX_MFG_TYPE_OPN_0 + word,
6047e38a742SDavid Thompson 			      sizeof(u64), opn[word], 0, 0, 0, 0, &res);
6057e38a742SDavid Thompson 		if (res.a0) {
6067e38a742SDavid Thompson 			mutex_unlock(&mfg_ops_lock);
6077e38a742SDavid Thompson 			return -EPERM;
6087e38a742SDavid Thompson 		}
6097e38a742SDavid Thompson 	}
6107e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
6117e38a742SDavid Thompson 
6127e38a742SDavid Thompson 	return count;
6137e38a742SDavid Thompson }
6147e38a742SDavid Thompson 
sku_show(struct device * dev,struct device_attribute * attr,char * buf)6157e38a742SDavid Thompson static ssize_t sku_show(struct device *dev,
6167e38a742SDavid Thompson 			struct device_attribute *attr, char *buf)
6177e38a742SDavid Thompson {
6187e38a742SDavid Thompson 	u64 sku_data[MLNX_MFG_VAL_QWORD_CNT(SKU) + 1] = { 0 };
6197e38a742SDavid Thompson 	struct arm_smccc_res res;
6207e38a742SDavid Thompson 	int word;
6217e38a742SDavid Thompson 
6227e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
6237e38a742SDavid Thompson 	for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SKU); word++) {
6247e38a742SDavid Thompson 		arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO,
6257e38a742SDavid Thompson 			      MLNX_MFG_TYPE_SKU_0 + word,
6267e38a742SDavid Thompson 			      0, 0, 0, 0, 0, 0, &res);
6277e38a742SDavid Thompson 		if (res.a0) {
6287e38a742SDavid Thompson 			mutex_unlock(&mfg_ops_lock);
6297e38a742SDavid Thompson 			return -EPERM;
6307e38a742SDavid Thompson 		}
6317e38a742SDavid Thompson 		sku_data[word] = res.a1;
6327e38a742SDavid Thompson 	}
6337e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
6347e38a742SDavid Thompson 
635*e5afa3d7SLi Zhijian 	return sysfs_emit(buf, "%s", (char *)sku_data);
6367e38a742SDavid Thompson }
6377e38a742SDavid Thompson 
sku_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)6387e38a742SDavid Thompson static ssize_t sku_store(struct device *dev,
6397e38a742SDavid Thompson 			 struct device_attribute *attr,
6407e38a742SDavid Thompson 			 const char *buf, size_t count)
6417e38a742SDavid Thompson {
6427e38a742SDavid Thompson 	u64 sku[MLNX_MFG_VAL_QWORD_CNT(SKU)] = { 0 };
6437e38a742SDavid Thompson 	struct arm_smccc_res res;
6447e38a742SDavid Thompson 	int word;
6457e38a742SDavid Thompson 
6467e38a742SDavid Thompson 	if (count > MLNX_MFG_SKU_VAL_LEN)
6477e38a742SDavid Thompson 		return -EINVAL;
6487e38a742SDavid Thompson 
6497e38a742SDavid Thompson 	memcpy(sku, buf, count);
6507e38a742SDavid Thompson 
6517e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
6527e38a742SDavid Thompson 	for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SKU); word++) {
6537e38a742SDavid Thompson 		arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO,
6547e38a742SDavid Thompson 			      MLNX_MFG_TYPE_SKU_0 + word,
6557e38a742SDavid Thompson 			      sizeof(u64), sku[word], 0, 0, 0, 0, &res);
6567e38a742SDavid Thompson 		if (res.a0) {
6577e38a742SDavid Thompson 			mutex_unlock(&mfg_ops_lock);
6587e38a742SDavid Thompson 			return -EPERM;
6597e38a742SDavid Thompson 		}
6607e38a742SDavid Thompson 	}
6617e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
6627e38a742SDavid Thompson 
6637e38a742SDavid Thompson 	return count;
6647e38a742SDavid Thompson }
6657e38a742SDavid Thompson 
modl_show(struct device * dev,struct device_attribute * attr,char * buf)6667e38a742SDavid Thompson static ssize_t modl_show(struct device *dev,
6677e38a742SDavid Thompson 			 struct device_attribute *attr, char *buf)
6687e38a742SDavid Thompson {
6697e38a742SDavid Thompson 	u64 modl_data[MLNX_MFG_VAL_QWORD_CNT(MODL) + 1] = { 0 };
6707e38a742SDavid Thompson 	struct arm_smccc_res res;
6717e38a742SDavid Thompson 	int word;
6727e38a742SDavid Thompson 
6737e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
6747e38a742SDavid Thompson 	for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(MODL); word++) {
6757e38a742SDavid Thompson 		arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO,
6767e38a742SDavid Thompson 			      MLNX_MFG_TYPE_MODL_0 + word,
6777e38a742SDavid Thompson 			      0, 0, 0, 0, 0, 0, &res);
6787e38a742SDavid Thompson 		if (res.a0) {
6797e38a742SDavid Thompson 			mutex_unlock(&mfg_ops_lock);
6807e38a742SDavid Thompson 			return -EPERM;
6817e38a742SDavid Thompson 		}
6827e38a742SDavid Thompson 		modl_data[word] = res.a1;
6837e38a742SDavid Thompson 	}
6847e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
6857e38a742SDavid Thompson 
686*e5afa3d7SLi Zhijian 	return sysfs_emit(buf, "%s", (char *)modl_data);
6877e38a742SDavid Thompson }
6887e38a742SDavid Thompson 
modl_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)6897e38a742SDavid Thompson static ssize_t modl_store(struct device *dev,
6907e38a742SDavid Thompson 			  struct device_attribute *attr,
6917e38a742SDavid Thompson 			  const char *buf, size_t count)
6927e38a742SDavid Thompson {
6937e38a742SDavid Thompson 	u64 modl[MLNX_MFG_VAL_QWORD_CNT(MODL)] = { 0 };
6947e38a742SDavid Thompson 	struct arm_smccc_res res;
6957e38a742SDavid Thompson 	int word;
6967e38a742SDavid Thompson 
6977e38a742SDavid Thompson 	if (count > MLNX_MFG_MODL_VAL_LEN)
6987e38a742SDavid Thompson 		return -EINVAL;
6997e38a742SDavid Thompson 
7007e38a742SDavid Thompson 	memcpy(modl, buf, count);
7017e38a742SDavid Thompson 
7027e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
7037e38a742SDavid Thompson 	for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(MODL); word++) {
7047e38a742SDavid Thompson 		arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO,
7057e38a742SDavid Thompson 			      MLNX_MFG_TYPE_MODL_0 + word,
7067e38a742SDavid Thompson 			      sizeof(u64), modl[word], 0, 0, 0, 0, &res);
7077e38a742SDavid Thompson 		if (res.a0) {
7087e38a742SDavid Thompson 			mutex_unlock(&mfg_ops_lock);
7097e38a742SDavid Thompson 			return -EPERM;
7107e38a742SDavid Thompson 		}
7117e38a742SDavid Thompson 	}
7127e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
7137e38a742SDavid Thompson 
7147e38a742SDavid Thompson 	return count;
7157e38a742SDavid Thompson }
7167e38a742SDavid Thompson 
sn_show(struct device * dev,struct device_attribute * attr,char * buf)7177e38a742SDavid Thompson static ssize_t sn_show(struct device *dev,
7187e38a742SDavid Thompson 		       struct device_attribute *attr, char *buf)
7197e38a742SDavid Thompson {
7207e38a742SDavid Thompson 	u64 sn_data[MLNX_MFG_VAL_QWORD_CNT(SN) + 1] = { 0 };
7217e38a742SDavid Thompson 	struct arm_smccc_res res;
7227e38a742SDavid Thompson 	int word;
7237e38a742SDavid Thompson 
7247e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
7257e38a742SDavid Thompson 	for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SN); word++) {
7267e38a742SDavid Thompson 		arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO,
7277e38a742SDavid Thompson 			      MLNX_MFG_TYPE_SN_0 + word,
7287e38a742SDavid Thompson 			      0, 0, 0, 0, 0, 0, &res);
7297e38a742SDavid Thompson 		if (res.a0) {
7307e38a742SDavid Thompson 			mutex_unlock(&mfg_ops_lock);
7317e38a742SDavid Thompson 			return -EPERM;
7327e38a742SDavid Thompson 		}
7337e38a742SDavid Thompson 		sn_data[word] = res.a1;
7347e38a742SDavid Thompson 	}
7357e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
7367e38a742SDavid Thompson 
737*e5afa3d7SLi Zhijian 	return sysfs_emit(buf, "%s", (char *)sn_data);
7387e38a742SDavid Thompson }
7397e38a742SDavid Thompson 
sn_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)7407e38a742SDavid Thompson static ssize_t sn_store(struct device *dev,
7417e38a742SDavid Thompson 			struct device_attribute *attr,
7427e38a742SDavid Thompson 			const char *buf, size_t count)
7437e38a742SDavid Thompson {
7447e38a742SDavid Thompson 	u64 sn[MLNX_MFG_VAL_QWORD_CNT(SN)] = { 0 };
7457e38a742SDavid Thompson 	struct arm_smccc_res res;
7467e38a742SDavid Thompson 	int word;
7477e38a742SDavid Thompson 
7487e38a742SDavid Thompson 	if (count > MLNX_MFG_SN_VAL_LEN)
7497e38a742SDavid Thompson 		return -EINVAL;
7507e38a742SDavid Thompson 
7517e38a742SDavid Thompson 	memcpy(sn, buf, count);
7527e38a742SDavid Thompson 
7537e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
7547e38a742SDavid Thompson 	for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SN); word++) {
7557e38a742SDavid Thompson 		arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO,
7567e38a742SDavid Thompson 			      MLNX_MFG_TYPE_SN_0 + word,
7577e38a742SDavid Thompson 			      sizeof(u64), sn[word], 0, 0, 0, 0, &res);
7587e38a742SDavid Thompson 		if (res.a0) {
7597e38a742SDavid Thompson 			mutex_unlock(&mfg_ops_lock);
7607e38a742SDavid Thompson 			return -EPERM;
7617e38a742SDavid Thompson 		}
7627e38a742SDavid Thompson 	}
7637e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
7647e38a742SDavid Thompson 
7657e38a742SDavid Thompson 	return count;
7667e38a742SDavid Thompson }
7677e38a742SDavid Thompson 
uuid_show(struct device * dev,struct device_attribute * attr,char * buf)7687e38a742SDavid Thompson static ssize_t uuid_show(struct device *dev,
7697e38a742SDavid Thompson 			 struct device_attribute *attr, char *buf)
7707e38a742SDavid Thompson {
7717e38a742SDavid Thompson 	u64 uuid_data[MLNX_MFG_VAL_QWORD_CNT(UUID) + 1] = { 0 };
7727e38a742SDavid Thompson 	struct arm_smccc_res res;
7737e38a742SDavid Thompson 	int word;
7747e38a742SDavid Thompson 
7757e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
7767e38a742SDavid Thompson 	for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(UUID); word++) {
7777e38a742SDavid Thompson 		arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO,
7787e38a742SDavid Thompson 			      MLNX_MFG_TYPE_UUID_0 + word,
7797e38a742SDavid Thompson 			      0, 0, 0, 0, 0, 0, &res);
7807e38a742SDavid Thompson 		if (res.a0) {
7817e38a742SDavid Thompson 			mutex_unlock(&mfg_ops_lock);
7827e38a742SDavid Thompson 			return -EPERM;
7837e38a742SDavid Thompson 		}
7847e38a742SDavid Thompson 		uuid_data[word] = res.a1;
7857e38a742SDavid Thompson 	}
7867e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
7877e38a742SDavid Thompson 
788*e5afa3d7SLi Zhijian 	return sysfs_emit(buf, "%s", (char *)uuid_data);
7897e38a742SDavid Thompson }
7907e38a742SDavid Thompson 
uuid_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)7917e38a742SDavid Thompson static ssize_t uuid_store(struct device *dev,
7927e38a742SDavid Thompson 			  struct device_attribute *attr,
7937e38a742SDavid Thompson 			  const char *buf, size_t count)
7947e38a742SDavid Thompson {
7957e38a742SDavid Thompson 	u64 uuid[MLNX_MFG_VAL_QWORD_CNT(UUID)] = { 0 };
7967e38a742SDavid Thompson 	struct arm_smccc_res res;
7977e38a742SDavid Thompson 	int word;
7987e38a742SDavid Thompson 
7997e38a742SDavid Thompson 	if (count > MLNX_MFG_UUID_VAL_LEN)
8007e38a742SDavid Thompson 		return -EINVAL;
8017e38a742SDavid Thompson 
8027e38a742SDavid Thompson 	memcpy(uuid, buf, count);
8037e38a742SDavid Thompson 
8047e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
8057e38a742SDavid Thompson 	for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(UUID); word++) {
8067e38a742SDavid Thompson 		arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO,
8077e38a742SDavid Thompson 			      MLNX_MFG_TYPE_UUID_0 + word,
8087e38a742SDavid Thompson 			      sizeof(u64), uuid[word], 0, 0, 0, 0, &res);
8097e38a742SDavid Thompson 		if (res.a0) {
8107e38a742SDavid Thompson 			mutex_unlock(&mfg_ops_lock);
8117e38a742SDavid Thompson 			return -EPERM;
8127e38a742SDavid Thompson 		}
8137e38a742SDavid Thompson 	}
8147e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
8157e38a742SDavid Thompson 
8167e38a742SDavid Thompson 	return count;
8177e38a742SDavid Thompson }
8187e38a742SDavid Thompson 
rev_show(struct device * dev,struct device_attribute * attr,char * buf)8197e38a742SDavid Thompson static ssize_t rev_show(struct device *dev,
8207e38a742SDavid Thompson 			struct device_attribute *attr, char *buf)
8217e38a742SDavid Thompson {
8227e38a742SDavid Thompson 	u64 rev_data[MLNX_MFG_VAL_QWORD_CNT(REV) + 1] = { 0 };
8237e38a742SDavid Thompson 	struct arm_smccc_res res;
8247e38a742SDavid Thompson 	int word;
8257e38a742SDavid Thompson 
8267e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
8277e38a742SDavid Thompson 	for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(REV); word++) {
8287e38a742SDavid Thompson 		arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO,
8297e38a742SDavid Thompson 			      MLNX_MFG_TYPE_REV + word,
8307e38a742SDavid Thompson 			      0, 0, 0, 0, 0, 0, &res);
8317e38a742SDavid Thompson 		if (res.a0) {
8327e38a742SDavid Thompson 			mutex_unlock(&mfg_ops_lock);
8337e38a742SDavid Thompson 			return -EPERM;
8347e38a742SDavid Thompson 		}
8357e38a742SDavid Thompson 		rev_data[word] = res.a1;
8367e38a742SDavid Thompson 	}
8377e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
8387e38a742SDavid Thompson 
839*e5afa3d7SLi Zhijian 	return sysfs_emit(buf, "%s", (char *)rev_data);
8407e38a742SDavid Thompson }
8417e38a742SDavid Thompson 
rev_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)8427e38a742SDavid Thompson static ssize_t rev_store(struct device *dev,
8437e38a742SDavid Thompson 			 struct device_attribute *attr,
8447e38a742SDavid Thompson 			 const char *buf, size_t count)
8457e38a742SDavid Thompson {
8467e38a742SDavid Thompson 	u64 rev[MLNX_MFG_VAL_QWORD_CNT(REV)] = { 0 };
8477e38a742SDavid Thompson 	struct arm_smccc_res res;
8487e38a742SDavid Thompson 	int word;
8497e38a742SDavid Thompson 
8507e38a742SDavid Thompson 	if (count > MLNX_MFG_REV_VAL_LEN)
8517e38a742SDavid Thompson 		return -EINVAL;
8527e38a742SDavid Thompson 
8537e38a742SDavid Thompson 	memcpy(rev, buf, count);
8547e38a742SDavid Thompson 
8557e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
8567e38a742SDavid Thompson 	for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(REV); word++) {
8577e38a742SDavid Thompson 		arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO,
8587e38a742SDavid Thompson 			      MLNX_MFG_TYPE_REV + word,
8597e38a742SDavid Thompson 			      sizeof(u64), rev[word], 0, 0, 0, 0, &res);
8607e38a742SDavid Thompson 		if (res.a0) {
8617e38a742SDavid Thompson 			mutex_unlock(&mfg_ops_lock);
8627e38a742SDavid Thompson 			return -EPERM;
8637e38a742SDavid Thompson 		}
8647e38a742SDavid Thompson 	}
8657e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
8667e38a742SDavid Thompson 
8677e38a742SDavid Thompson 	return count;
8687e38a742SDavid Thompson }
8697e38a742SDavid Thompson 
mfg_lock_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)8707e38a742SDavid Thompson static ssize_t mfg_lock_store(struct device *dev,
8717e38a742SDavid Thompson 			      struct device_attribute *attr,
8727e38a742SDavid Thompson 			      const char *buf, size_t count)
8737e38a742SDavid Thompson {
8747e38a742SDavid Thompson 	struct arm_smccc_res res;
8757e38a742SDavid Thompson 	unsigned long val;
8767e38a742SDavid Thompson 	int err;
8777e38a742SDavid Thompson 
8787e38a742SDavid Thompson 	err = kstrtoul(buf, 10, &val);
8797e38a742SDavid Thompson 	if (err)
8807e38a742SDavid Thompson 		return err;
8817e38a742SDavid Thompson 
8827e38a742SDavid Thompson 	if (val != 1)
8837e38a742SDavid Thompson 		return -EINVAL;
8847e38a742SDavid Thompson 
8857e38a742SDavid Thompson 	mutex_lock(&mfg_ops_lock);
8867e38a742SDavid Thompson 	arm_smccc_smc(MLXBF_BOOTCTL_LOCK_MFG_INFO, 0, 0, 0, 0, 0, 0, 0, &res);
8877e38a742SDavid Thompson 	mutex_unlock(&mfg_ops_lock);
8887e38a742SDavid Thompson 
8897e38a742SDavid Thompson 	return count;
8907e38a742SDavid Thompson }
8917e38a742SDavid Thompson 
89279e29cb8SLiming Sun static DEVICE_ATTR_RW(post_reset_wdog);
89379e29cb8SLiming Sun static DEVICE_ATTR_RW(reset_action);
89479e29cb8SLiming Sun static DEVICE_ATTR_RW(second_reset_action);
89579e29cb8SLiming Sun static DEVICE_ATTR_RO(lifecycle_state);
89679e29cb8SLiming Sun static DEVICE_ATTR_RO(secure_boot_fuse_state);
8977bef7eeaSKhalil Blaiech static DEVICE_ATTR_WO(fw_reset);
898e9d1b2d0SLiming Sun static DEVICE_ATTR_WO(rsh_log);
899b18a97edSAsmaa Mnebhi static DEVICE_ATTR_RW(large_icm);
900e3205d41SAsmaa Mnebhi static DEVICE_ATTR_WO(os_up);
9017e38a742SDavid Thompson static DEVICE_ATTR_RW(oob_mac);
9027e38a742SDavid Thompson static DEVICE_ATTR_RW(opn);
9037e38a742SDavid Thompson static DEVICE_ATTR_RW(sku);
9047e38a742SDavid Thompson static DEVICE_ATTR_RW(modl);
9057e38a742SDavid Thompson static DEVICE_ATTR_RW(sn);
9067e38a742SDavid Thompson static DEVICE_ATTR_RW(uuid);
9077e38a742SDavid Thompson static DEVICE_ATTR_RW(rev);
9087e38a742SDavid Thompson static DEVICE_ATTR_WO(mfg_lock);
90979e29cb8SLiming Sun 
91079e29cb8SLiming Sun static struct attribute *mlxbf_bootctl_attrs[] = {
91179e29cb8SLiming Sun 	&dev_attr_post_reset_wdog.attr,
91279e29cb8SLiming Sun 	&dev_attr_reset_action.attr,
91379e29cb8SLiming Sun 	&dev_attr_second_reset_action.attr,
91479e29cb8SLiming Sun 	&dev_attr_lifecycle_state.attr,
91579e29cb8SLiming Sun 	&dev_attr_secure_boot_fuse_state.attr,
9167bef7eeaSKhalil Blaiech 	&dev_attr_fw_reset.attr,
917e9d1b2d0SLiming Sun 	&dev_attr_rsh_log.attr,
918b18a97edSAsmaa Mnebhi 	&dev_attr_large_icm.attr,
919e3205d41SAsmaa Mnebhi 	&dev_attr_os_up.attr,
9207e38a742SDavid Thompson 	&dev_attr_oob_mac.attr,
9217e38a742SDavid Thompson 	&dev_attr_opn.attr,
9227e38a742SDavid Thompson 	&dev_attr_sku.attr,
9237e38a742SDavid Thompson 	&dev_attr_modl.attr,
9247e38a742SDavid Thompson 	&dev_attr_sn.attr,
9257e38a742SDavid Thompson 	&dev_attr_uuid.attr,
9267e38a742SDavid Thompson 	&dev_attr_rev.attr,
9277e38a742SDavid Thompson 	&dev_attr_mfg_lock.attr,
92879e29cb8SLiming Sun 	NULL
92979e29cb8SLiming Sun };
93079e29cb8SLiming Sun 
93179e29cb8SLiming Sun ATTRIBUTE_GROUPS(mlxbf_bootctl);
93279e29cb8SLiming Sun 
93379e29cb8SLiming Sun static const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
93479e29cb8SLiming Sun 	{"MLNXBF04", 0},
93579e29cb8SLiming Sun 	{}
93679e29cb8SLiming Sun };
93779e29cb8SLiming Sun 
93879e29cb8SLiming Sun MODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
93979e29cb8SLiming Sun 
mlxbf_bootctl_bootfifo_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t pos,size_t count)94082c3a0b7SLiming Sun static ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp,
94182c3a0b7SLiming Sun 					   struct kobject *kobj,
94282c3a0b7SLiming Sun 					   struct bin_attribute *bin_attr,
94382c3a0b7SLiming Sun 					   char *buf, loff_t pos,
94482c3a0b7SLiming Sun 					   size_t count)
94582c3a0b7SLiming Sun {
94682c3a0b7SLiming Sun 	unsigned long timeout = msecs_to_jiffies(500);
94782c3a0b7SLiming Sun 	unsigned long expire = jiffies + timeout;
94882c3a0b7SLiming Sun 	u64 data, cnt = 0;
94982c3a0b7SLiming Sun 	char *p = buf;
95082c3a0b7SLiming Sun 
95182c3a0b7SLiming Sun 	while (count >= sizeof(data)) {
95282c3a0b7SLiming Sun 		/* Give up reading if no more data within 500ms. */
95382c3a0b7SLiming Sun 		if (!cnt) {
95482c3a0b7SLiming Sun 			cnt = readq(mlxbf_rsh_boot_cnt);
95582c3a0b7SLiming Sun 			if (!cnt) {
95682c3a0b7SLiming Sun 				if (time_after(jiffies, expire))
95782c3a0b7SLiming Sun 					break;
95882c3a0b7SLiming Sun 				usleep_range(10, 50);
95982c3a0b7SLiming Sun 				continue;
96082c3a0b7SLiming Sun 			}
96182c3a0b7SLiming Sun 		}
96282c3a0b7SLiming Sun 
96382c3a0b7SLiming Sun 		data = readq(mlxbf_rsh_boot_data);
96482c3a0b7SLiming Sun 		memcpy(p, &data, sizeof(data));
96582c3a0b7SLiming Sun 		count -= sizeof(data);
96682c3a0b7SLiming Sun 		p += sizeof(data);
96782c3a0b7SLiming Sun 		cnt--;
96882c3a0b7SLiming Sun 		expire = jiffies + timeout;
96982c3a0b7SLiming Sun 	}
97082c3a0b7SLiming Sun 
97182c3a0b7SLiming Sun 	return p - buf;
97282c3a0b7SLiming Sun }
97382c3a0b7SLiming Sun 
97482c3a0b7SLiming Sun static struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = {
97582c3a0b7SLiming Sun 	.attr = { .name = "bootfifo", .mode = 0400 },
97682c3a0b7SLiming Sun 	.read = mlxbf_bootctl_bootfifo_read,
97782c3a0b7SLiming Sun };
97882c3a0b7SLiming Sun 
mlxbf_bootctl_guid_match(const guid_t * guid,const struct arm_smccc_res * res)97979e29cb8SLiming Sun static bool mlxbf_bootctl_guid_match(const guid_t *guid,
98079e29cb8SLiming Sun 				     const struct arm_smccc_res *res)
98179e29cb8SLiming Sun {
98279e29cb8SLiming Sun 	guid_t id = GUID_INIT(res->a0, res->a1, res->a1 >> 16,
98379e29cb8SLiming Sun 			      res->a2, res->a2 >> 8, res->a2 >> 16,
98479e29cb8SLiming Sun 			      res->a2 >> 24, res->a3, res->a3 >> 8,
98579e29cb8SLiming Sun 			      res->a3 >> 16, res->a3 >> 24);
98679e29cb8SLiming Sun 
98779e29cb8SLiming Sun 	return guid_equal(guid, &id);
98879e29cb8SLiming Sun }
98979e29cb8SLiming Sun 
mlxbf_bootctl_probe(struct platform_device * pdev)99079e29cb8SLiming Sun static int mlxbf_bootctl_probe(struct platform_device *pdev)
99179e29cb8SLiming Sun {
99279e29cb8SLiming Sun 	struct arm_smccc_res res = { 0 };
993e9d1b2d0SLiming Sun 	void __iomem *reg;
99479e29cb8SLiming Sun 	guid_t guid;
99579e29cb8SLiming Sun 	int ret;
99679e29cb8SLiming Sun 
997e9d1b2d0SLiming Sun 	/* Map the resource of the bootfifo data register. */
99882c3a0b7SLiming Sun 	mlxbf_rsh_boot_data = devm_platform_ioremap_resource(pdev, 0);
99982c3a0b7SLiming Sun 	if (IS_ERR(mlxbf_rsh_boot_data))
100082c3a0b7SLiming Sun 		return PTR_ERR(mlxbf_rsh_boot_data);
100182c3a0b7SLiming Sun 
1002e9d1b2d0SLiming Sun 	/* Map the resource of the bootfifo counter register. */
100382c3a0b7SLiming Sun 	mlxbf_rsh_boot_cnt = devm_platform_ioremap_resource(pdev, 1);
100482c3a0b7SLiming Sun 	if (IS_ERR(mlxbf_rsh_boot_cnt))
100582c3a0b7SLiming Sun 		return PTR_ERR(mlxbf_rsh_boot_cnt);
100682c3a0b7SLiming Sun 
1007e9d1b2d0SLiming Sun 	/* Map the resource of the rshim semaphore register. */
1008e9d1b2d0SLiming Sun 	mlxbf_rsh_semaphore = devm_platform_ioremap_resource(pdev, 2);
1009e9d1b2d0SLiming Sun 	if (IS_ERR(mlxbf_rsh_semaphore))
1010e9d1b2d0SLiming Sun 		return PTR_ERR(mlxbf_rsh_semaphore);
1011e9d1b2d0SLiming Sun 
1012e9d1b2d0SLiming Sun 	/* Map the resource of the scratch buffer (log) registers. */
1013e9d1b2d0SLiming Sun 	reg = devm_platform_ioremap_resource(pdev, 3);
1014e9d1b2d0SLiming Sun 	if (IS_ERR(reg))
1015e9d1b2d0SLiming Sun 		return PTR_ERR(reg);
1016e9d1b2d0SLiming Sun 	mlxbf_rsh_scratch_buf_ctl = reg + MLXBF_RSH_SCRATCH_BUF_CTL_OFF;
1017e9d1b2d0SLiming Sun 	mlxbf_rsh_scratch_buf_data = reg + MLXBF_RSH_SCRATCH_BUF_DATA_OFF;
1018e9d1b2d0SLiming Sun 
101979e29cb8SLiming Sun 	/* Ensure we have the UUID we expect for this service. */
102079e29cb8SLiming Sun 	arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
102179e29cb8SLiming Sun 	guid_parse(mlxbf_bootctl_svc_uuid_str, &guid);
102279e29cb8SLiming Sun 	if (!mlxbf_bootctl_guid_match(&guid, &res))
102379e29cb8SLiming Sun 		return -ENODEV;
102479e29cb8SLiming Sun 
102579e29cb8SLiming Sun 	/*
102679e29cb8SLiming Sun 	 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_SWAP_EMMC
102779e29cb8SLiming Sun 	 * in case of boot failures. However it doesn't clear the state if there
102879e29cb8SLiming Sun 	 * is no failure. Restore the default boot mode here to avoid any
102979e29cb8SLiming Sun 	 * unnecessary boot partition swapping.
103079e29cb8SLiming Sun 	 */
103179e29cb8SLiming Sun 	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_RESET_ACTION,
103279e29cb8SLiming Sun 				MLXBF_BOOTCTL_EMMC);
103379e29cb8SLiming Sun 	if (ret < 0)
103479e29cb8SLiming Sun 		dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n");
103579e29cb8SLiming Sun 
103682c3a0b7SLiming Sun 	ret = sysfs_create_bin_file(&pdev->dev.kobj,
103782c3a0b7SLiming Sun 				    &mlxbf_bootctl_bootfifo_sysfs_attr);
103882c3a0b7SLiming Sun 	if (ret)
103982c3a0b7SLiming Sun 		pr_err("Unable to create bootfifo sysfs file, error %d\n", ret);
104082c3a0b7SLiming Sun 
104182c3a0b7SLiming Sun 	return ret;
104282c3a0b7SLiming Sun }
104382c3a0b7SLiming Sun 
mlxbf_bootctl_remove(struct platform_device * pdev)1044a6cff421SUwe Kleine-König static void mlxbf_bootctl_remove(struct platform_device *pdev)
104582c3a0b7SLiming Sun {
104682c3a0b7SLiming Sun 	sysfs_remove_bin_file(&pdev->dev.kobj,
104782c3a0b7SLiming Sun 			      &mlxbf_bootctl_bootfifo_sysfs_attr);
104879e29cb8SLiming Sun }
104979e29cb8SLiming Sun 
105079e29cb8SLiming Sun static struct platform_driver mlxbf_bootctl_driver = {
105179e29cb8SLiming Sun 	.probe = mlxbf_bootctl_probe,
1052a6cff421SUwe Kleine-König 	.remove_new = mlxbf_bootctl_remove,
105379e29cb8SLiming Sun 	.driver = {
105479e29cb8SLiming Sun 		.name = "mlxbf-bootctl",
105577dcc95eSLiming Sun 		.dev_groups = mlxbf_bootctl_groups,
105679e29cb8SLiming Sun 		.acpi_match_table = mlxbf_bootctl_acpi_ids,
105779e29cb8SLiming Sun 	}
105879e29cb8SLiming Sun };
105979e29cb8SLiming Sun 
106079e29cb8SLiming Sun module_platform_driver(mlxbf_bootctl_driver);
106179e29cb8SLiming Sun 
106279e29cb8SLiming Sun MODULE_DESCRIPTION("Mellanox boot control driver");
106379e29cb8SLiming Sun MODULE_LICENSE("GPL v2");
106479e29cb8SLiming Sun MODULE_AUTHOR("Mellanox Technologies");
1065