162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Mellanox boot control driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This driver provides a sysfs interface for systems management 662306a36Sopenharmony_ci * software to manage reset-time actions. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2019 Mellanox Technologies 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/acpi.h> 1262306a36Sopenharmony_ci#include <linux/arm-smccc.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/if_ether.h> 1562306a36Sopenharmony_ci#include <linux/iopoll.h> 1662306a36Sopenharmony_ci#include <linux/module.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include "mlxbf-bootctl.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define MLXBF_BOOTCTL_SB_SECURE_MASK 0x03 2262306a36Sopenharmony_ci#define MLXBF_BOOTCTL_SB_TEST_MASK 0x0c 2362306a36Sopenharmony_ci#define MLXBF_BOOTCTL_SB_DEV_MASK BIT(4) 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define MLXBF_SB_KEY_NUM 4 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* UUID used to probe ATF service. */ 2862306a36Sopenharmony_cistatic const char *mlxbf_bootctl_svc_uuid_str = 2962306a36Sopenharmony_ci "89c036b4-e7d7-11e6-8797-001aca00bfc4"; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistruct mlxbf_bootctl_name { 3262306a36Sopenharmony_ci u32 value; 3362306a36Sopenharmony_ci const char *name; 3462306a36Sopenharmony_ci}; 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_cistatic struct mlxbf_bootctl_name boot_names[] = { 3762306a36Sopenharmony_ci { MLXBF_BOOTCTL_EXTERNAL, "external" }, 3862306a36Sopenharmony_ci { MLXBF_BOOTCTL_EMMC, "emmc" }, 3962306a36Sopenharmony_ci { MLNX_BOOTCTL_SWAP_EMMC, "swap_emmc" }, 4062306a36Sopenharmony_ci { MLXBF_BOOTCTL_EMMC_LEGACY, "emmc_legacy" }, 4162306a36Sopenharmony_ci { MLXBF_BOOTCTL_NONE, "none" }, 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cienum { 4562306a36Sopenharmony_ci MLXBF_BOOTCTL_SB_LIFECYCLE_PRODUCTION = 0, 4662306a36Sopenharmony_ci MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE = 1, 4762306a36Sopenharmony_ci MLXBF_BOOTCTL_SB_LIFECYCLE_GA_NON_SECURE = 2, 4862306a36Sopenharmony_ci MLXBF_BOOTCTL_SB_LIFECYCLE_RMA = 3 4962306a36Sopenharmony_ci}; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic const char * const mlxbf_bootctl_lifecycle_states[] = { 5262306a36Sopenharmony_ci [MLXBF_BOOTCTL_SB_LIFECYCLE_PRODUCTION] = "Production", 5362306a36Sopenharmony_ci [MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE] = "GA Secured", 5462306a36Sopenharmony_ci [MLXBF_BOOTCTL_SB_LIFECYCLE_GA_NON_SECURE] = "GA Non-Secured", 5562306a36Sopenharmony_ci [MLXBF_BOOTCTL_SB_LIFECYCLE_RMA] = "RMA", 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* Log header format. */ 5962306a36Sopenharmony_ci#define MLXBF_RSH_LOG_TYPE_MASK GENMASK_ULL(59, 56) 6062306a36Sopenharmony_ci#define MLXBF_RSH_LOG_LEN_MASK GENMASK_ULL(54, 48) 6162306a36Sopenharmony_ci#define MLXBF_RSH_LOG_LEVEL_MASK GENMASK_ULL(7, 0) 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci/* Log module ID and type (only MSG type in Linux driver for now). */ 6462306a36Sopenharmony_ci#define MLXBF_RSH_LOG_TYPE_MSG 0x04ULL 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci/* Log ctl/data register offset. */ 6762306a36Sopenharmony_ci#define MLXBF_RSH_SCRATCH_BUF_CTL_OFF 0 6862306a36Sopenharmony_ci#define MLXBF_RSH_SCRATCH_BUF_DATA_OFF 0x10 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* Log message levels. */ 7162306a36Sopenharmony_cienum { 7262306a36Sopenharmony_ci MLXBF_RSH_LOG_INFO, 7362306a36Sopenharmony_ci MLXBF_RSH_LOG_WARN, 7462306a36Sopenharmony_ci MLXBF_RSH_LOG_ERR, 7562306a36Sopenharmony_ci MLXBF_RSH_LOG_ASSERT 7662306a36Sopenharmony_ci}; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci/* Mapped pointer for RSH_BOOT_FIFO_DATA and RSH_BOOT_FIFO_COUNT register. */ 7962306a36Sopenharmony_cistatic void __iomem *mlxbf_rsh_boot_data; 8062306a36Sopenharmony_cistatic void __iomem *mlxbf_rsh_boot_cnt; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* Mapped pointer for rsh log semaphore/ctrl/data register. */ 8362306a36Sopenharmony_cistatic void __iomem *mlxbf_rsh_semaphore; 8462306a36Sopenharmony_cistatic void __iomem *mlxbf_rsh_scratch_buf_ctl; 8562306a36Sopenharmony_cistatic void __iomem *mlxbf_rsh_scratch_buf_data; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci/* Rsh log levels. */ 8862306a36Sopenharmony_cistatic const char * const mlxbf_rsh_log_level[] = { 8962306a36Sopenharmony_ci "INFO", "WARN", "ERR", "ASSERT"}; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic DEFINE_MUTEX(icm_ops_lock); 9262306a36Sopenharmony_cistatic DEFINE_MUTEX(os_up_lock); 9362306a36Sopenharmony_cistatic DEFINE_MUTEX(mfg_ops_lock); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci/* 9662306a36Sopenharmony_ci * Objects are stored within the MFG partition per type. 9762306a36Sopenharmony_ci * Type 0 is not supported. 9862306a36Sopenharmony_ci */ 9962306a36Sopenharmony_cienum { 10062306a36Sopenharmony_ci MLNX_MFG_TYPE_OOB_MAC = 1, 10162306a36Sopenharmony_ci MLNX_MFG_TYPE_OPN_0, 10262306a36Sopenharmony_ci MLNX_MFG_TYPE_OPN_1, 10362306a36Sopenharmony_ci MLNX_MFG_TYPE_OPN_2, 10462306a36Sopenharmony_ci MLNX_MFG_TYPE_SKU_0, 10562306a36Sopenharmony_ci MLNX_MFG_TYPE_SKU_1, 10662306a36Sopenharmony_ci MLNX_MFG_TYPE_SKU_2, 10762306a36Sopenharmony_ci MLNX_MFG_TYPE_MODL_0, 10862306a36Sopenharmony_ci MLNX_MFG_TYPE_MODL_1, 10962306a36Sopenharmony_ci MLNX_MFG_TYPE_MODL_2, 11062306a36Sopenharmony_ci MLNX_MFG_TYPE_SN_0, 11162306a36Sopenharmony_ci MLNX_MFG_TYPE_SN_1, 11262306a36Sopenharmony_ci MLNX_MFG_TYPE_SN_2, 11362306a36Sopenharmony_ci MLNX_MFG_TYPE_UUID_0, 11462306a36Sopenharmony_ci MLNX_MFG_TYPE_UUID_1, 11562306a36Sopenharmony_ci MLNX_MFG_TYPE_UUID_2, 11662306a36Sopenharmony_ci MLNX_MFG_TYPE_UUID_3, 11762306a36Sopenharmony_ci MLNX_MFG_TYPE_UUID_4, 11862306a36Sopenharmony_ci MLNX_MFG_TYPE_REV, 11962306a36Sopenharmony_ci}; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci#define MLNX_MFG_OPN_VAL_LEN 24 12262306a36Sopenharmony_ci#define MLNX_MFG_SKU_VAL_LEN 24 12362306a36Sopenharmony_ci#define MLNX_MFG_MODL_VAL_LEN 24 12462306a36Sopenharmony_ci#define MLNX_MFG_SN_VAL_LEN 24 12562306a36Sopenharmony_ci#define MLNX_MFG_UUID_VAL_LEN 40 12662306a36Sopenharmony_ci#define MLNX_MFG_REV_VAL_LEN 8 12762306a36Sopenharmony_ci#define MLNX_MFG_VAL_QWORD_CNT(type) \ 12862306a36Sopenharmony_ci (MLNX_MFG_##type##_VAL_LEN / sizeof(u64)) 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci/* 13162306a36Sopenharmony_ci * The MAC address consists of 6 bytes (2 digits each) separated by ':'. 13262306a36Sopenharmony_ci * The expected format is: "XX:XX:XX:XX:XX:XX" 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci#define MLNX_MFG_OOB_MAC_FORMAT_LEN \ 13562306a36Sopenharmony_ci ((ETH_ALEN * 2) + (ETH_ALEN - 1)) 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci/* ARM SMC call which is atomic and no need for lock. */ 13862306a36Sopenharmony_cistatic int mlxbf_bootctl_smc(unsigned int smc_op, int smc_arg) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci struct arm_smccc_res res; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci arm_smccc_smc(smc_op, smc_arg, 0, 0, 0, 0, 0, 0, &res); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci return res.a0; 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* Return the action in integer or an error code. */ 14862306a36Sopenharmony_cistatic int mlxbf_bootctl_reset_action_to_val(const char *action) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci int i; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(boot_names); i++) 15362306a36Sopenharmony_ci if (sysfs_streq(boot_names[i].name, action)) 15462306a36Sopenharmony_ci return boot_names[i].value; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return -EINVAL; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci/* Return the action in string. */ 16062306a36Sopenharmony_cistatic const char *mlxbf_bootctl_action_to_string(int action) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci int i; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(boot_names); i++) 16562306a36Sopenharmony_ci if (boot_names[i].value == action) 16662306a36Sopenharmony_ci return boot_names[i].name; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci return "invalid action"; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic ssize_t post_reset_wdog_show(struct device *dev, 17262306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci int ret; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_POST_RESET_WDOG, 0); 17762306a36Sopenharmony_ci if (ret < 0) 17862306a36Sopenharmony_ci return ret; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci return sprintf(buf, "%d\n", ret); 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic ssize_t post_reset_wdog_store(struct device *dev, 18462306a36Sopenharmony_ci struct device_attribute *attr, 18562306a36Sopenharmony_ci const char *buf, size_t count) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci unsigned long value; 18862306a36Sopenharmony_ci int ret; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci ret = kstrtoul(buf, 10, &value); 19162306a36Sopenharmony_ci if (ret) 19262306a36Sopenharmony_ci return ret; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_POST_RESET_WDOG, value); 19562306a36Sopenharmony_ci if (ret < 0) 19662306a36Sopenharmony_ci return ret; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci return count; 19962306a36Sopenharmony_ci} 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_cistatic ssize_t mlxbf_bootctl_show(int smc_op, char *buf) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci int action; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci action = mlxbf_bootctl_smc(smc_op, 0); 20662306a36Sopenharmony_ci if (action < 0) 20762306a36Sopenharmony_ci return action; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return sprintf(buf, "%s\n", mlxbf_bootctl_action_to_string(action)); 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic int mlxbf_bootctl_store(int smc_op, const char *buf, size_t count) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci int ret, action; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci action = mlxbf_bootctl_reset_action_to_val(buf); 21762306a36Sopenharmony_ci if (action < 0) 21862306a36Sopenharmony_ci return action; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci ret = mlxbf_bootctl_smc(smc_op, action); 22162306a36Sopenharmony_ci if (ret < 0) 22262306a36Sopenharmony_ci return ret; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return count; 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic ssize_t reset_action_show(struct device *dev, 22862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_RESET_ACTION, buf); 23162306a36Sopenharmony_ci} 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_cistatic ssize_t reset_action_store(struct device *dev, 23462306a36Sopenharmony_ci struct device_attribute *attr, 23562306a36Sopenharmony_ci const char *buf, size_t count) 23662306a36Sopenharmony_ci{ 23762306a36Sopenharmony_ci return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_RESET_ACTION, buf, count); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic ssize_t second_reset_action_show(struct device *dev, 24162306a36Sopenharmony_ci struct device_attribute *attr, 24262306a36Sopenharmony_ci char *buf) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci return mlxbf_bootctl_show(MLXBF_BOOTCTL_GET_SECOND_RESET_ACTION, buf); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic ssize_t second_reset_action_store(struct device *dev, 24862306a36Sopenharmony_ci struct device_attribute *attr, 24962306a36Sopenharmony_ci const char *buf, size_t count) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci return mlxbf_bootctl_store(MLXBF_BOOTCTL_SET_SECOND_RESET_ACTION, buf, 25262306a36Sopenharmony_ci count); 25362306a36Sopenharmony_ci} 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistatic ssize_t lifecycle_state_show(struct device *dev, 25662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci int status_bits; 25962306a36Sopenharmony_ci int use_dev_key; 26062306a36Sopenharmony_ci int test_state; 26162306a36Sopenharmony_ci int lc_state; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci status_bits = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS, 26462306a36Sopenharmony_ci MLXBF_BOOTCTL_FUSE_STATUS_LIFECYCLE); 26562306a36Sopenharmony_ci if (status_bits < 0) 26662306a36Sopenharmony_ci return status_bits; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci use_dev_key = status_bits & MLXBF_BOOTCTL_SB_DEV_MASK; 26962306a36Sopenharmony_ci test_state = status_bits & MLXBF_BOOTCTL_SB_TEST_MASK; 27062306a36Sopenharmony_ci lc_state = status_bits & MLXBF_BOOTCTL_SB_SECURE_MASK; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* 27362306a36Sopenharmony_ci * If the test bits are set, we specify that the current state may be 27462306a36Sopenharmony_ci * due to using the test bits. 27562306a36Sopenharmony_ci */ 27662306a36Sopenharmony_ci if (test_state) { 27762306a36Sopenharmony_ci return sprintf(buf, "%s(test)\n", 27862306a36Sopenharmony_ci mlxbf_bootctl_lifecycle_states[lc_state]); 27962306a36Sopenharmony_ci } else if (use_dev_key && 28062306a36Sopenharmony_ci (lc_state == MLXBF_BOOTCTL_SB_LIFECYCLE_GA_SECURE)) { 28162306a36Sopenharmony_ci return sprintf(buf, "Secured (development)\n"); 28262306a36Sopenharmony_ci } 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci return sprintf(buf, "%s\n", mlxbf_bootctl_lifecycle_states[lc_state]); 28562306a36Sopenharmony_ci} 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_cistatic ssize_t secure_boot_fuse_state_show(struct device *dev, 28862306a36Sopenharmony_ci struct device_attribute *attr, 28962306a36Sopenharmony_ci char *buf) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci int burnt, valid, key, key_state, buf_len = 0, upper_key_used = 0; 29262306a36Sopenharmony_ci const char *status; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci key_state = mlxbf_bootctl_smc(MLXBF_BOOTCTL_GET_TBB_FUSE_STATUS, 29562306a36Sopenharmony_ci MLXBF_BOOTCTL_FUSE_STATUS_KEYS); 29662306a36Sopenharmony_ci if (key_state < 0) 29762306a36Sopenharmony_ci return key_state; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* 30062306a36Sopenharmony_ci * key_state contains the bits for 4 Key versions, loaded from eFuses 30162306a36Sopenharmony_ci * after a hard reset. Lower 4 bits are a thermometer code indicating 30262306a36Sopenharmony_ci * key programming has started for key n (0000 = none, 0001 = version 0, 30362306a36Sopenharmony_ci * 0011 = version 1, 0111 = version 2, 1111 = version 3). Upper 4 bits 30462306a36Sopenharmony_ci * are a thermometer code indicating key programming has completed for 30562306a36Sopenharmony_ci * key n (same encodings as the start bits). This allows for detection 30662306a36Sopenharmony_ci * of an interruption in the programming process which has left the key 30762306a36Sopenharmony_ci * partially programmed (and thus invalid). The process is to burn the 30862306a36Sopenharmony_ci * eFuse for the new key start bit, burn the key eFuses, then burn the 30962306a36Sopenharmony_ci * eFuse for the new key complete bit. 31062306a36Sopenharmony_ci * 31162306a36Sopenharmony_ci * For example 0000_0000: no key valid, 0001_0001: key version 0 valid, 31262306a36Sopenharmony_ci * 0011_0011: key 1 version valid, 0011_0111: key version 2 started 31362306a36Sopenharmony_ci * programming but did not complete, etc. The most recent key for which 31462306a36Sopenharmony_ci * both start and complete bit is set is loaded. On soft reset, this 31562306a36Sopenharmony_ci * register is not modified. 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ci for (key = MLXBF_SB_KEY_NUM - 1; key >= 0; key--) { 31862306a36Sopenharmony_ci burnt = key_state & BIT(key); 31962306a36Sopenharmony_ci valid = key_state & BIT(key + MLXBF_SB_KEY_NUM); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (burnt && valid) 32262306a36Sopenharmony_ci upper_key_used = 1; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (upper_key_used) { 32562306a36Sopenharmony_ci if (burnt) 32662306a36Sopenharmony_ci status = valid ? "Used" : "Wasted"; 32762306a36Sopenharmony_ci else 32862306a36Sopenharmony_ci status = valid ? "Invalid" : "Skipped"; 32962306a36Sopenharmony_ci } else { 33062306a36Sopenharmony_ci if (burnt) 33162306a36Sopenharmony_ci status = valid ? "InUse" : "Incomplete"; 33262306a36Sopenharmony_ci else 33362306a36Sopenharmony_ci status = valid ? "Invalid" : "Free"; 33462306a36Sopenharmony_ci } 33562306a36Sopenharmony_ci buf_len += sprintf(buf + buf_len, "%d:%s ", key, status); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci buf_len += sprintf(buf + buf_len, "\n"); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return buf_len; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic ssize_t fw_reset_store(struct device *dev, 34362306a36Sopenharmony_ci struct device_attribute *attr, 34462306a36Sopenharmony_ci const char *buf, size_t count) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci unsigned long key; 34762306a36Sopenharmony_ci int err; 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci err = kstrtoul(buf, 16, &key); 35062306a36Sopenharmony_ci if (err) 35162306a36Sopenharmony_ci return err; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci if (mlxbf_bootctl_smc(MLXBF_BOOTCTL_FW_RESET, key) < 0) 35462306a36Sopenharmony_ci return -EINVAL; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return count; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci/* Size(8-byte words) of the log buffer. */ 36062306a36Sopenharmony_ci#define RSH_SCRATCH_BUF_CTL_IDX_MASK 0x7f 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci/* 100ms timeout */ 36362306a36Sopenharmony_ci#define RSH_SCRATCH_BUF_POLL_TIMEOUT 100000 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic int mlxbf_rsh_log_sem_lock(void) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci unsigned long reg; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci return readq_poll_timeout(mlxbf_rsh_semaphore, reg, !reg, 0, 37062306a36Sopenharmony_ci RSH_SCRATCH_BUF_POLL_TIMEOUT); 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_cistatic void mlxbf_rsh_log_sem_unlock(void) 37462306a36Sopenharmony_ci{ 37562306a36Sopenharmony_ci writeq(0, mlxbf_rsh_semaphore); 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic ssize_t rsh_log_store(struct device *dev, 37962306a36Sopenharmony_ci struct device_attribute *attr, 38062306a36Sopenharmony_ci const char *buf, size_t count) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci int rc, idx, num, len, level = MLXBF_RSH_LOG_INFO; 38362306a36Sopenharmony_ci size_t size = count; 38462306a36Sopenharmony_ci u64 data; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci if (!size) 38762306a36Sopenharmony_ci return -EINVAL; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (!mlxbf_rsh_semaphore || !mlxbf_rsh_scratch_buf_ctl) 39062306a36Sopenharmony_ci return -EOPNOTSUPP; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Ignore line break at the end. */ 39362306a36Sopenharmony_ci if (buf[size - 1] == '\n') 39462306a36Sopenharmony_ci size--; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci /* Check the message prefix. */ 39762306a36Sopenharmony_ci for (idx = 0; idx < ARRAY_SIZE(mlxbf_rsh_log_level); idx++) { 39862306a36Sopenharmony_ci len = strlen(mlxbf_rsh_log_level[idx]); 39962306a36Sopenharmony_ci if (len + 1 < size && 40062306a36Sopenharmony_ci !strncmp(buf, mlxbf_rsh_log_level[idx], len)) { 40162306a36Sopenharmony_ci buf += len; 40262306a36Sopenharmony_ci size -= len; 40362306a36Sopenharmony_ci level = idx; 40462306a36Sopenharmony_ci break; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* Ignore leading spaces. */ 40962306a36Sopenharmony_ci while (size > 0 && buf[0] == ' ') { 41062306a36Sopenharmony_ci size--; 41162306a36Sopenharmony_ci buf++; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* Take the semaphore. */ 41562306a36Sopenharmony_ci rc = mlxbf_rsh_log_sem_lock(); 41662306a36Sopenharmony_ci if (rc) 41762306a36Sopenharmony_ci return rc; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Calculate how many words are available. */ 42062306a36Sopenharmony_ci idx = readq(mlxbf_rsh_scratch_buf_ctl); 42162306a36Sopenharmony_ci num = min((int)DIV_ROUND_UP(size, sizeof(u64)), 42262306a36Sopenharmony_ci RSH_SCRATCH_BUF_CTL_IDX_MASK - idx - 1); 42362306a36Sopenharmony_ci if (num <= 0) 42462306a36Sopenharmony_ci goto done; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci /* Write Header. */ 42762306a36Sopenharmony_ci data = FIELD_PREP(MLXBF_RSH_LOG_TYPE_MASK, MLXBF_RSH_LOG_TYPE_MSG); 42862306a36Sopenharmony_ci data |= FIELD_PREP(MLXBF_RSH_LOG_LEN_MASK, num); 42962306a36Sopenharmony_ci data |= FIELD_PREP(MLXBF_RSH_LOG_LEVEL_MASK, level); 43062306a36Sopenharmony_ci writeq(data, mlxbf_rsh_scratch_buf_data); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Write message. */ 43362306a36Sopenharmony_ci for (idx = 0; idx < num && size > 0; idx++) { 43462306a36Sopenharmony_ci if (size < sizeof(u64)) { 43562306a36Sopenharmony_ci data = 0; 43662306a36Sopenharmony_ci memcpy(&data, buf, size); 43762306a36Sopenharmony_ci size = 0; 43862306a36Sopenharmony_ci } else { 43962306a36Sopenharmony_ci memcpy(&data, buf, sizeof(u64)); 44062306a36Sopenharmony_ci size -= sizeof(u64); 44162306a36Sopenharmony_ci buf += sizeof(u64); 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci writeq(data, mlxbf_rsh_scratch_buf_data); 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_cidone: 44762306a36Sopenharmony_ci /* Release the semaphore. */ 44862306a36Sopenharmony_ci mlxbf_rsh_log_sem_unlock(); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci /* Ignore the rest if no more space. */ 45162306a36Sopenharmony_ci return count; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic ssize_t large_icm_show(struct device *dev, 45562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct arm_smccc_res res; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci mutex_lock(&icm_ops_lock); 46062306a36Sopenharmony_ci arm_smccc_smc(MLNX_HANDLE_GET_ICM_INFO, 0, 0, 0, 0, 46162306a36Sopenharmony_ci 0, 0, 0, &res); 46262306a36Sopenharmony_ci mutex_unlock(&icm_ops_lock); 46362306a36Sopenharmony_ci if (res.a0) 46462306a36Sopenharmony_ci return -EPERM; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "0x%lx", res.a1); 46762306a36Sopenharmony_ci} 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_cistatic ssize_t large_icm_store(struct device *dev, 47062306a36Sopenharmony_ci struct device_attribute *attr, 47162306a36Sopenharmony_ci const char *buf, size_t count) 47262306a36Sopenharmony_ci{ 47362306a36Sopenharmony_ci struct arm_smccc_res res; 47462306a36Sopenharmony_ci unsigned long icm_data; 47562306a36Sopenharmony_ci int err; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci err = kstrtoul(buf, MLXBF_LARGE_ICMC_MAX_STRING_SIZE, &icm_data); 47862306a36Sopenharmony_ci if (err) 47962306a36Sopenharmony_ci return err; 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if ((icm_data != 0 && icm_data < MLXBF_LARGE_ICMC_SIZE_MIN) || 48262306a36Sopenharmony_ci icm_data > MLXBF_LARGE_ICMC_SIZE_MAX || icm_data % MLXBF_LARGE_ICMC_GRANULARITY) 48362306a36Sopenharmony_ci return -EPERM; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci mutex_lock(&icm_ops_lock); 48662306a36Sopenharmony_ci arm_smccc_smc(MLNX_HANDLE_SET_ICM_INFO, icm_data, 0, 0, 0, 0, 0, 0, &res); 48762306a36Sopenharmony_ci mutex_unlock(&icm_ops_lock); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci return res.a0 ? -EPERM : count; 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic ssize_t os_up_store(struct device *dev, 49362306a36Sopenharmony_ci struct device_attribute *attr, 49462306a36Sopenharmony_ci const char *buf, size_t count) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci struct arm_smccc_res res; 49762306a36Sopenharmony_ci unsigned long val; 49862306a36Sopenharmony_ci int err; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci err = kstrtoul(buf, 10, &val); 50162306a36Sopenharmony_ci if (err) 50262306a36Sopenharmony_ci return err; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (val != 1) 50562306a36Sopenharmony_ci return -EINVAL; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci mutex_lock(&os_up_lock); 50862306a36Sopenharmony_ci arm_smccc_smc(MLNX_HANDLE_OS_UP, 0, 0, 0, 0, 0, 0, 0, &res); 50962306a36Sopenharmony_ci mutex_unlock(&os_up_lock); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return count; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_cistatic ssize_t oob_mac_show(struct device *dev, 51562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct arm_smccc_res res; 51862306a36Sopenharmony_ci u8 *mac_byte_ptr; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 52162306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, MLNX_MFG_TYPE_OOB_MAC, 0, 0, 0, 52262306a36Sopenharmony_ci 0, 0, 0, &res); 52362306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 52462306a36Sopenharmony_ci if (res.a0) 52562306a36Sopenharmony_ci return -EPERM; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci mac_byte_ptr = (u8 *)&res.a1; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci return sysfs_format_mac(buf, mac_byte_ptr, ETH_ALEN); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic ssize_t oob_mac_store(struct device *dev, 53362306a36Sopenharmony_ci struct device_attribute *attr, 53462306a36Sopenharmony_ci const char *buf, size_t count) 53562306a36Sopenharmony_ci{ 53662306a36Sopenharmony_ci unsigned int byte[MLNX_MFG_OOB_MAC_FORMAT_LEN] = { 0 }; 53762306a36Sopenharmony_ci struct arm_smccc_res res; 53862306a36Sopenharmony_ci int byte_idx, len; 53962306a36Sopenharmony_ci u64 mac_addr = 0; 54062306a36Sopenharmony_ci u8 *mac_byte_ptr; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci if ((count - 1) != MLNX_MFG_OOB_MAC_FORMAT_LEN) 54362306a36Sopenharmony_ci return -EINVAL; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci len = sscanf(buf, "%02x:%02x:%02x:%02x:%02x:%02x", 54662306a36Sopenharmony_ci &byte[0], &byte[1], &byte[2], 54762306a36Sopenharmony_ci &byte[3], &byte[4], &byte[5]); 54862306a36Sopenharmony_ci if (len != ETH_ALEN) 54962306a36Sopenharmony_ci return -EINVAL; 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci mac_byte_ptr = (u8 *)&mac_addr; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci for (byte_idx = 0; byte_idx < ETH_ALEN; byte_idx++) 55462306a36Sopenharmony_ci mac_byte_ptr[byte_idx] = (u8)byte[byte_idx]; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 55762306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, MLNX_MFG_TYPE_OOB_MAC, 55862306a36Sopenharmony_ci ETH_ALEN, mac_addr, 0, 0, 0, 0, &res); 55962306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return res.a0 ? -EPERM : count; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic ssize_t opn_show(struct device *dev, 56562306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 56662306a36Sopenharmony_ci{ 56762306a36Sopenharmony_ci u64 opn_data[MLNX_MFG_VAL_QWORD_CNT(OPN) + 1] = { 0 }; 56862306a36Sopenharmony_ci struct arm_smccc_res res; 56962306a36Sopenharmony_ci int word; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 57262306a36Sopenharmony_ci for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(OPN); word++) { 57362306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, 57462306a36Sopenharmony_ci MLNX_MFG_TYPE_OPN_0 + word, 57562306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, &res); 57662306a36Sopenharmony_ci if (res.a0) { 57762306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 57862306a36Sopenharmony_ci return -EPERM; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci opn_data[word] = res.a1; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s", (char *)opn_data); 58562306a36Sopenharmony_ci} 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_cistatic ssize_t opn_store(struct device *dev, 58862306a36Sopenharmony_ci struct device_attribute *attr, 58962306a36Sopenharmony_ci const char *buf, size_t count) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci u64 opn[MLNX_MFG_VAL_QWORD_CNT(OPN)] = { 0 }; 59262306a36Sopenharmony_ci struct arm_smccc_res res; 59362306a36Sopenharmony_ci int word; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (count > MLNX_MFG_OPN_VAL_LEN) 59662306a36Sopenharmony_ci return -EINVAL; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci memcpy(opn, buf, count); 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 60162306a36Sopenharmony_ci for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(OPN); word++) { 60262306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, 60362306a36Sopenharmony_ci MLNX_MFG_TYPE_OPN_0 + word, 60462306a36Sopenharmony_ci sizeof(u64), opn[word], 0, 0, 0, 0, &res); 60562306a36Sopenharmony_ci if (res.a0) { 60662306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 60762306a36Sopenharmony_ci return -EPERM; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci return count; 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_cistatic ssize_t sku_show(struct device *dev, 61662306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci u64 sku_data[MLNX_MFG_VAL_QWORD_CNT(SKU) + 1] = { 0 }; 61962306a36Sopenharmony_ci struct arm_smccc_res res; 62062306a36Sopenharmony_ci int word; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 62362306a36Sopenharmony_ci for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SKU); word++) { 62462306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, 62562306a36Sopenharmony_ci MLNX_MFG_TYPE_SKU_0 + word, 62662306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, &res); 62762306a36Sopenharmony_ci if (res.a0) { 62862306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 62962306a36Sopenharmony_ci return -EPERM; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci sku_data[word] = res.a1; 63262306a36Sopenharmony_ci } 63362306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s", (char *)sku_data); 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_cistatic ssize_t sku_store(struct device *dev, 63962306a36Sopenharmony_ci struct device_attribute *attr, 64062306a36Sopenharmony_ci const char *buf, size_t count) 64162306a36Sopenharmony_ci{ 64262306a36Sopenharmony_ci u64 sku[MLNX_MFG_VAL_QWORD_CNT(SKU)] = { 0 }; 64362306a36Sopenharmony_ci struct arm_smccc_res res; 64462306a36Sopenharmony_ci int word; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (count > MLNX_MFG_SKU_VAL_LEN) 64762306a36Sopenharmony_ci return -EINVAL; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci memcpy(sku, buf, count); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 65262306a36Sopenharmony_ci for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SKU); word++) { 65362306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, 65462306a36Sopenharmony_ci MLNX_MFG_TYPE_SKU_0 + word, 65562306a36Sopenharmony_ci sizeof(u64), sku[word], 0, 0, 0, 0, &res); 65662306a36Sopenharmony_ci if (res.a0) { 65762306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 65862306a36Sopenharmony_ci return -EPERM; 65962306a36Sopenharmony_ci } 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return count; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic ssize_t modl_show(struct device *dev, 66762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 66862306a36Sopenharmony_ci{ 66962306a36Sopenharmony_ci u64 modl_data[MLNX_MFG_VAL_QWORD_CNT(MODL) + 1] = { 0 }; 67062306a36Sopenharmony_ci struct arm_smccc_res res; 67162306a36Sopenharmony_ci int word; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 67462306a36Sopenharmony_ci for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(MODL); word++) { 67562306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, 67662306a36Sopenharmony_ci MLNX_MFG_TYPE_MODL_0 + word, 67762306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, &res); 67862306a36Sopenharmony_ci if (res.a0) { 67962306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 68062306a36Sopenharmony_ci return -EPERM; 68162306a36Sopenharmony_ci } 68262306a36Sopenharmony_ci modl_data[word] = res.a1; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s", (char *)modl_data); 68762306a36Sopenharmony_ci} 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_cistatic ssize_t modl_store(struct device *dev, 69062306a36Sopenharmony_ci struct device_attribute *attr, 69162306a36Sopenharmony_ci const char *buf, size_t count) 69262306a36Sopenharmony_ci{ 69362306a36Sopenharmony_ci u64 modl[MLNX_MFG_VAL_QWORD_CNT(MODL)] = { 0 }; 69462306a36Sopenharmony_ci struct arm_smccc_res res; 69562306a36Sopenharmony_ci int word; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (count > MLNX_MFG_MODL_VAL_LEN) 69862306a36Sopenharmony_ci return -EINVAL; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci memcpy(modl, buf, count); 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 70362306a36Sopenharmony_ci for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(MODL); word++) { 70462306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, 70562306a36Sopenharmony_ci MLNX_MFG_TYPE_MODL_0 + word, 70662306a36Sopenharmony_ci sizeof(u64), modl[word], 0, 0, 0, 0, &res); 70762306a36Sopenharmony_ci if (res.a0) { 70862306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 70962306a36Sopenharmony_ci return -EPERM; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci } 71262306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci return count; 71562306a36Sopenharmony_ci} 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_cistatic ssize_t sn_show(struct device *dev, 71862306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci u64 sn_data[MLNX_MFG_VAL_QWORD_CNT(SN) + 1] = { 0 }; 72162306a36Sopenharmony_ci struct arm_smccc_res res; 72262306a36Sopenharmony_ci int word; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 72562306a36Sopenharmony_ci for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SN); word++) { 72662306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, 72762306a36Sopenharmony_ci MLNX_MFG_TYPE_SN_0 + word, 72862306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, &res); 72962306a36Sopenharmony_ci if (res.a0) { 73062306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 73162306a36Sopenharmony_ci return -EPERM; 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci sn_data[word] = res.a1; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s", (char *)sn_data); 73862306a36Sopenharmony_ci} 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic ssize_t sn_store(struct device *dev, 74162306a36Sopenharmony_ci struct device_attribute *attr, 74262306a36Sopenharmony_ci const char *buf, size_t count) 74362306a36Sopenharmony_ci{ 74462306a36Sopenharmony_ci u64 sn[MLNX_MFG_VAL_QWORD_CNT(SN)] = { 0 }; 74562306a36Sopenharmony_ci struct arm_smccc_res res; 74662306a36Sopenharmony_ci int word; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci if (count > MLNX_MFG_SN_VAL_LEN) 74962306a36Sopenharmony_ci return -EINVAL; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci memcpy(sn, buf, count); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 75462306a36Sopenharmony_ci for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(SN); word++) { 75562306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, 75662306a36Sopenharmony_ci MLNX_MFG_TYPE_SN_0 + word, 75762306a36Sopenharmony_ci sizeof(u64), sn[word], 0, 0, 0, 0, &res); 75862306a36Sopenharmony_ci if (res.a0) { 75962306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 76062306a36Sopenharmony_ci return -EPERM; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return count; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic ssize_t uuid_show(struct device *dev, 76962306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci u64 uuid_data[MLNX_MFG_VAL_QWORD_CNT(UUID) + 1] = { 0 }; 77262306a36Sopenharmony_ci struct arm_smccc_res res; 77362306a36Sopenharmony_ci int word; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 77662306a36Sopenharmony_ci for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(UUID); word++) { 77762306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, 77862306a36Sopenharmony_ci MLNX_MFG_TYPE_UUID_0 + word, 77962306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, &res); 78062306a36Sopenharmony_ci if (res.a0) { 78162306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 78262306a36Sopenharmony_ci return -EPERM; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci uuid_data[word] = res.a1; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s", (char *)uuid_data); 78962306a36Sopenharmony_ci} 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cistatic ssize_t uuid_store(struct device *dev, 79262306a36Sopenharmony_ci struct device_attribute *attr, 79362306a36Sopenharmony_ci const char *buf, size_t count) 79462306a36Sopenharmony_ci{ 79562306a36Sopenharmony_ci u64 uuid[MLNX_MFG_VAL_QWORD_CNT(UUID)] = { 0 }; 79662306a36Sopenharmony_ci struct arm_smccc_res res; 79762306a36Sopenharmony_ci int word; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci if (count > MLNX_MFG_UUID_VAL_LEN) 80062306a36Sopenharmony_ci return -EINVAL; 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci memcpy(uuid, buf, count); 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 80562306a36Sopenharmony_ci for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(UUID); word++) { 80662306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, 80762306a36Sopenharmony_ci MLNX_MFG_TYPE_UUID_0 + word, 80862306a36Sopenharmony_ci sizeof(u64), uuid[word], 0, 0, 0, 0, &res); 80962306a36Sopenharmony_ci if (res.a0) { 81062306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 81162306a36Sopenharmony_ci return -EPERM; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci return count; 81762306a36Sopenharmony_ci} 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_cistatic ssize_t rev_show(struct device *dev, 82062306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci u64 rev_data[MLNX_MFG_VAL_QWORD_CNT(REV) + 1] = { 0 }; 82362306a36Sopenharmony_ci struct arm_smccc_res res; 82462306a36Sopenharmony_ci int word; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 82762306a36Sopenharmony_ci for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(REV); word++) { 82862306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_GET_MFG_INFO, 82962306a36Sopenharmony_ci MLNX_MFG_TYPE_REV + word, 83062306a36Sopenharmony_ci 0, 0, 0, 0, 0, 0, &res); 83162306a36Sopenharmony_ci if (res.a0) { 83262306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 83362306a36Sopenharmony_ci return -EPERM; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci rev_data[word] = res.a1; 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci return snprintf(buf, PAGE_SIZE, "%s", (char *)rev_data); 84062306a36Sopenharmony_ci} 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_cistatic ssize_t rev_store(struct device *dev, 84362306a36Sopenharmony_ci struct device_attribute *attr, 84462306a36Sopenharmony_ci const char *buf, size_t count) 84562306a36Sopenharmony_ci{ 84662306a36Sopenharmony_ci u64 rev[MLNX_MFG_VAL_QWORD_CNT(REV)] = { 0 }; 84762306a36Sopenharmony_ci struct arm_smccc_res res; 84862306a36Sopenharmony_ci int word; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (count > MLNX_MFG_REV_VAL_LEN) 85162306a36Sopenharmony_ci return -EINVAL; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci memcpy(rev, buf, count); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 85662306a36Sopenharmony_ci for (word = 0; word < MLNX_MFG_VAL_QWORD_CNT(REV); word++) { 85762306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_SET_MFG_INFO, 85862306a36Sopenharmony_ci MLNX_MFG_TYPE_REV + word, 85962306a36Sopenharmony_ci sizeof(u64), rev[word], 0, 0, 0, 0, &res); 86062306a36Sopenharmony_ci if (res.a0) { 86162306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 86262306a36Sopenharmony_ci return -EPERM; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 86662306a36Sopenharmony_ci 86762306a36Sopenharmony_ci return count; 86862306a36Sopenharmony_ci} 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_cistatic ssize_t mfg_lock_store(struct device *dev, 87162306a36Sopenharmony_ci struct device_attribute *attr, 87262306a36Sopenharmony_ci const char *buf, size_t count) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci struct arm_smccc_res res; 87562306a36Sopenharmony_ci unsigned long val; 87662306a36Sopenharmony_ci int err; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci err = kstrtoul(buf, 10, &val); 87962306a36Sopenharmony_ci if (err) 88062306a36Sopenharmony_ci return err; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (val != 1) 88362306a36Sopenharmony_ci return -EINVAL; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci mutex_lock(&mfg_ops_lock); 88662306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_LOCK_MFG_INFO, 0, 0, 0, 0, 0, 0, 0, &res); 88762306a36Sopenharmony_ci mutex_unlock(&mfg_ops_lock); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci return count; 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(post_reset_wdog); 89362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(reset_action); 89462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(second_reset_action); 89562306a36Sopenharmony_cistatic DEVICE_ATTR_RO(lifecycle_state); 89662306a36Sopenharmony_cistatic DEVICE_ATTR_RO(secure_boot_fuse_state); 89762306a36Sopenharmony_cistatic DEVICE_ATTR_WO(fw_reset); 89862306a36Sopenharmony_cistatic DEVICE_ATTR_WO(rsh_log); 89962306a36Sopenharmony_cistatic DEVICE_ATTR_RW(large_icm); 90062306a36Sopenharmony_cistatic DEVICE_ATTR_WO(os_up); 90162306a36Sopenharmony_cistatic DEVICE_ATTR_RW(oob_mac); 90262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(opn); 90362306a36Sopenharmony_cistatic DEVICE_ATTR_RW(sku); 90462306a36Sopenharmony_cistatic DEVICE_ATTR_RW(modl); 90562306a36Sopenharmony_cistatic DEVICE_ATTR_RW(sn); 90662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(uuid); 90762306a36Sopenharmony_cistatic DEVICE_ATTR_RW(rev); 90862306a36Sopenharmony_cistatic DEVICE_ATTR_WO(mfg_lock); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_cistatic struct attribute *mlxbf_bootctl_attrs[] = { 91162306a36Sopenharmony_ci &dev_attr_post_reset_wdog.attr, 91262306a36Sopenharmony_ci &dev_attr_reset_action.attr, 91362306a36Sopenharmony_ci &dev_attr_second_reset_action.attr, 91462306a36Sopenharmony_ci &dev_attr_lifecycle_state.attr, 91562306a36Sopenharmony_ci &dev_attr_secure_boot_fuse_state.attr, 91662306a36Sopenharmony_ci &dev_attr_fw_reset.attr, 91762306a36Sopenharmony_ci &dev_attr_rsh_log.attr, 91862306a36Sopenharmony_ci &dev_attr_large_icm.attr, 91962306a36Sopenharmony_ci &dev_attr_os_up.attr, 92062306a36Sopenharmony_ci &dev_attr_oob_mac.attr, 92162306a36Sopenharmony_ci &dev_attr_opn.attr, 92262306a36Sopenharmony_ci &dev_attr_sku.attr, 92362306a36Sopenharmony_ci &dev_attr_modl.attr, 92462306a36Sopenharmony_ci &dev_attr_sn.attr, 92562306a36Sopenharmony_ci &dev_attr_uuid.attr, 92662306a36Sopenharmony_ci &dev_attr_rev.attr, 92762306a36Sopenharmony_ci &dev_attr_mfg_lock.attr, 92862306a36Sopenharmony_ci NULL 92962306a36Sopenharmony_ci}; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ciATTRIBUTE_GROUPS(mlxbf_bootctl); 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_cistatic const struct acpi_device_id mlxbf_bootctl_acpi_ids[] = { 93462306a36Sopenharmony_ci {"MLNXBF04", 0}, 93562306a36Sopenharmony_ci {} 93662306a36Sopenharmony_ci}; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, mlxbf_bootctl_acpi_ids); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic ssize_t mlxbf_bootctl_bootfifo_read(struct file *filp, 94162306a36Sopenharmony_ci struct kobject *kobj, 94262306a36Sopenharmony_ci struct bin_attribute *bin_attr, 94362306a36Sopenharmony_ci char *buf, loff_t pos, 94462306a36Sopenharmony_ci size_t count) 94562306a36Sopenharmony_ci{ 94662306a36Sopenharmony_ci unsigned long timeout = msecs_to_jiffies(500); 94762306a36Sopenharmony_ci unsigned long expire = jiffies + timeout; 94862306a36Sopenharmony_ci u64 data, cnt = 0; 94962306a36Sopenharmony_ci char *p = buf; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci while (count >= sizeof(data)) { 95262306a36Sopenharmony_ci /* Give up reading if no more data within 500ms. */ 95362306a36Sopenharmony_ci if (!cnt) { 95462306a36Sopenharmony_ci cnt = readq(mlxbf_rsh_boot_cnt); 95562306a36Sopenharmony_ci if (!cnt) { 95662306a36Sopenharmony_ci if (time_after(jiffies, expire)) 95762306a36Sopenharmony_ci break; 95862306a36Sopenharmony_ci usleep_range(10, 50); 95962306a36Sopenharmony_ci continue; 96062306a36Sopenharmony_ci } 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci data = readq(mlxbf_rsh_boot_data); 96462306a36Sopenharmony_ci memcpy(p, &data, sizeof(data)); 96562306a36Sopenharmony_ci count -= sizeof(data); 96662306a36Sopenharmony_ci p += sizeof(data); 96762306a36Sopenharmony_ci cnt--; 96862306a36Sopenharmony_ci expire = jiffies + timeout; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci return p - buf; 97262306a36Sopenharmony_ci} 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_cistatic struct bin_attribute mlxbf_bootctl_bootfifo_sysfs_attr = { 97562306a36Sopenharmony_ci .attr = { .name = "bootfifo", .mode = 0400 }, 97662306a36Sopenharmony_ci .read = mlxbf_bootctl_bootfifo_read, 97762306a36Sopenharmony_ci}; 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_cistatic bool mlxbf_bootctl_guid_match(const guid_t *guid, 98062306a36Sopenharmony_ci const struct arm_smccc_res *res) 98162306a36Sopenharmony_ci{ 98262306a36Sopenharmony_ci guid_t id = GUID_INIT(res->a0, res->a1, res->a1 >> 16, 98362306a36Sopenharmony_ci res->a2, res->a2 >> 8, res->a2 >> 16, 98462306a36Sopenharmony_ci res->a2 >> 24, res->a3, res->a3 >> 8, 98562306a36Sopenharmony_ci res->a3 >> 16, res->a3 >> 24); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci return guid_equal(guid, &id); 98862306a36Sopenharmony_ci} 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_cistatic int mlxbf_bootctl_probe(struct platform_device *pdev) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci struct arm_smccc_res res = { 0 }; 99362306a36Sopenharmony_ci void __iomem *reg; 99462306a36Sopenharmony_ci guid_t guid; 99562306a36Sopenharmony_ci int ret; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci /* Map the resource of the bootfifo data register. */ 99862306a36Sopenharmony_ci mlxbf_rsh_boot_data = devm_platform_ioremap_resource(pdev, 0); 99962306a36Sopenharmony_ci if (IS_ERR(mlxbf_rsh_boot_data)) 100062306a36Sopenharmony_ci return PTR_ERR(mlxbf_rsh_boot_data); 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci /* Map the resource of the bootfifo counter register. */ 100362306a36Sopenharmony_ci mlxbf_rsh_boot_cnt = devm_platform_ioremap_resource(pdev, 1); 100462306a36Sopenharmony_ci if (IS_ERR(mlxbf_rsh_boot_cnt)) 100562306a36Sopenharmony_ci return PTR_ERR(mlxbf_rsh_boot_cnt); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci /* Map the resource of the rshim semaphore register. */ 100862306a36Sopenharmony_ci mlxbf_rsh_semaphore = devm_platform_ioremap_resource(pdev, 2); 100962306a36Sopenharmony_ci if (IS_ERR(mlxbf_rsh_semaphore)) 101062306a36Sopenharmony_ci return PTR_ERR(mlxbf_rsh_semaphore); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci /* Map the resource of the scratch buffer (log) registers. */ 101362306a36Sopenharmony_ci reg = devm_platform_ioremap_resource(pdev, 3); 101462306a36Sopenharmony_ci if (IS_ERR(reg)) 101562306a36Sopenharmony_ci return PTR_ERR(reg); 101662306a36Sopenharmony_ci mlxbf_rsh_scratch_buf_ctl = reg + MLXBF_RSH_SCRATCH_BUF_CTL_OFF; 101762306a36Sopenharmony_ci mlxbf_rsh_scratch_buf_data = reg + MLXBF_RSH_SCRATCH_BUF_DATA_OFF; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci /* Ensure we have the UUID we expect for this service. */ 102062306a36Sopenharmony_ci arm_smccc_smc(MLXBF_BOOTCTL_SIP_SVC_UID, 0, 0, 0, 0, 0, 0, 0, &res); 102162306a36Sopenharmony_ci guid_parse(mlxbf_bootctl_svc_uuid_str, &guid); 102262306a36Sopenharmony_ci if (!mlxbf_bootctl_guid_match(&guid, &res)) 102362306a36Sopenharmony_ci return -ENODEV; 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* 102662306a36Sopenharmony_ci * When watchdog is used, it sets boot mode to MLXBF_BOOTCTL_SWAP_EMMC 102762306a36Sopenharmony_ci * in case of boot failures. However it doesn't clear the state if there 102862306a36Sopenharmony_ci * is no failure. Restore the default boot mode here to avoid any 102962306a36Sopenharmony_ci * unnecessary boot partition swapping. 103062306a36Sopenharmony_ci */ 103162306a36Sopenharmony_ci ret = mlxbf_bootctl_smc(MLXBF_BOOTCTL_SET_RESET_ACTION, 103262306a36Sopenharmony_ci MLXBF_BOOTCTL_EMMC); 103362306a36Sopenharmony_ci if (ret < 0) 103462306a36Sopenharmony_ci dev_warn(&pdev->dev, "Unable to reset the EMMC boot mode\n"); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci ret = sysfs_create_bin_file(&pdev->dev.kobj, 103762306a36Sopenharmony_ci &mlxbf_bootctl_bootfifo_sysfs_attr); 103862306a36Sopenharmony_ci if (ret) 103962306a36Sopenharmony_ci pr_err("Unable to create bootfifo sysfs file, error %d\n", ret); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci return ret; 104262306a36Sopenharmony_ci} 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_cistatic int mlxbf_bootctl_remove(struct platform_device *pdev) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci sysfs_remove_bin_file(&pdev->dev.kobj, 104762306a36Sopenharmony_ci &mlxbf_bootctl_bootfifo_sysfs_attr); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci return 0; 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic struct platform_driver mlxbf_bootctl_driver = { 105362306a36Sopenharmony_ci .probe = mlxbf_bootctl_probe, 105462306a36Sopenharmony_ci .remove = mlxbf_bootctl_remove, 105562306a36Sopenharmony_ci .driver = { 105662306a36Sopenharmony_ci .name = "mlxbf-bootctl", 105762306a36Sopenharmony_ci .dev_groups = mlxbf_bootctl_groups, 105862306a36Sopenharmony_ci .acpi_match_table = mlxbf_bootctl_acpi_ids, 105962306a36Sopenharmony_ci } 106062306a36Sopenharmony_ci}; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cimodule_platform_driver(mlxbf_bootctl_driver); 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_ciMODULE_DESCRIPTION("Mellanox boot control driver"); 106562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 106662306a36Sopenharmony_ciMODULE_AUTHOR("Mellanox Technologies"); 1067