162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright(c) 2018 Intel Corporation. All rights reserved. */
362306a36Sopenharmony_ci#include <linux/libnvdimm.h>
462306a36Sopenharmony_ci#include <linux/ndctl.h>
562306a36Sopenharmony_ci#include <linux/acpi.h>
662306a36Sopenharmony_ci#include <linux/memregion.h>
762306a36Sopenharmony_ci#include <asm/smp.h>
862306a36Sopenharmony_ci#include "intel.h"
962306a36Sopenharmony_ci#include "nfit.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistatic ssize_t firmware_activate_noidle_show(struct device *dev,
1262306a36Sopenharmony_ci		struct device_attribute *attr, char *buf)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
1562306a36Sopenharmony_ci	struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
1662306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	return sprintf(buf, "%s\n", acpi_desc->fwa_noidle ? "Y" : "N");
1962306a36Sopenharmony_ci}
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic ssize_t firmware_activate_noidle_store(struct device *dev,
2262306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t size)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
2562306a36Sopenharmony_ci	struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
2662306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
2762306a36Sopenharmony_ci	ssize_t rc;
2862306a36Sopenharmony_ci	bool val;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	rc = kstrtobool(buf, &val);
3162306a36Sopenharmony_ci	if (rc)
3262306a36Sopenharmony_ci		return rc;
3362306a36Sopenharmony_ci	if (val != acpi_desc->fwa_noidle)
3462306a36Sopenharmony_ci		acpi_desc->fwa_cap = NVDIMM_FWA_CAP_INVALID;
3562306a36Sopenharmony_ci	acpi_desc->fwa_noidle = val;
3662306a36Sopenharmony_ci	return size;
3762306a36Sopenharmony_ci}
3862306a36Sopenharmony_ciDEVICE_ATTR_RW(firmware_activate_noidle);
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cibool intel_fwa_supported(struct nvdimm_bus *nvdimm_bus)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
4362306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
4462306a36Sopenharmony_ci	unsigned long *mask;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (!test_bit(NVDIMM_BUS_FAMILY_INTEL, &nd_desc->bus_family_mask))
4762306a36Sopenharmony_ci		return false;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL];
5062306a36Sopenharmony_ci	return *mask == NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic unsigned long intel_security_flags(struct nvdimm *nvdimm,
5462306a36Sopenharmony_ci		enum nvdimm_passphrase_type ptype)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
5762306a36Sopenharmony_ci	unsigned long security_flags = 0;
5862306a36Sopenharmony_ci	struct {
5962306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
6062306a36Sopenharmony_ci		struct nd_intel_get_security_state cmd;
6162306a36Sopenharmony_ci	} nd_cmd = {
6262306a36Sopenharmony_ci		.pkg = {
6362306a36Sopenharmony_ci			.nd_command = NVDIMM_INTEL_GET_SECURITY_STATE,
6462306a36Sopenharmony_ci			.nd_family = NVDIMM_FAMILY_INTEL,
6562306a36Sopenharmony_ci			.nd_size_out =
6662306a36Sopenharmony_ci				sizeof(struct nd_intel_get_security_state),
6762306a36Sopenharmony_ci			.nd_fw_size =
6862306a36Sopenharmony_ci				sizeof(struct nd_intel_get_security_state),
6962306a36Sopenharmony_ci		},
7062306a36Sopenharmony_ci	};
7162306a36Sopenharmony_ci	int rc;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (!test_bit(NVDIMM_INTEL_GET_SECURITY_STATE, &nfit_mem->dsm_mask))
7462306a36Sopenharmony_ci		return 0;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/*
7762306a36Sopenharmony_ci	 * Short circuit the state retrieval while we are doing overwrite.
7862306a36Sopenharmony_ci	 * The DSM spec states that the security state is indeterminate
7962306a36Sopenharmony_ci	 * until the overwrite DSM completes.
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	if (nvdimm_in_overwrite(nvdimm) && ptype == NVDIMM_USER)
8262306a36Sopenharmony_ci		return BIT(NVDIMM_SECURITY_OVERWRITE);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
8562306a36Sopenharmony_ci	if (rc < 0 || nd_cmd.cmd.status) {
8662306a36Sopenharmony_ci		pr_err("%s: security state retrieval failed (%d:%#x)\n",
8762306a36Sopenharmony_ci				nvdimm_name(nvdimm), rc, nd_cmd.cmd.status);
8862306a36Sopenharmony_ci		return 0;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* check and see if security is enabled and locked */
9262306a36Sopenharmony_ci	if (ptype == NVDIMM_MASTER) {
9362306a36Sopenharmony_ci		if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_ENABLED)
9462306a36Sopenharmony_ci			set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
9562306a36Sopenharmony_ci		else
9662306a36Sopenharmony_ci			set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
9762306a36Sopenharmony_ci		if (nd_cmd.cmd.extended_state & ND_INTEL_SEC_ESTATE_PLIMIT)
9862306a36Sopenharmony_ci			set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
9962306a36Sopenharmony_ci		return security_flags;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_UNSUPPORTED)
10362306a36Sopenharmony_ci		return 0;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_ENABLED) {
10662306a36Sopenharmony_ci		if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_FROZEN ||
10762306a36Sopenharmony_ci		    nd_cmd.cmd.state & ND_INTEL_SEC_STATE_PLIMIT)
10862306a36Sopenharmony_ci			set_bit(NVDIMM_SECURITY_FROZEN, &security_flags);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		if (nd_cmd.cmd.state & ND_INTEL_SEC_STATE_LOCKED)
11162306a36Sopenharmony_ci			set_bit(NVDIMM_SECURITY_LOCKED, &security_flags);
11262306a36Sopenharmony_ci		else
11362306a36Sopenharmony_ci			set_bit(NVDIMM_SECURITY_UNLOCKED, &security_flags);
11462306a36Sopenharmony_ci	} else
11562306a36Sopenharmony_ci		set_bit(NVDIMM_SECURITY_DISABLED, &security_flags);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return security_flags;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int intel_security_freeze(struct nvdimm *nvdimm)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
12362306a36Sopenharmony_ci	struct {
12462306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
12562306a36Sopenharmony_ci		struct nd_intel_freeze_lock cmd;
12662306a36Sopenharmony_ci	} nd_cmd = {
12762306a36Sopenharmony_ci		.pkg = {
12862306a36Sopenharmony_ci			.nd_command = NVDIMM_INTEL_FREEZE_LOCK,
12962306a36Sopenharmony_ci			.nd_family = NVDIMM_FAMILY_INTEL,
13062306a36Sopenharmony_ci			.nd_size_out = ND_INTEL_STATUS_SIZE,
13162306a36Sopenharmony_ci			.nd_fw_size = ND_INTEL_STATUS_SIZE,
13262306a36Sopenharmony_ci		},
13362306a36Sopenharmony_ci	};
13462306a36Sopenharmony_ci	int rc;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (!test_bit(NVDIMM_INTEL_FREEZE_LOCK, &nfit_mem->dsm_mask))
13762306a36Sopenharmony_ci		return -ENOTTY;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
14062306a36Sopenharmony_ci	if (rc < 0)
14162306a36Sopenharmony_ci		return rc;
14262306a36Sopenharmony_ci	if (nd_cmd.cmd.status)
14362306a36Sopenharmony_ci		return -EIO;
14462306a36Sopenharmony_ci	return 0;
14562306a36Sopenharmony_ci}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_cistatic int intel_security_change_key(struct nvdimm *nvdimm,
14862306a36Sopenharmony_ci		const struct nvdimm_key_data *old_data,
14962306a36Sopenharmony_ci		const struct nvdimm_key_data *new_data,
15062306a36Sopenharmony_ci		enum nvdimm_passphrase_type ptype)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
15362306a36Sopenharmony_ci	unsigned int cmd = ptype == NVDIMM_MASTER ?
15462306a36Sopenharmony_ci		NVDIMM_INTEL_SET_MASTER_PASSPHRASE :
15562306a36Sopenharmony_ci		NVDIMM_INTEL_SET_PASSPHRASE;
15662306a36Sopenharmony_ci	struct {
15762306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
15862306a36Sopenharmony_ci		struct nd_intel_set_passphrase cmd;
15962306a36Sopenharmony_ci	} nd_cmd = {
16062306a36Sopenharmony_ci		.pkg = {
16162306a36Sopenharmony_ci			.nd_family = NVDIMM_FAMILY_INTEL,
16262306a36Sopenharmony_ci			.nd_size_in = ND_INTEL_PASSPHRASE_SIZE * 2,
16362306a36Sopenharmony_ci			.nd_size_out = ND_INTEL_STATUS_SIZE,
16462306a36Sopenharmony_ci			.nd_fw_size = ND_INTEL_STATUS_SIZE,
16562306a36Sopenharmony_ci			.nd_command = cmd,
16662306a36Sopenharmony_ci		},
16762306a36Sopenharmony_ci	};
16862306a36Sopenharmony_ci	int rc;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	if (!test_bit(cmd, &nfit_mem->dsm_mask))
17162306a36Sopenharmony_ci		return -ENOTTY;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	memcpy(nd_cmd.cmd.old_pass, old_data->data,
17462306a36Sopenharmony_ci			sizeof(nd_cmd.cmd.old_pass));
17562306a36Sopenharmony_ci	memcpy(nd_cmd.cmd.new_pass, new_data->data,
17662306a36Sopenharmony_ci			sizeof(nd_cmd.cmd.new_pass));
17762306a36Sopenharmony_ci	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
17862306a36Sopenharmony_ci	if (rc < 0)
17962306a36Sopenharmony_ci		return rc;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	switch (nd_cmd.cmd.status) {
18262306a36Sopenharmony_ci	case 0:
18362306a36Sopenharmony_ci		return 0;
18462306a36Sopenharmony_ci	case ND_INTEL_STATUS_INVALID_PASS:
18562306a36Sopenharmony_ci		return -EINVAL;
18662306a36Sopenharmony_ci	case ND_INTEL_STATUS_NOT_SUPPORTED:
18762306a36Sopenharmony_ci		return -EOPNOTSUPP;
18862306a36Sopenharmony_ci	case ND_INTEL_STATUS_INVALID_STATE:
18962306a36Sopenharmony_ci	default:
19062306a36Sopenharmony_ci		return -EIO;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic int __maybe_unused intel_security_unlock(struct nvdimm *nvdimm,
19562306a36Sopenharmony_ci		const struct nvdimm_key_data *key_data)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
19862306a36Sopenharmony_ci	struct {
19962306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
20062306a36Sopenharmony_ci		struct nd_intel_unlock_unit cmd;
20162306a36Sopenharmony_ci	} nd_cmd = {
20262306a36Sopenharmony_ci		.pkg = {
20362306a36Sopenharmony_ci			.nd_command = NVDIMM_INTEL_UNLOCK_UNIT,
20462306a36Sopenharmony_ci			.nd_family = NVDIMM_FAMILY_INTEL,
20562306a36Sopenharmony_ci			.nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
20662306a36Sopenharmony_ci			.nd_size_out = ND_INTEL_STATUS_SIZE,
20762306a36Sopenharmony_ci			.nd_fw_size = ND_INTEL_STATUS_SIZE,
20862306a36Sopenharmony_ci		},
20962306a36Sopenharmony_ci	};
21062306a36Sopenharmony_ci	int rc;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (!test_bit(NVDIMM_INTEL_UNLOCK_UNIT, &nfit_mem->dsm_mask))
21362306a36Sopenharmony_ci		return -ENOTTY;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	memcpy(nd_cmd.cmd.passphrase, key_data->data,
21662306a36Sopenharmony_ci			sizeof(nd_cmd.cmd.passphrase));
21762306a36Sopenharmony_ci	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
21862306a36Sopenharmony_ci	if (rc < 0)
21962306a36Sopenharmony_ci		return rc;
22062306a36Sopenharmony_ci	switch (nd_cmd.cmd.status) {
22162306a36Sopenharmony_ci	case 0:
22262306a36Sopenharmony_ci		break;
22362306a36Sopenharmony_ci	case ND_INTEL_STATUS_INVALID_PASS:
22462306a36Sopenharmony_ci		return -EINVAL;
22562306a36Sopenharmony_ci	default:
22662306a36Sopenharmony_ci		return -EIO;
22762306a36Sopenharmony_ci	}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	return 0;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic int intel_security_disable(struct nvdimm *nvdimm,
23362306a36Sopenharmony_ci		const struct nvdimm_key_data *key_data)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	int rc;
23662306a36Sopenharmony_ci	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
23762306a36Sopenharmony_ci	struct {
23862306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
23962306a36Sopenharmony_ci		struct nd_intel_disable_passphrase cmd;
24062306a36Sopenharmony_ci	} nd_cmd = {
24162306a36Sopenharmony_ci		.pkg = {
24262306a36Sopenharmony_ci			.nd_command = NVDIMM_INTEL_DISABLE_PASSPHRASE,
24362306a36Sopenharmony_ci			.nd_family = NVDIMM_FAMILY_INTEL,
24462306a36Sopenharmony_ci			.nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
24562306a36Sopenharmony_ci			.nd_size_out = ND_INTEL_STATUS_SIZE,
24662306a36Sopenharmony_ci			.nd_fw_size = ND_INTEL_STATUS_SIZE,
24762306a36Sopenharmony_ci		},
24862306a36Sopenharmony_ci	};
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (!test_bit(NVDIMM_INTEL_DISABLE_PASSPHRASE, &nfit_mem->dsm_mask))
25162306a36Sopenharmony_ci		return -ENOTTY;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	memcpy(nd_cmd.cmd.passphrase, key_data->data,
25462306a36Sopenharmony_ci			sizeof(nd_cmd.cmd.passphrase));
25562306a36Sopenharmony_ci	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
25662306a36Sopenharmony_ci	if (rc < 0)
25762306a36Sopenharmony_ci		return rc;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	switch (nd_cmd.cmd.status) {
26062306a36Sopenharmony_ci	case 0:
26162306a36Sopenharmony_ci		break;
26262306a36Sopenharmony_ci	case ND_INTEL_STATUS_INVALID_PASS:
26362306a36Sopenharmony_ci		return -EINVAL;
26462306a36Sopenharmony_ci	case ND_INTEL_STATUS_INVALID_STATE:
26562306a36Sopenharmony_ci	default:
26662306a36Sopenharmony_ci		return -ENXIO;
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	return 0;
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_cistatic int __maybe_unused intel_security_erase(struct nvdimm *nvdimm,
27362306a36Sopenharmony_ci		const struct nvdimm_key_data *key,
27462306a36Sopenharmony_ci		enum nvdimm_passphrase_type ptype)
27562306a36Sopenharmony_ci{
27662306a36Sopenharmony_ci	int rc;
27762306a36Sopenharmony_ci	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
27862306a36Sopenharmony_ci	unsigned int cmd = ptype == NVDIMM_MASTER ?
27962306a36Sopenharmony_ci		NVDIMM_INTEL_MASTER_SECURE_ERASE : NVDIMM_INTEL_SECURE_ERASE;
28062306a36Sopenharmony_ci	struct {
28162306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
28262306a36Sopenharmony_ci		struct nd_intel_secure_erase cmd;
28362306a36Sopenharmony_ci	} nd_cmd = {
28462306a36Sopenharmony_ci		.pkg = {
28562306a36Sopenharmony_ci			.nd_family = NVDIMM_FAMILY_INTEL,
28662306a36Sopenharmony_ci			.nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
28762306a36Sopenharmony_ci			.nd_size_out = ND_INTEL_STATUS_SIZE,
28862306a36Sopenharmony_ci			.nd_fw_size = ND_INTEL_STATUS_SIZE,
28962306a36Sopenharmony_ci			.nd_command = cmd,
29062306a36Sopenharmony_ci		},
29162306a36Sopenharmony_ci	};
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	if (!test_bit(cmd, &nfit_mem->dsm_mask))
29462306a36Sopenharmony_ci		return -ENOTTY;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	memcpy(nd_cmd.cmd.passphrase, key->data,
29762306a36Sopenharmony_ci			sizeof(nd_cmd.cmd.passphrase));
29862306a36Sopenharmony_ci	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
29962306a36Sopenharmony_ci	if (rc < 0)
30062306a36Sopenharmony_ci		return rc;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	switch (nd_cmd.cmd.status) {
30362306a36Sopenharmony_ci	case 0:
30462306a36Sopenharmony_ci		break;
30562306a36Sopenharmony_ci	case ND_INTEL_STATUS_NOT_SUPPORTED:
30662306a36Sopenharmony_ci		return -EOPNOTSUPP;
30762306a36Sopenharmony_ci	case ND_INTEL_STATUS_INVALID_PASS:
30862306a36Sopenharmony_ci		return -EINVAL;
30962306a36Sopenharmony_ci	case ND_INTEL_STATUS_INVALID_STATE:
31062306a36Sopenharmony_ci	default:
31162306a36Sopenharmony_ci		return -ENXIO;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return 0;
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_cistatic int __maybe_unused intel_security_query_overwrite(struct nvdimm *nvdimm)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	int rc;
32062306a36Sopenharmony_ci	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
32162306a36Sopenharmony_ci	struct {
32262306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
32362306a36Sopenharmony_ci		struct nd_intel_query_overwrite cmd;
32462306a36Sopenharmony_ci	} nd_cmd = {
32562306a36Sopenharmony_ci		.pkg = {
32662306a36Sopenharmony_ci			.nd_command = NVDIMM_INTEL_QUERY_OVERWRITE,
32762306a36Sopenharmony_ci			.nd_family = NVDIMM_FAMILY_INTEL,
32862306a36Sopenharmony_ci			.nd_size_out = ND_INTEL_STATUS_SIZE,
32962306a36Sopenharmony_ci			.nd_fw_size = ND_INTEL_STATUS_SIZE,
33062306a36Sopenharmony_ci		},
33162306a36Sopenharmony_ci	};
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	if (!test_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &nfit_mem->dsm_mask))
33462306a36Sopenharmony_ci		return -ENOTTY;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
33762306a36Sopenharmony_ci	if (rc < 0)
33862306a36Sopenharmony_ci		return rc;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	switch (nd_cmd.cmd.status) {
34162306a36Sopenharmony_ci	case 0:
34262306a36Sopenharmony_ci		break;
34362306a36Sopenharmony_ci	case ND_INTEL_STATUS_OQUERY_INPROGRESS:
34462306a36Sopenharmony_ci		return -EBUSY;
34562306a36Sopenharmony_ci	default:
34662306a36Sopenharmony_ci		return -ENXIO;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	return 0;
35062306a36Sopenharmony_ci}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_cistatic int __maybe_unused intel_security_overwrite(struct nvdimm *nvdimm,
35362306a36Sopenharmony_ci		const struct nvdimm_key_data *nkey)
35462306a36Sopenharmony_ci{
35562306a36Sopenharmony_ci	int rc;
35662306a36Sopenharmony_ci	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
35762306a36Sopenharmony_ci	struct {
35862306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
35962306a36Sopenharmony_ci		struct nd_intel_overwrite cmd;
36062306a36Sopenharmony_ci	} nd_cmd = {
36162306a36Sopenharmony_ci		.pkg = {
36262306a36Sopenharmony_ci			.nd_command = NVDIMM_INTEL_OVERWRITE,
36362306a36Sopenharmony_ci			.nd_family = NVDIMM_FAMILY_INTEL,
36462306a36Sopenharmony_ci			.nd_size_in = ND_INTEL_PASSPHRASE_SIZE,
36562306a36Sopenharmony_ci			.nd_size_out = ND_INTEL_STATUS_SIZE,
36662306a36Sopenharmony_ci			.nd_fw_size = ND_INTEL_STATUS_SIZE,
36762306a36Sopenharmony_ci		},
36862306a36Sopenharmony_ci	};
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	if (!test_bit(NVDIMM_INTEL_OVERWRITE, &nfit_mem->dsm_mask))
37162306a36Sopenharmony_ci		return -ENOTTY;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	memcpy(nd_cmd.cmd.passphrase, nkey->data,
37462306a36Sopenharmony_ci			sizeof(nd_cmd.cmd.passphrase));
37562306a36Sopenharmony_ci	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
37662306a36Sopenharmony_ci	if (rc < 0)
37762306a36Sopenharmony_ci		return rc;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	switch (nd_cmd.cmd.status) {
38062306a36Sopenharmony_ci	case 0:
38162306a36Sopenharmony_ci		return 0;
38262306a36Sopenharmony_ci	case ND_INTEL_STATUS_OVERWRITE_UNSUPPORTED:
38362306a36Sopenharmony_ci		return -ENOTSUPP;
38462306a36Sopenharmony_ci	case ND_INTEL_STATUS_INVALID_PASS:
38562306a36Sopenharmony_ci		return -EINVAL;
38662306a36Sopenharmony_ci	case ND_INTEL_STATUS_INVALID_STATE:
38762306a36Sopenharmony_ci	default:
38862306a36Sopenharmony_ci		return -ENXIO;
38962306a36Sopenharmony_ci	}
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_cistatic const struct nvdimm_security_ops __intel_security_ops = {
39362306a36Sopenharmony_ci	.get_flags = intel_security_flags,
39462306a36Sopenharmony_ci	.freeze = intel_security_freeze,
39562306a36Sopenharmony_ci	.change_key = intel_security_change_key,
39662306a36Sopenharmony_ci	.disable = intel_security_disable,
39762306a36Sopenharmony_ci#ifdef CONFIG_X86
39862306a36Sopenharmony_ci	.unlock = intel_security_unlock,
39962306a36Sopenharmony_ci	.erase = intel_security_erase,
40062306a36Sopenharmony_ci	.overwrite = intel_security_overwrite,
40162306a36Sopenharmony_ci	.query_overwrite = intel_security_query_overwrite,
40262306a36Sopenharmony_ci#endif
40362306a36Sopenharmony_ci};
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ciconst struct nvdimm_security_ops *intel_security_ops = &__intel_security_ops;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_cistatic int intel_bus_fwa_businfo(struct nvdimm_bus_descriptor *nd_desc,
40862306a36Sopenharmony_ci		struct nd_intel_bus_fw_activate_businfo *info)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct {
41162306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
41262306a36Sopenharmony_ci		struct nd_intel_bus_fw_activate_businfo cmd;
41362306a36Sopenharmony_ci	} nd_cmd = {
41462306a36Sopenharmony_ci		.pkg = {
41562306a36Sopenharmony_ci			.nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO,
41662306a36Sopenharmony_ci			.nd_family = NVDIMM_BUS_FAMILY_INTEL,
41762306a36Sopenharmony_ci			.nd_size_out =
41862306a36Sopenharmony_ci				sizeof(struct nd_intel_bus_fw_activate_businfo),
41962306a36Sopenharmony_ci			.nd_fw_size =
42062306a36Sopenharmony_ci				sizeof(struct nd_intel_bus_fw_activate_businfo),
42162306a36Sopenharmony_ci		},
42262306a36Sopenharmony_ci	};
42362306a36Sopenharmony_ci	int rc;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd),
42662306a36Sopenharmony_ci			NULL);
42762306a36Sopenharmony_ci	*info = nd_cmd.cmd;
42862306a36Sopenharmony_ci	return rc;
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci/* The fw_ops expect to be called with the nvdimm_bus_lock() held */
43262306a36Sopenharmony_cistatic enum nvdimm_fwa_state intel_bus_fwa_state(
43362306a36Sopenharmony_ci		struct nvdimm_bus_descriptor *nd_desc)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
43662306a36Sopenharmony_ci	struct nd_intel_bus_fw_activate_businfo info;
43762306a36Sopenharmony_ci	struct device *dev = acpi_desc->dev;
43862306a36Sopenharmony_ci	enum nvdimm_fwa_state state;
43962306a36Sopenharmony_ci	int rc;
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_ci	/*
44262306a36Sopenharmony_ci	 * It should not be possible for platform firmware to return
44362306a36Sopenharmony_ci	 * busy because activate is a synchronous operation. Treat it
44462306a36Sopenharmony_ci	 * similar to invalid, i.e. always refresh / poll the status.
44562306a36Sopenharmony_ci	 */
44662306a36Sopenharmony_ci	switch (acpi_desc->fwa_state) {
44762306a36Sopenharmony_ci	case NVDIMM_FWA_INVALID:
44862306a36Sopenharmony_ci	case NVDIMM_FWA_BUSY:
44962306a36Sopenharmony_ci		break;
45062306a36Sopenharmony_ci	default:
45162306a36Sopenharmony_ci		/* check if capability needs to be refreshed */
45262306a36Sopenharmony_ci		if (acpi_desc->fwa_cap == NVDIMM_FWA_CAP_INVALID)
45362306a36Sopenharmony_ci			break;
45462306a36Sopenharmony_ci		return acpi_desc->fwa_state;
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	/* Refresh with platform firmware */
45862306a36Sopenharmony_ci	rc = intel_bus_fwa_businfo(nd_desc, &info);
45962306a36Sopenharmony_ci	if (rc)
46062306a36Sopenharmony_ci		return NVDIMM_FWA_INVALID;
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	switch (info.state) {
46362306a36Sopenharmony_ci	case ND_INTEL_FWA_IDLE:
46462306a36Sopenharmony_ci		state = NVDIMM_FWA_IDLE;
46562306a36Sopenharmony_ci		break;
46662306a36Sopenharmony_ci	case ND_INTEL_FWA_BUSY:
46762306a36Sopenharmony_ci		state = NVDIMM_FWA_BUSY;
46862306a36Sopenharmony_ci		break;
46962306a36Sopenharmony_ci	case ND_INTEL_FWA_ARMED:
47062306a36Sopenharmony_ci		if (info.activate_tmo > info.max_quiesce_tmo)
47162306a36Sopenharmony_ci			state = NVDIMM_FWA_ARM_OVERFLOW;
47262306a36Sopenharmony_ci		else
47362306a36Sopenharmony_ci			state = NVDIMM_FWA_ARMED;
47462306a36Sopenharmony_ci		break;
47562306a36Sopenharmony_ci	default:
47662306a36Sopenharmony_ci		dev_err_once(dev, "invalid firmware activate state %d\n",
47762306a36Sopenharmony_ci				info.state);
47862306a36Sopenharmony_ci		return NVDIMM_FWA_INVALID;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	/*
48262306a36Sopenharmony_ci	 * Capability data is available in the same payload as state. It
48362306a36Sopenharmony_ci	 * is expected to be static.
48462306a36Sopenharmony_ci	 */
48562306a36Sopenharmony_ci	if (acpi_desc->fwa_cap == NVDIMM_FWA_CAP_INVALID) {
48662306a36Sopenharmony_ci		if (info.capability & ND_INTEL_BUS_FWA_CAP_FWQUIESCE)
48762306a36Sopenharmony_ci			acpi_desc->fwa_cap = NVDIMM_FWA_CAP_QUIESCE;
48862306a36Sopenharmony_ci		else if (info.capability & ND_INTEL_BUS_FWA_CAP_OSQUIESCE) {
48962306a36Sopenharmony_ci			/*
49062306a36Sopenharmony_ci			 * Skip hibernate cycle by default if platform
49162306a36Sopenharmony_ci			 * indicates that it does not need devices to be
49262306a36Sopenharmony_ci			 * quiesced.
49362306a36Sopenharmony_ci			 */
49462306a36Sopenharmony_ci			acpi_desc->fwa_cap = NVDIMM_FWA_CAP_LIVE;
49562306a36Sopenharmony_ci		} else
49662306a36Sopenharmony_ci			acpi_desc->fwa_cap = NVDIMM_FWA_CAP_NONE;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	acpi_desc->fwa_state = state;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	return state;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_cistatic enum nvdimm_fwa_capability intel_bus_fwa_capability(
50562306a36Sopenharmony_ci		struct nvdimm_bus_descriptor *nd_desc)
50662306a36Sopenharmony_ci{
50762306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	if (acpi_desc->fwa_cap > NVDIMM_FWA_CAP_INVALID)
51062306a36Sopenharmony_ci		return acpi_desc->fwa_cap;
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	if (intel_bus_fwa_state(nd_desc) > NVDIMM_FWA_INVALID)
51362306a36Sopenharmony_ci		return acpi_desc->fwa_cap;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	return NVDIMM_FWA_CAP_INVALID;
51662306a36Sopenharmony_ci}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_cistatic int intel_bus_fwa_activate(struct nvdimm_bus_descriptor *nd_desc)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
52162306a36Sopenharmony_ci	struct {
52262306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
52362306a36Sopenharmony_ci		struct nd_intel_bus_fw_activate cmd;
52462306a36Sopenharmony_ci	} nd_cmd = {
52562306a36Sopenharmony_ci		.pkg = {
52662306a36Sopenharmony_ci			.nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE,
52762306a36Sopenharmony_ci			.nd_family = NVDIMM_BUS_FAMILY_INTEL,
52862306a36Sopenharmony_ci			.nd_size_in = sizeof(nd_cmd.cmd.iodev_state),
52962306a36Sopenharmony_ci			.nd_size_out =
53062306a36Sopenharmony_ci				sizeof(struct nd_intel_bus_fw_activate),
53162306a36Sopenharmony_ci			.nd_fw_size =
53262306a36Sopenharmony_ci				sizeof(struct nd_intel_bus_fw_activate),
53362306a36Sopenharmony_ci		},
53462306a36Sopenharmony_ci		/*
53562306a36Sopenharmony_ci		 * Even though activate is run from a suspended context,
53662306a36Sopenharmony_ci		 * for safety, still ask platform firmware to force
53762306a36Sopenharmony_ci		 * quiesce devices by default. Let a module
53862306a36Sopenharmony_ci		 * parameter override that policy.
53962306a36Sopenharmony_ci		 */
54062306a36Sopenharmony_ci		.cmd = {
54162306a36Sopenharmony_ci			.iodev_state = acpi_desc->fwa_noidle
54262306a36Sopenharmony_ci				? ND_INTEL_BUS_FWA_IODEV_OS_IDLE
54362306a36Sopenharmony_ci				: ND_INTEL_BUS_FWA_IODEV_FORCE_IDLE,
54462306a36Sopenharmony_ci		},
54562306a36Sopenharmony_ci	};
54662306a36Sopenharmony_ci	int rc;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	switch (intel_bus_fwa_state(nd_desc)) {
54962306a36Sopenharmony_ci	case NVDIMM_FWA_ARMED:
55062306a36Sopenharmony_ci	case NVDIMM_FWA_ARM_OVERFLOW:
55162306a36Sopenharmony_ci		break;
55262306a36Sopenharmony_ci	default:
55362306a36Sopenharmony_ci		return -ENXIO;
55462306a36Sopenharmony_ci	}
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd),
55762306a36Sopenharmony_ci			NULL);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	/*
56062306a36Sopenharmony_ci	 * Whether the command succeeded, or failed, the agent checking
56162306a36Sopenharmony_ci	 * for the result needs to query the DIMMs individually.
56262306a36Sopenharmony_ci	 * Increment the activation count to invalidate all the DIMM
56362306a36Sopenharmony_ci	 * states at once (it's otherwise not possible to take
56462306a36Sopenharmony_ci	 * acpi_desc->init_mutex in this context)
56562306a36Sopenharmony_ci	 */
56662306a36Sopenharmony_ci	acpi_desc->fwa_state = NVDIMM_FWA_INVALID;
56762306a36Sopenharmony_ci	acpi_desc->fwa_count++;
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci	dev_dbg(acpi_desc->dev, "result: %d\n", rc);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	return rc;
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_cistatic const struct nvdimm_bus_fw_ops __intel_bus_fw_ops = {
57562306a36Sopenharmony_ci	.activate_state = intel_bus_fwa_state,
57662306a36Sopenharmony_ci	.capability = intel_bus_fwa_capability,
57762306a36Sopenharmony_ci	.activate = intel_bus_fwa_activate,
57862306a36Sopenharmony_ci};
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ciconst struct nvdimm_bus_fw_ops *intel_bus_fw_ops = &__intel_bus_fw_ops;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic int intel_fwa_dimminfo(struct nvdimm *nvdimm,
58362306a36Sopenharmony_ci		struct nd_intel_fw_activate_dimminfo *info)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct {
58662306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
58762306a36Sopenharmony_ci		struct nd_intel_fw_activate_dimminfo cmd;
58862306a36Sopenharmony_ci	} nd_cmd = {
58962306a36Sopenharmony_ci		.pkg = {
59062306a36Sopenharmony_ci			.nd_command = NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO,
59162306a36Sopenharmony_ci			.nd_family = NVDIMM_FAMILY_INTEL,
59262306a36Sopenharmony_ci			.nd_size_out =
59362306a36Sopenharmony_ci				sizeof(struct nd_intel_fw_activate_dimminfo),
59462306a36Sopenharmony_ci			.nd_fw_size =
59562306a36Sopenharmony_ci				sizeof(struct nd_intel_fw_activate_dimminfo),
59662306a36Sopenharmony_ci		},
59762306a36Sopenharmony_ci	};
59862306a36Sopenharmony_ci	int rc;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
60162306a36Sopenharmony_ci	*info = nd_cmd.cmd;
60262306a36Sopenharmony_ci	return rc;
60362306a36Sopenharmony_ci}
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cistatic enum nvdimm_fwa_state intel_fwa_state(struct nvdimm *nvdimm)
60662306a36Sopenharmony_ci{
60762306a36Sopenharmony_ci	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
60862306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
60962306a36Sopenharmony_ci	struct nd_intel_fw_activate_dimminfo info;
61062306a36Sopenharmony_ci	int rc;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	/*
61362306a36Sopenharmony_ci	 * Similar to the bus state, since activate is synchronous the
61462306a36Sopenharmony_ci	 * busy state should resolve within the context of 'activate'.
61562306a36Sopenharmony_ci	 */
61662306a36Sopenharmony_ci	switch (nfit_mem->fwa_state) {
61762306a36Sopenharmony_ci	case NVDIMM_FWA_INVALID:
61862306a36Sopenharmony_ci	case NVDIMM_FWA_BUSY:
61962306a36Sopenharmony_ci		break;
62062306a36Sopenharmony_ci	default:
62162306a36Sopenharmony_ci		/* If no activations occurred the old state is still valid */
62262306a36Sopenharmony_ci		if (nfit_mem->fwa_count == acpi_desc->fwa_count)
62362306a36Sopenharmony_ci			return nfit_mem->fwa_state;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	rc = intel_fwa_dimminfo(nvdimm, &info);
62762306a36Sopenharmony_ci	if (rc)
62862306a36Sopenharmony_ci		return NVDIMM_FWA_INVALID;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	switch (info.state) {
63162306a36Sopenharmony_ci	case ND_INTEL_FWA_IDLE:
63262306a36Sopenharmony_ci		nfit_mem->fwa_state = NVDIMM_FWA_IDLE;
63362306a36Sopenharmony_ci		break;
63462306a36Sopenharmony_ci	case ND_INTEL_FWA_BUSY:
63562306a36Sopenharmony_ci		nfit_mem->fwa_state = NVDIMM_FWA_BUSY;
63662306a36Sopenharmony_ci		break;
63762306a36Sopenharmony_ci	case ND_INTEL_FWA_ARMED:
63862306a36Sopenharmony_ci		nfit_mem->fwa_state = NVDIMM_FWA_ARMED;
63962306a36Sopenharmony_ci		break;
64062306a36Sopenharmony_ci	default:
64162306a36Sopenharmony_ci		nfit_mem->fwa_state = NVDIMM_FWA_INVALID;
64262306a36Sopenharmony_ci		break;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci
64562306a36Sopenharmony_ci	switch (info.result) {
64662306a36Sopenharmony_ci	case ND_INTEL_DIMM_FWA_NONE:
64762306a36Sopenharmony_ci		nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NONE;
64862306a36Sopenharmony_ci		break;
64962306a36Sopenharmony_ci	case ND_INTEL_DIMM_FWA_SUCCESS:
65062306a36Sopenharmony_ci		nfit_mem->fwa_result = NVDIMM_FWA_RESULT_SUCCESS;
65162306a36Sopenharmony_ci		break;
65262306a36Sopenharmony_ci	case ND_INTEL_DIMM_FWA_NOTSTAGED:
65362306a36Sopenharmony_ci		nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NOTSTAGED;
65462306a36Sopenharmony_ci		break;
65562306a36Sopenharmony_ci	case ND_INTEL_DIMM_FWA_NEEDRESET:
65662306a36Sopenharmony_ci		nfit_mem->fwa_result = NVDIMM_FWA_RESULT_NEEDRESET;
65762306a36Sopenharmony_ci		break;
65862306a36Sopenharmony_ci	case ND_INTEL_DIMM_FWA_MEDIAFAILED:
65962306a36Sopenharmony_ci	case ND_INTEL_DIMM_FWA_ABORT:
66062306a36Sopenharmony_ci	case ND_INTEL_DIMM_FWA_NOTSUPP:
66162306a36Sopenharmony_ci	case ND_INTEL_DIMM_FWA_ERROR:
66262306a36Sopenharmony_ci	default:
66362306a36Sopenharmony_ci		nfit_mem->fwa_result = NVDIMM_FWA_RESULT_FAIL;
66462306a36Sopenharmony_ci		break;
66562306a36Sopenharmony_ci	}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_ci	nfit_mem->fwa_count = acpi_desc->fwa_count;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	return nfit_mem->fwa_state;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_cistatic enum nvdimm_fwa_result intel_fwa_result(struct nvdimm *nvdimm)
67362306a36Sopenharmony_ci{
67462306a36Sopenharmony_ci	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
67562306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	if (nfit_mem->fwa_count == acpi_desc->fwa_count
67862306a36Sopenharmony_ci			&& nfit_mem->fwa_result > NVDIMM_FWA_RESULT_INVALID)
67962306a36Sopenharmony_ci		return nfit_mem->fwa_result;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (intel_fwa_state(nvdimm) > NVDIMM_FWA_INVALID)
68262306a36Sopenharmony_ci		return nfit_mem->fwa_result;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	return NVDIMM_FWA_RESULT_INVALID;
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic int intel_fwa_arm(struct nvdimm *nvdimm, enum nvdimm_fwa_trigger arm)
68862306a36Sopenharmony_ci{
68962306a36Sopenharmony_ci	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
69062306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
69162306a36Sopenharmony_ci	struct {
69262306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
69362306a36Sopenharmony_ci		struct nd_intel_fw_activate_arm cmd;
69462306a36Sopenharmony_ci	} nd_cmd = {
69562306a36Sopenharmony_ci		.pkg = {
69662306a36Sopenharmony_ci			.nd_command = NVDIMM_INTEL_FW_ACTIVATE_ARM,
69762306a36Sopenharmony_ci			.nd_family = NVDIMM_FAMILY_INTEL,
69862306a36Sopenharmony_ci			.nd_size_in = sizeof(nd_cmd.cmd.activate_arm),
69962306a36Sopenharmony_ci			.nd_size_out =
70062306a36Sopenharmony_ci				sizeof(struct nd_intel_fw_activate_arm),
70162306a36Sopenharmony_ci			.nd_fw_size =
70262306a36Sopenharmony_ci				sizeof(struct nd_intel_fw_activate_arm),
70362306a36Sopenharmony_ci		},
70462306a36Sopenharmony_ci		.cmd = {
70562306a36Sopenharmony_ci			.activate_arm = arm == NVDIMM_FWA_ARM
70662306a36Sopenharmony_ci				? ND_INTEL_DIMM_FWA_ARM
70762306a36Sopenharmony_ci				: ND_INTEL_DIMM_FWA_DISARM,
70862306a36Sopenharmony_ci		},
70962306a36Sopenharmony_ci	};
71062306a36Sopenharmony_ci	int rc;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	switch (intel_fwa_state(nvdimm)) {
71362306a36Sopenharmony_ci	case NVDIMM_FWA_INVALID:
71462306a36Sopenharmony_ci		return -ENXIO;
71562306a36Sopenharmony_ci	case NVDIMM_FWA_BUSY:
71662306a36Sopenharmony_ci		return -EBUSY;
71762306a36Sopenharmony_ci	case NVDIMM_FWA_IDLE:
71862306a36Sopenharmony_ci		if (arm == NVDIMM_FWA_DISARM)
71962306a36Sopenharmony_ci			return 0;
72062306a36Sopenharmony_ci		break;
72162306a36Sopenharmony_ci	case NVDIMM_FWA_ARMED:
72262306a36Sopenharmony_ci		if (arm == NVDIMM_FWA_ARM)
72362306a36Sopenharmony_ci			return 0;
72462306a36Sopenharmony_ci		break;
72562306a36Sopenharmony_ci	default:
72662306a36Sopenharmony_ci		return -ENXIO;
72762306a36Sopenharmony_ci	}
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	/*
73062306a36Sopenharmony_ci	 * Invalidate the bus-level state, now that we're committed to
73162306a36Sopenharmony_ci	 * changing the 'arm' state.
73262306a36Sopenharmony_ci	 */
73362306a36Sopenharmony_ci	acpi_desc->fwa_state = NVDIMM_FWA_INVALID;
73462306a36Sopenharmony_ci	nfit_mem->fwa_state = NVDIMM_FWA_INVALID;
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	rc = nvdimm_ctl(nvdimm, ND_CMD_CALL, &nd_cmd, sizeof(nd_cmd), NULL);
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci	dev_dbg(acpi_desc->dev, "%s result: %d\n", arm == NVDIMM_FWA_ARM
73962306a36Sopenharmony_ci			? "arm" : "disarm", rc);
74062306a36Sopenharmony_ci	return rc;
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_cistatic const struct nvdimm_fw_ops __intel_fw_ops = {
74462306a36Sopenharmony_ci	.activate_state = intel_fwa_state,
74562306a36Sopenharmony_ci	.activate_result = intel_fwa_result,
74662306a36Sopenharmony_ci	.arm = intel_fwa_arm,
74762306a36Sopenharmony_ci};
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ciconst struct nvdimm_fw_ops *intel_fw_ops = &__intel_fw_ops;
750