18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Mellanox boot control driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This driver provides a sysfs interface for systems management
68c2ecf20Sopenharmony_ci * software to manage reset-time actions.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright (C) 2019 Mellanox Technologies
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/acpi.h>
128c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "mlxbf-bootctl.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#define MLXBF_BOOTCTL_SB_SECURE_MASK		0x03
198c2ecf20Sopenharmony_ci#define MLXBF_BOOTCTL_SB_TEST_MASK		0x0c
208c2ecf20Sopenharmony_ci#define MLXBF_BOOTCTL_SB_DEV_MASK		BIT(4)
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define MLXBF_SB_KEY_NUM			4
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* UUID used to probe ATF service. */
258c2ecf20Sopenharmony_cistatic const char *mlxbf_bootctl_svc_uuid_str =
268c2ecf20Sopenharmony_ci	"89c036b4-e7d7-11e6-8797-001aca00bfc4";
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistruct mlxbf_bootctl_name {
298c2ecf20Sopenharmony_ci	u32 value;
308c2ecf20Sopenharmony_ci	const char *name;
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic struct mlxbf_bootctl_name boot_names[] = {
348c2ecf20Sopenharmony_ci	{ MLXBF_BOOTCTL_EXTERNAL, "external" },
358c2ecf20Sopenharmony_ci	{ MLXBF_BOOTCTL_EMMC, "emmc" },
368c2ecf20Sopenharmony_ci	{ MLNX_BOOTCTL_SWAP_EMMC, "swap_emmc" },
378c2ecf20Sopenharmony_ci	{ MLXBF_BOOTCTL_EMMC_LEGACY, "emmc_legacy" },
388c2ecf20Sopenharmony_ci	{ MLXBF_BOOTCTL_NONE, "none" },
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cienum {
428c2ecf20Sopenharmony_ci	MLXBF_BOOTCTL_SB_LIFECYCLE_PRODUCTION = 0,
438c2ecf20Sopenharmony_ci	MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE = 1,
448c2ecf20Sopenharmony_ci	MLXBF_BOOTCTL_SB_LIFECYCLE_GA_NON_SECURE = 2,
458c2ecf20Sopenharmony_ci	MLXBF_BOOTCTL_SB_LIFECYCLE_RMA = 3
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cistatic const char * const mlxbf_bootctl_lifecycle_states[] = {
498c2ecf20Sopenharmony_ci	[MLXBF_BOOTCTL_SB_LIFECYCLE_PRODUCTION] = "Production",
508c2ecf20Sopenharmony_ci	[MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE] = "GA Secured",
518c2ecf20Sopenharmony_ci	[MLXBF_BOOTCTL_SB_LIFECYCLE_GA_NON_SECURE] = "GA Non-Secured",
528c2ecf20Sopenharmony_ci	[MLXBF_BOOTCTL_SB_LIFECYCLE_RMA] = "RMA",
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/* ARM SMC call which is atomic and no need for lock. */
568c2ecf20Sopenharmony_cistatic int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct arm_smccc_res res;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	return res.a0;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* Return the action in integer or an error code. */
668c2ecf20Sopenharmony_cistatic int mlxbf_bootctl_reset_action_to_val(const char *action)
678c2ecf20Sopenharmony_ci{
688c2ecf20Sopenharmony_ci	int i;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
718c2ecf20Sopenharmony_ci		if (sysfs_streq(boot_names[i].name, action))
728c2ecf20Sopenharmony_ci			return boot_names[i].value;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return -EINVAL;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/* Return the action in string. */
788c2ecf20Sopenharmony_cistatic const char *mlxbf_bootctl_action_to_string(int action)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	int i;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(boot_names); i++)
838c2ecf20Sopenharmony_ci		if (boot_names[i].value == action)
848c2ecf20Sopenharmony_ci			return boot_names[i].name;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return "invalid action";
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic ssize_t post_reset_wdog_show(struct device *dev,
908c2ecf20Sopenharmony_ci				    struct device_attribute *attr, char *buf)
918c2ecf20Sopenharmony_ci{
928c2ecf20Sopenharmony_ci	int ret;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0);
958c2ecf20Sopenharmony_ci	if (ret < 0)
968c2ecf20Sopenharmony_ci		return ret;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", ret);
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic ssize_t post_reset_wdog_store(struct device *dev,
1028c2ecf20Sopenharmony_ci				     struct device_attribute *attr,
1038c2ecf20Sopenharmony_ci				     const char *buf, size_t count)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	unsigned long value;
1068c2ecf20Sopenharmony_ci	int ret;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	ret = kstrtoul(buf, 10, &value);
1098c2ecf20Sopenharmony_ci	if (ret)
1108c2ecf20Sopenharmony_ci		return ret;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value);
1138c2ecf20Sopenharmony_ci	if (ret < 0)
1148c2ecf20Sopenharmony_ci		return ret;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	return count;
1178c2ecf20Sopenharmony_ci}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_cistatic ssize_t mlxbf_bootctl_show(int smc_op, char *buf)
1208c2ecf20Sopenharmony_ci{
1218c2ecf20Sopenharmony_ci	int action;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	action = mlxbf_bootctl_smc(smc_op, 0);
1248c2ecf20Sopenharmony_ci	if (action < 0)
1258c2ecf20Sopenharmony_ci		return action;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action));
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_cistatic int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	int ret, action;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	action = mlxbf_bootctl_reset_action_to_val(buf);
1358c2ecf20Sopenharmony_ci	if (action < 0)
1368c2ecf20Sopenharmony_ci		return action;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	ret = mlxbf_bootctl_smc(smc_op, action);
1398c2ecf20Sopenharmony_ci	if (ret < 0)
1408c2ecf20Sopenharmony_ci		return ret;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return count;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic ssize_t reset_action_show(struct device *dev,
1468c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf);
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cistatic ssize_t reset_action_store(struct device *dev,
1528c2ecf20Sopenharmony_ci				  struct device_attribute *attr,
1538c2ecf20Sopenharmony_ci				  const char *buf, size_t count)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count);
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic ssize_t second_reset_action_show(struct device *dev,
1598c2ecf20Sopenharmony_ci					struct device_attribute *attr,
1608c2ecf20Sopenharmony_ci					char *buf)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic ssize_t second_reset_action_store(struct device *dev,
1668c2ecf20Sopenharmony_ci					 struct device_attribute *attr,
1678c2ecf20Sopenharmony_ci					 const char *buf, size_t count)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf,
1708c2ecf20Sopenharmony_ci				   count);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic ssize_t lifecycle_state_show(struct device *dev,
1748c2ecf20Sopenharmony_ci				    struct device_attribute *attr, char *buf)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	int status_bits;
1778c2ecf20Sopenharmony_ci	int use_dev_key;
1788c2ecf20Sopenharmony_ci	int test_state;
1798c2ecf20Sopenharmony_ci	int lc_state;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	status_bits = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
1828c2ecf20Sopenharmony_ci					MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE);
1838c2ecf20Sopenharmony_ci	if (status_bits < 0)
1848c2ecf20Sopenharmony_ci		return status_bits;
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	use_dev_key = status_bits & MLXBF_BOOTCTL_SB_DEV_MASK;
1878c2ecf20Sopenharmony_ci	test_state = status_bits & MLXBF_BOOTCTL_SB_TEST_MASK;
1888c2ecf20Sopenharmony_ci	lc_state = status_bits & MLXBF_BOOTCTL_SB_SECURE_MASK;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/*
1918c2ecf20Sopenharmony_ci	 * If the test bits are set, we specify that the current state may be
1928c2ecf20Sopenharmony_ci	 * due to using the test bits.
1938c2ecf20Sopenharmony_ci	 */
1948c2ecf20Sopenharmony_ci	if (test_state) {
1958c2ecf20Sopenharmony_ci		return sprintf(buf, "%s(test)\n",
1968c2ecf20Sopenharmony_ci			       mlxbf_bootctl_lifecycle_states[lc_state]);
1978c2ecf20Sopenharmony_ci	} else if (use_dev_key &&
1988c2ecf20Sopenharmony_ci		   (lc_state == MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE)) {
1998c2ecf20Sopenharmony_ci		return sprintf(buf, "Secured (development)\n");
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic ssize_t secure_boot_fuse_state_show(struct device *dev,
2068c2ecf20Sopenharmony_ci					   struct device_attribute *attr,
2078c2ecf20Sopenharmony_ci					   char *buf)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0;
2108c2ecf20Sopenharmony_ci	const char *status;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS,
2138c2ecf20Sopenharmony_ci				      MLXBF_BOOTCTL_FUSE_STATUS_KEYS);
2148c2ecf20Sopenharmony_ci	if (key_state < 0)
2158c2ecf20Sopenharmony_ci		return key_state;
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	/*
2188c2ecf20Sopenharmony_ci	 * key_state contains the bits for 4 Key versions, loaded from eFuses
2198c2ecf20Sopenharmony_ci	 * after a hard reset. Lower 4 bits are a thermometer code indicating
2208c2ecf20Sopenharmony_ci	 * key programming has started for key n (0000 = none, 0001 = version 0,
2218c2ecf20Sopenharmony_ci	 * 0011 = version 1, 0111 = version 2, 1111 = version 3). Upper 4 bits
2228c2ecf20Sopenharmony_ci	 * are a thermometer code indicating key programming has completed for
2238c2ecf20Sopenharmony_ci	 * key n (same encodings as the start bits). This allows for detection
2248c2ecf20Sopenharmony_ci	 * of an interruption in the progamming process which has left the key
2258c2ecf20Sopenharmony_ci	 * partially programmed (and thus invalid). The process is to burn the
2268c2ecf20Sopenharmony_ci	 * eFuse for the new key start bit, burn the key eFuses, then burn the
2278c2ecf20Sopenharmony_ci	 * eFuse for the new key complete bit.
2288c2ecf20Sopenharmony_ci	 *
2298c2ecf20Sopenharmony_ci	 * For example 0000_0000: no key valid, 0001_0001: key version 0 valid,
2308c2ecf20Sopenharmony_ci	 * 0011_0011: key 1 version valid, 0011_0111: key version 2 started
2318c2ecf20Sopenharmony_ci	 * programming but did not complete, etc. The most recent key for which
2328c2ecf20Sopenharmony_ci	 * both start and complete bit is set is loaded. On soft reset, this
2338c2ecf20Sopenharmony_ci	 * register is not modified.
2348c2ecf20Sopenharmony_ci	 */
2358c2ecf20Sopenharmony_ci	for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) {
2368c2ecf20Sopenharmony_ci		burnt = key_state & BIT(key);
2378c2ecf20Sopenharmony_ci		valid = key_state & BIT(key + MLXBF_SB_KEY_NUM);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		if (burnt && valid)
2408c2ecf20Sopenharmony_ci			upper_key_used = 1;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci		if (upper_key_used) {
2438c2ecf20Sopenharmony_ci			if (burnt)
2448c2ecf20Sopenharmony_ci				status = valid ? "Used" : "Wasted";
2458c2ecf20Sopenharmony_ci			else
2468c2ecf20Sopenharmony_ci				status = valid ? "Invalid" : "Skipped";
2478c2ecf20Sopenharmony_ci		} else {
2488c2ecf20Sopenharmony_ci			if (burnt)
2498c2ecf20Sopenharmony_ci				status = valid ? "InUse" : "Incomplete";
2508c2ecf20Sopenharmony_ci			else
2518c2ecf20Sopenharmony_ci				status = valid ? "Invalid" : "Free";
2528c2ecf20Sopenharmony_ci		}
2538c2ecf20Sopenharmony_ci		buf_len += sprintf(buf + buf_len, "%d:%s ", key, status);
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	buf_len += sprintf(buf + buf_len, "\n");
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return buf_len;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(post_reset_wdog);
2618c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(reset_action);
2628c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RW(second_reset_action);
2638c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(lifecycle_state);
2648c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(secure_boot_fuse_state);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic struct attribute *mlxbf_bootctl_attrs[] = {
2678c2ecf20Sopenharmony_ci	&dev_attr_post_reset_wdog.attr,
2688c2ecf20Sopenharmony_ci	&dev_attr_reset_action.attr,
2698c2ecf20Sopenharmony_ci	&dev_attr_second_reset_action.attr,
2708c2ecf20Sopenharmony_ci	&dev_attr_lifecycle_state.attr,
2718c2ecf20Sopenharmony_ci	&dev_attr_secure_boot_fuse_state.attr,
2728c2ecf20Sopenharmony_ci	NULL
2738c2ecf20Sopenharmony_ci};
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(mlxbf_bootctl);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = {
2788c2ecf20Sopenharmony_ci	{"MLNXBF04", 0},
2798c2ecf20Sopenharmony_ci	{}
2808c2ecf20Sopenharmony_ci};
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic bool mlxbf_bootctl_guid_match(const guid_t *guid,
2858c2ecf20Sopenharmony_ci				     const struct arm_smccc_res *res)
2868c2ecf20Sopenharmony_ci{
2878c2ecf20Sopenharmony_ci	guid_t id = GUID_INIT(res->a0, res->a1, res->a1 >> 16,
2888c2ecf20Sopenharmony_ci			      res->a2, res->a2 >> 8, res->a2 >> 16,
2898c2ecf20Sopenharmony_ci			      res->a2 >> 24, res->a3, res->a3 >> 8,
2908c2ecf20Sopenharmony_ci			      res->a3 >> 16, res->a3 >> 24);
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	return guid_equal(guid, &id);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic int mlxbf_bootctl_probe(struct platform_device *pdev)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct arm_smccc_res res = { 0 };
2988c2ecf20Sopenharmony_ci	guid_t guid;
2998c2ecf20Sopenharmony_ci	int ret;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	/* Ensure we have the UUID we expect for this service. */
3028c2ecf20Sopenharmony_ci	arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res);
3038c2ecf20Sopenharmony_ci	guid_parse(mlxbf_bootctl_svc_uuid_str, &guid);
3048c2ecf20Sopenharmony_ci	if (!mlxbf_bootctl_guid_match(&guid, &res))
3058c2ecf20Sopenharmony_ci		return -ENODEV;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	/*
3088c2ecf20Sopenharmony_ci	 * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_SWAP_EMMC
3098c2ecf20Sopenharmony_ci	 * in case of boot failures. However it doesn't clear the state if there
3108c2ecf20Sopenharmony_ci	 * is no failure. Restore the default boot mode here to avoid any
3118c2ecf20Sopenharmony_ci	 * unnecessary boot partition swapping.
3128c2ecf20Sopenharmony_ci	 */
3138c2ecf20Sopenharmony_ci	ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_RESET_ACTION,
3148c2ecf20Sopenharmony_ci				MLXBF_BOOTCTL_EMMC);
3158c2ecf20Sopenharmony_ci	if (ret < 0)
3168c2ecf20Sopenharmony_ci		dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n");
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic struct platform_driver mlxbf_bootctl_driver = {
3228c2ecf20Sopenharmony_ci	.probe = mlxbf_bootctl_probe,
3238c2ecf20Sopenharmony_ci	.driver = {
3248c2ecf20Sopenharmony_ci		.name = "mlxbf-bootctl",
3258c2ecf20Sopenharmony_ci		.dev_groups = mlxbf_bootctl_groups,
3268c2ecf20Sopenharmony_ci		.acpi_match_table = mlxbf_bootctl_acpi_ids,
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci};
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cimodule_platform_driver(mlxbf_bootctl_driver);
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Mellanox boot control driver");
3338c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
3348c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mellanox Technologies");
335