162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
662306a36Sopenharmony_ci#include <linux/platform_device.h>
762306a36Sopenharmony_ci#include <linux/dma-mapping.h>
862306a36Sopenharmony_ci#include <linux/workqueue.h>
962306a36Sopenharmony_ci#include <linux/libnvdimm.h>
1062306a36Sopenharmony_ci#include <linux/genalloc.h>
1162306a36Sopenharmony_ci#include <linux/vmalloc.h>
1262306a36Sopenharmony_ci#include <linux/device.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/mutex.h>
1562306a36Sopenharmony_ci#include <linux/ndctl.h>
1662306a36Sopenharmony_ci#include <linux/sizes.h>
1762306a36Sopenharmony_ci#include <linux/list.h>
1862306a36Sopenharmony_ci#include <linux/slab.h>
1962306a36Sopenharmony_ci#include <nd-core.h>
2062306a36Sopenharmony_ci#include <intel.h>
2162306a36Sopenharmony_ci#include <nfit.h>
2262306a36Sopenharmony_ci#include <nd.h>
2362306a36Sopenharmony_ci#include "nfit_test.h"
2462306a36Sopenharmony_ci#include "../watermark.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci * Generate an NFIT table to describe the following topology:
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * BUS0: Interleaved PMEM regions, and aliasing with BLK regions
3062306a36Sopenharmony_ci *
3162306a36Sopenharmony_ci *                     (a)                       (b)            DIMM   BLK-REGION
3262306a36Sopenharmony_ci *           +----------+--------------+----------+---------+
3362306a36Sopenharmony_ci * +------+  |  blk2.0  |     pm0.0    |  blk2.1  |  pm1.0  |    0      region2
3462306a36Sopenharmony_ci * | imc0 +--+- - - - - region0 - - - -+----------+         +
3562306a36Sopenharmony_ci * +--+---+  |  blk3.0  |     pm0.0    |  blk3.1  |  pm1.0  |    1      region3
3662306a36Sopenharmony_ci *    |      +----------+--------------v----------v         v
3762306a36Sopenharmony_ci * +--+---+                            |                    |
3862306a36Sopenharmony_ci * | cpu0 |                                    region1
3962306a36Sopenharmony_ci * +--+---+                            |                    |
4062306a36Sopenharmony_ci *    |      +-------------------------^----------^         ^
4162306a36Sopenharmony_ci * +--+---+  |                 blk4.0             |  pm1.0  |    2      region4
4262306a36Sopenharmony_ci * | imc1 +--+-------------------------+----------+         +
4362306a36Sopenharmony_ci * +------+  |                 blk5.0             |  pm1.0  |    3      region5
4462306a36Sopenharmony_ci *           +-------------------------+----------+-+-------+
4562306a36Sopenharmony_ci *
4662306a36Sopenharmony_ci * +--+---+
4762306a36Sopenharmony_ci * | cpu1 |
4862306a36Sopenharmony_ci * +--+---+                   (Hotplug DIMM)
4962306a36Sopenharmony_ci *    |      +----------------------------------------------+
5062306a36Sopenharmony_ci * +--+---+  |                 blk6.0/pm7.0                 |    4      region6/7
5162306a36Sopenharmony_ci * | imc0 +--+----------------------------------------------+
5262306a36Sopenharmony_ci * +------+
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci *
5562306a36Sopenharmony_ci * *) In this layout we have four dimms and two memory controllers in one
5662306a36Sopenharmony_ci *    socket.  Each unique interface (BLK or PMEM) to DPA space
5762306a36Sopenharmony_ci *    is identified by a region device with a dynamically assigned id.
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci * *) The first portion of dimm0 and dimm1 are interleaved as REGION0.
6062306a36Sopenharmony_ci *    A single PMEM namespace "pm0.0" is created using half of the
6162306a36Sopenharmony_ci *    REGION0 SPA-range.  REGION0 spans dimm0 and dimm1.  PMEM namespace
6262306a36Sopenharmony_ci *    allocate from from the bottom of a region.  The unallocated
6362306a36Sopenharmony_ci *    portion of REGION0 aliases with REGION2 and REGION3.  That
6462306a36Sopenharmony_ci *    unallacted capacity is reclaimed as BLK namespaces ("blk2.0" and
6562306a36Sopenharmony_ci *    "blk3.0") starting at the base of each DIMM to offset (a) in those
6662306a36Sopenharmony_ci *    DIMMs.  "pm0.0", "blk2.0" and "blk3.0" are free-form readable
6762306a36Sopenharmony_ci *    names that can be assigned to a namespace.
6862306a36Sopenharmony_ci *
6962306a36Sopenharmony_ci * *) In the last portion of dimm0 and dimm1 we have an interleaved
7062306a36Sopenharmony_ci *    SPA range, REGION1, that spans those two dimms as well as dimm2
7162306a36Sopenharmony_ci *    and dimm3.  Some of REGION1 allocated to a PMEM namespace named
7262306a36Sopenharmony_ci *    "pm1.0" the rest is reclaimed in 4 BLK namespaces (for each
7362306a36Sopenharmony_ci *    dimm in the interleave set), "blk2.1", "blk3.1", "blk4.0", and
7462306a36Sopenharmony_ci *    "blk5.0".
7562306a36Sopenharmony_ci *
7662306a36Sopenharmony_ci * *) The portion of dimm2 and dimm3 that do not participate in the
7762306a36Sopenharmony_ci *    REGION1 interleaved SPA range (i.e. the DPA address below offset
7862306a36Sopenharmony_ci *    (b) are also included in the "blk4.0" and "blk5.0" namespaces.
7962306a36Sopenharmony_ci *    Note, that BLK namespaces need not be contiguous in DPA-space, and
8062306a36Sopenharmony_ci *    can consume aliased capacity from multiple interleave sets.
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * BUS1: Legacy NVDIMM (single contiguous range)
8362306a36Sopenharmony_ci *
8462306a36Sopenharmony_ci *  region2
8562306a36Sopenharmony_ci * +---------------------+
8662306a36Sopenharmony_ci * |---------------------|
8762306a36Sopenharmony_ci * ||       pm2.0       ||
8862306a36Sopenharmony_ci * |---------------------|
8962306a36Sopenharmony_ci * +---------------------+
9062306a36Sopenharmony_ci *
9162306a36Sopenharmony_ci * *) A NFIT-table may describe a simple system-physical-address range
9262306a36Sopenharmony_ci *    with no BLK aliasing.  This type of region may optionally
9362306a36Sopenharmony_ci *    reference an NVDIMM.
9462306a36Sopenharmony_ci */
9562306a36Sopenharmony_cienum {
9662306a36Sopenharmony_ci	NUM_PM  = 3,
9762306a36Sopenharmony_ci	NUM_DCR = 5,
9862306a36Sopenharmony_ci	NUM_HINTS = 8,
9962306a36Sopenharmony_ci	NUM_BDW = NUM_DCR,
10062306a36Sopenharmony_ci	NUM_SPA = NUM_PM + NUM_DCR + NUM_BDW,
10162306a36Sopenharmony_ci	NUM_MEM = NUM_DCR + NUM_BDW + 2 /* spa0 iset */
10262306a36Sopenharmony_ci		+ 4 /* spa1 iset */ + 1 /* spa11 iset */,
10362306a36Sopenharmony_ci	DIMM_SIZE = SZ_32M,
10462306a36Sopenharmony_ci	LABEL_SIZE = SZ_128K,
10562306a36Sopenharmony_ci	SPA_VCD_SIZE = SZ_4M,
10662306a36Sopenharmony_ci	SPA0_SIZE = DIMM_SIZE,
10762306a36Sopenharmony_ci	SPA1_SIZE = DIMM_SIZE*2,
10862306a36Sopenharmony_ci	SPA2_SIZE = DIMM_SIZE,
10962306a36Sopenharmony_ci	BDW_SIZE = 64 << 8,
11062306a36Sopenharmony_ci	DCR_SIZE = 12,
11162306a36Sopenharmony_ci	NUM_NFITS = 2, /* permit testing multiple NFITs per system */
11262306a36Sopenharmony_ci};
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_cistruct nfit_test_dcr {
11562306a36Sopenharmony_ci	__le64 bdw_addr;
11662306a36Sopenharmony_ci	__le32 bdw_status;
11762306a36Sopenharmony_ci	__u8 aperature[BDW_SIZE];
11862306a36Sopenharmony_ci};
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci#define NFIT_DIMM_HANDLE(node, socket, imc, chan, dimm) \
12162306a36Sopenharmony_ci	(((node & 0xfff) << 16) | ((socket & 0xf) << 12) \
12262306a36Sopenharmony_ci	 | ((imc & 0xf) << 8) | ((chan & 0xf) << 4) | (dimm & 0xf))
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic u32 handle[] = {
12562306a36Sopenharmony_ci	[0] = NFIT_DIMM_HANDLE(0, 0, 0, 0, 0),
12662306a36Sopenharmony_ci	[1] = NFIT_DIMM_HANDLE(0, 0, 0, 0, 1),
12762306a36Sopenharmony_ci	[2] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 0),
12862306a36Sopenharmony_ci	[3] = NFIT_DIMM_HANDLE(0, 0, 1, 0, 1),
12962306a36Sopenharmony_ci	[4] = NFIT_DIMM_HANDLE(0, 1, 0, 0, 0),
13062306a36Sopenharmony_ci	[5] = NFIT_DIMM_HANDLE(1, 0, 0, 0, 0),
13162306a36Sopenharmony_ci	[6] = NFIT_DIMM_HANDLE(1, 0, 0, 0, 1),
13262306a36Sopenharmony_ci};
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cistatic unsigned long dimm_fail_cmd_flags[ARRAY_SIZE(handle)];
13562306a36Sopenharmony_cistatic int dimm_fail_cmd_code[ARRAY_SIZE(handle)];
13662306a36Sopenharmony_cistruct nfit_test_sec {
13762306a36Sopenharmony_ci	u8 state;
13862306a36Sopenharmony_ci	u8 ext_state;
13962306a36Sopenharmony_ci	u8 old_state;
14062306a36Sopenharmony_ci	u8 passphrase[32];
14162306a36Sopenharmony_ci	u8 master_passphrase[32];
14262306a36Sopenharmony_ci	u64 overwrite_end_time;
14362306a36Sopenharmony_ci} dimm_sec_info[NUM_DCR];
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic const struct nd_intel_smart smart_def = {
14662306a36Sopenharmony_ci	.flags = ND_INTEL_SMART_HEALTH_VALID
14762306a36Sopenharmony_ci		| ND_INTEL_SMART_SPARES_VALID
14862306a36Sopenharmony_ci		| ND_INTEL_SMART_ALARM_VALID
14962306a36Sopenharmony_ci		| ND_INTEL_SMART_USED_VALID
15062306a36Sopenharmony_ci		| ND_INTEL_SMART_SHUTDOWN_VALID
15162306a36Sopenharmony_ci		| ND_INTEL_SMART_SHUTDOWN_COUNT_VALID
15262306a36Sopenharmony_ci		| ND_INTEL_SMART_MTEMP_VALID
15362306a36Sopenharmony_ci		| ND_INTEL_SMART_CTEMP_VALID,
15462306a36Sopenharmony_ci	.health = ND_INTEL_SMART_NON_CRITICAL_HEALTH,
15562306a36Sopenharmony_ci	.media_temperature = 23 * 16,
15662306a36Sopenharmony_ci	.ctrl_temperature = 25 * 16,
15762306a36Sopenharmony_ci	.pmic_temperature = 40 * 16,
15862306a36Sopenharmony_ci	.spares = 75,
15962306a36Sopenharmony_ci	.alarm_flags = ND_INTEL_SMART_SPARE_TRIP
16062306a36Sopenharmony_ci		| ND_INTEL_SMART_TEMP_TRIP,
16162306a36Sopenharmony_ci	.ait_status = 1,
16262306a36Sopenharmony_ci	.life_used = 5,
16362306a36Sopenharmony_ci	.shutdown_state = 0,
16462306a36Sopenharmony_ci	.shutdown_count = 42,
16562306a36Sopenharmony_ci	.vendor_size = 0,
16662306a36Sopenharmony_ci};
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_cistruct nfit_test_fw {
16962306a36Sopenharmony_ci	enum intel_fw_update_state state;
17062306a36Sopenharmony_ci	u32 context;
17162306a36Sopenharmony_ci	u64 version;
17262306a36Sopenharmony_ci	u32 size_received;
17362306a36Sopenharmony_ci	u64 end_time;
17462306a36Sopenharmony_ci	bool armed;
17562306a36Sopenharmony_ci	bool missed_activate;
17662306a36Sopenharmony_ci	unsigned long last_activate;
17762306a36Sopenharmony_ci};
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistruct nfit_test {
18062306a36Sopenharmony_ci	struct acpi_nfit_desc acpi_desc;
18162306a36Sopenharmony_ci	struct platform_device pdev;
18262306a36Sopenharmony_ci	struct list_head resources;
18362306a36Sopenharmony_ci	void *nfit_buf;
18462306a36Sopenharmony_ci	dma_addr_t nfit_dma;
18562306a36Sopenharmony_ci	size_t nfit_size;
18662306a36Sopenharmony_ci	size_t nfit_filled;
18762306a36Sopenharmony_ci	int dcr_idx;
18862306a36Sopenharmony_ci	int num_dcr;
18962306a36Sopenharmony_ci	int num_pm;
19062306a36Sopenharmony_ci	void **dimm;
19162306a36Sopenharmony_ci	dma_addr_t *dimm_dma;
19262306a36Sopenharmony_ci	void **flush;
19362306a36Sopenharmony_ci	dma_addr_t *flush_dma;
19462306a36Sopenharmony_ci	void **label;
19562306a36Sopenharmony_ci	dma_addr_t *label_dma;
19662306a36Sopenharmony_ci	void **spa_set;
19762306a36Sopenharmony_ci	dma_addr_t *spa_set_dma;
19862306a36Sopenharmony_ci	struct nfit_test_dcr **dcr;
19962306a36Sopenharmony_ci	dma_addr_t *dcr_dma;
20062306a36Sopenharmony_ci	int (*alloc)(struct nfit_test *t);
20162306a36Sopenharmony_ci	void (*setup)(struct nfit_test *t);
20262306a36Sopenharmony_ci	int setup_hotplug;
20362306a36Sopenharmony_ci	union acpi_object **_fit;
20462306a36Sopenharmony_ci	dma_addr_t _fit_dma;
20562306a36Sopenharmony_ci	struct ars_state {
20662306a36Sopenharmony_ci		struct nd_cmd_ars_status *ars_status;
20762306a36Sopenharmony_ci		unsigned long deadline;
20862306a36Sopenharmony_ci		spinlock_t lock;
20962306a36Sopenharmony_ci	} ars_state;
21062306a36Sopenharmony_ci	struct device *dimm_dev[ARRAY_SIZE(handle)];
21162306a36Sopenharmony_ci	struct nd_intel_smart *smart;
21262306a36Sopenharmony_ci	struct nd_intel_smart_threshold *smart_threshold;
21362306a36Sopenharmony_ci	struct badrange badrange;
21462306a36Sopenharmony_ci	struct work_struct work;
21562306a36Sopenharmony_ci	struct nfit_test_fw *fw;
21662306a36Sopenharmony_ci};
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic struct workqueue_struct *nfit_wq;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic struct gen_pool *nfit_pool;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic const char zero_key[NVDIMM_PASSPHRASE_LEN];
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic struct nfit_test *to_nfit_test(struct device *dev)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	return container_of(pdev, struct nfit_test, pdev);
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic int nd_intel_test_get_fw_info(struct nfit_test *t,
23262306a36Sopenharmony_ci		struct nd_intel_fw_info *nd_cmd, unsigned int buf_len,
23362306a36Sopenharmony_ci		int idx)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
23662306a36Sopenharmony_ci	struct nfit_test_fw *fw = &t->fw[idx];
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p, buf_len: %u, idx: %d\n",
23962306a36Sopenharmony_ci			__func__, t, nd_cmd, buf_len, idx);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (buf_len < sizeof(*nd_cmd))
24262306a36Sopenharmony_ci		return -EINVAL;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	nd_cmd->status = 0;
24562306a36Sopenharmony_ci	nd_cmd->storage_size = INTEL_FW_STORAGE_SIZE;
24662306a36Sopenharmony_ci	nd_cmd->max_send_len = INTEL_FW_MAX_SEND_LEN;
24762306a36Sopenharmony_ci	nd_cmd->query_interval = INTEL_FW_QUERY_INTERVAL;
24862306a36Sopenharmony_ci	nd_cmd->max_query_time = INTEL_FW_QUERY_MAX_TIME;
24962306a36Sopenharmony_ci	nd_cmd->update_cap = 0;
25062306a36Sopenharmony_ci	nd_cmd->fis_version = INTEL_FW_FIS_VERSION;
25162306a36Sopenharmony_ci	nd_cmd->run_version = 0;
25262306a36Sopenharmony_ci	nd_cmd->updated_version = fw->version;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return 0;
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic int nd_intel_test_start_update(struct nfit_test *t,
25862306a36Sopenharmony_ci		struct nd_intel_fw_start *nd_cmd, unsigned int buf_len,
25962306a36Sopenharmony_ci		int idx)
26062306a36Sopenharmony_ci{
26162306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
26262306a36Sopenharmony_ci	struct nfit_test_fw *fw = &t->fw[idx];
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n",
26562306a36Sopenharmony_ci			__func__, t, nd_cmd, buf_len, idx);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (buf_len < sizeof(*nd_cmd))
26862306a36Sopenharmony_ci		return -EINVAL;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (fw->state != FW_STATE_NEW) {
27162306a36Sopenharmony_ci		/* extended status, FW update in progress */
27262306a36Sopenharmony_ci		nd_cmd->status = 0x10007;
27362306a36Sopenharmony_ci		return 0;
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	fw->state = FW_STATE_IN_PROGRESS;
27762306a36Sopenharmony_ci	fw->context++;
27862306a36Sopenharmony_ci	fw->size_received = 0;
27962306a36Sopenharmony_ci	nd_cmd->status = 0;
28062306a36Sopenharmony_ci	nd_cmd->context = fw->context;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	dev_dbg(dev, "%s: context issued: %#x\n", __func__, nd_cmd->context);
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	return 0;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_cistatic int nd_intel_test_send_data(struct nfit_test *t,
28862306a36Sopenharmony_ci		struct nd_intel_fw_send_data *nd_cmd, unsigned int buf_len,
28962306a36Sopenharmony_ci		int idx)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
29262306a36Sopenharmony_ci	struct nfit_test_fw *fw = &t->fw[idx];
29362306a36Sopenharmony_ci	u32 *status = (u32 *)&nd_cmd->data[nd_cmd->length];
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n",
29662306a36Sopenharmony_ci			__func__, t, nd_cmd, buf_len, idx);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	if (buf_len < sizeof(*nd_cmd))
29962306a36Sopenharmony_ci		return -EINVAL;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	dev_dbg(dev, "%s: cmd->status: %#x\n", __func__, *status);
30362306a36Sopenharmony_ci	dev_dbg(dev, "%s: cmd->data[0]: %#x\n", __func__, nd_cmd->data[0]);
30462306a36Sopenharmony_ci	dev_dbg(dev, "%s: cmd->data[%u]: %#x\n", __func__, nd_cmd->length-1,
30562306a36Sopenharmony_ci			nd_cmd->data[nd_cmd->length-1]);
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	if (fw->state != FW_STATE_IN_PROGRESS) {
30862306a36Sopenharmony_ci		dev_dbg(dev, "%s: not in IN_PROGRESS state\n", __func__);
30962306a36Sopenharmony_ci		*status = 0x5;
31062306a36Sopenharmony_ci		return 0;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (nd_cmd->context != fw->context) {
31462306a36Sopenharmony_ci		dev_dbg(dev, "%s: incorrect context: in: %#x correct: %#x\n",
31562306a36Sopenharmony_ci				__func__, nd_cmd->context, fw->context);
31662306a36Sopenharmony_ci		*status = 0x10007;
31762306a36Sopenharmony_ci		return 0;
31862306a36Sopenharmony_ci	}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/*
32162306a36Sopenharmony_ci	 * check offset + len > size of fw storage
32262306a36Sopenharmony_ci	 * check length is > max send length
32362306a36Sopenharmony_ci	 */
32462306a36Sopenharmony_ci	if (nd_cmd->offset + nd_cmd->length > INTEL_FW_STORAGE_SIZE ||
32562306a36Sopenharmony_ci			nd_cmd->length > INTEL_FW_MAX_SEND_LEN) {
32662306a36Sopenharmony_ci		*status = 0x3;
32762306a36Sopenharmony_ci		dev_dbg(dev, "%s: buffer boundary violation\n", __func__);
32862306a36Sopenharmony_ci		return 0;
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	fw->size_received += nd_cmd->length;
33262306a36Sopenharmony_ci	dev_dbg(dev, "%s: copying %u bytes, %u bytes so far\n",
33362306a36Sopenharmony_ci			__func__, nd_cmd->length, fw->size_received);
33462306a36Sopenharmony_ci	*status = 0;
33562306a36Sopenharmony_ci	return 0;
33662306a36Sopenharmony_ci}
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_cistatic int nd_intel_test_finish_fw(struct nfit_test *t,
33962306a36Sopenharmony_ci		struct nd_intel_fw_finish_update *nd_cmd,
34062306a36Sopenharmony_ci		unsigned int buf_len, int idx)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
34362306a36Sopenharmony_ci	struct nfit_test_fw *fw = &t->fw[idx];
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n",
34662306a36Sopenharmony_ci			__func__, t, nd_cmd, buf_len, idx);
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	if (fw->state == FW_STATE_UPDATED) {
34962306a36Sopenharmony_ci		/* update already done, need activation */
35062306a36Sopenharmony_ci		nd_cmd->status = 0x20007;
35162306a36Sopenharmony_ci		return 0;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	dev_dbg(dev, "%s: context: %#x  ctrl_flags: %#x\n",
35562306a36Sopenharmony_ci			__func__, nd_cmd->context, nd_cmd->ctrl_flags);
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	switch (nd_cmd->ctrl_flags) {
35862306a36Sopenharmony_ci	case 0: /* finish */
35962306a36Sopenharmony_ci		if (nd_cmd->context != fw->context) {
36062306a36Sopenharmony_ci			dev_dbg(dev, "%s: incorrect context: in: %#x correct: %#x\n",
36162306a36Sopenharmony_ci					__func__, nd_cmd->context,
36262306a36Sopenharmony_ci					fw->context);
36362306a36Sopenharmony_ci			nd_cmd->status = 0x10007;
36462306a36Sopenharmony_ci			return 0;
36562306a36Sopenharmony_ci		}
36662306a36Sopenharmony_ci		nd_cmd->status = 0;
36762306a36Sopenharmony_ci		fw->state = FW_STATE_VERIFY;
36862306a36Sopenharmony_ci		/* set 1 second of time for firmware "update" */
36962306a36Sopenharmony_ci		fw->end_time = jiffies + HZ;
37062306a36Sopenharmony_ci		break;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	case 1: /* abort */
37362306a36Sopenharmony_ci		fw->size_received = 0;
37462306a36Sopenharmony_ci		/* successfully aborted status */
37562306a36Sopenharmony_ci		nd_cmd->status = 0x40007;
37662306a36Sopenharmony_ci		fw->state = FW_STATE_NEW;
37762306a36Sopenharmony_ci		dev_dbg(dev, "%s: abort successful\n", __func__);
37862306a36Sopenharmony_ci		break;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	default: /* bad control flag */
38162306a36Sopenharmony_ci		dev_warn(dev, "%s: unknown control flag: %#x\n",
38262306a36Sopenharmony_ci				__func__, nd_cmd->ctrl_flags);
38362306a36Sopenharmony_ci		return -EINVAL;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return 0;
38762306a36Sopenharmony_ci}
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_cistatic int nd_intel_test_finish_query(struct nfit_test *t,
39062306a36Sopenharmony_ci		struct nd_intel_fw_finish_query *nd_cmd,
39162306a36Sopenharmony_ci		unsigned int buf_len, int idx)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
39462306a36Sopenharmony_ci	struct nfit_test_fw *fw = &t->fw[idx];
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	dev_dbg(dev, "%s(nfit_test: %p nd_cmd: %p buf_len: %u idx: %d)\n",
39762306a36Sopenharmony_ci			__func__, t, nd_cmd, buf_len, idx);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci	if (buf_len < sizeof(*nd_cmd))
40062306a36Sopenharmony_ci		return -EINVAL;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	if (nd_cmd->context != fw->context) {
40362306a36Sopenharmony_ci		dev_dbg(dev, "%s: incorrect context: in: %#x correct: %#x\n",
40462306a36Sopenharmony_ci				__func__, nd_cmd->context, fw->context);
40562306a36Sopenharmony_ci		nd_cmd->status = 0x10007;
40662306a36Sopenharmony_ci		return 0;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	dev_dbg(dev, "%s context: %#x\n", __func__, nd_cmd->context);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	switch (fw->state) {
41262306a36Sopenharmony_ci	case FW_STATE_NEW:
41362306a36Sopenharmony_ci		nd_cmd->updated_fw_rev = 0;
41462306a36Sopenharmony_ci		nd_cmd->status = 0;
41562306a36Sopenharmony_ci		dev_dbg(dev, "%s: new state\n", __func__);
41662306a36Sopenharmony_ci		break;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	case FW_STATE_IN_PROGRESS:
41962306a36Sopenharmony_ci		/* sequencing error */
42062306a36Sopenharmony_ci		nd_cmd->status = 0x40007;
42162306a36Sopenharmony_ci		nd_cmd->updated_fw_rev = 0;
42262306a36Sopenharmony_ci		dev_dbg(dev, "%s: sequence error\n", __func__);
42362306a36Sopenharmony_ci		break;
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	case FW_STATE_VERIFY:
42662306a36Sopenharmony_ci		if (time_is_after_jiffies64(fw->end_time)) {
42762306a36Sopenharmony_ci			nd_cmd->updated_fw_rev = 0;
42862306a36Sopenharmony_ci			nd_cmd->status = 0x20007;
42962306a36Sopenharmony_ci			dev_dbg(dev, "%s: still verifying\n", __func__);
43062306a36Sopenharmony_ci			break;
43162306a36Sopenharmony_ci		}
43262306a36Sopenharmony_ci		dev_dbg(dev, "%s: transition out verify\n", __func__);
43362306a36Sopenharmony_ci		fw->state = FW_STATE_UPDATED;
43462306a36Sopenharmony_ci		fw->missed_activate = false;
43562306a36Sopenharmony_ci		fallthrough;
43662306a36Sopenharmony_ci	case FW_STATE_UPDATED:
43762306a36Sopenharmony_ci		nd_cmd->status = 0;
43862306a36Sopenharmony_ci		/* bogus test version */
43962306a36Sopenharmony_ci		fw->version = nd_cmd->updated_fw_rev =
44062306a36Sopenharmony_ci			INTEL_FW_FAKE_VERSION;
44162306a36Sopenharmony_ci		dev_dbg(dev, "%s: updated\n", __func__);
44262306a36Sopenharmony_ci		break;
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	default: /* we should never get here */
44562306a36Sopenharmony_ci		return -EINVAL;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	return 0;
44962306a36Sopenharmony_ci}
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic int nfit_test_cmd_get_config_size(struct nd_cmd_get_config_size *nd_cmd,
45262306a36Sopenharmony_ci		unsigned int buf_len)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	if (buf_len < sizeof(*nd_cmd))
45562306a36Sopenharmony_ci		return -EINVAL;
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	nd_cmd->status = 0;
45862306a36Sopenharmony_ci	nd_cmd->config_size = LABEL_SIZE;
45962306a36Sopenharmony_ci	nd_cmd->max_xfer = SZ_4K;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	return 0;
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic int nfit_test_cmd_get_config_data(struct nd_cmd_get_config_data_hdr
46562306a36Sopenharmony_ci		*nd_cmd, unsigned int buf_len, void *label)
46662306a36Sopenharmony_ci{
46762306a36Sopenharmony_ci	unsigned int len, offset = nd_cmd->in_offset;
46862306a36Sopenharmony_ci	int rc;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	if (buf_len < sizeof(*nd_cmd))
47162306a36Sopenharmony_ci		return -EINVAL;
47262306a36Sopenharmony_ci	if (offset >= LABEL_SIZE)
47362306a36Sopenharmony_ci		return -EINVAL;
47462306a36Sopenharmony_ci	if (nd_cmd->in_length + sizeof(*nd_cmd) > buf_len)
47562306a36Sopenharmony_ci		return -EINVAL;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	nd_cmd->status = 0;
47862306a36Sopenharmony_ci	len = min(nd_cmd->in_length, LABEL_SIZE - offset);
47962306a36Sopenharmony_ci	memcpy(nd_cmd->out_buf, label + offset, len);
48062306a36Sopenharmony_ci	rc = buf_len - sizeof(*nd_cmd) - len;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci	return rc;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd,
48662306a36Sopenharmony_ci		unsigned int buf_len, void *label)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	unsigned int len, offset = nd_cmd->in_offset;
48962306a36Sopenharmony_ci	u32 *status;
49062306a36Sopenharmony_ci	int rc;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (buf_len < sizeof(*nd_cmd))
49362306a36Sopenharmony_ci		return -EINVAL;
49462306a36Sopenharmony_ci	if (offset >= LABEL_SIZE)
49562306a36Sopenharmony_ci		return -EINVAL;
49662306a36Sopenharmony_ci	if (nd_cmd->in_length + sizeof(*nd_cmd) + 4 > buf_len)
49762306a36Sopenharmony_ci		return -EINVAL;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	status = (void *)nd_cmd + nd_cmd->in_length + sizeof(*nd_cmd);
50062306a36Sopenharmony_ci	*status = 0;
50162306a36Sopenharmony_ci	len = min(nd_cmd->in_length, LABEL_SIZE - offset);
50262306a36Sopenharmony_ci	memcpy(label + offset, nd_cmd->in_buf, len);
50362306a36Sopenharmony_ci	rc = buf_len - sizeof(*nd_cmd) - (len + 4);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci	return rc;
50662306a36Sopenharmony_ci}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci#define NFIT_TEST_CLEAR_ERR_UNIT 256
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_cistatic int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd,
51162306a36Sopenharmony_ci		unsigned int buf_len)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	int ars_recs;
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci	if (buf_len < sizeof(*nd_cmd))
51662306a36Sopenharmony_ci		return -EINVAL;
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	/* for testing, only store up to n records that fit within 4k */
51962306a36Sopenharmony_ci	ars_recs = SZ_4K / sizeof(struct nd_ars_record);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	nd_cmd->max_ars_out = sizeof(struct nd_cmd_ars_status)
52262306a36Sopenharmony_ci		+ ars_recs * sizeof(struct nd_ars_record);
52362306a36Sopenharmony_ci	nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16;
52462306a36Sopenharmony_ci	nd_cmd->clear_err_unit = NFIT_TEST_CLEAR_ERR_UNIT;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	return 0;
52762306a36Sopenharmony_ci}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_cistatic void post_ars_status(struct ars_state *ars_state,
53062306a36Sopenharmony_ci		struct badrange *badrange, u64 addr, u64 len)
53162306a36Sopenharmony_ci{
53262306a36Sopenharmony_ci	struct nd_cmd_ars_status *ars_status;
53362306a36Sopenharmony_ci	struct nd_ars_record *ars_record;
53462306a36Sopenharmony_ci	struct badrange_entry *be;
53562306a36Sopenharmony_ci	u64 end = addr + len - 1;
53662306a36Sopenharmony_ci	int i = 0;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	ars_state->deadline = jiffies + 1*HZ;
53962306a36Sopenharmony_ci	ars_status = ars_state->ars_status;
54062306a36Sopenharmony_ci	ars_status->status = 0;
54162306a36Sopenharmony_ci	ars_status->address = addr;
54262306a36Sopenharmony_ci	ars_status->length = len;
54362306a36Sopenharmony_ci	ars_status->type = ND_ARS_PERSISTENT;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	spin_lock(&badrange->lock);
54662306a36Sopenharmony_ci	list_for_each_entry(be, &badrange->list, list) {
54762306a36Sopenharmony_ci		u64 be_end = be->start + be->length - 1;
54862306a36Sopenharmony_ci		u64 rstart, rend;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci		/* skip entries outside the range */
55162306a36Sopenharmony_ci		if (be_end < addr || be->start > end)
55262306a36Sopenharmony_ci			continue;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci		rstart = (be->start < addr) ? addr : be->start;
55562306a36Sopenharmony_ci		rend = (be_end < end) ? be_end : end;
55662306a36Sopenharmony_ci		ars_record = &ars_status->records[i];
55762306a36Sopenharmony_ci		ars_record->handle = 0;
55862306a36Sopenharmony_ci		ars_record->err_address = rstart;
55962306a36Sopenharmony_ci		ars_record->length = rend - rstart + 1;
56062306a36Sopenharmony_ci		i++;
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci	spin_unlock(&badrange->lock);
56362306a36Sopenharmony_ci	ars_status->num_records = i;
56462306a36Sopenharmony_ci	ars_status->out_length = sizeof(struct nd_cmd_ars_status)
56562306a36Sopenharmony_ci		+ i * sizeof(struct nd_ars_record);
56662306a36Sopenharmony_ci}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_cistatic int nfit_test_cmd_ars_start(struct nfit_test *t,
56962306a36Sopenharmony_ci		struct ars_state *ars_state,
57062306a36Sopenharmony_ci		struct nd_cmd_ars_start *ars_start, unsigned int buf_len,
57162306a36Sopenharmony_ci		int *cmd_rc)
57262306a36Sopenharmony_ci{
57362306a36Sopenharmony_ci	if (buf_len < sizeof(*ars_start))
57462306a36Sopenharmony_ci		return -EINVAL;
57562306a36Sopenharmony_ci
57662306a36Sopenharmony_ci	spin_lock(&ars_state->lock);
57762306a36Sopenharmony_ci	if (time_before(jiffies, ars_state->deadline)) {
57862306a36Sopenharmony_ci		ars_start->status = NFIT_ARS_START_BUSY;
57962306a36Sopenharmony_ci		*cmd_rc = -EBUSY;
58062306a36Sopenharmony_ci	} else {
58162306a36Sopenharmony_ci		ars_start->status = 0;
58262306a36Sopenharmony_ci		ars_start->scrub_time = 1;
58362306a36Sopenharmony_ci		post_ars_status(ars_state, &t->badrange, ars_start->address,
58462306a36Sopenharmony_ci				ars_start->length);
58562306a36Sopenharmony_ci		*cmd_rc = 0;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci	spin_unlock(&ars_state->lock);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	return 0;
59062306a36Sopenharmony_ci}
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_cistatic int nfit_test_cmd_ars_status(struct ars_state *ars_state,
59362306a36Sopenharmony_ci		struct nd_cmd_ars_status *ars_status, unsigned int buf_len,
59462306a36Sopenharmony_ci		int *cmd_rc)
59562306a36Sopenharmony_ci{
59662306a36Sopenharmony_ci	if (buf_len < ars_state->ars_status->out_length)
59762306a36Sopenharmony_ci		return -EINVAL;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	spin_lock(&ars_state->lock);
60062306a36Sopenharmony_ci	if (time_before(jiffies, ars_state->deadline)) {
60162306a36Sopenharmony_ci		memset(ars_status, 0, buf_len);
60262306a36Sopenharmony_ci		ars_status->status = NFIT_ARS_STATUS_BUSY;
60362306a36Sopenharmony_ci		ars_status->out_length = sizeof(*ars_status);
60462306a36Sopenharmony_ci		*cmd_rc = -EBUSY;
60562306a36Sopenharmony_ci	} else {
60662306a36Sopenharmony_ci		memcpy(ars_status, ars_state->ars_status,
60762306a36Sopenharmony_ci				ars_state->ars_status->out_length);
60862306a36Sopenharmony_ci		*cmd_rc = 0;
60962306a36Sopenharmony_ci	}
61062306a36Sopenharmony_ci	spin_unlock(&ars_state->lock);
61162306a36Sopenharmony_ci	return 0;
61262306a36Sopenharmony_ci}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_cistatic int nfit_test_cmd_clear_error(struct nfit_test *t,
61562306a36Sopenharmony_ci		struct nd_cmd_clear_error *clear_err,
61662306a36Sopenharmony_ci		unsigned int buf_len, int *cmd_rc)
61762306a36Sopenharmony_ci{
61862306a36Sopenharmony_ci	const u64 mask = NFIT_TEST_CLEAR_ERR_UNIT - 1;
61962306a36Sopenharmony_ci	if (buf_len < sizeof(*clear_err))
62062306a36Sopenharmony_ci		return -EINVAL;
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	if ((clear_err->address & mask) || (clear_err->length & mask))
62362306a36Sopenharmony_ci		return -EINVAL;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	badrange_forget(&t->badrange, clear_err->address, clear_err->length);
62662306a36Sopenharmony_ci	clear_err->status = 0;
62762306a36Sopenharmony_ci	clear_err->cleared = clear_err->length;
62862306a36Sopenharmony_ci	*cmd_rc = 0;
62962306a36Sopenharmony_ci	return 0;
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_cistruct region_search_spa {
63362306a36Sopenharmony_ci	u64 addr;
63462306a36Sopenharmony_ci	struct nd_region *region;
63562306a36Sopenharmony_ci};
63662306a36Sopenharmony_ci
63762306a36Sopenharmony_cistatic int is_region_device(struct device *dev)
63862306a36Sopenharmony_ci{
63962306a36Sopenharmony_ci	return !strncmp(dev->kobj.name, "region", 6);
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic int nfit_test_search_region_spa(struct device *dev, void *data)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct region_search_spa *ctx = data;
64562306a36Sopenharmony_ci	struct nd_region *nd_region;
64662306a36Sopenharmony_ci	resource_size_t ndr_end;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	if (!is_region_device(dev))
64962306a36Sopenharmony_ci		return 0;
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	nd_region = to_nd_region(dev);
65262306a36Sopenharmony_ci	ndr_end = nd_region->ndr_start + nd_region->ndr_size;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	if (ctx->addr >= nd_region->ndr_start && ctx->addr < ndr_end) {
65562306a36Sopenharmony_ci		ctx->region = nd_region;
65662306a36Sopenharmony_ci		return 1;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	return 0;
66062306a36Sopenharmony_ci}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_cistatic int nfit_test_search_spa(struct nvdimm_bus *bus,
66362306a36Sopenharmony_ci		struct nd_cmd_translate_spa *spa)
66462306a36Sopenharmony_ci{
66562306a36Sopenharmony_ci	int ret;
66662306a36Sopenharmony_ci	struct nd_region *nd_region = NULL;
66762306a36Sopenharmony_ci	struct nvdimm *nvdimm = NULL;
66862306a36Sopenharmony_ci	struct nd_mapping *nd_mapping = NULL;
66962306a36Sopenharmony_ci	struct region_search_spa ctx = {
67062306a36Sopenharmony_ci		.addr = spa->spa,
67162306a36Sopenharmony_ci		.region = NULL,
67262306a36Sopenharmony_ci	};
67362306a36Sopenharmony_ci	u64 dpa;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	ret = device_for_each_child(&bus->dev, &ctx,
67662306a36Sopenharmony_ci				nfit_test_search_region_spa);
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	if (!ret)
67962306a36Sopenharmony_ci		return -ENODEV;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	nd_region = ctx.region;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	dpa = ctx.addr - nd_region->ndr_start;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	/*
68662306a36Sopenharmony_ci	 * last dimm is selected for test
68762306a36Sopenharmony_ci	 */
68862306a36Sopenharmony_ci	nd_mapping = &nd_region->mapping[nd_region->ndr_mappings - 1];
68962306a36Sopenharmony_ci	nvdimm = nd_mapping->nvdimm;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	spa->devices[0].nfit_device_handle = handle[nvdimm->id];
69262306a36Sopenharmony_ci	spa->num_nvdimms = 1;
69362306a36Sopenharmony_ci	spa->devices[0].dpa = dpa;
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	return 0;
69662306a36Sopenharmony_ci}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_cistatic int nfit_test_cmd_translate_spa(struct nvdimm_bus *bus,
69962306a36Sopenharmony_ci		struct nd_cmd_translate_spa *spa, unsigned int buf_len)
70062306a36Sopenharmony_ci{
70162306a36Sopenharmony_ci	if (buf_len < spa->translate_length)
70262306a36Sopenharmony_ci		return -EINVAL;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (nfit_test_search_spa(bus, spa) < 0 || !spa->num_nvdimms)
70562306a36Sopenharmony_ci		spa->status = 2;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	return 0;
70862306a36Sopenharmony_ci}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_cistatic int nfit_test_cmd_smart(struct nd_intel_smart *smart, unsigned int buf_len,
71162306a36Sopenharmony_ci		struct nd_intel_smart *smart_data)
71262306a36Sopenharmony_ci{
71362306a36Sopenharmony_ci	if (buf_len < sizeof(*smart))
71462306a36Sopenharmony_ci		return -EINVAL;
71562306a36Sopenharmony_ci	memcpy(smart, smart_data, sizeof(*smart));
71662306a36Sopenharmony_ci	return 0;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic int nfit_test_cmd_smart_threshold(
72062306a36Sopenharmony_ci		struct nd_intel_smart_threshold *out,
72162306a36Sopenharmony_ci		unsigned int buf_len,
72262306a36Sopenharmony_ci		struct nd_intel_smart_threshold *smart_t)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	if (buf_len < sizeof(*smart_t))
72562306a36Sopenharmony_ci		return -EINVAL;
72662306a36Sopenharmony_ci	memcpy(out, smart_t, sizeof(*smart_t));
72762306a36Sopenharmony_ci	return 0;
72862306a36Sopenharmony_ci}
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_cistatic void smart_notify(struct device *bus_dev,
73162306a36Sopenharmony_ci		struct device *dimm_dev, struct nd_intel_smart *smart,
73262306a36Sopenharmony_ci		struct nd_intel_smart_threshold *thresh)
73362306a36Sopenharmony_ci{
73462306a36Sopenharmony_ci	dev_dbg(dimm_dev, "%s: alarm: %#x spares: %d (%d) mtemp: %d (%d) ctemp: %d (%d)\n",
73562306a36Sopenharmony_ci			__func__, thresh->alarm_control, thresh->spares,
73662306a36Sopenharmony_ci			smart->spares, thresh->media_temperature,
73762306a36Sopenharmony_ci			smart->media_temperature, thresh->ctrl_temperature,
73862306a36Sopenharmony_ci			smart->ctrl_temperature);
73962306a36Sopenharmony_ci	if (((thresh->alarm_control & ND_INTEL_SMART_SPARE_TRIP)
74062306a36Sopenharmony_ci				&& smart->spares
74162306a36Sopenharmony_ci				<= thresh->spares)
74262306a36Sopenharmony_ci			|| ((thresh->alarm_control & ND_INTEL_SMART_TEMP_TRIP)
74362306a36Sopenharmony_ci				&& smart->media_temperature
74462306a36Sopenharmony_ci				>= thresh->media_temperature)
74562306a36Sopenharmony_ci			|| ((thresh->alarm_control & ND_INTEL_SMART_CTEMP_TRIP)
74662306a36Sopenharmony_ci				&& smart->ctrl_temperature
74762306a36Sopenharmony_ci				>= thresh->ctrl_temperature)
74862306a36Sopenharmony_ci			|| (smart->health != ND_INTEL_SMART_NON_CRITICAL_HEALTH)
74962306a36Sopenharmony_ci			|| (smart->shutdown_state != 0)) {
75062306a36Sopenharmony_ci		device_lock(bus_dev);
75162306a36Sopenharmony_ci		__acpi_nvdimm_notify(dimm_dev, 0x81);
75262306a36Sopenharmony_ci		device_unlock(bus_dev);
75362306a36Sopenharmony_ci	}
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_cistatic int nfit_test_cmd_smart_set_threshold(
75762306a36Sopenharmony_ci		struct nd_intel_smart_set_threshold *in,
75862306a36Sopenharmony_ci		unsigned int buf_len,
75962306a36Sopenharmony_ci		struct nd_intel_smart_threshold *thresh,
76062306a36Sopenharmony_ci		struct nd_intel_smart *smart,
76162306a36Sopenharmony_ci		struct device *bus_dev, struct device *dimm_dev)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	unsigned int size;
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	size = sizeof(*in) - 4;
76662306a36Sopenharmony_ci	if (buf_len < size)
76762306a36Sopenharmony_ci		return -EINVAL;
76862306a36Sopenharmony_ci	memcpy(thresh->data, in, size);
76962306a36Sopenharmony_ci	in->status = 0;
77062306a36Sopenharmony_ci	smart_notify(bus_dev, dimm_dev, smart, thresh);
77162306a36Sopenharmony_ci
77262306a36Sopenharmony_ci	return 0;
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic int nfit_test_cmd_smart_inject(
77662306a36Sopenharmony_ci		struct nd_intel_smart_inject *inj,
77762306a36Sopenharmony_ci		unsigned int buf_len,
77862306a36Sopenharmony_ci		struct nd_intel_smart_threshold *thresh,
77962306a36Sopenharmony_ci		struct nd_intel_smart *smart,
78062306a36Sopenharmony_ci		struct device *bus_dev, struct device *dimm_dev)
78162306a36Sopenharmony_ci{
78262306a36Sopenharmony_ci	if (buf_len != sizeof(*inj))
78362306a36Sopenharmony_ci		return -EINVAL;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	if (inj->flags & ND_INTEL_SMART_INJECT_MTEMP) {
78662306a36Sopenharmony_ci		if (inj->mtemp_enable)
78762306a36Sopenharmony_ci			smart->media_temperature = inj->media_temperature;
78862306a36Sopenharmony_ci		else
78962306a36Sopenharmony_ci			smart->media_temperature = smart_def.media_temperature;
79062306a36Sopenharmony_ci	}
79162306a36Sopenharmony_ci	if (inj->flags & ND_INTEL_SMART_INJECT_SPARE) {
79262306a36Sopenharmony_ci		if (inj->spare_enable)
79362306a36Sopenharmony_ci			smart->spares = inj->spares;
79462306a36Sopenharmony_ci		else
79562306a36Sopenharmony_ci			smart->spares = smart_def.spares;
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci	if (inj->flags & ND_INTEL_SMART_INJECT_FATAL) {
79862306a36Sopenharmony_ci		if (inj->fatal_enable)
79962306a36Sopenharmony_ci			smart->health = ND_INTEL_SMART_FATAL_HEALTH;
80062306a36Sopenharmony_ci		else
80162306a36Sopenharmony_ci			smart->health = ND_INTEL_SMART_NON_CRITICAL_HEALTH;
80262306a36Sopenharmony_ci	}
80362306a36Sopenharmony_ci	if (inj->flags & ND_INTEL_SMART_INJECT_SHUTDOWN) {
80462306a36Sopenharmony_ci		if (inj->unsafe_shutdown_enable) {
80562306a36Sopenharmony_ci			smart->shutdown_state = 1;
80662306a36Sopenharmony_ci			smart->shutdown_count++;
80762306a36Sopenharmony_ci		} else
80862306a36Sopenharmony_ci			smart->shutdown_state = 0;
80962306a36Sopenharmony_ci	}
81062306a36Sopenharmony_ci	inj->status = 0;
81162306a36Sopenharmony_ci	smart_notify(bus_dev, dimm_dev, smart, thresh);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	return 0;
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_cistatic void uc_error_notify(struct work_struct *work)
81762306a36Sopenharmony_ci{
81862306a36Sopenharmony_ci	struct nfit_test *t = container_of(work, typeof(*t), work);
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	__acpi_nfit_notify(&t->pdev.dev, t, NFIT_NOTIFY_UC_MEMORY_ERROR);
82162306a36Sopenharmony_ci}
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_cistatic int nfit_test_cmd_ars_error_inject(struct nfit_test *t,
82462306a36Sopenharmony_ci		struct nd_cmd_ars_err_inj *err_inj, unsigned int buf_len)
82562306a36Sopenharmony_ci{
82662306a36Sopenharmony_ci	int rc;
82762306a36Sopenharmony_ci
82862306a36Sopenharmony_ci	if (buf_len != sizeof(*err_inj)) {
82962306a36Sopenharmony_ci		rc = -EINVAL;
83062306a36Sopenharmony_ci		goto err;
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	if (err_inj->err_inj_spa_range_length <= 0) {
83462306a36Sopenharmony_ci		rc = -EINVAL;
83562306a36Sopenharmony_ci		goto err;
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	rc =  badrange_add(&t->badrange, err_inj->err_inj_spa_range_base,
83962306a36Sopenharmony_ci			err_inj->err_inj_spa_range_length);
84062306a36Sopenharmony_ci	if (rc < 0)
84162306a36Sopenharmony_ci		goto err;
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci	if (err_inj->err_inj_options & (1 << ND_ARS_ERR_INJ_OPT_NOTIFY))
84462306a36Sopenharmony_ci		queue_work(nfit_wq, &t->work);
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_ci	err_inj->status = 0;
84762306a36Sopenharmony_ci	return 0;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_cierr:
85062306a36Sopenharmony_ci	err_inj->status = NFIT_ARS_INJECT_INVALID;
85162306a36Sopenharmony_ci	return rc;
85262306a36Sopenharmony_ci}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_cistatic int nfit_test_cmd_ars_inject_clear(struct nfit_test *t,
85562306a36Sopenharmony_ci		struct nd_cmd_ars_err_inj_clr *err_clr, unsigned int buf_len)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	int rc;
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (buf_len != sizeof(*err_clr)) {
86062306a36Sopenharmony_ci		rc = -EINVAL;
86162306a36Sopenharmony_ci		goto err;
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	if (err_clr->err_inj_clr_spa_range_length <= 0) {
86562306a36Sopenharmony_ci		rc = -EINVAL;
86662306a36Sopenharmony_ci		goto err;
86762306a36Sopenharmony_ci	}
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	badrange_forget(&t->badrange, err_clr->err_inj_clr_spa_range_base,
87062306a36Sopenharmony_ci			err_clr->err_inj_clr_spa_range_length);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	err_clr->status = 0;
87362306a36Sopenharmony_ci	return 0;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_cierr:
87662306a36Sopenharmony_ci	err_clr->status = NFIT_ARS_INJECT_INVALID;
87762306a36Sopenharmony_ci	return rc;
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_cistatic int nfit_test_cmd_ars_inject_status(struct nfit_test *t,
88162306a36Sopenharmony_ci		struct nd_cmd_ars_err_inj_stat *err_stat,
88262306a36Sopenharmony_ci		unsigned int buf_len)
88362306a36Sopenharmony_ci{
88462306a36Sopenharmony_ci	struct badrange_entry *be;
88562306a36Sopenharmony_ci	int max = SZ_4K / sizeof(struct nd_error_stat_query_record);
88662306a36Sopenharmony_ci	int i = 0;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	err_stat->status = 0;
88962306a36Sopenharmony_ci	spin_lock(&t->badrange.lock);
89062306a36Sopenharmony_ci	list_for_each_entry(be, &t->badrange.list, list) {
89162306a36Sopenharmony_ci		err_stat->record[i].err_inj_stat_spa_range_base = be->start;
89262306a36Sopenharmony_ci		err_stat->record[i].err_inj_stat_spa_range_length = be->length;
89362306a36Sopenharmony_ci		i++;
89462306a36Sopenharmony_ci		if (i > max)
89562306a36Sopenharmony_ci			break;
89662306a36Sopenharmony_ci	}
89762306a36Sopenharmony_ci	spin_unlock(&t->badrange.lock);
89862306a36Sopenharmony_ci	err_stat->inj_err_rec_count = i;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	return 0;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic int nd_intel_test_cmd_set_lss_status(struct nfit_test *t,
90462306a36Sopenharmony_ci		struct nd_intel_lss *nd_cmd, unsigned int buf_len)
90562306a36Sopenharmony_ci{
90662306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
90762306a36Sopenharmony_ci
90862306a36Sopenharmony_ci	if (buf_len < sizeof(*nd_cmd))
90962306a36Sopenharmony_ci		return -EINVAL;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	switch (nd_cmd->enable) {
91262306a36Sopenharmony_ci	case 0:
91362306a36Sopenharmony_ci		nd_cmd->status = 0;
91462306a36Sopenharmony_ci		dev_dbg(dev, "%s: Latch System Shutdown Status disabled\n",
91562306a36Sopenharmony_ci				__func__);
91662306a36Sopenharmony_ci		break;
91762306a36Sopenharmony_ci	case 1:
91862306a36Sopenharmony_ci		nd_cmd->status = 0;
91962306a36Sopenharmony_ci		dev_dbg(dev, "%s: Latch System Shutdown Status enabled\n",
92062306a36Sopenharmony_ci				__func__);
92162306a36Sopenharmony_ci		break;
92262306a36Sopenharmony_ci	default:
92362306a36Sopenharmony_ci		dev_warn(dev, "Unknown enable value: %#x\n", nd_cmd->enable);
92462306a36Sopenharmony_ci		nd_cmd->status = 0x3;
92562306a36Sopenharmony_ci		break;
92662306a36Sopenharmony_ci	}
92762306a36Sopenharmony_ci
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	return 0;
93062306a36Sopenharmony_ci}
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_cistatic int override_return_code(int dimm, unsigned int func, int rc)
93362306a36Sopenharmony_ci{
93462306a36Sopenharmony_ci	if ((1 << func) & dimm_fail_cmd_flags[dimm]) {
93562306a36Sopenharmony_ci		if (dimm_fail_cmd_code[dimm])
93662306a36Sopenharmony_ci			return dimm_fail_cmd_code[dimm];
93762306a36Sopenharmony_ci		return -EIO;
93862306a36Sopenharmony_ci	}
93962306a36Sopenharmony_ci	return rc;
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_cistatic int nd_intel_test_cmd_security_status(struct nfit_test *t,
94362306a36Sopenharmony_ci		struct nd_intel_get_security_state *nd_cmd,
94462306a36Sopenharmony_ci		unsigned int buf_len, int dimm)
94562306a36Sopenharmony_ci{
94662306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
94762306a36Sopenharmony_ci	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	nd_cmd->status = 0;
95062306a36Sopenharmony_ci	nd_cmd->state = sec->state;
95162306a36Sopenharmony_ci	nd_cmd->extended_state = sec->ext_state;
95262306a36Sopenharmony_ci	dev_dbg(dev, "security state (%#x) returned\n", nd_cmd->state);
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	return 0;
95562306a36Sopenharmony_ci}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_cistatic int nd_intel_test_cmd_unlock_unit(struct nfit_test *t,
95862306a36Sopenharmony_ci		struct nd_intel_unlock_unit *nd_cmd,
95962306a36Sopenharmony_ci		unsigned int buf_len, int dimm)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
96262306a36Sopenharmony_ci	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	if (!(sec->state & ND_INTEL_SEC_STATE_LOCKED) ||
96562306a36Sopenharmony_ci			(sec->state & ND_INTEL_SEC_STATE_FROZEN)) {
96662306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE;
96762306a36Sopenharmony_ci		dev_dbg(dev, "unlock unit: invalid state: %#x\n",
96862306a36Sopenharmony_ci				sec->state);
96962306a36Sopenharmony_ci	} else if (memcmp(nd_cmd->passphrase, sec->passphrase,
97062306a36Sopenharmony_ci				ND_INTEL_PASSPHRASE_SIZE) != 0) {
97162306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS;
97262306a36Sopenharmony_ci		dev_dbg(dev, "unlock unit: invalid passphrase\n");
97362306a36Sopenharmony_ci	} else {
97462306a36Sopenharmony_ci		nd_cmd->status = 0;
97562306a36Sopenharmony_ci		sec->state = ND_INTEL_SEC_STATE_ENABLED;
97662306a36Sopenharmony_ci		dev_dbg(dev, "Unit unlocked\n");
97762306a36Sopenharmony_ci	}
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci	dev_dbg(dev, "unlocking status returned: %#x\n", nd_cmd->status);
98062306a36Sopenharmony_ci	return 0;
98162306a36Sopenharmony_ci}
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_cistatic int nd_intel_test_cmd_set_pass(struct nfit_test *t,
98462306a36Sopenharmony_ci		struct nd_intel_set_passphrase *nd_cmd,
98562306a36Sopenharmony_ci		unsigned int buf_len, int dimm)
98662306a36Sopenharmony_ci{
98762306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
98862306a36Sopenharmony_ci	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
98962306a36Sopenharmony_ci
99062306a36Sopenharmony_ci	if (sec->state & ND_INTEL_SEC_STATE_FROZEN) {
99162306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE;
99262306a36Sopenharmony_ci		dev_dbg(dev, "set passphrase: wrong security state\n");
99362306a36Sopenharmony_ci	} else if (memcmp(nd_cmd->old_pass, sec->passphrase,
99462306a36Sopenharmony_ci				ND_INTEL_PASSPHRASE_SIZE) != 0) {
99562306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS;
99662306a36Sopenharmony_ci		dev_dbg(dev, "set passphrase: wrong passphrase\n");
99762306a36Sopenharmony_ci	} else {
99862306a36Sopenharmony_ci		memcpy(sec->passphrase, nd_cmd->new_pass,
99962306a36Sopenharmony_ci				ND_INTEL_PASSPHRASE_SIZE);
100062306a36Sopenharmony_ci		sec->state |= ND_INTEL_SEC_STATE_ENABLED;
100162306a36Sopenharmony_ci		nd_cmd->status = 0;
100262306a36Sopenharmony_ci		dev_dbg(dev, "passphrase updated\n");
100362306a36Sopenharmony_ci	}
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci	return 0;
100662306a36Sopenharmony_ci}
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_cistatic int nd_intel_test_cmd_freeze_lock(struct nfit_test *t,
100962306a36Sopenharmony_ci		struct nd_intel_freeze_lock *nd_cmd,
101062306a36Sopenharmony_ci		unsigned int buf_len, int dimm)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
101362306a36Sopenharmony_ci	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci	if (!(sec->state & ND_INTEL_SEC_STATE_ENABLED)) {
101662306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE;
101762306a36Sopenharmony_ci		dev_dbg(dev, "freeze lock: wrong security state\n");
101862306a36Sopenharmony_ci	} else {
101962306a36Sopenharmony_ci		sec->state |= ND_INTEL_SEC_STATE_FROZEN;
102062306a36Sopenharmony_ci		nd_cmd->status = 0;
102162306a36Sopenharmony_ci		dev_dbg(dev, "security frozen\n");
102262306a36Sopenharmony_ci	}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	return 0;
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_cistatic int nd_intel_test_cmd_disable_pass(struct nfit_test *t,
102862306a36Sopenharmony_ci		struct nd_intel_disable_passphrase *nd_cmd,
102962306a36Sopenharmony_ci		unsigned int buf_len, int dimm)
103062306a36Sopenharmony_ci{
103162306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
103262306a36Sopenharmony_ci	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	if (!(sec->state & ND_INTEL_SEC_STATE_ENABLED) ||
103562306a36Sopenharmony_ci			(sec->state & ND_INTEL_SEC_STATE_FROZEN)) {
103662306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE;
103762306a36Sopenharmony_ci		dev_dbg(dev, "disable passphrase: wrong security state\n");
103862306a36Sopenharmony_ci	} else if (memcmp(nd_cmd->passphrase, sec->passphrase,
103962306a36Sopenharmony_ci				ND_INTEL_PASSPHRASE_SIZE) != 0) {
104062306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS;
104162306a36Sopenharmony_ci		dev_dbg(dev, "disable passphrase: wrong passphrase\n");
104262306a36Sopenharmony_ci	} else {
104362306a36Sopenharmony_ci		memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE);
104462306a36Sopenharmony_ci		sec->state = 0;
104562306a36Sopenharmony_ci		dev_dbg(dev, "disable passphrase: done\n");
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	return 0;
104962306a36Sopenharmony_ci}
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_cistatic int nd_intel_test_cmd_secure_erase(struct nfit_test *t,
105262306a36Sopenharmony_ci		struct nd_intel_secure_erase *nd_cmd,
105362306a36Sopenharmony_ci		unsigned int buf_len, int dimm)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
105662306a36Sopenharmony_ci	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	if (sec->state & ND_INTEL_SEC_STATE_FROZEN) {
105962306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE;
106062306a36Sopenharmony_ci		dev_dbg(dev, "secure erase: wrong security state\n");
106162306a36Sopenharmony_ci	} else if (memcmp(nd_cmd->passphrase, sec->passphrase,
106262306a36Sopenharmony_ci				ND_INTEL_PASSPHRASE_SIZE) != 0) {
106362306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS;
106462306a36Sopenharmony_ci		dev_dbg(dev, "secure erase: wrong passphrase\n");
106562306a36Sopenharmony_ci	} else {
106662306a36Sopenharmony_ci		if (!(sec->state & ND_INTEL_SEC_STATE_ENABLED)
106762306a36Sopenharmony_ci				&& (memcmp(nd_cmd->passphrase, zero_key,
106862306a36Sopenharmony_ci					ND_INTEL_PASSPHRASE_SIZE) != 0)) {
106962306a36Sopenharmony_ci			dev_dbg(dev, "invalid zero key\n");
107062306a36Sopenharmony_ci			return 0;
107162306a36Sopenharmony_ci		}
107262306a36Sopenharmony_ci		memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE);
107362306a36Sopenharmony_ci		memset(sec->master_passphrase, 0, ND_INTEL_PASSPHRASE_SIZE);
107462306a36Sopenharmony_ci		sec->state = 0;
107562306a36Sopenharmony_ci		sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED;
107662306a36Sopenharmony_ci		dev_dbg(dev, "secure erase: done\n");
107762306a36Sopenharmony_ci	}
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	return 0;
108062306a36Sopenharmony_ci}
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_cistatic int nd_intel_test_cmd_overwrite(struct nfit_test *t,
108362306a36Sopenharmony_ci		struct nd_intel_overwrite *nd_cmd,
108462306a36Sopenharmony_ci		unsigned int buf_len, int dimm)
108562306a36Sopenharmony_ci{
108662306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
108762306a36Sopenharmony_ci	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
108862306a36Sopenharmony_ci
108962306a36Sopenharmony_ci	if ((sec->state & ND_INTEL_SEC_STATE_ENABLED) &&
109062306a36Sopenharmony_ci			memcmp(nd_cmd->passphrase, sec->passphrase,
109162306a36Sopenharmony_ci				ND_INTEL_PASSPHRASE_SIZE) != 0) {
109262306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS;
109362306a36Sopenharmony_ci		dev_dbg(dev, "overwrite: wrong passphrase\n");
109462306a36Sopenharmony_ci		return 0;
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	sec->old_state = sec->state;
109862306a36Sopenharmony_ci	sec->state = ND_INTEL_SEC_STATE_OVERWRITE;
109962306a36Sopenharmony_ci	dev_dbg(dev, "overwrite progressing.\n");
110062306a36Sopenharmony_ci	sec->overwrite_end_time = get_jiffies_64() + 5 * HZ;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	return 0;
110362306a36Sopenharmony_ci}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_cistatic int nd_intel_test_cmd_query_overwrite(struct nfit_test *t,
110662306a36Sopenharmony_ci		struct nd_intel_query_overwrite *nd_cmd,
110762306a36Sopenharmony_ci		unsigned int buf_len, int dimm)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
111062306a36Sopenharmony_ci	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	if (!(sec->state & ND_INTEL_SEC_STATE_OVERWRITE)) {
111362306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_OQUERY_SEQUENCE_ERR;
111462306a36Sopenharmony_ci		return 0;
111562306a36Sopenharmony_ci	}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci	if (time_is_before_jiffies64(sec->overwrite_end_time)) {
111862306a36Sopenharmony_ci		sec->overwrite_end_time = 0;
111962306a36Sopenharmony_ci		sec->state = sec->old_state;
112062306a36Sopenharmony_ci		sec->old_state = 0;
112162306a36Sopenharmony_ci		sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED;
112262306a36Sopenharmony_ci		dev_dbg(dev, "overwrite is complete\n");
112362306a36Sopenharmony_ci	} else
112462306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_OQUERY_INPROGRESS;
112562306a36Sopenharmony_ci	return 0;
112662306a36Sopenharmony_ci}
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_cistatic int nd_intel_test_cmd_master_set_pass(struct nfit_test *t,
112962306a36Sopenharmony_ci		struct nd_intel_set_master_passphrase *nd_cmd,
113062306a36Sopenharmony_ci		unsigned int buf_len, int dimm)
113162306a36Sopenharmony_ci{
113262306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
113362306a36Sopenharmony_ci	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	if (!(sec->ext_state & ND_INTEL_SEC_ESTATE_ENABLED)) {
113662306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_NOT_SUPPORTED;
113762306a36Sopenharmony_ci		dev_dbg(dev, "master set passphrase: in wrong state\n");
113862306a36Sopenharmony_ci	} else if (sec->ext_state & ND_INTEL_SEC_ESTATE_PLIMIT) {
113962306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE;
114062306a36Sopenharmony_ci		dev_dbg(dev, "master set passphrase: in wrong security state\n");
114162306a36Sopenharmony_ci	} else if (memcmp(nd_cmd->old_pass, sec->master_passphrase,
114262306a36Sopenharmony_ci				ND_INTEL_PASSPHRASE_SIZE) != 0) {
114362306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS;
114462306a36Sopenharmony_ci		dev_dbg(dev, "master set passphrase: wrong passphrase\n");
114562306a36Sopenharmony_ci	} else {
114662306a36Sopenharmony_ci		memcpy(sec->master_passphrase, nd_cmd->new_pass,
114762306a36Sopenharmony_ci				ND_INTEL_PASSPHRASE_SIZE);
114862306a36Sopenharmony_ci		sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED;
114962306a36Sopenharmony_ci		dev_dbg(dev, "master passphrase: updated\n");
115062306a36Sopenharmony_ci	}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci	return 0;
115362306a36Sopenharmony_ci}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_cistatic int nd_intel_test_cmd_master_secure_erase(struct nfit_test *t,
115662306a36Sopenharmony_ci		struct nd_intel_master_secure_erase *nd_cmd,
115762306a36Sopenharmony_ci		unsigned int buf_len, int dimm)
115862306a36Sopenharmony_ci{
115962306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
116062306a36Sopenharmony_ci	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
116162306a36Sopenharmony_ci
116262306a36Sopenharmony_ci	if (!(sec->ext_state & ND_INTEL_SEC_ESTATE_ENABLED)) {
116362306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_NOT_SUPPORTED;
116462306a36Sopenharmony_ci		dev_dbg(dev, "master secure erase: in wrong state\n");
116562306a36Sopenharmony_ci	} else if (sec->ext_state & ND_INTEL_SEC_ESTATE_PLIMIT) {
116662306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_STATE;
116762306a36Sopenharmony_ci		dev_dbg(dev, "master secure erase: in wrong security state\n");
116862306a36Sopenharmony_ci	} else if (memcmp(nd_cmd->passphrase, sec->master_passphrase,
116962306a36Sopenharmony_ci				ND_INTEL_PASSPHRASE_SIZE) != 0) {
117062306a36Sopenharmony_ci		nd_cmd->status = ND_INTEL_STATUS_INVALID_PASS;
117162306a36Sopenharmony_ci		dev_dbg(dev, "master secure erase: wrong passphrase\n");
117262306a36Sopenharmony_ci	} else {
117362306a36Sopenharmony_ci		/* we do not erase master state passphrase ever */
117462306a36Sopenharmony_ci		sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED;
117562306a36Sopenharmony_ci		memset(sec->passphrase, 0, ND_INTEL_PASSPHRASE_SIZE);
117662306a36Sopenharmony_ci		sec->state = 0;
117762306a36Sopenharmony_ci		dev_dbg(dev, "master secure erase: done\n");
117862306a36Sopenharmony_ci	}
117962306a36Sopenharmony_ci
118062306a36Sopenharmony_ci	return 0;
118162306a36Sopenharmony_ci}
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic unsigned long last_activate;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_cistatic int nvdimm_bus_intel_fw_activate_businfo(struct nfit_test *t,
118662306a36Sopenharmony_ci		struct nd_intel_bus_fw_activate_businfo *nd_cmd,
118762306a36Sopenharmony_ci		unsigned int buf_len)
118862306a36Sopenharmony_ci{
118962306a36Sopenharmony_ci	int i, armed = 0;
119062306a36Sopenharmony_ci	int state;
119162306a36Sopenharmony_ci	u64 tmo;
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	for (i = 0; i < NUM_DCR; i++) {
119462306a36Sopenharmony_ci		struct nfit_test_fw *fw = &t->fw[i];
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci		if (fw->armed)
119762306a36Sopenharmony_ci			armed++;
119862306a36Sopenharmony_ci	}
119962306a36Sopenharmony_ci
120062306a36Sopenharmony_ci	/*
120162306a36Sopenharmony_ci	 * Emulate 3 second activation max, and 1 second incremental
120262306a36Sopenharmony_ci	 * quiesce time per dimm requiring multiple activates to get all
120362306a36Sopenharmony_ci	 * DIMMs updated.
120462306a36Sopenharmony_ci	 */
120562306a36Sopenharmony_ci	if (armed)
120662306a36Sopenharmony_ci		state = ND_INTEL_FWA_ARMED;
120762306a36Sopenharmony_ci	else if (!last_activate || time_after(jiffies, last_activate + 3 * HZ))
120862306a36Sopenharmony_ci		state = ND_INTEL_FWA_IDLE;
120962306a36Sopenharmony_ci	else
121062306a36Sopenharmony_ci		state = ND_INTEL_FWA_BUSY;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	tmo = armed * USEC_PER_SEC;
121362306a36Sopenharmony_ci	*nd_cmd = (struct nd_intel_bus_fw_activate_businfo) {
121462306a36Sopenharmony_ci		.capability = ND_INTEL_BUS_FWA_CAP_FWQUIESCE
121562306a36Sopenharmony_ci			| ND_INTEL_BUS_FWA_CAP_OSQUIESCE
121662306a36Sopenharmony_ci			| ND_INTEL_BUS_FWA_CAP_RESET,
121762306a36Sopenharmony_ci		.state = state,
121862306a36Sopenharmony_ci		.activate_tmo = tmo,
121962306a36Sopenharmony_ci		.cpu_quiesce_tmo = tmo,
122062306a36Sopenharmony_ci		.io_quiesce_tmo = tmo,
122162306a36Sopenharmony_ci		.max_quiesce_tmo = 3 * USEC_PER_SEC,
122262306a36Sopenharmony_ci	};
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	return 0;
122562306a36Sopenharmony_ci}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_cistatic int nvdimm_bus_intel_fw_activate(struct nfit_test *t,
122862306a36Sopenharmony_ci		struct nd_intel_bus_fw_activate *nd_cmd,
122962306a36Sopenharmony_ci		unsigned int buf_len)
123062306a36Sopenharmony_ci{
123162306a36Sopenharmony_ci	struct nd_intel_bus_fw_activate_businfo info;
123262306a36Sopenharmony_ci	u32 status = 0;
123362306a36Sopenharmony_ci	int i;
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	nvdimm_bus_intel_fw_activate_businfo(t, &info, sizeof(info));
123662306a36Sopenharmony_ci	if (info.state == ND_INTEL_FWA_BUSY)
123762306a36Sopenharmony_ci		status = ND_INTEL_BUS_FWA_STATUS_BUSY;
123862306a36Sopenharmony_ci	else if (info.activate_tmo > info.max_quiesce_tmo)
123962306a36Sopenharmony_ci		status = ND_INTEL_BUS_FWA_STATUS_TMO;
124062306a36Sopenharmony_ci	else if (info.state == ND_INTEL_FWA_IDLE)
124162306a36Sopenharmony_ci		status = ND_INTEL_BUS_FWA_STATUS_NOARM;
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	dev_dbg(&t->pdev.dev, "status: %d\n", status);
124462306a36Sopenharmony_ci	nd_cmd->status = status;
124562306a36Sopenharmony_ci	if (status && status != ND_INTEL_BUS_FWA_STATUS_TMO)
124662306a36Sopenharmony_ci		return 0;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	last_activate = jiffies;
124962306a36Sopenharmony_ci	for (i = 0; i < NUM_DCR; i++) {
125062306a36Sopenharmony_ci		struct nfit_test_fw *fw = &t->fw[i];
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci		if (!fw->armed)
125362306a36Sopenharmony_ci			continue;
125462306a36Sopenharmony_ci		if (fw->state != FW_STATE_UPDATED)
125562306a36Sopenharmony_ci			fw->missed_activate = true;
125662306a36Sopenharmony_ci		else
125762306a36Sopenharmony_ci			fw->state = FW_STATE_NEW;
125862306a36Sopenharmony_ci		fw->armed = false;
125962306a36Sopenharmony_ci		fw->last_activate = last_activate;
126062306a36Sopenharmony_ci	}
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	return 0;
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_cistatic int nd_intel_test_cmd_fw_activate_dimminfo(struct nfit_test *t,
126662306a36Sopenharmony_ci		struct nd_intel_fw_activate_dimminfo *nd_cmd,
126762306a36Sopenharmony_ci		unsigned int buf_len, int dimm)
126862306a36Sopenharmony_ci{
126962306a36Sopenharmony_ci	struct nd_intel_bus_fw_activate_businfo info;
127062306a36Sopenharmony_ci	struct nfit_test_fw *fw = &t->fw[dimm];
127162306a36Sopenharmony_ci	u32 result, state;
127262306a36Sopenharmony_ci
127362306a36Sopenharmony_ci	nvdimm_bus_intel_fw_activate_businfo(t, &info, sizeof(info));
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	if (info.state == ND_INTEL_FWA_BUSY)
127662306a36Sopenharmony_ci		state = ND_INTEL_FWA_BUSY;
127762306a36Sopenharmony_ci	else if (info.state == ND_INTEL_FWA_IDLE)
127862306a36Sopenharmony_ci		state = ND_INTEL_FWA_IDLE;
127962306a36Sopenharmony_ci	else if (fw->armed)
128062306a36Sopenharmony_ci		state = ND_INTEL_FWA_ARMED;
128162306a36Sopenharmony_ci	else
128262306a36Sopenharmony_ci		state = ND_INTEL_FWA_IDLE;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	result = ND_INTEL_DIMM_FWA_NONE;
128562306a36Sopenharmony_ci	if (last_activate && fw->last_activate == last_activate &&
128662306a36Sopenharmony_ci			state == ND_INTEL_FWA_IDLE) {
128762306a36Sopenharmony_ci		if (fw->missed_activate)
128862306a36Sopenharmony_ci			result = ND_INTEL_DIMM_FWA_NOTSTAGED;
128962306a36Sopenharmony_ci		else
129062306a36Sopenharmony_ci			result = ND_INTEL_DIMM_FWA_SUCCESS;
129162306a36Sopenharmony_ci	}
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	*nd_cmd = (struct nd_intel_fw_activate_dimminfo) {
129462306a36Sopenharmony_ci		.result = result,
129562306a36Sopenharmony_ci		.state = state,
129662306a36Sopenharmony_ci	};
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	return 0;
129962306a36Sopenharmony_ci}
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_cistatic int nd_intel_test_cmd_fw_activate_arm(struct nfit_test *t,
130262306a36Sopenharmony_ci		struct nd_intel_fw_activate_arm *nd_cmd,
130362306a36Sopenharmony_ci		unsigned int buf_len, int dimm)
130462306a36Sopenharmony_ci{
130562306a36Sopenharmony_ci	struct nfit_test_fw *fw = &t->fw[dimm];
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	fw->armed = nd_cmd->activate_arm == ND_INTEL_DIMM_FWA_ARM;
130862306a36Sopenharmony_ci	nd_cmd->status = 0;
130962306a36Sopenharmony_ci	return 0;
131062306a36Sopenharmony_ci}
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_cistatic int get_dimm(struct nfit_mem *nfit_mem, unsigned int func)
131362306a36Sopenharmony_ci{
131462306a36Sopenharmony_ci	int i;
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	/* lookup per-dimm data */
131762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(handle); i++)
131862306a36Sopenharmony_ci		if (__to_nfit_memdev(nfit_mem)->device_handle == handle[i])
131962306a36Sopenharmony_ci			break;
132062306a36Sopenharmony_ci	if (i >= ARRAY_SIZE(handle))
132162306a36Sopenharmony_ci		return -ENXIO;
132262306a36Sopenharmony_ci	return i;
132362306a36Sopenharmony_ci}
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_cistatic void nfit_ctl_dbg(struct acpi_nfit_desc *acpi_desc,
132662306a36Sopenharmony_ci		struct nvdimm *nvdimm, unsigned int cmd, void *buf,
132762306a36Sopenharmony_ci		unsigned int len)
132862306a36Sopenharmony_ci{
132962306a36Sopenharmony_ci	struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc);
133062306a36Sopenharmony_ci	unsigned int func = cmd;
133162306a36Sopenharmony_ci	unsigned int family = 0;
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci	if (cmd == ND_CMD_CALL) {
133462306a36Sopenharmony_ci		struct nd_cmd_pkg *pkg = buf;
133562306a36Sopenharmony_ci
133662306a36Sopenharmony_ci		len = pkg->nd_size_in;
133762306a36Sopenharmony_ci		family = pkg->nd_family;
133862306a36Sopenharmony_ci		buf = pkg->nd_payload;
133962306a36Sopenharmony_ci		func = pkg->nd_command;
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci	dev_dbg(&t->pdev.dev, "%s family: %d cmd: %d: func: %d input length: %d\n",
134262306a36Sopenharmony_ci			nvdimm ? nvdimm_name(nvdimm) : "bus", family, cmd, func,
134362306a36Sopenharmony_ci			len);
134462306a36Sopenharmony_ci	print_hex_dump_debug("nvdimm in  ", DUMP_PREFIX_OFFSET, 16, 4,
134562306a36Sopenharmony_ci			buf, min(len, 256u), true);
134662306a36Sopenharmony_ci}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_cistatic int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
134962306a36Sopenharmony_ci		struct nvdimm *nvdimm, unsigned int cmd, void *buf,
135062306a36Sopenharmony_ci		unsigned int buf_len, int *cmd_rc)
135162306a36Sopenharmony_ci{
135262306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
135362306a36Sopenharmony_ci	struct nfit_test *t = container_of(acpi_desc, typeof(*t), acpi_desc);
135462306a36Sopenharmony_ci	unsigned int func = cmd;
135562306a36Sopenharmony_ci	int i, rc = 0, __cmd_rc;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	if (!cmd_rc)
135862306a36Sopenharmony_ci		cmd_rc = &__cmd_rc;
135962306a36Sopenharmony_ci	*cmd_rc = 0;
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	nfit_ctl_dbg(acpi_desc, nvdimm, cmd, buf, buf_len);
136262306a36Sopenharmony_ci
136362306a36Sopenharmony_ci	if (nvdimm) {
136462306a36Sopenharmony_ci		struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
136562306a36Sopenharmony_ci		unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci		if (!nfit_mem)
136862306a36Sopenharmony_ci			return -ENOTTY;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci		if (cmd == ND_CMD_CALL) {
137162306a36Sopenharmony_ci			struct nd_cmd_pkg *call_pkg = buf;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci			buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out;
137462306a36Sopenharmony_ci			buf = (void *) call_pkg->nd_payload;
137562306a36Sopenharmony_ci			func = call_pkg->nd_command;
137662306a36Sopenharmony_ci			if (call_pkg->nd_family != nfit_mem->family)
137762306a36Sopenharmony_ci				return -ENOTTY;
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_ci			i = get_dimm(nfit_mem, func);
138062306a36Sopenharmony_ci			if (i < 0)
138162306a36Sopenharmony_ci				return i;
138262306a36Sopenharmony_ci			if (i >= NUM_DCR) {
138362306a36Sopenharmony_ci				dev_WARN_ONCE(&t->pdev.dev, 1,
138462306a36Sopenharmony_ci						"ND_CMD_CALL only valid for nfit_test0\n");
138562306a36Sopenharmony_ci				return -EINVAL;
138662306a36Sopenharmony_ci			}
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci			switch (func) {
138962306a36Sopenharmony_ci			case NVDIMM_INTEL_GET_SECURITY_STATE:
139062306a36Sopenharmony_ci				rc = nd_intel_test_cmd_security_status(t,
139162306a36Sopenharmony_ci						buf, buf_len, i);
139262306a36Sopenharmony_ci				break;
139362306a36Sopenharmony_ci			case NVDIMM_INTEL_UNLOCK_UNIT:
139462306a36Sopenharmony_ci				rc = nd_intel_test_cmd_unlock_unit(t,
139562306a36Sopenharmony_ci						buf, buf_len, i);
139662306a36Sopenharmony_ci				break;
139762306a36Sopenharmony_ci			case NVDIMM_INTEL_SET_PASSPHRASE:
139862306a36Sopenharmony_ci				rc = nd_intel_test_cmd_set_pass(t,
139962306a36Sopenharmony_ci						buf, buf_len, i);
140062306a36Sopenharmony_ci				break;
140162306a36Sopenharmony_ci			case NVDIMM_INTEL_DISABLE_PASSPHRASE:
140262306a36Sopenharmony_ci				rc = nd_intel_test_cmd_disable_pass(t,
140362306a36Sopenharmony_ci						buf, buf_len, i);
140462306a36Sopenharmony_ci				break;
140562306a36Sopenharmony_ci			case NVDIMM_INTEL_FREEZE_LOCK:
140662306a36Sopenharmony_ci				rc = nd_intel_test_cmd_freeze_lock(t,
140762306a36Sopenharmony_ci						buf, buf_len, i);
140862306a36Sopenharmony_ci				break;
140962306a36Sopenharmony_ci			case NVDIMM_INTEL_SECURE_ERASE:
141062306a36Sopenharmony_ci				rc = nd_intel_test_cmd_secure_erase(t,
141162306a36Sopenharmony_ci						buf, buf_len, i);
141262306a36Sopenharmony_ci				break;
141362306a36Sopenharmony_ci			case NVDIMM_INTEL_OVERWRITE:
141462306a36Sopenharmony_ci				rc = nd_intel_test_cmd_overwrite(t,
141562306a36Sopenharmony_ci						buf, buf_len, i);
141662306a36Sopenharmony_ci				break;
141762306a36Sopenharmony_ci			case NVDIMM_INTEL_QUERY_OVERWRITE:
141862306a36Sopenharmony_ci				rc = nd_intel_test_cmd_query_overwrite(t,
141962306a36Sopenharmony_ci						buf, buf_len, i);
142062306a36Sopenharmony_ci				break;
142162306a36Sopenharmony_ci			case NVDIMM_INTEL_SET_MASTER_PASSPHRASE:
142262306a36Sopenharmony_ci				rc = nd_intel_test_cmd_master_set_pass(t,
142362306a36Sopenharmony_ci						buf, buf_len, i);
142462306a36Sopenharmony_ci				break;
142562306a36Sopenharmony_ci			case NVDIMM_INTEL_MASTER_SECURE_ERASE:
142662306a36Sopenharmony_ci				rc = nd_intel_test_cmd_master_secure_erase(t,
142762306a36Sopenharmony_ci						buf, buf_len, i);
142862306a36Sopenharmony_ci				break;
142962306a36Sopenharmony_ci			case NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO:
143062306a36Sopenharmony_ci				rc = nd_intel_test_cmd_fw_activate_dimminfo(
143162306a36Sopenharmony_ci					t, buf, buf_len, i);
143262306a36Sopenharmony_ci				break;
143362306a36Sopenharmony_ci			case NVDIMM_INTEL_FW_ACTIVATE_ARM:
143462306a36Sopenharmony_ci				rc = nd_intel_test_cmd_fw_activate_arm(
143562306a36Sopenharmony_ci					t, buf, buf_len, i);
143662306a36Sopenharmony_ci				break;
143762306a36Sopenharmony_ci			case ND_INTEL_ENABLE_LSS_STATUS:
143862306a36Sopenharmony_ci				rc = nd_intel_test_cmd_set_lss_status(t,
143962306a36Sopenharmony_ci						buf, buf_len);
144062306a36Sopenharmony_ci				break;
144162306a36Sopenharmony_ci			case ND_INTEL_FW_GET_INFO:
144262306a36Sopenharmony_ci				rc = nd_intel_test_get_fw_info(t, buf,
144362306a36Sopenharmony_ci						buf_len, i);
144462306a36Sopenharmony_ci				break;
144562306a36Sopenharmony_ci			case ND_INTEL_FW_START_UPDATE:
144662306a36Sopenharmony_ci				rc = nd_intel_test_start_update(t, buf,
144762306a36Sopenharmony_ci						buf_len, i);
144862306a36Sopenharmony_ci				break;
144962306a36Sopenharmony_ci			case ND_INTEL_FW_SEND_DATA:
145062306a36Sopenharmony_ci				rc = nd_intel_test_send_data(t, buf,
145162306a36Sopenharmony_ci						buf_len, i);
145262306a36Sopenharmony_ci				break;
145362306a36Sopenharmony_ci			case ND_INTEL_FW_FINISH_UPDATE:
145462306a36Sopenharmony_ci				rc = nd_intel_test_finish_fw(t, buf,
145562306a36Sopenharmony_ci						buf_len, i);
145662306a36Sopenharmony_ci				break;
145762306a36Sopenharmony_ci			case ND_INTEL_FW_FINISH_QUERY:
145862306a36Sopenharmony_ci				rc = nd_intel_test_finish_query(t, buf,
145962306a36Sopenharmony_ci						buf_len, i);
146062306a36Sopenharmony_ci				break;
146162306a36Sopenharmony_ci			case ND_INTEL_SMART:
146262306a36Sopenharmony_ci				rc = nfit_test_cmd_smart(buf, buf_len,
146362306a36Sopenharmony_ci						&t->smart[i]);
146462306a36Sopenharmony_ci				break;
146562306a36Sopenharmony_ci			case ND_INTEL_SMART_THRESHOLD:
146662306a36Sopenharmony_ci				rc = nfit_test_cmd_smart_threshold(buf,
146762306a36Sopenharmony_ci						buf_len,
146862306a36Sopenharmony_ci						&t->smart_threshold[i]);
146962306a36Sopenharmony_ci				break;
147062306a36Sopenharmony_ci			case ND_INTEL_SMART_SET_THRESHOLD:
147162306a36Sopenharmony_ci				rc = nfit_test_cmd_smart_set_threshold(buf,
147262306a36Sopenharmony_ci						buf_len,
147362306a36Sopenharmony_ci						&t->smart_threshold[i],
147462306a36Sopenharmony_ci						&t->smart[i],
147562306a36Sopenharmony_ci						&t->pdev.dev, t->dimm_dev[i]);
147662306a36Sopenharmony_ci				break;
147762306a36Sopenharmony_ci			case ND_INTEL_SMART_INJECT:
147862306a36Sopenharmony_ci				rc = nfit_test_cmd_smart_inject(buf,
147962306a36Sopenharmony_ci						buf_len,
148062306a36Sopenharmony_ci						&t->smart_threshold[i],
148162306a36Sopenharmony_ci						&t->smart[i],
148262306a36Sopenharmony_ci						&t->pdev.dev, t->dimm_dev[i]);
148362306a36Sopenharmony_ci				break;
148462306a36Sopenharmony_ci			default:
148562306a36Sopenharmony_ci				return -ENOTTY;
148662306a36Sopenharmony_ci			}
148762306a36Sopenharmony_ci			return override_return_code(i, func, rc);
148862306a36Sopenharmony_ci		}
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci		if (!test_bit(cmd, &cmd_mask)
149162306a36Sopenharmony_ci				|| !test_bit(func, &nfit_mem->dsm_mask))
149262306a36Sopenharmony_ci			return -ENOTTY;
149362306a36Sopenharmony_ci
149462306a36Sopenharmony_ci		i = get_dimm(nfit_mem, func);
149562306a36Sopenharmony_ci		if (i < 0)
149662306a36Sopenharmony_ci			return i;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci		switch (func) {
149962306a36Sopenharmony_ci		case ND_CMD_GET_CONFIG_SIZE:
150062306a36Sopenharmony_ci			rc = nfit_test_cmd_get_config_size(buf, buf_len);
150162306a36Sopenharmony_ci			break;
150262306a36Sopenharmony_ci		case ND_CMD_GET_CONFIG_DATA:
150362306a36Sopenharmony_ci			rc = nfit_test_cmd_get_config_data(buf, buf_len,
150462306a36Sopenharmony_ci				t->label[i - t->dcr_idx]);
150562306a36Sopenharmony_ci			break;
150662306a36Sopenharmony_ci		case ND_CMD_SET_CONFIG_DATA:
150762306a36Sopenharmony_ci			rc = nfit_test_cmd_set_config_data(buf, buf_len,
150862306a36Sopenharmony_ci				t->label[i - t->dcr_idx]);
150962306a36Sopenharmony_ci			break;
151062306a36Sopenharmony_ci		default:
151162306a36Sopenharmony_ci			return -ENOTTY;
151262306a36Sopenharmony_ci		}
151362306a36Sopenharmony_ci		return override_return_code(i, func, rc);
151462306a36Sopenharmony_ci	} else {
151562306a36Sopenharmony_ci		struct ars_state *ars_state = &t->ars_state;
151662306a36Sopenharmony_ci		struct nd_cmd_pkg *call_pkg = buf;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci		if (!nd_desc)
151962306a36Sopenharmony_ci			return -ENOTTY;
152062306a36Sopenharmony_ci
152162306a36Sopenharmony_ci		if (cmd == ND_CMD_CALL && call_pkg->nd_family
152262306a36Sopenharmony_ci				== NVDIMM_BUS_FAMILY_NFIT) {
152362306a36Sopenharmony_ci			func = call_pkg->nd_command;
152462306a36Sopenharmony_ci			buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out;
152562306a36Sopenharmony_ci			buf = (void *) call_pkg->nd_payload;
152662306a36Sopenharmony_ci
152762306a36Sopenharmony_ci			switch (func) {
152862306a36Sopenharmony_ci			case NFIT_CMD_TRANSLATE_SPA:
152962306a36Sopenharmony_ci				rc = nfit_test_cmd_translate_spa(
153062306a36Sopenharmony_ci					acpi_desc->nvdimm_bus, buf, buf_len);
153162306a36Sopenharmony_ci				return rc;
153262306a36Sopenharmony_ci			case NFIT_CMD_ARS_INJECT_SET:
153362306a36Sopenharmony_ci				rc = nfit_test_cmd_ars_error_inject(t, buf,
153462306a36Sopenharmony_ci					buf_len);
153562306a36Sopenharmony_ci				return rc;
153662306a36Sopenharmony_ci			case NFIT_CMD_ARS_INJECT_CLEAR:
153762306a36Sopenharmony_ci				rc = nfit_test_cmd_ars_inject_clear(t, buf,
153862306a36Sopenharmony_ci					buf_len);
153962306a36Sopenharmony_ci				return rc;
154062306a36Sopenharmony_ci			case NFIT_CMD_ARS_INJECT_GET:
154162306a36Sopenharmony_ci				rc = nfit_test_cmd_ars_inject_status(t, buf,
154262306a36Sopenharmony_ci					buf_len);
154362306a36Sopenharmony_ci				return rc;
154462306a36Sopenharmony_ci			default:
154562306a36Sopenharmony_ci				return -ENOTTY;
154662306a36Sopenharmony_ci			}
154762306a36Sopenharmony_ci		} else if (cmd == ND_CMD_CALL && call_pkg->nd_family
154862306a36Sopenharmony_ci				== NVDIMM_BUS_FAMILY_INTEL) {
154962306a36Sopenharmony_ci			func = call_pkg->nd_command;
155062306a36Sopenharmony_ci			buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out;
155162306a36Sopenharmony_ci			buf = (void *) call_pkg->nd_payload;
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci			switch (func) {
155462306a36Sopenharmony_ci			case NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO:
155562306a36Sopenharmony_ci				rc = nvdimm_bus_intel_fw_activate_businfo(t,
155662306a36Sopenharmony_ci						buf, buf_len);
155762306a36Sopenharmony_ci				return rc;
155862306a36Sopenharmony_ci			case NVDIMM_BUS_INTEL_FW_ACTIVATE:
155962306a36Sopenharmony_ci				rc = nvdimm_bus_intel_fw_activate(t, buf,
156062306a36Sopenharmony_ci						buf_len);
156162306a36Sopenharmony_ci				return rc;
156262306a36Sopenharmony_ci			default:
156362306a36Sopenharmony_ci				return -ENOTTY;
156462306a36Sopenharmony_ci			}
156562306a36Sopenharmony_ci		} else if (cmd == ND_CMD_CALL)
156662306a36Sopenharmony_ci			return -ENOTTY;
156762306a36Sopenharmony_ci
156862306a36Sopenharmony_ci		if (!nd_desc || !test_bit(cmd, &nd_desc->cmd_mask))
156962306a36Sopenharmony_ci			return -ENOTTY;
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci		switch (func) {
157262306a36Sopenharmony_ci		case ND_CMD_ARS_CAP:
157362306a36Sopenharmony_ci			rc = nfit_test_cmd_ars_cap(buf, buf_len);
157462306a36Sopenharmony_ci			break;
157562306a36Sopenharmony_ci		case ND_CMD_ARS_START:
157662306a36Sopenharmony_ci			rc = nfit_test_cmd_ars_start(t, ars_state, buf,
157762306a36Sopenharmony_ci					buf_len, cmd_rc);
157862306a36Sopenharmony_ci			break;
157962306a36Sopenharmony_ci		case ND_CMD_ARS_STATUS:
158062306a36Sopenharmony_ci			rc = nfit_test_cmd_ars_status(ars_state, buf, buf_len,
158162306a36Sopenharmony_ci					cmd_rc);
158262306a36Sopenharmony_ci			break;
158362306a36Sopenharmony_ci		case ND_CMD_CLEAR_ERROR:
158462306a36Sopenharmony_ci			rc = nfit_test_cmd_clear_error(t, buf, buf_len, cmd_rc);
158562306a36Sopenharmony_ci			break;
158662306a36Sopenharmony_ci		default:
158762306a36Sopenharmony_ci			return -ENOTTY;
158862306a36Sopenharmony_ci		}
158962306a36Sopenharmony_ci	}
159062306a36Sopenharmony_ci
159162306a36Sopenharmony_ci	return rc;
159262306a36Sopenharmony_ci}
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(nfit_test_lock);
159562306a36Sopenharmony_cistatic struct nfit_test *instances[NUM_NFITS];
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_cistatic void release_nfit_res(void *data)
159862306a36Sopenharmony_ci{
159962306a36Sopenharmony_ci	struct nfit_test_resource *nfit_res = data;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	spin_lock(&nfit_test_lock);
160262306a36Sopenharmony_ci	list_del(&nfit_res->list);
160362306a36Sopenharmony_ci	spin_unlock(&nfit_test_lock);
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	if (resource_size(&nfit_res->res) >= DIMM_SIZE)
160662306a36Sopenharmony_ci		gen_pool_free(nfit_pool, nfit_res->res.start,
160762306a36Sopenharmony_ci				resource_size(&nfit_res->res));
160862306a36Sopenharmony_ci	vfree(nfit_res->buf);
160962306a36Sopenharmony_ci	kfree(nfit_res);
161062306a36Sopenharmony_ci}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_cistatic void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma,
161362306a36Sopenharmony_ci		void *buf)
161462306a36Sopenharmony_ci{
161562306a36Sopenharmony_ci	struct device *dev = &t->pdev.dev;
161662306a36Sopenharmony_ci	struct nfit_test_resource *nfit_res = kzalloc(sizeof(*nfit_res),
161762306a36Sopenharmony_ci			GFP_KERNEL);
161862306a36Sopenharmony_ci	int rc;
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	if (!buf || !nfit_res || !*dma)
162162306a36Sopenharmony_ci		goto err;
162262306a36Sopenharmony_ci	rc = devm_add_action(dev, release_nfit_res, nfit_res);
162362306a36Sopenharmony_ci	if (rc)
162462306a36Sopenharmony_ci		goto err;
162562306a36Sopenharmony_ci	INIT_LIST_HEAD(&nfit_res->list);
162662306a36Sopenharmony_ci	memset(buf, 0, size);
162762306a36Sopenharmony_ci	nfit_res->dev = dev;
162862306a36Sopenharmony_ci	nfit_res->buf = buf;
162962306a36Sopenharmony_ci	nfit_res->res.start = *dma;
163062306a36Sopenharmony_ci	nfit_res->res.end = *dma + size - 1;
163162306a36Sopenharmony_ci	nfit_res->res.name = "NFIT";
163262306a36Sopenharmony_ci	spin_lock_init(&nfit_res->lock);
163362306a36Sopenharmony_ci	INIT_LIST_HEAD(&nfit_res->requests);
163462306a36Sopenharmony_ci	spin_lock(&nfit_test_lock);
163562306a36Sopenharmony_ci	list_add(&nfit_res->list, &t->resources);
163662306a36Sopenharmony_ci	spin_unlock(&nfit_test_lock);
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	return nfit_res->buf;
163962306a36Sopenharmony_ci err:
164062306a36Sopenharmony_ci	if (*dma && size >= DIMM_SIZE)
164162306a36Sopenharmony_ci		gen_pool_free(nfit_pool, *dma, size);
164262306a36Sopenharmony_ci	if (buf)
164362306a36Sopenharmony_ci		vfree(buf);
164462306a36Sopenharmony_ci	kfree(nfit_res);
164562306a36Sopenharmony_ci	return NULL;
164662306a36Sopenharmony_ci}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_cistatic void *test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma)
164962306a36Sopenharmony_ci{
165062306a36Sopenharmony_ci	struct genpool_data_align data = {
165162306a36Sopenharmony_ci		.align = SZ_128M,
165262306a36Sopenharmony_ci	};
165362306a36Sopenharmony_ci	void *buf = vmalloc(size);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	if (size >= DIMM_SIZE)
165662306a36Sopenharmony_ci		*dma = gen_pool_alloc_algo(nfit_pool, size,
165762306a36Sopenharmony_ci				gen_pool_first_fit_align, &data);
165862306a36Sopenharmony_ci	else
165962306a36Sopenharmony_ci		*dma = (unsigned long) buf;
166062306a36Sopenharmony_ci	return __test_alloc(t, size, dma, buf);
166162306a36Sopenharmony_ci}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_cistatic struct nfit_test_resource *nfit_test_lookup(resource_size_t addr)
166462306a36Sopenharmony_ci{
166562306a36Sopenharmony_ci	int i;
166662306a36Sopenharmony_ci
166762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(instances); i++) {
166862306a36Sopenharmony_ci		struct nfit_test_resource *n, *nfit_res = NULL;
166962306a36Sopenharmony_ci		struct nfit_test *t = instances[i];
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci		if (!t)
167262306a36Sopenharmony_ci			continue;
167362306a36Sopenharmony_ci		spin_lock(&nfit_test_lock);
167462306a36Sopenharmony_ci		list_for_each_entry(n, &t->resources, list) {
167562306a36Sopenharmony_ci			if (addr >= n->res.start && (addr < n->res.start
167662306a36Sopenharmony_ci						+ resource_size(&n->res))) {
167762306a36Sopenharmony_ci				nfit_res = n;
167862306a36Sopenharmony_ci				break;
167962306a36Sopenharmony_ci			} else if (addr >= (unsigned long) n->buf
168062306a36Sopenharmony_ci					&& (addr < (unsigned long) n->buf
168162306a36Sopenharmony_ci						+ resource_size(&n->res))) {
168262306a36Sopenharmony_ci				nfit_res = n;
168362306a36Sopenharmony_ci				break;
168462306a36Sopenharmony_ci			}
168562306a36Sopenharmony_ci		}
168662306a36Sopenharmony_ci		spin_unlock(&nfit_test_lock);
168762306a36Sopenharmony_ci		if (nfit_res)
168862306a36Sopenharmony_ci			return nfit_res;
168962306a36Sopenharmony_ci	}
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	return NULL;
169262306a36Sopenharmony_ci}
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_cistatic int ars_state_init(struct device *dev, struct ars_state *ars_state)
169562306a36Sopenharmony_ci{
169662306a36Sopenharmony_ci	/* for testing, only store up to n records that fit within 4k */
169762306a36Sopenharmony_ci	ars_state->ars_status = devm_kzalloc(dev,
169862306a36Sopenharmony_ci			sizeof(struct nd_cmd_ars_status) + SZ_4K, GFP_KERNEL);
169962306a36Sopenharmony_ci	if (!ars_state->ars_status)
170062306a36Sopenharmony_ci		return -ENOMEM;
170162306a36Sopenharmony_ci	spin_lock_init(&ars_state->lock);
170262306a36Sopenharmony_ci	return 0;
170362306a36Sopenharmony_ci}
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_cistatic void put_dimms(void *data)
170662306a36Sopenharmony_ci{
170762306a36Sopenharmony_ci	struct nfit_test *t = data;
170862306a36Sopenharmony_ci	int i;
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_ci	for (i = 0; i < t->num_dcr; i++)
171162306a36Sopenharmony_ci		if (t->dimm_dev[i])
171262306a36Sopenharmony_ci			device_unregister(t->dimm_dev[i]);
171362306a36Sopenharmony_ci}
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_cistatic struct class *nfit_test_dimm;
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_cistatic int dimm_name_to_id(struct device *dev)
171862306a36Sopenharmony_ci{
171962306a36Sopenharmony_ci	int dimm;
172062306a36Sopenharmony_ci
172162306a36Sopenharmony_ci	if (sscanf(dev_name(dev), "test_dimm%d", &dimm) != 1)
172262306a36Sopenharmony_ci		return -ENXIO;
172362306a36Sopenharmony_ci	return dimm;
172462306a36Sopenharmony_ci}
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_cistatic ssize_t handle_show(struct device *dev, struct device_attribute *attr,
172762306a36Sopenharmony_ci		char *buf)
172862306a36Sopenharmony_ci{
172962306a36Sopenharmony_ci	int dimm = dimm_name_to_id(dev);
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	if (dimm < 0)
173262306a36Sopenharmony_ci		return dimm;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	return sprintf(buf, "%#x\n", handle[dimm]);
173562306a36Sopenharmony_ci}
173662306a36Sopenharmony_ciDEVICE_ATTR_RO(handle);
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_cistatic ssize_t fail_cmd_show(struct device *dev, struct device_attribute *attr,
173962306a36Sopenharmony_ci		char *buf)
174062306a36Sopenharmony_ci{
174162306a36Sopenharmony_ci	int dimm = dimm_name_to_id(dev);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	if (dimm < 0)
174462306a36Sopenharmony_ci		return dimm;
174562306a36Sopenharmony_ci
174662306a36Sopenharmony_ci	return sprintf(buf, "%#lx\n", dimm_fail_cmd_flags[dimm]);
174762306a36Sopenharmony_ci}
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_cistatic ssize_t fail_cmd_store(struct device *dev, struct device_attribute *attr,
175062306a36Sopenharmony_ci		const char *buf, size_t size)
175162306a36Sopenharmony_ci{
175262306a36Sopenharmony_ci	int dimm = dimm_name_to_id(dev);
175362306a36Sopenharmony_ci	unsigned long val;
175462306a36Sopenharmony_ci	ssize_t rc;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	if (dimm < 0)
175762306a36Sopenharmony_ci		return dimm;
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	rc = kstrtol(buf, 0, &val);
176062306a36Sopenharmony_ci	if (rc)
176162306a36Sopenharmony_ci		return rc;
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	dimm_fail_cmd_flags[dimm] = val;
176462306a36Sopenharmony_ci	return size;
176562306a36Sopenharmony_ci}
176662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(fail_cmd);
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_cistatic ssize_t fail_cmd_code_show(struct device *dev, struct device_attribute *attr,
176962306a36Sopenharmony_ci		char *buf)
177062306a36Sopenharmony_ci{
177162306a36Sopenharmony_ci	int dimm = dimm_name_to_id(dev);
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	if (dimm < 0)
177462306a36Sopenharmony_ci		return dimm;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	return sprintf(buf, "%d\n", dimm_fail_cmd_code[dimm]);
177762306a36Sopenharmony_ci}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_cistatic ssize_t fail_cmd_code_store(struct device *dev, struct device_attribute *attr,
178062306a36Sopenharmony_ci		const char *buf, size_t size)
178162306a36Sopenharmony_ci{
178262306a36Sopenharmony_ci	int dimm = dimm_name_to_id(dev);
178362306a36Sopenharmony_ci	unsigned long val;
178462306a36Sopenharmony_ci	ssize_t rc;
178562306a36Sopenharmony_ci
178662306a36Sopenharmony_ci	if (dimm < 0)
178762306a36Sopenharmony_ci		return dimm;
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci	rc = kstrtol(buf, 0, &val);
179062306a36Sopenharmony_ci	if (rc)
179162306a36Sopenharmony_ci		return rc;
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	dimm_fail_cmd_code[dimm] = val;
179462306a36Sopenharmony_ci	return size;
179562306a36Sopenharmony_ci}
179662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(fail_cmd_code);
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_cistatic ssize_t lock_dimm_store(struct device *dev,
179962306a36Sopenharmony_ci		struct device_attribute *attr, const char *buf, size_t size)
180062306a36Sopenharmony_ci{
180162306a36Sopenharmony_ci	int dimm = dimm_name_to_id(dev);
180262306a36Sopenharmony_ci	struct nfit_test_sec *sec = &dimm_sec_info[dimm];
180362306a36Sopenharmony_ci
180462306a36Sopenharmony_ci	sec->state = ND_INTEL_SEC_STATE_ENABLED | ND_INTEL_SEC_STATE_LOCKED;
180562306a36Sopenharmony_ci	return size;
180662306a36Sopenharmony_ci}
180762306a36Sopenharmony_cistatic DEVICE_ATTR_WO(lock_dimm);
180862306a36Sopenharmony_ci
180962306a36Sopenharmony_cistatic struct attribute *nfit_test_dimm_attributes[] = {
181062306a36Sopenharmony_ci	&dev_attr_fail_cmd.attr,
181162306a36Sopenharmony_ci	&dev_attr_fail_cmd_code.attr,
181262306a36Sopenharmony_ci	&dev_attr_handle.attr,
181362306a36Sopenharmony_ci	&dev_attr_lock_dimm.attr,
181462306a36Sopenharmony_ci	NULL,
181562306a36Sopenharmony_ci};
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_cistatic struct attribute_group nfit_test_dimm_attribute_group = {
181862306a36Sopenharmony_ci	.attrs = nfit_test_dimm_attributes,
181962306a36Sopenharmony_ci};
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_cistatic const struct attribute_group *nfit_test_dimm_attribute_groups[] = {
182262306a36Sopenharmony_ci	&nfit_test_dimm_attribute_group,
182362306a36Sopenharmony_ci	NULL,
182462306a36Sopenharmony_ci};
182562306a36Sopenharmony_ci
182662306a36Sopenharmony_cistatic int nfit_test_dimm_init(struct nfit_test *t)
182762306a36Sopenharmony_ci{
182862306a36Sopenharmony_ci	int i;
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ci	if (devm_add_action_or_reset(&t->pdev.dev, put_dimms, t))
183162306a36Sopenharmony_ci		return -ENOMEM;
183262306a36Sopenharmony_ci	for (i = 0; i < t->num_dcr; i++) {
183362306a36Sopenharmony_ci		t->dimm_dev[i] = device_create_with_groups(nfit_test_dimm,
183462306a36Sopenharmony_ci				&t->pdev.dev, 0, NULL,
183562306a36Sopenharmony_ci				nfit_test_dimm_attribute_groups,
183662306a36Sopenharmony_ci				"test_dimm%d", i + t->dcr_idx);
183762306a36Sopenharmony_ci		if (!t->dimm_dev[i])
183862306a36Sopenharmony_ci			return -ENOMEM;
183962306a36Sopenharmony_ci	}
184062306a36Sopenharmony_ci	return 0;
184162306a36Sopenharmony_ci}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_cistatic void nfit_security_init(struct nfit_test *t)
184462306a36Sopenharmony_ci{
184562306a36Sopenharmony_ci	int i;
184662306a36Sopenharmony_ci
184762306a36Sopenharmony_ci	for (i = 0; i < t->num_dcr; i++) {
184862306a36Sopenharmony_ci		struct nfit_test_sec *sec = &dimm_sec_info[i];
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci		sec->ext_state = ND_INTEL_SEC_ESTATE_ENABLED;
185162306a36Sopenharmony_ci	}
185262306a36Sopenharmony_ci}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_cistatic void smart_init(struct nfit_test *t)
185562306a36Sopenharmony_ci{
185662306a36Sopenharmony_ci	int i;
185762306a36Sopenharmony_ci	const struct nd_intel_smart_threshold smart_t_data = {
185862306a36Sopenharmony_ci		.alarm_control = ND_INTEL_SMART_SPARE_TRIP
185962306a36Sopenharmony_ci			| ND_INTEL_SMART_TEMP_TRIP,
186062306a36Sopenharmony_ci		.media_temperature = 40 * 16,
186162306a36Sopenharmony_ci		.ctrl_temperature = 30 * 16,
186262306a36Sopenharmony_ci		.spares = 5,
186362306a36Sopenharmony_ci	};
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci	for (i = 0; i < t->num_dcr; i++) {
186662306a36Sopenharmony_ci		memcpy(&t->smart[i], &smart_def, sizeof(smart_def));
186762306a36Sopenharmony_ci		memcpy(&t->smart_threshold[i], &smart_t_data,
186862306a36Sopenharmony_ci				sizeof(smart_t_data));
186962306a36Sopenharmony_ci	}
187062306a36Sopenharmony_ci}
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_cistatic size_t sizeof_spa(struct acpi_nfit_system_address *spa)
187362306a36Sopenharmony_ci{
187462306a36Sopenharmony_ci	/* until spa location cookie support is added... */
187562306a36Sopenharmony_ci	return sizeof(*spa) - 8;
187662306a36Sopenharmony_ci}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_cistatic int nfit_test0_alloc(struct nfit_test *t)
187962306a36Sopenharmony_ci{
188062306a36Sopenharmony_ci	struct acpi_nfit_system_address *spa = NULL;
188162306a36Sopenharmony_ci	struct acpi_nfit_flush_address *flush;
188262306a36Sopenharmony_ci	size_t nfit_size = sizeof_spa(spa) * NUM_SPA
188362306a36Sopenharmony_ci			+ sizeof(struct acpi_nfit_memory_map) * NUM_MEM
188462306a36Sopenharmony_ci			+ sizeof(struct acpi_nfit_control_region) * NUM_DCR
188562306a36Sopenharmony_ci			+ offsetof(struct acpi_nfit_control_region,
188662306a36Sopenharmony_ci					window_size) * NUM_DCR
188762306a36Sopenharmony_ci			+ sizeof(struct acpi_nfit_data_region) * NUM_BDW
188862306a36Sopenharmony_ci			+ struct_size(flush, hint_address, NUM_HINTS) * NUM_DCR
188962306a36Sopenharmony_ci			+ sizeof(struct acpi_nfit_capabilities);
189062306a36Sopenharmony_ci	int i;
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	t->nfit_buf = test_alloc(t, nfit_size, &t->nfit_dma);
189362306a36Sopenharmony_ci	if (!t->nfit_buf)
189462306a36Sopenharmony_ci		return -ENOMEM;
189562306a36Sopenharmony_ci	t->nfit_size = nfit_size;
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	t->spa_set[0] = test_alloc(t, SPA0_SIZE, &t->spa_set_dma[0]);
189862306a36Sopenharmony_ci	if (!t->spa_set[0])
189962306a36Sopenharmony_ci		return -ENOMEM;
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	t->spa_set[1] = test_alloc(t, SPA1_SIZE, &t->spa_set_dma[1]);
190262306a36Sopenharmony_ci	if (!t->spa_set[1])
190362306a36Sopenharmony_ci		return -ENOMEM;
190462306a36Sopenharmony_ci
190562306a36Sopenharmony_ci	t->spa_set[2] = test_alloc(t, SPA0_SIZE, &t->spa_set_dma[2]);
190662306a36Sopenharmony_ci	if (!t->spa_set[2])
190762306a36Sopenharmony_ci		return -ENOMEM;
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	for (i = 0; i < t->num_dcr; i++) {
191062306a36Sopenharmony_ci		t->dimm[i] = test_alloc(t, DIMM_SIZE, &t->dimm_dma[i]);
191162306a36Sopenharmony_ci		if (!t->dimm[i])
191262306a36Sopenharmony_ci			return -ENOMEM;
191362306a36Sopenharmony_ci
191462306a36Sopenharmony_ci		t->label[i] = test_alloc(t, LABEL_SIZE, &t->label_dma[i]);
191562306a36Sopenharmony_ci		if (!t->label[i])
191662306a36Sopenharmony_ci			return -ENOMEM;
191762306a36Sopenharmony_ci		sprintf(t->label[i], "label%d", i);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci		t->flush[i] = test_alloc(t, max(PAGE_SIZE,
192062306a36Sopenharmony_ci					sizeof(u64) * NUM_HINTS),
192162306a36Sopenharmony_ci				&t->flush_dma[i]);
192262306a36Sopenharmony_ci		if (!t->flush[i])
192362306a36Sopenharmony_ci			return -ENOMEM;
192462306a36Sopenharmony_ci	}
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	for (i = 0; i < t->num_dcr; i++) {
192762306a36Sopenharmony_ci		t->dcr[i] = test_alloc(t, LABEL_SIZE, &t->dcr_dma[i]);
192862306a36Sopenharmony_ci		if (!t->dcr[i])
192962306a36Sopenharmony_ci			return -ENOMEM;
193062306a36Sopenharmony_ci	}
193162306a36Sopenharmony_ci
193262306a36Sopenharmony_ci	t->_fit = test_alloc(t, sizeof(union acpi_object **), &t->_fit_dma);
193362306a36Sopenharmony_ci	if (!t->_fit)
193462306a36Sopenharmony_ci		return -ENOMEM;
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci	if (nfit_test_dimm_init(t))
193762306a36Sopenharmony_ci		return -ENOMEM;
193862306a36Sopenharmony_ci	smart_init(t);
193962306a36Sopenharmony_ci	nfit_security_init(t);
194062306a36Sopenharmony_ci	return ars_state_init(&t->pdev.dev, &t->ars_state);
194162306a36Sopenharmony_ci}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_cistatic int nfit_test1_alloc(struct nfit_test *t)
194462306a36Sopenharmony_ci{
194562306a36Sopenharmony_ci	struct acpi_nfit_system_address *spa = NULL;
194662306a36Sopenharmony_ci	size_t nfit_size = sizeof_spa(spa) * 2
194762306a36Sopenharmony_ci		+ sizeof(struct acpi_nfit_memory_map) * 2
194862306a36Sopenharmony_ci		+ offsetof(struct acpi_nfit_control_region, window_size) * 2;
194962306a36Sopenharmony_ci	int i;
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	t->nfit_buf = test_alloc(t, nfit_size, &t->nfit_dma);
195262306a36Sopenharmony_ci	if (!t->nfit_buf)
195362306a36Sopenharmony_ci		return -ENOMEM;
195462306a36Sopenharmony_ci	t->nfit_size = nfit_size;
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	t->spa_set[0] = test_alloc(t, SPA2_SIZE, &t->spa_set_dma[0]);
195762306a36Sopenharmony_ci	if (!t->spa_set[0])
195862306a36Sopenharmony_ci		return -ENOMEM;
195962306a36Sopenharmony_ci
196062306a36Sopenharmony_ci	for (i = 0; i < t->num_dcr; i++) {
196162306a36Sopenharmony_ci		t->label[i] = test_alloc(t, LABEL_SIZE, &t->label_dma[i]);
196262306a36Sopenharmony_ci		if (!t->label[i])
196362306a36Sopenharmony_ci			return -ENOMEM;
196462306a36Sopenharmony_ci		sprintf(t->label[i], "label%d", i);
196562306a36Sopenharmony_ci	}
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_ci	t->spa_set[1] = test_alloc(t, SPA_VCD_SIZE, &t->spa_set_dma[1]);
196862306a36Sopenharmony_ci	if (!t->spa_set[1])
196962306a36Sopenharmony_ci		return -ENOMEM;
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_ci	if (nfit_test_dimm_init(t))
197262306a36Sopenharmony_ci		return -ENOMEM;
197362306a36Sopenharmony_ci	smart_init(t);
197462306a36Sopenharmony_ci	return ars_state_init(&t->pdev.dev, &t->ars_state);
197562306a36Sopenharmony_ci}
197662306a36Sopenharmony_ci
197762306a36Sopenharmony_cistatic void dcr_common_init(struct acpi_nfit_control_region *dcr)
197862306a36Sopenharmony_ci{
197962306a36Sopenharmony_ci	dcr->vendor_id = 0xabcd;
198062306a36Sopenharmony_ci	dcr->device_id = 0;
198162306a36Sopenharmony_ci	dcr->revision_id = 1;
198262306a36Sopenharmony_ci	dcr->valid_fields = 1;
198362306a36Sopenharmony_ci	dcr->manufacturing_location = 0xa;
198462306a36Sopenharmony_ci	dcr->manufacturing_date = cpu_to_be16(2016);
198562306a36Sopenharmony_ci}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_cistatic void nfit_test0_setup(struct nfit_test *t)
198862306a36Sopenharmony_ci{
198962306a36Sopenharmony_ci	const int flush_hint_size = sizeof(struct acpi_nfit_flush_address)
199062306a36Sopenharmony_ci		+ (sizeof(u64) * NUM_HINTS);
199162306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc;
199262306a36Sopenharmony_ci	struct acpi_nfit_memory_map *memdev;
199362306a36Sopenharmony_ci	void *nfit_buf = t->nfit_buf;
199462306a36Sopenharmony_ci	struct acpi_nfit_system_address *spa;
199562306a36Sopenharmony_ci	struct acpi_nfit_control_region *dcr;
199662306a36Sopenharmony_ci	struct acpi_nfit_data_region *bdw;
199762306a36Sopenharmony_ci	struct acpi_nfit_flush_address *flush;
199862306a36Sopenharmony_ci	struct acpi_nfit_capabilities *pcap;
199962306a36Sopenharmony_ci	unsigned int offset = 0, i;
200062306a36Sopenharmony_ci	unsigned long *acpi_mask;
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	/*
200362306a36Sopenharmony_ci	 * spa0 (interleave first half of dimm0 and dimm1, note storage
200462306a36Sopenharmony_ci	 * does not actually alias the related block-data-window
200562306a36Sopenharmony_ci	 * regions)
200662306a36Sopenharmony_ci	 */
200762306a36Sopenharmony_ci	spa = nfit_buf;
200862306a36Sopenharmony_ci	spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
200962306a36Sopenharmony_ci	spa->header.length = sizeof_spa(spa);
201062306a36Sopenharmony_ci	memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16);
201162306a36Sopenharmony_ci	spa->range_index = 0+1;
201262306a36Sopenharmony_ci	spa->address = t->spa_set_dma[0];
201362306a36Sopenharmony_ci	spa->length = SPA0_SIZE;
201462306a36Sopenharmony_ci	offset += spa->header.length;
201562306a36Sopenharmony_ci
201662306a36Sopenharmony_ci	/*
201762306a36Sopenharmony_ci	 * spa1 (interleave last half of the 4 DIMMS, note storage
201862306a36Sopenharmony_ci	 * does not actually alias the related block-data-window
201962306a36Sopenharmony_ci	 * regions)
202062306a36Sopenharmony_ci	 */
202162306a36Sopenharmony_ci	spa = nfit_buf + offset;
202262306a36Sopenharmony_ci	spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
202362306a36Sopenharmony_ci	spa->header.length = sizeof_spa(spa);
202462306a36Sopenharmony_ci	memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16);
202562306a36Sopenharmony_ci	spa->range_index = 1+1;
202662306a36Sopenharmony_ci	spa->address = t->spa_set_dma[1];
202762306a36Sopenharmony_ci	spa->length = SPA1_SIZE;
202862306a36Sopenharmony_ci	offset += spa->header.length;
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci	/* spa2 (dcr0) dimm0 */
203162306a36Sopenharmony_ci	spa = nfit_buf + offset;
203262306a36Sopenharmony_ci	spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
203362306a36Sopenharmony_ci	spa->header.length = sizeof_spa(spa);
203462306a36Sopenharmony_ci	memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16);
203562306a36Sopenharmony_ci	spa->range_index = 2+1;
203662306a36Sopenharmony_ci	spa->address = t->dcr_dma[0];
203762306a36Sopenharmony_ci	spa->length = DCR_SIZE;
203862306a36Sopenharmony_ci	offset += spa->header.length;
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	/* spa3 (dcr1) dimm1 */
204162306a36Sopenharmony_ci	spa = nfit_buf + offset;
204262306a36Sopenharmony_ci	spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
204362306a36Sopenharmony_ci	spa->header.length = sizeof_spa(spa);
204462306a36Sopenharmony_ci	memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16);
204562306a36Sopenharmony_ci	spa->range_index = 3+1;
204662306a36Sopenharmony_ci	spa->address = t->dcr_dma[1];
204762306a36Sopenharmony_ci	spa->length = DCR_SIZE;
204862306a36Sopenharmony_ci	offset += spa->header.length;
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci	/* spa4 (dcr2) dimm2 */
205162306a36Sopenharmony_ci	spa = nfit_buf + offset;
205262306a36Sopenharmony_ci	spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
205362306a36Sopenharmony_ci	spa->header.length = sizeof_spa(spa);
205462306a36Sopenharmony_ci	memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16);
205562306a36Sopenharmony_ci	spa->range_index = 4+1;
205662306a36Sopenharmony_ci	spa->address = t->dcr_dma[2];
205762306a36Sopenharmony_ci	spa->length = DCR_SIZE;
205862306a36Sopenharmony_ci	offset += spa->header.length;
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	/* spa5 (dcr3) dimm3 */
206162306a36Sopenharmony_ci	spa = nfit_buf + offset;
206262306a36Sopenharmony_ci	spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
206362306a36Sopenharmony_ci	spa->header.length = sizeof_spa(spa);
206462306a36Sopenharmony_ci	memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16);
206562306a36Sopenharmony_ci	spa->range_index = 5+1;
206662306a36Sopenharmony_ci	spa->address = t->dcr_dma[3];
206762306a36Sopenharmony_ci	spa->length = DCR_SIZE;
206862306a36Sopenharmony_ci	offset += spa->header.length;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	/* spa6 (bdw for dcr0) dimm0 */
207162306a36Sopenharmony_ci	spa = nfit_buf + offset;
207262306a36Sopenharmony_ci	spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
207362306a36Sopenharmony_ci	spa->header.length = sizeof_spa(spa);
207462306a36Sopenharmony_ci	memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16);
207562306a36Sopenharmony_ci	spa->range_index = 6+1;
207662306a36Sopenharmony_ci	spa->address = t->dimm_dma[0];
207762306a36Sopenharmony_ci	spa->length = DIMM_SIZE;
207862306a36Sopenharmony_ci	offset += spa->header.length;
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_ci	/* spa7 (bdw for dcr1) dimm1 */
208162306a36Sopenharmony_ci	spa = nfit_buf + offset;
208262306a36Sopenharmony_ci	spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
208362306a36Sopenharmony_ci	spa->header.length = sizeof_spa(spa);
208462306a36Sopenharmony_ci	memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16);
208562306a36Sopenharmony_ci	spa->range_index = 7+1;
208662306a36Sopenharmony_ci	spa->address = t->dimm_dma[1];
208762306a36Sopenharmony_ci	spa->length = DIMM_SIZE;
208862306a36Sopenharmony_ci	offset += spa->header.length;
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	/* spa8 (bdw for dcr2) dimm2 */
209162306a36Sopenharmony_ci	spa = nfit_buf + offset;
209262306a36Sopenharmony_ci	spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
209362306a36Sopenharmony_ci	spa->header.length = sizeof_spa(spa);
209462306a36Sopenharmony_ci	memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16);
209562306a36Sopenharmony_ci	spa->range_index = 8+1;
209662306a36Sopenharmony_ci	spa->address = t->dimm_dma[2];
209762306a36Sopenharmony_ci	spa->length = DIMM_SIZE;
209862306a36Sopenharmony_ci	offset += spa->header.length;
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	/* spa9 (bdw for dcr3) dimm3 */
210162306a36Sopenharmony_ci	spa = nfit_buf + offset;
210262306a36Sopenharmony_ci	spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
210362306a36Sopenharmony_ci	spa->header.length = sizeof_spa(spa);
210462306a36Sopenharmony_ci	memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16);
210562306a36Sopenharmony_ci	spa->range_index = 9+1;
210662306a36Sopenharmony_ci	spa->address = t->dimm_dma[3];
210762306a36Sopenharmony_ci	spa->length = DIMM_SIZE;
210862306a36Sopenharmony_ci	offset += spa->header.length;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	/* mem-region0 (spa0, dimm0) */
211162306a36Sopenharmony_ci	memdev = nfit_buf + offset;
211262306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
211362306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
211462306a36Sopenharmony_ci	memdev->device_handle = handle[0];
211562306a36Sopenharmony_ci	memdev->physical_id = 0;
211662306a36Sopenharmony_ci	memdev->region_id = 0;
211762306a36Sopenharmony_ci	memdev->range_index = 0+1;
211862306a36Sopenharmony_ci	memdev->region_index = 4+1;
211962306a36Sopenharmony_ci	memdev->region_size = SPA0_SIZE/2;
212062306a36Sopenharmony_ci	memdev->region_offset = 1;
212162306a36Sopenharmony_ci	memdev->address = 0;
212262306a36Sopenharmony_ci	memdev->interleave_index = 0;
212362306a36Sopenharmony_ci	memdev->interleave_ways = 2;
212462306a36Sopenharmony_ci	offset += memdev->header.length;
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci	/* mem-region1 (spa0, dimm1) */
212762306a36Sopenharmony_ci	memdev = nfit_buf + offset;
212862306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
212962306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
213062306a36Sopenharmony_ci	memdev->device_handle = handle[1];
213162306a36Sopenharmony_ci	memdev->physical_id = 1;
213262306a36Sopenharmony_ci	memdev->region_id = 0;
213362306a36Sopenharmony_ci	memdev->range_index = 0+1;
213462306a36Sopenharmony_ci	memdev->region_index = 5+1;
213562306a36Sopenharmony_ci	memdev->region_size = SPA0_SIZE/2;
213662306a36Sopenharmony_ci	memdev->region_offset = (1 << 8);
213762306a36Sopenharmony_ci	memdev->address = 0;
213862306a36Sopenharmony_ci	memdev->interleave_index = 0;
213962306a36Sopenharmony_ci	memdev->interleave_ways = 2;
214062306a36Sopenharmony_ci	memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED;
214162306a36Sopenharmony_ci	offset += memdev->header.length;
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	/* mem-region2 (spa1, dimm0) */
214462306a36Sopenharmony_ci	memdev = nfit_buf + offset;
214562306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
214662306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
214762306a36Sopenharmony_ci	memdev->device_handle = handle[0];
214862306a36Sopenharmony_ci	memdev->physical_id = 0;
214962306a36Sopenharmony_ci	memdev->region_id = 1;
215062306a36Sopenharmony_ci	memdev->range_index = 1+1;
215162306a36Sopenharmony_ci	memdev->region_index = 4+1;
215262306a36Sopenharmony_ci	memdev->region_size = SPA1_SIZE/4;
215362306a36Sopenharmony_ci	memdev->region_offset = (1 << 16);
215462306a36Sopenharmony_ci	memdev->address = SPA0_SIZE/2;
215562306a36Sopenharmony_ci	memdev->interleave_index = 0;
215662306a36Sopenharmony_ci	memdev->interleave_ways = 4;
215762306a36Sopenharmony_ci	memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED;
215862306a36Sopenharmony_ci	offset += memdev->header.length;
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	/* mem-region3 (spa1, dimm1) */
216162306a36Sopenharmony_ci	memdev = nfit_buf + offset;
216262306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
216362306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
216462306a36Sopenharmony_ci	memdev->device_handle = handle[1];
216562306a36Sopenharmony_ci	memdev->physical_id = 1;
216662306a36Sopenharmony_ci	memdev->region_id = 1;
216762306a36Sopenharmony_ci	memdev->range_index = 1+1;
216862306a36Sopenharmony_ci	memdev->region_index = 5+1;
216962306a36Sopenharmony_ci	memdev->region_size = SPA1_SIZE/4;
217062306a36Sopenharmony_ci	memdev->region_offset = (1 << 24);
217162306a36Sopenharmony_ci	memdev->address = SPA0_SIZE/2;
217262306a36Sopenharmony_ci	memdev->interleave_index = 0;
217362306a36Sopenharmony_ci	memdev->interleave_ways = 4;
217462306a36Sopenharmony_ci	offset += memdev->header.length;
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	/* mem-region4 (spa1, dimm2) */
217762306a36Sopenharmony_ci	memdev = nfit_buf + offset;
217862306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
217962306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
218062306a36Sopenharmony_ci	memdev->device_handle = handle[2];
218162306a36Sopenharmony_ci	memdev->physical_id = 2;
218262306a36Sopenharmony_ci	memdev->region_id = 0;
218362306a36Sopenharmony_ci	memdev->range_index = 1+1;
218462306a36Sopenharmony_ci	memdev->region_index = 6+1;
218562306a36Sopenharmony_ci	memdev->region_size = SPA1_SIZE/4;
218662306a36Sopenharmony_ci	memdev->region_offset = (1ULL << 32);
218762306a36Sopenharmony_ci	memdev->address = SPA0_SIZE/2;
218862306a36Sopenharmony_ci	memdev->interleave_index = 0;
218962306a36Sopenharmony_ci	memdev->interleave_ways = 4;
219062306a36Sopenharmony_ci	memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED;
219162306a36Sopenharmony_ci	offset += memdev->header.length;
219262306a36Sopenharmony_ci
219362306a36Sopenharmony_ci	/* mem-region5 (spa1, dimm3) */
219462306a36Sopenharmony_ci	memdev = nfit_buf + offset;
219562306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
219662306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
219762306a36Sopenharmony_ci	memdev->device_handle = handle[3];
219862306a36Sopenharmony_ci	memdev->physical_id = 3;
219962306a36Sopenharmony_ci	memdev->region_id = 0;
220062306a36Sopenharmony_ci	memdev->range_index = 1+1;
220162306a36Sopenharmony_ci	memdev->region_index = 7+1;
220262306a36Sopenharmony_ci	memdev->region_size = SPA1_SIZE/4;
220362306a36Sopenharmony_ci	memdev->region_offset = (1ULL << 40);
220462306a36Sopenharmony_ci	memdev->address = SPA0_SIZE/2;
220562306a36Sopenharmony_ci	memdev->interleave_index = 0;
220662306a36Sopenharmony_ci	memdev->interleave_ways = 4;
220762306a36Sopenharmony_ci	offset += memdev->header.length;
220862306a36Sopenharmony_ci
220962306a36Sopenharmony_ci	/* mem-region6 (spa/dcr0, dimm0) */
221062306a36Sopenharmony_ci	memdev = nfit_buf + offset;
221162306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
221262306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
221362306a36Sopenharmony_ci	memdev->device_handle = handle[0];
221462306a36Sopenharmony_ci	memdev->physical_id = 0;
221562306a36Sopenharmony_ci	memdev->region_id = 0;
221662306a36Sopenharmony_ci	memdev->range_index = 2+1;
221762306a36Sopenharmony_ci	memdev->region_index = 0+1;
221862306a36Sopenharmony_ci	memdev->region_size = 0;
221962306a36Sopenharmony_ci	memdev->region_offset = 0;
222062306a36Sopenharmony_ci	memdev->address = 0;
222162306a36Sopenharmony_ci	memdev->interleave_index = 0;
222262306a36Sopenharmony_ci	memdev->interleave_ways = 1;
222362306a36Sopenharmony_ci	offset += memdev->header.length;
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci	/* mem-region7 (spa/dcr1, dimm1) */
222662306a36Sopenharmony_ci	memdev = nfit_buf + offset;
222762306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
222862306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
222962306a36Sopenharmony_ci	memdev->device_handle = handle[1];
223062306a36Sopenharmony_ci	memdev->physical_id = 1;
223162306a36Sopenharmony_ci	memdev->region_id = 0;
223262306a36Sopenharmony_ci	memdev->range_index = 3+1;
223362306a36Sopenharmony_ci	memdev->region_index = 1+1;
223462306a36Sopenharmony_ci	memdev->region_size = 0;
223562306a36Sopenharmony_ci	memdev->region_offset = 0;
223662306a36Sopenharmony_ci	memdev->address = 0;
223762306a36Sopenharmony_ci	memdev->interleave_index = 0;
223862306a36Sopenharmony_ci	memdev->interleave_ways = 1;
223962306a36Sopenharmony_ci	offset += memdev->header.length;
224062306a36Sopenharmony_ci
224162306a36Sopenharmony_ci	/* mem-region8 (spa/dcr2, dimm2) */
224262306a36Sopenharmony_ci	memdev = nfit_buf + offset;
224362306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
224462306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
224562306a36Sopenharmony_ci	memdev->device_handle = handle[2];
224662306a36Sopenharmony_ci	memdev->physical_id = 2;
224762306a36Sopenharmony_ci	memdev->region_id = 0;
224862306a36Sopenharmony_ci	memdev->range_index = 4+1;
224962306a36Sopenharmony_ci	memdev->region_index = 2+1;
225062306a36Sopenharmony_ci	memdev->region_size = 0;
225162306a36Sopenharmony_ci	memdev->region_offset = 0;
225262306a36Sopenharmony_ci	memdev->address = 0;
225362306a36Sopenharmony_ci	memdev->interleave_index = 0;
225462306a36Sopenharmony_ci	memdev->interleave_ways = 1;
225562306a36Sopenharmony_ci	offset += memdev->header.length;
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	/* mem-region9 (spa/dcr3, dimm3) */
225862306a36Sopenharmony_ci	memdev = nfit_buf + offset;
225962306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
226062306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
226162306a36Sopenharmony_ci	memdev->device_handle = handle[3];
226262306a36Sopenharmony_ci	memdev->physical_id = 3;
226362306a36Sopenharmony_ci	memdev->region_id = 0;
226462306a36Sopenharmony_ci	memdev->range_index = 5+1;
226562306a36Sopenharmony_ci	memdev->region_index = 3+1;
226662306a36Sopenharmony_ci	memdev->region_size = 0;
226762306a36Sopenharmony_ci	memdev->region_offset = 0;
226862306a36Sopenharmony_ci	memdev->address = 0;
226962306a36Sopenharmony_ci	memdev->interleave_index = 0;
227062306a36Sopenharmony_ci	memdev->interleave_ways = 1;
227162306a36Sopenharmony_ci	offset += memdev->header.length;
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	/* mem-region10 (spa/bdw0, dimm0) */
227462306a36Sopenharmony_ci	memdev = nfit_buf + offset;
227562306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
227662306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
227762306a36Sopenharmony_ci	memdev->device_handle = handle[0];
227862306a36Sopenharmony_ci	memdev->physical_id = 0;
227962306a36Sopenharmony_ci	memdev->region_id = 0;
228062306a36Sopenharmony_ci	memdev->range_index = 6+1;
228162306a36Sopenharmony_ci	memdev->region_index = 0+1;
228262306a36Sopenharmony_ci	memdev->region_size = 0;
228362306a36Sopenharmony_ci	memdev->region_offset = 0;
228462306a36Sopenharmony_ci	memdev->address = 0;
228562306a36Sopenharmony_ci	memdev->interleave_index = 0;
228662306a36Sopenharmony_ci	memdev->interleave_ways = 1;
228762306a36Sopenharmony_ci	offset += memdev->header.length;
228862306a36Sopenharmony_ci
228962306a36Sopenharmony_ci	/* mem-region11 (spa/bdw1, dimm1) */
229062306a36Sopenharmony_ci	memdev = nfit_buf + offset;
229162306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
229262306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
229362306a36Sopenharmony_ci	memdev->device_handle = handle[1];
229462306a36Sopenharmony_ci	memdev->physical_id = 1;
229562306a36Sopenharmony_ci	memdev->region_id = 0;
229662306a36Sopenharmony_ci	memdev->range_index = 7+1;
229762306a36Sopenharmony_ci	memdev->region_index = 1+1;
229862306a36Sopenharmony_ci	memdev->region_size = 0;
229962306a36Sopenharmony_ci	memdev->region_offset = 0;
230062306a36Sopenharmony_ci	memdev->address = 0;
230162306a36Sopenharmony_ci	memdev->interleave_index = 0;
230262306a36Sopenharmony_ci	memdev->interleave_ways = 1;
230362306a36Sopenharmony_ci	offset += memdev->header.length;
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	/* mem-region12 (spa/bdw2, dimm2) */
230662306a36Sopenharmony_ci	memdev = nfit_buf + offset;
230762306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
230862306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
230962306a36Sopenharmony_ci	memdev->device_handle = handle[2];
231062306a36Sopenharmony_ci	memdev->physical_id = 2;
231162306a36Sopenharmony_ci	memdev->region_id = 0;
231262306a36Sopenharmony_ci	memdev->range_index = 8+1;
231362306a36Sopenharmony_ci	memdev->region_index = 2+1;
231462306a36Sopenharmony_ci	memdev->region_size = 0;
231562306a36Sopenharmony_ci	memdev->region_offset = 0;
231662306a36Sopenharmony_ci	memdev->address = 0;
231762306a36Sopenharmony_ci	memdev->interleave_index = 0;
231862306a36Sopenharmony_ci	memdev->interleave_ways = 1;
231962306a36Sopenharmony_ci	offset += memdev->header.length;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	/* mem-region13 (spa/dcr3, dimm3) */
232262306a36Sopenharmony_ci	memdev = nfit_buf + offset;
232362306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
232462306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
232562306a36Sopenharmony_ci	memdev->device_handle = handle[3];
232662306a36Sopenharmony_ci	memdev->physical_id = 3;
232762306a36Sopenharmony_ci	memdev->region_id = 0;
232862306a36Sopenharmony_ci	memdev->range_index = 9+1;
232962306a36Sopenharmony_ci	memdev->region_index = 3+1;
233062306a36Sopenharmony_ci	memdev->region_size = 0;
233162306a36Sopenharmony_ci	memdev->region_offset = 0;
233262306a36Sopenharmony_ci	memdev->address = 0;
233362306a36Sopenharmony_ci	memdev->interleave_index = 0;
233462306a36Sopenharmony_ci	memdev->interleave_ways = 1;
233562306a36Sopenharmony_ci	memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED;
233662306a36Sopenharmony_ci	offset += memdev->header.length;
233762306a36Sopenharmony_ci
233862306a36Sopenharmony_ci	/* dcr-descriptor0: blk */
233962306a36Sopenharmony_ci	dcr = nfit_buf + offset;
234062306a36Sopenharmony_ci	dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
234162306a36Sopenharmony_ci	dcr->header.length = sizeof(*dcr);
234262306a36Sopenharmony_ci	dcr->region_index = 0+1;
234362306a36Sopenharmony_ci	dcr_common_init(dcr);
234462306a36Sopenharmony_ci	dcr->serial_number = ~handle[0];
234562306a36Sopenharmony_ci	dcr->code = NFIT_FIC_BLK;
234662306a36Sopenharmony_ci	dcr->windows = 1;
234762306a36Sopenharmony_ci	dcr->window_size = DCR_SIZE;
234862306a36Sopenharmony_ci	dcr->command_offset = 0;
234962306a36Sopenharmony_ci	dcr->command_size = 8;
235062306a36Sopenharmony_ci	dcr->status_offset = 8;
235162306a36Sopenharmony_ci	dcr->status_size = 4;
235262306a36Sopenharmony_ci	offset += dcr->header.length;
235362306a36Sopenharmony_ci
235462306a36Sopenharmony_ci	/* dcr-descriptor1: blk */
235562306a36Sopenharmony_ci	dcr = nfit_buf + offset;
235662306a36Sopenharmony_ci	dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
235762306a36Sopenharmony_ci	dcr->header.length = sizeof(*dcr);
235862306a36Sopenharmony_ci	dcr->region_index = 1+1;
235962306a36Sopenharmony_ci	dcr_common_init(dcr);
236062306a36Sopenharmony_ci	dcr->serial_number = ~handle[1];
236162306a36Sopenharmony_ci	dcr->code = NFIT_FIC_BLK;
236262306a36Sopenharmony_ci	dcr->windows = 1;
236362306a36Sopenharmony_ci	dcr->window_size = DCR_SIZE;
236462306a36Sopenharmony_ci	dcr->command_offset = 0;
236562306a36Sopenharmony_ci	dcr->command_size = 8;
236662306a36Sopenharmony_ci	dcr->status_offset = 8;
236762306a36Sopenharmony_ci	dcr->status_size = 4;
236862306a36Sopenharmony_ci	offset += dcr->header.length;
236962306a36Sopenharmony_ci
237062306a36Sopenharmony_ci	/* dcr-descriptor2: blk */
237162306a36Sopenharmony_ci	dcr = nfit_buf + offset;
237262306a36Sopenharmony_ci	dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
237362306a36Sopenharmony_ci	dcr->header.length = sizeof(*dcr);
237462306a36Sopenharmony_ci	dcr->region_index = 2+1;
237562306a36Sopenharmony_ci	dcr_common_init(dcr);
237662306a36Sopenharmony_ci	dcr->serial_number = ~handle[2];
237762306a36Sopenharmony_ci	dcr->code = NFIT_FIC_BLK;
237862306a36Sopenharmony_ci	dcr->windows = 1;
237962306a36Sopenharmony_ci	dcr->window_size = DCR_SIZE;
238062306a36Sopenharmony_ci	dcr->command_offset = 0;
238162306a36Sopenharmony_ci	dcr->command_size = 8;
238262306a36Sopenharmony_ci	dcr->status_offset = 8;
238362306a36Sopenharmony_ci	dcr->status_size = 4;
238462306a36Sopenharmony_ci	offset += dcr->header.length;
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci	/* dcr-descriptor3: blk */
238762306a36Sopenharmony_ci	dcr = nfit_buf + offset;
238862306a36Sopenharmony_ci	dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
238962306a36Sopenharmony_ci	dcr->header.length = sizeof(*dcr);
239062306a36Sopenharmony_ci	dcr->region_index = 3+1;
239162306a36Sopenharmony_ci	dcr_common_init(dcr);
239262306a36Sopenharmony_ci	dcr->serial_number = ~handle[3];
239362306a36Sopenharmony_ci	dcr->code = NFIT_FIC_BLK;
239462306a36Sopenharmony_ci	dcr->windows = 1;
239562306a36Sopenharmony_ci	dcr->window_size = DCR_SIZE;
239662306a36Sopenharmony_ci	dcr->command_offset = 0;
239762306a36Sopenharmony_ci	dcr->command_size = 8;
239862306a36Sopenharmony_ci	dcr->status_offset = 8;
239962306a36Sopenharmony_ci	dcr->status_size = 4;
240062306a36Sopenharmony_ci	offset += dcr->header.length;
240162306a36Sopenharmony_ci
240262306a36Sopenharmony_ci	/* dcr-descriptor0: pmem */
240362306a36Sopenharmony_ci	dcr = nfit_buf + offset;
240462306a36Sopenharmony_ci	dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
240562306a36Sopenharmony_ci	dcr->header.length = offsetof(struct acpi_nfit_control_region,
240662306a36Sopenharmony_ci			window_size);
240762306a36Sopenharmony_ci	dcr->region_index = 4+1;
240862306a36Sopenharmony_ci	dcr_common_init(dcr);
240962306a36Sopenharmony_ci	dcr->serial_number = ~handle[0];
241062306a36Sopenharmony_ci	dcr->code = NFIT_FIC_BYTEN;
241162306a36Sopenharmony_ci	dcr->windows = 0;
241262306a36Sopenharmony_ci	offset += dcr->header.length;
241362306a36Sopenharmony_ci
241462306a36Sopenharmony_ci	/* dcr-descriptor1: pmem */
241562306a36Sopenharmony_ci	dcr = nfit_buf + offset;
241662306a36Sopenharmony_ci	dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
241762306a36Sopenharmony_ci	dcr->header.length = offsetof(struct acpi_nfit_control_region,
241862306a36Sopenharmony_ci			window_size);
241962306a36Sopenharmony_ci	dcr->region_index = 5+1;
242062306a36Sopenharmony_ci	dcr_common_init(dcr);
242162306a36Sopenharmony_ci	dcr->serial_number = ~handle[1];
242262306a36Sopenharmony_ci	dcr->code = NFIT_FIC_BYTEN;
242362306a36Sopenharmony_ci	dcr->windows = 0;
242462306a36Sopenharmony_ci	offset += dcr->header.length;
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci	/* dcr-descriptor2: pmem */
242762306a36Sopenharmony_ci	dcr = nfit_buf + offset;
242862306a36Sopenharmony_ci	dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
242962306a36Sopenharmony_ci	dcr->header.length = offsetof(struct acpi_nfit_control_region,
243062306a36Sopenharmony_ci			window_size);
243162306a36Sopenharmony_ci	dcr->region_index = 6+1;
243262306a36Sopenharmony_ci	dcr_common_init(dcr);
243362306a36Sopenharmony_ci	dcr->serial_number = ~handle[2];
243462306a36Sopenharmony_ci	dcr->code = NFIT_FIC_BYTEN;
243562306a36Sopenharmony_ci	dcr->windows = 0;
243662306a36Sopenharmony_ci	offset += dcr->header.length;
243762306a36Sopenharmony_ci
243862306a36Sopenharmony_ci	/* dcr-descriptor3: pmem */
243962306a36Sopenharmony_ci	dcr = nfit_buf + offset;
244062306a36Sopenharmony_ci	dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
244162306a36Sopenharmony_ci	dcr->header.length = offsetof(struct acpi_nfit_control_region,
244262306a36Sopenharmony_ci			window_size);
244362306a36Sopenharmony_ci	dcr->region_index = 7+1;
244462306a36Sopenharmony_ci	dcr_common_init(dcr);
244562306a36Sopenharmony_ci	dcr->serial_number = ~handle[3];
244662306a36Sopenharmony_ci	dcr->code = NFIT_FIC_BYTEN;
244762306a36Sopenharmony_ci	dcr->windows = 0;
244862306a36Sopenharmony_ci	offset += dcr->header.length;
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	/* bdw0 (spa/dcr0, dimm0) */
245162306a36Sopenharmony_ci	bdw = nfit_buf + offset;
245262306a36Sopenharmony_ci	bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION;
245362306a36Sopenharmony_ci	bdw->header.length = sizeof(*bdw);
245462306a36Sopenharmony_ci	bdw->region_index = 0+1;
245562306a36Sopenharmony_ci	bdw->windows = 1;
245662306a36Sopenharmony_ci	bdw->offset = 0;
245762306a36Sopenharmony_ci	bdw->size = BDW_SIZE;
245862306a36Sopenharmony_ci	bdw->capacity = DIMM_SIZE;
245962306a36Sopenharmony_ci	bdw->start_address = 0;
246062306a36Sopenharmony_ci	offset += bdw->header.length;
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_ci	/* bdw1 (spa/dcr1, dimm1) */
246362306a36Sopenharmony_ci	bdw = nfit_buf + offset;
246462306a36Sopenharmony_ci	bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION;
246562306a36Sopenharmony_ci	bdw->header.length = sizeof(*bdw);
246662306a36Sopenharmony_ci	bdw->region_index = 1+1;
246762306a36Sopenharmony_ci	bdw->windows = 1;
246862306a36Sopenharmony_ci	bdw->offset = 0;
246962306a36Sopenharmony_ci	bdw->size = BDW_SIZE;
247062306a36Sopenharmony_ci	bdw->capacity = DIMM_SIZE;
247162306a36Sopenharmony_ci	bdw->start_address = 0;
247262306a36Sopenharmony_ci	offset += bdw->header.length;
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	/* bdw2 (spa/dcr2, dimm2) */
247562306a36Sopenharmony_ci	bdw = nfit_buf + offset;
247662306a36Sopenharmony_ci	bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION;
247762306a36Sopenharmony_ci	bdw->header.length = sizeof(*bdw);
247862306a36Sopenharmony_ci	bdw->region_index = 2+1;
247962306a36Sopenharmony_ci	bdw->windows = 1;
248062306a36Sopenharmony_ci	bdw->offset = 0;
248162306a36Sopenharmony_ci	bdw->size = BDW_SIZE;
248262306a36Sopenharmony_ci	bdw->capacity = DIMM_SIZE;
248362306a36Sopenharmony_ci	bdw->start_address = 0;
248462306a36Sopenharmony_ci	offset += bdw->header.length;
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_ci	/* bdw3 (spa/dcr3, dimm3) */
248762306a36Sopenharmony_ci	bdw = nfit_buf + offset;
248862306a36Sopenharmony_ci	bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION;
248962306a36Sopenharmony_ci	bdw->header.length = sizeof(*bdw);
249062306a36Sopenharmony_ci	bdw->region_index = 3+1;
249162306a36Sopenharmony_ci	bdw->windows = 1;
249262306a36Sopenharmony_ci	bdw->offset = 0;
249362306a36Sopenharmony_ci	bdw->size = BDW_SIZE;
249462306a36Sopenharmony_ci	bdw->capacity = DIMM_SIZE;
249562306a36Sopenharmony_ci	bdw->start_address = 0;
249662306a36Sopenharmony_ci	offset += bdw->header.length;
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	/* flush0 (dimm0) */
249962306a36Sopenharmony_ci	flush = nfit_buf + offset;
250062306a36Sopenharmony_ci	flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS;
250162306a36Sopenharmony_ci	flush->header.length = flush_hint_size;
250262306a36Sopenharmony_ci	flush->device_handle = handle[0];
250362306a36Sopenharmony_ci	flush->hint_count = NUM_HINTS;
250462306a36Sopenharmony_ci	for (i = 0; i < NUM_HINTS; i++)
250562306a36Sopenharmony_ci		flush->hint_address[i] = t->flush_dma[0] + i * sizeof(u64);
250662306a36Sopenharmony_ci	offset += flush->header.length;
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	/* flush1 (dimm1) */
250962306a36Sopenharmony_ci	flush = nfit_buf + offset;
251062306a36Sopenharmony_ci	flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS;
251162306a36Sopenharmony_ci	flush->header.length = flush_hint_size;
251262306a36Sopenharmony_ci	flush->device_handle = handle[1];
251362306a36Sopenharmony_ci	flush->hint_count = NUM_HINTS;
251462306a36Sopenharmony_ci	for (i = 0; i < NUM_HINTS; i++)
251562306a36Sopenharmony_ci		flush->hint_address[i] = t->flush_dma[1] + i * sizeof(u64);
251662306a36Sopenharmony_ci	offset += flush->header.length;
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_ci	/* flush2 (dimm2) */
251962306a36Sopenharmony_ci	flush = nfit_buf + offset;
252062306a36Sopenharmony_ci	flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS;
252162306a36Sopenharmony_ci	flush->header.length = flush_hint_size;
252262306a36Sopenharmony_ci	flush->device_handle = handle[2];
252362306a36Sopenharmony_ci	flush->hint_count = NUM_HINTS;
252462306a36Sopenharmony_ci	for (i = 0; i < NUM_HINTS; i++)
252562306a36Sopenharmony_ci		flush->hint_address[i] = t->flush_dma[2] + i * sizeof(u64);
252662306a36Sopenharmony_ci	offset += flush->header.length;
252762306a36Sopenharmony_ci
252862306a36Sopenharmony_ci	/* flush3 (dimm3) */
252962306a36Sopenharmony_ci	flush = nfit_buf + offset;
253062306a36Sopenharmony_ci	flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS;
253162306a36Sopenharmony_ci	flush->header.length = flush_hint_size;
253262306a36Sopenharmony_ci	flush->device_handle = handle[3];
253362306a36Sopenharmony_ci	flush->hint_count = NUM_HINTS;
253462306a36Sopenharmony_ci	for (i = 0; i < NUM_HINTS; i++)
253562306a36Sopenharmony_ci		flush->hint_address[i] = t->flush_dma[3] + i * sizeof(u64);
253662306a36Sopenharmony_ci	offset += flush->header.length;
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci	/* platform capabilities */
253962306a36Sopenharmony_ci	pcap = nfit_buf + offset;
254062306a36Sopenharmony_ci	pcap->header.type = ACPI_NFIT_TYPE_CAPABILITIES;
254162306a36Sopenharmony_ci	pcap->header.length = sizeof(*pcap);
254262306a36Sopenharmony_ci	pcap->highest_capability = 1;
254362306a36Sopenharmony_ci	pcap->capabilities = ACPI_NFIT_CAPABILITY_MEM_FLUSH;
254462306a36Sopenharmony_ci	offset += pcap->header.length;
254562306a36Sopenharmony_ci
254662306a36Sopenharmony_ci	if (t->setup_hotplug) {
254762306a36Sopenharmony_ci		/* dcr-descriptor4: blk */
254862306a36Sopenharmony_ci		dcr = nfit_buf + offset;
254962306a36Sopenharmony_ci		dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
255062306a36Sopenharmony_ci		dcr->header.length = sizeof(*dcr);
255162306a36Sopenharmony_ci		dcr->region_index = 8+1;
255262306a36Sopenharmony_ci		dcr_common_init(dcr);
255362306a36Sopenharmony_ci		dcr->serial_number = ~handle[4];
255462306a36Sopenharmony_ci		dcr->code = NFIT_FIC_BLK;
255562306a36Sopenharmony_ci		dcr->windows = 1;
255662306a36Sopenharmony_ci		dcr->window_size = DCR_SIZE;
255762306a36Sopenharmony_ci		dcr->command_offset = 0;
255862306a36Sopenharmony_ci		dcr->command_size = 8;
255962306a36Sopenharmony_ci		dcr->status_offset = 8;
256062306a36Sopenharmony_ci		dcr->status_size = 4;
256162306a36Sopenharmony_ci		offset += dcr->header.length;
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci		/* dcr-descriptor4: pmem */
256462306a36Sopenharmony_ci		dcr = nfit_buf + offset;
256562306a36Sopenharmony_ci		dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
256662306a36Sopenharmony_ci		dcr->header.length = offsetof(struct acpi_nfit_control_region,
256762306a36Sopenharmony_ci				window_size);
256862306a36Sopenharmony_ci		dcr->region_index = 9+1;
256962306a36Sopenharmony_ci		dcr_common_init(dcr);
257062306a36Sopenharmony_ci		dcr->serial_number = ~handle[4];
257162306a36Sopenharmony_ci		dcr->code = NFIT_FIC_BYTEN;
257262306a36Sopenharmony_ci		dcr->windows = 0;
257362306a36Sopenharmony_ci		offset += dcr->header.length;
257462306a36Sopenharmony_ci
257562306a36Sopenharmony_ci		/* bdw4 (spa/dcr4, dimm4) */
257662306a36Sopenharmony_ci		bdw = nfit_buf + offset;
257762306a36Sopenharmony_ci		bdw->header.type = ACPI_NFIT_TYPE_DATA_REGION;
257862306a36Sopenharmony_ci		bdw->header.length = sizeof(*bdw);
257962306a36Sopenharmony_ci		bdw->region_index = 8+1;
258062306a36Sopenharmony_ci		bdw->windows = 1;
258162306a36Sopenharmony_ci		bdw->offset = 0;
258262306a36Sopenharmony_ci		bdw->size = BDW_SIZE;
258362306a36Sopenharmony_ci		bdw->capacity = DIMM_SIZE;
258462306a36Sopenharmony_ci		bdw->start_address = 0;
258562306a36Sopenharmony_ci		offset += bdw->header.length;
258662306a36Sopenharmony_ci
258762306a36Sopenharmony_ci		/* spa10 (dcr4) dimm4 */
258862306a36Sopenharmony_ci		spa = nfit_buf + offset;
258962306a36Sopenharmony_ci		spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
259062306a36Sopenharmony_ci		spa->header.length = sizeof_spa(spa);
259162306a36Sopenharmony_ci		memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_DCR), 16);
259262306a36Sopenharmony_ci		spa->range_index = 10+1;
259362306a36Sopenharmony_ci		spa->address = t->dcr_dma[4];
259462306a36Sopenharmony_ci		spa->length = DCR_SIZE;
259562306a36Sopenharmony_ci		offset += spa->header.length;
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci		/*
259862306a36Sopenharmony_ci		 * spa11 (single-dimm interleave for hotplug, note storage
259962306a36Sopenharmony_ci		 * does not actually alias the related block-data-window
260062306a36Sopenharmony_ci		 * regions)
260162306a36Sopenharmony_ci		 */
260262306a36Sopenharmony_ci		spa = nfit_buf + offset;
260362306a36Sopenharmony_ci		spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
260462306a36Sopenharmony_ci		spa->header.length = sizeof_spa(spa);
260562306a36Sopenharmony_ci		memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16);
260662306a36Sopenharmony_ci		spa->range_index = 11+1;
260762306a36Sopenharmony_ci		spa->address = t->spa_set_dma[2];
260862306a36Sopenharmony_ci		spa->length = SPA0_SIZE;
260962306a36Sopenharmony_ci		offset += spa->header.length;
261062306a36Sopenharmony_ci
261162306a36Sopenharmony_ci		/* spa12 (bdw for dcr4) dimm4 */
261262306a36Sopenharmony_ci		spa = nfit_buf + offset;
261362306a36Sopenharmony_ci		spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
261462306a36Sopenharmony_ci		spa->header.length = sizeof_spa(spa);
261562306a36Sopenharmony_ci		memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_BDW), 16);
261662306a36Sopenharmony_ci		spa->range_index = 12+1;
261762306a36Sopenharmony_ci		spa->address = t->dimm_dma[4];
261862306a36Sopenharmony_ci		spa->length = DIMM_SIZE;
261962306a36Sopenharmony_ci		offset += spa->header.length;
262062306a36Sopenharmony_ci
262162306a36Sopenharmony_ci		/* mem-region14 (spa/dcr4, dimm4) */
262262306a36Sopenharmony_ci		memdev = nfit_buf + offset;
262362306a36Sopenharmony_ci		memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
262462306a36Sopenharmony_ci		memdev->header.length = sizeof(*memdev);
262562306a36Sopenharmony_ci		memdev->device_handle = handle[4];
262662306a36Sopenharmony_ci		memdev->physical_id = 4;
262762306a36Sopenharmony_ci		memdev->region_id = 0;
262862306a36Sopenharmony_ci		memdev->range_index = 10+1;
262962306a36Sopenharmony_ci		memdev->region_index = 8+1;
263062306a36Sopenharmony_ci		memdev->region_size = 0;
263162306a36Sopenharmony_ci		memdev->region_offset = 0;
263262306a36Sopenharmony_ci		memdev->address = 0;
263362306a36Sopenharmony_ci		memdev->interleave_index = 0;
263462306a36Sopenharmony_ci		memdev->interleave_ways = 1;
263562306a36Sopenharmony_ci		offset += memdev->header.length;
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_ci		/* mem-region15 (spa11, dimm4) */
263862306a36Sopenharmony_ci		memdev = nfit_buf + offset;
263962306a36Sopenharmony_ci		memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
264062306a36Sopenharmony_ci		memdev->header.length = sizeof(*memdev);
264162306a36Sopenharmony_ci		memdev->device_handle = handle[4];
264262306a36Sopenharmony_ci		memdev->physical_id = 4;
264362306a36Sopenharmony_ci		memdev->region_id = 0;
264462306a36Sopenharmony_ci		memdev->range_index = 11+1;
264562306a36Sopenharmony_ci		memdev->region_index = 9+1;
264662306a36Sopenharmony_ci		memdev->region_size = SPA0_SIZE;
264762306a36Sopenharmony_ci		memdev->region_offset = (1ULL << 48);
264862306a36Sopenharmony_ci		memdev->address = 0;
264962306a36Sopenharmony_ci		memdev->interleave_index = 0;
265062306a36Sopenharmony_ci		memdev->interleave_ways = 1;
265162306a36Sopenharmony_ci		memdev->flags = ACPI_NFIT_MEM_HEALTH_ENABLED;
265262306a36Sopenharmony_ci		offset += memdev->header.length;
265362306a36Sopenharmony_ci
265462306a36Sopenharmony_ci		/* mem-region16 (spa/bdw4, dimm4) */
265562306a36Sopenharmony_ci		memdev = nfit_buf + offset;
265662306a36Sopenharmony_ci		memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
265762306a36Sopenharmony_ci		memdev->header.length = sizeof(*memdev);
265862306a36Sopenharmony_ci		memdev->device_handle = handle[4];
265962306a36Sopenharmony_ci		memdev->physical_id = 4;
266062306a36Sopenharmony_ci		memdev->region_id = 0;
266162306a36Sopenharmony_ci		memdev->range_index = 12+1;
266262306a36Sopenharmony_ci		memdev->region_index = 8+1;
266362306a36Sopenharmony_ci		memdev->region_size = 0;
266462306a36Sopenharmony_ci		memdev->region_offset = 0;
266562306a36Sopenharmony_ci		memdev->address = 0;
266662306a36Sopenharmony_ci		memdev->interleave_index = 0;
266762306a36Sopenharmony_ci		memdev->interleave_ways = 1;
266862306a36Sopenharmony_ci		offset += memdev->header.length;
266962306a36Sopenharmony_ci
267062306a36Sopenharmony_ci		/* flush3 (dimm4) */
267162306a36Sopenharmony_ci		flush = nfit_buf + offset;
267262306a36Sopenharmony_ci		flush->header.type = ACPI_NFIT_TYPE_FLUSH_ADDRESS;
267362306a36Sopenharmony_ci		flush->header.length = flush_hint_size;
267462306a36Sopenharmony_ci		flush->device_handle = handle[4];
267562306a36Sopenharmony_ci		flush->hint_count = NUM_HINTS;
267662306a36Sopenharmony_ci		for (i = 0; i < NUM_HINTS; i++)
267762306a36Sopenharmony_ci			flush->hint_address[i] = t->flush_dma[4]
267862306a36Sopenharmony_ci				+ i * sizeof(u64);
267962306a36Sopenharmony_ci		offset += flush->header.length;
268062306a36Sopenharmony_ci
268162306a36Sopenharmony_ci		/* sanity check to make sure we've filled the buffer */
268262306a36Sopenharmony_ci		WARN_ON(offset != t->nfit_size);
268362306a36Sopenharmony_ci	}
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	t->nfit_filled = offset;
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	post_ars_status(&t->ars_state, &t->badrange, t->spa_set_dma[0],
268862306a36Sopenharmony_ci			SPA0_SIZE);
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci	acpi_desc = &t->acpi_desc;
269162306a36Sopenharmony_ci	set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_cmd_force_en);
269262306a36Sopenharmony_ci	set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
269362306a36Sopenharmony_ci	set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
269462306a36Sopenharmony_ci	set_bit(ND_INTEL_SMART, &acpi_desc->dimm_cmd_force_en);
269562306a36Sopenharmony_ci	set_bit(ND_INTEL_SMART_THRESHOLD, &acpi_desc->dimm_cmd_force_en);
269662306a36Sopenharmony_ci	set_bit(ND_INTEL_SMART_SET_THRESHOLD, &acpi_desc->dimm_cmd_force_en);
269762306a36Sopenharmony_ci	set_bit(ND_INTEL_SMART_INJECT, &acpi_desc->dimm_cmd_force_en);
269862306a36Sopenharmony_ci	set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en);
269962306a36Sopenharmony_ci	set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en);
270062306a36Sopenharmony_ci	set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en);
270162306a36Sopenharmony_ci	set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en);
270262306a36Sopenharmony_ci	set_bit(ND_CMD_CALL, &acpi_desc->bus_cmd_force_en);
270362306a36Sopenharmony_ci	set_bit(NFIT_CMD_TRANSLATE_SPA, &acpi_desc->bus_dsm_mask);
270462306a36Sopenharmony_ci	set_bit(NFIT_CMD_ARS_INJECT_SET, &acpi_desc->bus_dsm_mask);
270562306a36Sopenharmony_ci	set_bit(NFIT_CMD_ARS_INJECT_CLEAR, &acpi_desc->bus_dsm_mask);
270662306a36Sopenharmony_ci	set_bit(NFIT_CMD_ARS_INJECT_GET, &acpi_desc->bus_dsm_mask);
270762306a36Sopenharmony_ci	set_bit(ND_INTEL_FW_GET_INFO, &acpi_desc->dimm_cmd_force_en);
270862306a36Sopenharmony_ci	set_bit(ND_INTEL_FW_START_UPDATE, &acpi_desc->dimm_cmd_force_en);
270962306a36Sopenharmony_ci	set_bit(ND_INTEL_FW_SEND_DATA, &acpi_desc->dimm_cmd_force_en);
271062306a36Sopenharmony_ci	set_bit(ND_INTEL_FW_FINISH_UPDATE, &acpi_desc->dimm_cmd_force_en);
271162306a36Sopenharmony_ci	set_bit(ND_INTEL_FW_FINISH_QUERY, &acpi_desc->dimm_cmd_force_en);
271262306a36Sopenharmony_ci	set_bit(ND_INTEL_ENABLE_LSS_STATUS, &acpi_desc->dimm_cmd_force_en);
271362306a36Sopenharmony_ci	set_bit(NVDIMM_INTEL_GET_SECURITY_STATE,
271462306a36Sopenharmony_ci			&acpi_desc->dimm_cmd_force_en);
271562306a36Sopenharmony_ci	set_bit(NVDIMM_INTEL_SET_PASSPHRASE, &acpi_desc->dimm_cmd_force_en);
271662306a36Sopenharmony_ci	set_bit(NVDIMM_INTEL_DISABLE_PASSPHRASE,
271762306a36Sopenharmony_ci			&acpi_desc->dimm_cmd_force_en);
271862306a36Sopenharmony_ci	set_bit(NVDIMM_INTEL_UNLOCK_UNIT, &acpi_desc->dimm_cmd_force_en);
271962306a36Sopenharmony_ci	set_bit(NVDIMM_INTEL_FREEZE_LOCK, &acpi_desc->dimm_cmd_force_en);
272062306a36Sopenharmony_ci	set_bit(NVDIMM_INTEL_SECURE_ERASE, &acpi_desc->dimm_cmd_force_en);
272162306a36Sopenharmony_ci	set_bit(NVDIMM_INTEL_OVERWRITE, &acpi_desc->dimm_cmd_force_en);
272262306a36Sopenharmony_ci	set_bit(NVDIMM_INTEL_QUERY_OVERWRITE, &acpi_desc->dimm_cmd_force_en);
272362306a36Sopenharmony_ci	set_bit(NVDIMM_INTEL_SET_MASTER_PASSPHRASE,
272462306a36Sopenharmony_ci			&acpi_desc->dimm_cmd_force_en);
272562306a36Sopenharmony_ci	set_bit(NVDIMM_INTEL_MASTER_SECURE_ERASE,
272662306a36Sopenharmony_ci			&acpi_desc->dimm_cmd_force_en);
272762306a36Sopenharmony_ci	set_bit(NVDIMM_INTEL_FW_ACTIVATE_DIMMINFO, &acpi_desc->dimm_cmd_force_en);
272862306a36Sopenharmony_ci	set_bit(NVDIMM_INTEL_FW_ACTIVATE_ARM, &acpi_desc->dimm_cmd_force_en);
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	acpi_mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL];
273162306a36Sopenharmony_ci	set_bit(NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO, acpi_mask);
273262306a36Sopenharmony_ci	set_bit(NVDIMM_BUS_INTEL_FW_ACTIVATE, acpi_mask);
273362306a36Sopenharmony_ci}
273462306a36Sopenharmony_ci
273562306a36Sopenharmony_cistatic void nfit_test1_setup(struct nfit_test *t)
273662306a36Sopenharmony_ci{
273762306a36Sopenharmony_ci	size_t offset;
273862306a36Sopenharmony_ci	void *nfit_buf = t->nfit_buf;
273962306a36Sopenharmony_ci	struct acpi_nfit_memory_map *memdev;
274062306a36Sopenharmony_ci	struct acpi_nfit_control_region *dcr;
274162306a36Sopenharmony_ci	struct acpi_nfit_system_address *spa;
274262306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc;
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_ci	offset = 0;
274562306a36Sopenharmony_ci	/* spa0 (flat range with no bdw aliasing) */
274662306a36Sopenharmony_ci	spa = nfit_buf + offset;
274762306a36Sopenharmony_ci	spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
274862306a36Sopenharmony_ci	spa->header.length = sizeof_spa(spa);
274962306a36Sopenharmony_ci	memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_PM), 16);
275062306a36Sopenharmony_ci	spa->range_index = 0+1;
275162306a36Sopenharmony_ci	spa->address = t->spa_set_dma[0];
275262306a36Sopenharmony_ci	spa->length = SPA2_SIZE;
275362306a36Sopenharmony_ci	offset += spa->header.length;
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci	/* virtual cd region */
275662306a36Sopenharmony_ci	spa = nfit_buf + offset;
275762306a36Sopenharmony_ci	spa->header.type = ACPI_NFIT_TYPE_SYSTEM_ADDRESS;
275862306a36Sopenharmony_ci	spa->header.length = sizeof_spa(spa);
275962306a36Sopenharmony_ci	memcpy(spa->range_guid, to_nfit_uuid(NFIT_SPA_VCD), 16);
276062306a36Sopenharmony_ci	spa->range_index = 0;
276162306a36Sopenharmony_ci	spa->address = t->spa_set_dma[1];
276262306a36Sopenharmony_ci	spa->length = SPA_VCD_SIZE;
276362306a36Sopenharmony_ci	offset += spa->header.length;
276462306a36Sopenharmony_ci
276562306a36Sopenharmony_ci	/* mem-region0 (spa0, dimm0) */
276662306a36Sopenharmony_ci	memdev = nfit_buf + offset;
276762306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
276862306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
276962306a36Sopenharmony_ci	memdev->device_handle = handle[5];
277062306a36Sopenharmony_ci	memdev->physical_id = 0;
277162306a36Sopenharmony_ci	memdev->region_id = 0;
277262306a36Sopenharmony_ci	memdev->range_index = 0+1;
277362306a36Sopenharmony_ci	memdev->region_index = 0+1;
277462306a36Sopenharmony_ci	memdev->region_size = SPA2_SIZE;
277562306a36Sopenharmony_ci	memdev->region_offset = 0;
277662306a36Sopenharmony_ci	memdev->address = 0;
277762306a36Sopenharmony_ci	memdev->interleave_index = 0;
277862306a36Sopenharmony_ci	memdev->interleave_ways = 1;
277962306a36Sopenharmony_ci	memdev->flags = ACPI_NFIT_MEM_SAVE_FAILED | ACPI_NFIT_MEM_RESTORE_FAILED
278062306a36Sopenharmony_ci		| ACPI_NFIT_MEM_FLUSH_FAILED | ACPI_NFIT_MEM_HEALTH_OBSERVED
278162306a36Sopenharmony_ci		| ACPI_NFIT_MEM_NOT_ARMED;
278262306a36Sopenharmony_ci	offset += memdev->header.length;
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci	/* dcr-descriptor0 */
278562306a36Sopenharmony_ci	dcr = nfit_buf + offset;
278662306a36Sopenharmony_ci	dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
278762306a36Sopenharmony_ci	dcr->header.length = offsetof(struct acpi_nfit_control_region,
278862306a36Sopenharmony_ci			window_size);
278962306a36Sopenharmony_ci	dcr->region_index = 0+1;
279062306a36Sopenharmony_ci	dcr_common_init(dcr);
279162306a36Sopenharmony_ci	dcr->serial_number = ~handle[5];
279262306a36Sopenharmony_ci	dcr->code = NFIT_FIC_BYTE;
279362306a36Sopenharmony_ci	dcr->windows = 0;
279462306a36Sopenharmony_ci	offset += dcr->header.length;
279562306a36Sopenharmony_ci
279662306a36Sopenharmony_ci	memdev = nfit_buf + offset;
279762306a36Sopenharmony_ci	memdev->header.type = ACPI_NFIT_TYPE_MEMORY_MAP;
279862306a36Sopenharmony_ci	memdev->header.length = sizeof(*memdev);
279962306a36Sopenharmony_ci	memdev->device_handle = handle[6];
280062306a36Sopenharmony_ci	memdev->physical_id = 0;
280162306a36Sopenharmony_ci	memdev->region_id = 0;
280262306a36Sopenharmony_ci	memdev->range_index = 0;
280362306a36Sopenharmony_ci	memdev->region_index = 0+2;
280462306a36Sopenharmony_ci	memdev->region_size = SPA2_SIZE;
280562306a36Sopenharmony_ci	memdev->region_offset = 0;
280662306a36Sopenharmony_ci	memdev->address = 0;
280762306a36Sopenharmony_ci	memdev->interleave_index = 0;
280862306a36Sopenharmony_ci	memdev->interleave_ways = 1;
280962306a36Sopenharmony_ci	memdev->flags = ACPI_NFIT_MEM_MAP_FAILED;
281062306a36Sopenharmony_ci	offset += memdev->header.length;
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci	/* dcr-descriptor1 */
281362306a36Sopenharmony_ci	dcr = nfit_buf + offset;
281462306a36Sopenharmony_ci	dcr->header.type = ACPI_NFIT_TYPE_CONTROL_REGION;
281562306a36Sopenharmony_ci	dcr->header.length = offsetof(struct acpi_nfit_control_region,
281662306a36Sopenharmony_ci			window_size);
281762306a36Sopenharmony_ci	dcr->region_index = 0+2;
281862306a36Sopenharmony_ci	dcr_common_init(dcr);
281962306a36Sopenharmony_ci	dcr->serial_number = ~handle[6];
282062306a36Sopenharmony_ci	dcr->code = NFIT_FIC_BYTE;
282162306a36Sopenharmony_ci	dcr->windows = 0;
282262306a36Sopenharmony_ci	offset += dcr->header.length;
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci	/* sanity check to make sure we've filled the buffer */
282562306a36Sopenharmony_ci	WARN_ON(offset != t->nfit_size);
282662306a36Sopenharmony_ci
282762306a36Sopenharmony_ci	t->nfit_filled = offset;
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci	post_ars_status(&t->ars_state, &t->badrange, t->spa_set_dma[0],
283062306a36Sopenharmony_ci			SPA2_SIZE);
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ci	acpi_desc = &t->acpi_desc;
283362306a36Sopenharmony_ci	set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en);
283462306a36Sopenharmony_ci	set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en);
283562306a36Sopenharmony_ci	set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en);
283662306a36Sopenharmony_ci	set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en);
283762306a36Sopenharmony_ci	set_bit(ND_INTEL_ENABLE_LSS_STATUS, &acpi_desc->dimm_cmd_force_en);
283862306a36Sopenharmony_ci	set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_cmd_force_en);
283962306a36Sopenharmony_ci	set_bit(ND_CMD_GET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
284062306a36Sopenharmony_ci	set_bit(ND_CMD_SET_CONFIG_DATA, &acpi_desc->dimm_cmd_force_en);
284162306a36Sopenharmony_ci}
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_cistatic unsigned long nfit_ctl_handle;
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ciunion acpi_object *result;
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_cistatic union acpi_object *nfit_test_evaluate_dsm(acpi_handle handle,
284862306a36Sopenharmony_ci		const guid_t *guid, u64 rev, u64 func, union acpi_object *argv4)
284962306a36Sopenharmony_ci{
285062306a36Sopenharmony_ci	if (handle != &nfit_ctl_handle)
285162306a36Sopenharmony_ci		return ERR_PTR(-ENXIO);
285262306a36Sopenharmony_ci
285362306a36Sopenharmony_ci	return result;
285462306a36Sopenharmony_ci}
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_cistatic int setup_result(void *buf, size_t size)
285762306a36Sopenharmony_ci{
285862306a36Sopenharmony_ci	result = kmalloc(sizeof(union acpi_object) + size, GFP_KERNEL);
285962306a36Sopenharmony_ci	if (!result)
286062306a36Sopenharmony_ci		return -ENOMEM;
286162306a36Sopenharmony_ci	result->package.type = ACPI_TYPE_BUFFER,
286262306a36Sopenharmony_ci	result->buffer.pointer = (void *) (result + 1);
286362306a36Sopenharmony_ci	result->buffer.length = size;
286462306a36Sopenharmony_ci	memcpy(result->buffer.pointer, buf, size);
286562306a36Sopenharmony_ci	memset(buf, 0, size);
286662306a36Sopenharmony_ci	return 0;
286762306a36Sopenharmony_ci}
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_cistatic int nfit_ctl_test(struct device *dev)
287062306a36Sopenharmony_ci{
287162306a36Sopenharmony_ci	int rc, cmd_rc;
287262306a36Sopenharmony_ci	struct nvdimm *nvdimm;
287362306a36Sopenharmony_ci	struct acpi_device *adev;
287462306a36Sopenharmony_ci	struct nfit_mem *nfit_mem;
287562306a36Sopenharmony_ci	struct nd_ars_record *record;
287662306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc;
287762306a36Sopenharmony_ci	const u64 test_val = 0x0123456789abcdefULL;
287862306a36Sopenharmony_ci	unsigned long mask, cmd_size, offset;
287962306a36Sopenharmony_ci	struct nfit_ctl_test_cmd {
288062306a36Sopenharmony_ci		struct nd_cmd_pkg pkg;
288162306a36Sopenharmony_ci		union {
288262306a36Sopenharmony_ci			struct nd_cmd_get_config_size cfg_size;
288362306a36Sopenharmony_ci			struct nd_cmd_clear_error clear_err;
288462306a36Sopenharmony_ci			struct nd_cmd_ars_status ars_stat;
288562306a36Sopenharmony_ci			struct nd_cmd_ars_cap ars_cap;
288662306a36Sopenharmony_ci			struct nd_intel_bus_fw_activate_businfo fwa_info;
288762306a36Sopenharmony_ci			char buf[sizeof(struct nd_cmd_ars_status)
288862306a36Sopenharmony_ci				+ sizeof(struct nd_ars_record)];
288962306a36Sopenharmony_ci		};
289062306a36Sopenharmony_ci	} cmd;
289162306a36Sopenharmony_ci
289262306a36Sopenharmony_ci	adev = devm_kzalloc(dev, sizeof(*adev), GFP_KERNEL);
289362306a36Sopenharmony_ci	if (!adev)
289462306a36Sopenharmony_ci		return -ENOMEM;
289562306a36Sopenharmony_ci	*adev = (struct acpi_device) {
289662306a36Sopenharmony_ci		.handle = &nfit_ctl_handle,
289762306a36Sopenharmony_ci		.dev = {
289862306a36Sopenharmony_ci			.init_name = "test-adev",
289962306a36Sopenharmony_ci		},
290062306a36Sopenharmony_ci	};
290162306a36Sopenharmony_ci
290262306a36Sopenharmony_ci	acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL);
290362306a36Sopenharmony_ci	if (!acpi_desc)
290462306a36Sopenharmony_ci		return -ENOMEM;
290562306a36Sopenharmony_ci	*acpi_desc = (struct acpi_nfit_desc) {
290662306a36Sopenharmony_ci		.nd_desc = {
290762306a36Sopenharmony_ci			.cmd_mask = 1UL << ND_CMD_ARS_CAP
290862306a36Sopenharmony_ci				| 1UL << ND_CMD_ARS_START
290962306a36Sopenharmony_ci				| 1UL << ND_CMD_ARS_STATUS
291062306a36Sopenharmony_ci				| 1UL << ND_CMD_CLEAR_ERROR
291162306a36Sopenharmony_ci				| 1UL << ND_CMD_CALL,
291262306a36Sopenharmony_ci			.module = THIS_MODULE,
291362306a36Sopenharmony_ci			.provider_name = "ACPI.NFIT",
291462306a36Sopenharmony_ci			.ndctl = acpi_nfit_ctl,
291562306a36Sopenharmony_ci			.bus_family_mask = 1UL << NVDIMM_BUS_FAMILY_NFIT
291662306a36Sopenharmony_ci				| 1UL << NVDIMM_BUS_FAMILY_INTEL,
291762306a36Sopenharmony_ci		},
291862306a36Sopenharmony_ci		.bus_dsm_mask = 1UL << NFIT_CMD_TRANSLATE_SPA
291962306a36Sopenharmony_ci			| 1UL << NFIT_CMD_ARS_INJECT_SET
292062306a36Sopenharmony_ci			| 1UL << NFIT_CMD_ARS_INJECT_CLEAR
292162306a36Sopenharmony_ci			| 1UL << NFIT_CMD_ARS_INJECT_GET,
292262306a36Sopenharmony_ci		.family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL] =
292362306a36Sopenharmony_ci			NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK,
292462306a36Sopenharmony_ci		.dev = &adev->dev,
292562306a36Sopenharmony_ci	};
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci	nfit_mem = devm_kzalloc(dev, sizeof(*nfit_mem), GFP_KERNEL);
292862306a36Sopenharmony_ci	if (!nfit_mem)
292962306a36Sopenharmony_ci		return -ENOMEM;
293062306a36Sopenharmony_ci
293162306a36Sopenharmony_ci	mask = 1UL << ND_CMD_SMART | 1UL << ND_CMD_SMART_THRESHOLD
293262306a36Sopenharmony_ci		| 1UL << ND_CMD_DIMM_FLAGS | 1UL << ND_CMD_GET_CONFIG_SIZE
293362306a36Sopenharmony_ci		| 1UL << ND_CMD_GET_CONFIG_DATA | 1UL << ND_CMD_SET_CONFIG_DATA
293462306a36Sopenharmony_ci		| 1UL << ND_CMD_VENDOR;
293562306a36Sopenharmony_ci	*nfit_mem = (struct nfit_mem) {
293662306a36Sopenharmony_ci		.adev = adev,
293762306a36Sopenharmony_ci		.family = NVDIMM_FAMILY_INTEL,
293862306a36Sopenharmony_ci		.dsm_mask = mask,
293962306a36Sopenharmony_ci	};
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_ci	nvdimm = devm_kzalloc(dev, sizeof(*nvdimm), GFP_KERNEL);
294262306a36Sopenharmony_ci	if (!nvdimm)
294362306a36Sopenharmony_ci		return -ENOMEM;
294462306a36Sopenharmony_ci	*nvdimm = (struct nvdimm) {
294562306a36Sopenharmony_ci		.provider_data = nfit_mem,
294662306a36Sopenharmony_ci		.cmd_mask = mask,
294762306a36Sopenharmony_ci		.dev = {
294862306a36Sopenharmony_ci			.init_name = "test-dimm",
294962306a36Sopenharmony_ci		},
295062306a36Sopenharmony_ci	};
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci
295362306a36Sopenharmony_ci	/* basic checkout of a typical 'get config size' command */
295462306a36Sopenharmony_ci	cmd_size = sizeof(cmd.cfg_size);
295562306a36Sopenharmony_ci	cmd.cfg_size = (struct nd_cmd_get_config_size) {
295662306a36Sopenharmony_ci		.status = 0,
295762306a36Sopenharmony_ci		.config_size = SZ_128K,
295862306a36Sopenharmony_ci		.max_xfer = SZ_4K,
295962306a36Sopenharmony_ci	};
296062306a36Sopenharmony_ci	rc = setup_result(cmd.buf, cmd_size);
296162306a36Sopenharmony_ci	if (rc)
296262306a36Sopenharmony_ci		return rc;
296362306a36Sopenharmony_ci	rc = acpi_nfit_ctl(&acpi_desc->nd_desc, nvdimm, ND_CMD_GET_CONFIG_SIZE,
296462306a36Sopenharmony_ci			cmd.buf, cmd_size, &cmd_rc);
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_ci	if (rc < 0 || cmd_rc || cmd.cfg_size.status != 0
296762306a36Sopenharmony_ci			|| cmd.cfg_size.config_size != SZ_128K
296862306a36Sopenharmony_ci			|| cmd.cfg_size.max_xfer != SZ_4K) {
296962306a36Sopenharmony_ci		dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
297062306a36Sopenharmony_ci				__func__, __LINE__, rc, cmd_rc);
297162306a36Sopenharmony_ci		return -EIO;
297262306a36Sopenharmony_ci	}
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci
297562306a36Sopenharmony_ci	/* test ars_status with zero output */
297662306a36Sopenharmony_ci	cmd_size = offsetof(struct nd_cmd_ars_status, address);
297762306a36Sopenharmony_ci	cmd.ars_stat = (struct nd_cmd_ars_status) {
297862306a36Sopenharmony_ci		.out_length = 0,
297962306a36Sopenharmony_ci	};
298062306a36Sopenharmony_ci	rc = setup_result(cmd.buf, cmd_size);
298162306a36Sopenharmony_ci	if (rc)
298262306a36Sopenharmony_ci		return rc;
298362306a36Sopenharmony_ci	rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_STATUS,
298462306a36Sopenharmony_ci			cmd.buf, cmd_size, &cmd_rc);
298562306a36Sopenharmony_ci
298662306a36Sopenharmony_ci	if (rc < 0 || cmd_rc) {
298762306a36Sopenharmony_ci		dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
298862306a36Sopenharmony_ci				__func__, __LINE__, rc, cmd_rc);
298962306a36Sopenharmony_ci		return -EIO;
299062306a36Sopenharmony_ci	}
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci
299362306a36Sopenharmony_ci	/* test ars_cap with benign extended status */
299462306a36Sopenharmony_ci	cmd_size = sizeof(cmd.ars_cap);
299562306a36Sopenharmony_ci	cmd.ars_cap = (struct nd_cmd_ars_cap) {
299662306a36Sopenharmony_ci		.status = ND_ARS_PERSISTENT << 16,
299762306a36Sopenharmony_ci	};
299862306a36Sopenharmony_ci	offset = offsetof(struct nd_cmd_ars_cap, status);
299962306a36Sopenharmony_ci	rc = setup_result(cmd.buf + offset, cmd_size - offset);
300062306a36Sopenharmony_ci	if (rc)
300162306a36Sopenharmony_ci		return rc;
300262306a36Sopenharmony_ci	rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_CAP,
300362306a36Sopenharmony_ci			cmd.buf, cmd_size, &cmd_rc);
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci	if (rc < 0 || cmd_rc) {
300662306a36Sopenharmony_ci		dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
300762306a36Sopenharmony_ci				__func__, __LINE__, rc, cmd_rc);
300862306a36Sopenharmony_ci		return -EIO;
300962306a36Sopenharmony_ci	}
301062306a36Sopenharmony_ci
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	/* test ars_status with 'status' trimmed from 'out_length' */
301362306a36Sopenharmony_ci	cmd_size = sizeof(cmd.ars_stat) + sizeof(struct nd_ars_record);
301462306a36Sopenharmony_ci	cmd.ars_stat = (struct nd_cmd_ars_status) {
301562306a36Sopenharmony_ci		.out_length = cmd_size - 4,
301662306a36Sopenharmony_ci	};
301762306a36Sopenharmony_ci	record = &cmd.ars_stat.records[0];
301862306a36Sopenharmony_ci	*record = (struct nd_ars_record) {
301962306a36Sopenharmony_ci		.length = test_val,
302062306a36Sopenharmony_ci	};
302162306a36Sopenharmony_ci	rc = setup_result(cmd.buf, cmd_size);
302262306a36Sopenharmony_ci	if (rc)
302362306a36Sopenharmony_ci		return rc;
302462306a36Sopenharmony_ci	rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_STATUS,
302562306a36Sopenharmony_ci			cmd.buf, cmd_size, &cmd_rc);
302662306a36Sopenharmony_ci
302762306a36Sopenharmony_ci	if (rc < 0 || cmd_rc || record->length != test_val) {
302862306a36Sopenharmony_ci		dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
302962306a36Sopenharmony_ci				__func__, __LINE__, rc, cmd_rc);
303062306a36Sopenharmony_ci		return -EIO;
303162306a36Sopenharmony_ci	}
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_ci
303462306a36Sopenharmony_ci	/* test ars_status with 'Output (Size)' including 'status' */
303562306a36Sopenharmony_ci	cmd_size = sizeof(cmd.ars_stat) + sizeof(struct nd_ars_record);
303662306a36Sopenharmony_ci	cmd.ars_stat = (struct nd_cmd_ars_status) {
303762306a36Sopenharmony_ci		.out_length = cmd_size,
303862306a36Sopenharmony_ci	};
303962306a36Sopenharmony_ci	record = &cmd.ars_stat.records[0];
304062306a36Sopenharmony_ci	*record = (struct nd_ars_record) {
304162306a36Sopenharmony_ci		.length = test_val,
304262306a36Sopenharmony_ci	};
304362306a36Sopenharmony_ci	rc = setup_result(cmd.buf, cmd_size);
304462306a36Sopenharmony_ci	if (rc)
304562306a36Sopenharmony_ci		return rc;
304662306a36Sopenharmony_ci	rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_ARS_STATUS,
304762306a36Sopenharmony_ci			cmd.buf, cmd_size, &cmd_rc);
304862306a36Sopenharmony_ci
304962306a36Sopenharmony_ci	if (rc < 0 || cmd_rc || record->length != test_val) {
305062306a36Sopenharmony_ci		dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
305162306a36Sopenharmony_ci				__func__, __LINE__, rc, cmd_rc);
305262306a36Sopenharmony_ci		return -EIO;
305362306a36Sopenharmony_ci	}
305462306a36Sopenharmony_ci
305562306a36Sopenharmony_ci
305662306a36Sopenharmony_ci	/* test extended status for get_config_size results in failure */
305762306a36Sopenharmony_ci	cmd_size = sizeof(cmd.cfg_size);
305862306a36Sopenharmony_ci	cmd.cfg_size = (struct nd_cmd_get_config_size) {
305962306a36Sopenharmony_ci		.status = 1 << 16,
306062306a36Sopenharmony_ci	};
306162306a36Sopenharmony_ci	rc = setup_result(cmd.buf, cmd_size);
306262306a36Sopenharmony_ci	if (rc)
306362306a36Sopenharmony_ci		return rc;
306462306a36Sopenharmony_ci	rc = acpi_nfit_ctl(&acpi_desc->nd_desc, nvdimm, ND_CMD_GET_CONFIG_SIZE,
306562306a36Sopenharmony_ci			cmd.buf, cmd_size, &cmd_rc);
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_ci	if (rc < 0 || cmd_rc >= 0) {
306862306a36Sopenharmony_ci		dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
306962306a36Sopenharmony_ci				__func__, __LINE__, rc, cmd_rc);
307062306a36Sopenharmony_ci		return -EIO;
307162306a36Sopenharmony_ci	}
307262306a36Sopenharmony_ci
307362306a36Sopenharmony_ci	/* test clear error */
307462306a36Sopenharmony_ci	cmd_size = sizeof(cmd.clear_err);
307562306a36Sopenharmony_ci	cmd.clear_err = (struct nd_cmd_clear_error) {
307662306a36Sopenharmony_ci		.length = 512,
307762306a36Sopenharmony_ci		.cleared = 512,
307862306a36Sopenharmony_ci	};
307962306a36Sopenharmony_ci	rc = setup_result(cmd.buf, cmd_size);
308062306a36Sopenharmony_ci	if (rc)
308162306a36Sopenharmony_ci		return rc;
308262306a36Sopenharmony_ci	rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_CLEAR_ERROR,
308362306a36Sopenharmony_ci			cmd.buf, cmd_size, &cmd_rc);
308462306a36Sopenharmony_ci	if (rc < 0 || cmd_rc) {
308562306a36Sopenharmony_ci		dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
308662306a36Sopenharmony_ci				__func__, __LINE__, rc, cmd_rc);
308762306a36Sopenharmony_ci		return -EIO;
308862306a36Sopenharmony_ci	}
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci	/* test firmware activate bus info */
309162306a36Sopenharmony_ci	cmd_size = sizeof(cmd.fwa_info);
309262306a36Sopenharmony_ci	cmd = (struct nfit_ctl_test_cmd) {
309362306a36Sopenharmony_ci		.pkg = {
309462306a36Sopenharmony_ci			.nd_command = NVDIMM_BUS_INTEL_FW_ACTIVATE_BUSINFO,
309562306a36Sopenharmony_ci			.nd_family = NVDIMM_BUS_FAMILY_INTEL,
309662306a36Sopenharmony_ci			.nd_size_out = cmd_size,
309762306a36Sopenharmony_ci			.nd_fw_size = cmd_size,
309862306a36Sopenharmony_ci		},
309962306a36Sopenharmony_ci		.fwa_info = {
310062306a36Sopenharmony_ci			.state = ND_INTEL_FWA_IDLE,
310162306a36Sopenharmony_ci			.capability = ND_INTEL_BUS_FWA_CAP_FWQUIESCE
310262306a36Sopenharmony_ci				| ND_INTEL_BUS_FWA_CAP_OSQUIESCE,
310362306a36Sopenharmony_ci			.activate_tmo = 1,
310462306a36Sopenharmony_ci			.cpu_quiesce_tmo = 1,
310562306a36Sopenharmony_ci			.io_quiesce_tmo = 1,
310662306a36Sopenharmony_ci			.max_quiesce_tmo = 1,
310762306a36Sopenharmony_ci		},
310862306a36Sopenharmony_ci	};
310962306a36Sopenharmony_ci	rc = setup_result(cmd.buf, cmd_size);
311062306a36Sopenharmony_ci	if (rc)
311162306a36Sopenharmony_ci		return rc;
311262306a36Sopenharmony_ci	rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_CALL,
311362306a36Sopenharmony_ci			&cmd, sizeof(cmd.pkg) + cmd_size, &cmd_rc);
311462306a36Sopenharmony_ci	if (rc < 0 || cmd_rc) {
311562306a36Sopenharmony_ci		dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n",
311662306a36Sopenharmony_ci				__func__, __LINE__, rc, cmd_rc);
311762306a36Sopenharmony_ci		return -EIO;
311862306a36Sopenharmony_ci	}
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_ci	return 0;
312162306a36Sopenharmony_ci}
312262306a36Sopenharmony_ci
312362306a36Sopenharmony_cistatic int nfit_test_probe(struct platform_device *pdev)
312462306a36Sopenharmony_ci{
312562306a36Sopenharmony_ci	struct nvdimm_bus_descriptor *nd_desc;
312662306a36Sopenharmony_ci	struct acpi_nfit_desc *acpi_desc;
312762306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
312862306a36Sopenharmony_ci	struct nfit_test *nfit_test;
312962306a36Sopenharmony_ci	struct nfit_mem *nfit_mem;
313062306a36Sopenharmony_ci	union acpi_object *obj;
313162306a36Sopenharmony_ci	int rc;
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci	if (strcmp(dev_name(&pdev->dev), "nfit_test.0") == 0) {
313462306a36Sopenharmony_ci		rc = nfit_ctl_test(&pdev->dev);
313562306a36Sopenharmony_ci		if (rc)
313662306a36Sopenharmony_ci			return rc;
313762306a36Sopenharmony_ci	}
313862306a36Sopenharmony_ci
313962306a36Sopenharmony_ci	nfit_test = to_nfit_test(&pdev->dev);
314062306a36Sopenharmony_ci
314162306a36Sopenharmony_ci	/* common alloc */
314262306a36Sopenharmony_ci	if (nfit_test->num_dcr) {
314362306a36Sopenharmony_ci		int num = nfit_test->num_dcr;
314462306a36Sopenharmony_ci
314562306a36Sopenharmony_ci		nfit_test->dimm = devm_kcalloc(dev, num, sizeof(void *),
314662306a36Sopenharmony_ci				GFP_KERNEL);
314762306a36Sopenharmony_ci		nfit_test->dimm_dma = devm_kcalloc(dev, num, sizeof(dma_addr_t),
314862306a36Sopenharmony_ci				GFP_KERNEL);
314962306a36Sopenharmony_ci		nfit_test->flush = devm_kcalloc(dev, num, sizeof(void *),
315062306a36Sopenharmony_ci				GFP_KERNEL);
315162306a36Sopenharmony_ci		nfit_test->flush_dma = devm_kcalloc(dev, num, sizeof(dma_addr_t),
315262306a36Sopenharmony_ci				GFP_KERNEL);
315362306a36Sopenharmony_ci		nfit_test->label = devm_kcalloc(dev, num, sizeof(void *),
315462306a36Sopenharmony_ci				GFP_KERNEL);
315562306a36Sopenharmony_ci		nfit_test->label_dma = devm_kcalloc(dev, num,
315662306a36Sopenharmony_ci				sizeof(dma_addr_t), GFP_KERNEL);
315762306a36Sopenharmony_ci		nfit_test->dcr = devm_kcalloc(dev, num,
315862306a36Sopenharmony_ci				sizeof(struct nfit_test_dcr *), GFP_KERNEL);
315962306a36Sopenharmony_ci		nfit_test->dcr_dma = devm_kcalloc(dev, num,
316062306a36Sopenharmony_ci				sizeof(dma_addr_t), GFP_KERNEL);
316162306a36Sopenharmony_ci		nfit_test->smart = devm_kcalloc(dev, num,
316262306a36Sopenharmony_ci				sizeof(struct nd_intel_smart), GFP_KERNEL);
316362306a36Sopenharmony_ci		nfit_test->smart_threshold = devm_kcalloc(dev, num,
316462306a36Sopenharmony_ci				sizeof(struct nd_intel_smart_threshold),
316562306a36Sopenharmony_ci				GFP_KERNEL);
316662306a36Sopenharmony_ci		nfit_test->fw = devm_kcalloc(dev, num,
316762306a36Sopenharmony_ci				sizeof(struct nfit_test_fw), GFP_KERNEL);
316862306a36Sopenharmony_ci		if (nfit_test->dimm && nfit_test->dimm_dma && nfit_test->label
316962306a36Sopenharmony_ci				&& nfit_test->label_dma && nfit_test->dcr
317062306a36Sopenharmony_ci				&& nfit_test->dcr_dma && nfit_test->flush
317162306a36Sopenharmony_ci				&& nfit_test->flush_dma
317262306a36Sopenharmony_ci				&& nfit_test->fw)
317362306a36Sopenharmony_ci			/* pass */;
317462306a36Sopenharmony_ci		else
317562306a36Sopenharmony_ci			return -ENOMEM;
317662306a36Sopenharmony_ci	}
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_ci	if (nfit_test->num_pm) {
317962306a36Sopenharmony_ci		int num = nfit_test->num_pm;
318062306a36Sopenharmony_ci
318162306a36Sopenharmony_ci		nfit_test->spa_set = devm_kcalloc(dev, num, sizeof(void *),
318262306a36Sopenharmony_ci				GFP_KERNEL);
318362306a36Sopenharmony_ci		nfit_test->spa_set_dma = devm_kcalloc(dev, num,
318462306a36Sopenharmony_ci				sizeof(dma_addr_t), GFP_KERNEL);
318562306a36Sopenharmony_ci		if (nfit_test->spa_set && nfit_test->spa_set_dma)
318662306a36Sopenharmony_ci			/* pass */;
318762306a36Sopenharmony_ci		else
318862306a36Sopenharmony_ci			return -ENOMEM;
318962306a36Sopenharmony_ci	}
319062306a36Sopenharmony_ci
319162306a36Sopenharmony_ci	/* per-nfit specific alloc */
319262306a36Sopenharmony_ci	if (nfit_test->alloc(nfit_test))
319362306a36Sopenharmony_ci		return -ENOMEM;
319462306a36Sopenharmony_ci
319562306a36Sopenharmony_ci	nfit_test->setup(nfit_test);
319662306a36Sopenharmony_ci	acpi_desc = &nfit_test->acpi_desc;
319762306a36Sopenharmony_ci	acpi_nfit_desc_init(acpi_desc, &pdev->dev);
319862306a36Sopenharmony_ci	nd_desc = &acpi_desc->nd_desc;
319962306a36Sopenharmony_ci	nd_desc->provider_name = NULL;
320062306a36Sopenharmony_ci	nd_desc->module = THIS_MODULE;
320162306a36Sopenharmony_ci	nd_desc->ndctl = nfit_test_ctl;
320262306a36Sopenharmony_ci
320362306a36Sopenharmony_ci	rc = acpi_nfit_init(acpi_desc, nfit_test->nfit_buf,
320462306a36Sopenharmony_ci			nfit_test->nfit_filled);
320562306a36Sopenharmony_ci	if (rc)
320662306a36Sopenharmony_ci		return rc;
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci	rc = devm_add_action_or_reset(&pdev->dev, acpi_nfit_shutdown, acpi_desc);
320962306a36Sopenharmony_ci	if (rc)
321062306a36Sopenharmony_ci		return rc;
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	if (nfit_test->setup != nfit_test0_setup)
321362306a36Sopenharmony_ci		return 0;
321462306a36Sopenharmony_ci
321562306a36Sopenharmony_ci	nfit_test->setup_hotplug = 1;
321662306a36Sopenharmony_ci	nfit_test->setup(nfit_test);
321762306a36Sopenharmony_ci
321862306a36Sopenharmony_ci	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
321962306a36Sopenharmony_ci	if (!obj)
322062306a36Sopenharmony_ci		return -ENOMEM;
322162306a36Sopenharmony_ci	obj->type = ACPI_TYPE_BUFFER;
322262306a36Sopenharmony_ci	obj->buffer.length = nfit_test->nfit_size;
322362306a36Sopenharmony_ci	obj->buffer.pointer = nfit_test->nfit_buf;
322462306a36Sopenharmony_ci	*(nfit_test->_fit) = obj;
322562306a36Sopenharmony_ci	__acpi_nfit_notify(&pdev->dev, nfit_test, 0x80);
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_ci	/* associate dimm devices with nfit_mem data for notification testing */
322862306a36Sopenharmony_ci	mutex_lock(&acpi_desc->init_mutex);
322962306a36Sopenharmony_ci	list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
323062306a36Sopenharmony_ci		u32 nfit_handle = __to_nfit_memdev(nfit_mem)->device_handle;
323162306a36Sopenharmony_ci		int i;
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(handle); i++)
323462306a36Sopenharmony_ci			if (nfit_handle == handle[i])
323562306a36Sopenharmony_ci				dev_set_drvdata(nfit_test->dimm_dev[i],
323662306a36Sopenharmony_ci						nfit_mem);
323762306a36Sopenharmony_ci	}
323862306a36Sopenharmony_ci	mutex_unlock(&acpi_desc->init_mutex);
323962306a36Sopenharmony_ci
324062306a36Sopenharmony_ci	return 0;
324162306a36Sopenharmony_ci}
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_cistatic void nfit_test_release(struct device *dev)
324462306a36Sopenharmony_ci{
324562306a36Sopenharmony_ci	struct nfit_test *nfit_test = to_nfit_test(dev);
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci	kfree(nfit_test);
324862306a36Sopenharmony_ci}
324962306a36Sopenharmony_ci
325062306a36Sopenharmony_cistatic const struct platform_device_id nfit_test_id[] = {
325162306a36Sopenharmony_ci	{ KBUILD_MODNAME },
325262306a36Sopenharmony_ci	{ },
325362306a36Sopenharmony_ci};
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_cistatic struct platform_driver nfit_test_driver = {
325662306a36Sopenharmony_ci	.probe = nfit_test_probe,
325762306a36Sopenharmony_ci	.driver = {
325862306a36Sopenharmony_ci		.name = KBUILD_MODNAME,
325962306a36Sopenharmony_ci	},
326062306a36Sopenharmony_ci	.id_table = nfit_test_id,
326162306a36Sopenharmony_ci};
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_cistatic __init int nfit_test_init(void)
326462306a36Sopenharmony_ci{
326562306a36Sopenharmony_ci	int rc, i;
326662306a36Sopenharmony_ci
326762306a36Sopenharmony_ci	pmem_test();
326862306a36Sopenharmony_ci	libnvdimm_test();
326962306a36Sopenharmony_ci	acpi_nfit_test();
327062306a36Sopenharmony_ci	device_dax_test();
327162306a36Sopenharmony_ci	dax_pmem_test();
327262306a36Sopenharmony_ci
327362306a36Sopenharmony_ci	nfit_test_setup(nfit_test_lookup, nfit_test_evaluate_dsm);
327462306a36Sopenharmony_ci
327562306a36Sopenharmony_ci	nfit_wq = create_singlethread_workqueue("nfit");
327662306a36Sopenharmony_ci	if (!nfit_wq)
327762306a36Sopenharmony_ci		return -ENOMEM;
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci	nfit_test_dimm = class_create("nfit_test_dimm");
328062306a36Sopenharmony_ci	if (IS_ERR(nfit_test_dimm)) {
328162306a36Sopenharmony_ci		rc = PTR_ERR(nfit_test_dimm);
328262306a36Sopenharmony_ci		goto err_register;
328362306a36Sopenharmony_ci	}
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_ci	nfit_pool = gen_pool_create(ilog2(SZ_4M), NUMA_NO_NODE);
328662306a36Sopenharmony_ci	if (!nfit_pool) {
328762306a36Sopenharmony_ci		rc = -ENOMEM;
328862306a36Sopenharmony_ci		goto err_register;
328962306a36Sopenharmony_ci	}
329062306a36Sopenharmony_ci
329162306a36Sopenharmony_ci	if (gen_pool_add(nfit_pool, SZ_4G, SZ_4G, NUMA_NO_NODE)) {
329262306a36Sopenharmony_ci		rc = -ENOMEM;
329362306a36Sopenharmony_ci		goto err_register;
329462306a36Sopenharmony_ci	}
329562306a36Sopenharmony_ci
329662306a36Sopenharmony_ci	for (i = 0; i < NUM_NFITS; i++) {
329762306a36Sopenharmony_ci		struct nfit_test *nfit_test;
329862306a36Sopenharmony_ci		struct platform_device *pdev;
329962306a36Sopenharmony_ci
330062306a36Sopenharmony_ci		nfit_test = kzalloc(sizeof(*nfit_test), GFP_KERNEL);
330162306a36Sopenharmony_ci		if (!nfit_test) {
330262306a36Sopenharmony_ci			rc = -ENOMEM;
330362306a36Sopenharmony_ci			goto err_register;
330462306a36Sopenharmony_ci		}
330562306a36Sopenharmony_ci		INIT_LIST_HEAD(&nfit_test->resources);
330662306a36Sopenharmony_ci		badrange_init(&nfit_test->badrange);
330762306a36Sopenharmony_ci		switch (i) {
330862306a36Sopenharmony_ci		case 0:
330962306a36Sopenharmony_ci			nfit_test->num_pm = NUM_PM;
331062306a36Sopenharmony_ci			nfit_test->dcr_idx = 0;
331162306a36Sopenharmony_ci			nfit_test->num_dcr = NUM_DCR;
331262306a36Sopenharmony_ci			nfit_test->alloc = nfit_test0_alloc;
331362306a36Sopenharmony_ci			nfit_test->setup = nfit_test0_setup;
331462306a36Sopenharmony_ci			break;
331562306a36Sopenharmony_ci		case 1:
331662306a36Sopenharmony_ci			nfit_test->num_pm = 2;
331762306a36Sopenharmony_ci			nfit_test->dcr_idx = NUM_DCR;
331862306a36Sopenharmony_ci			nfit_test->num_dcr = 2;
331962306a36Sopenharmony_ci			nfit_test->alloc = nfit_test1_alloc;
332062306a36Sopenharmony_ci			nfit_test->setup = nfit_test1_setup;
332162306a36Sopenharmony_ci			break;
332262306a36Sopenharmony_ci		default:
332362306a36Sopenharmony_ci			rc = -EINVAL;
332462306a36Sopenharmony_ci			goto err_register;
332562306a36Sopenharmony_ci		}
332662306a36Sopenharmony_ci		pdev = &nfit_test->pdev;
332762306a36Sopenharmony_ci		pdev->name = KBUILD_MODNAME;
332862306a36Sopenharmony_ci		pdev->id = i;
332962306a36Sopenharmony_ci		pdev->dev.release = nfit_test_release;
333062306a36Sopenharmony_ci		rc = platform_device_register(pdev);
333162306a36Sopenharmony_ci		if (rc) {
333262306a36Sopenharmony_ci			put_device(&pdev->dev);
333362306a36Sopenharmony_ci			goto err_register;
333462306a36Sopenharmony_ci		}
333562306a36Sopenharmony_ci		get_device(&pdev->dev);
333662306a36Sopenharmony_ci
333762306a36Sopenharmony_ci		rc = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
333862306a36Sopenharmony_ci		if (rc)
333962306a36Sopenharmony_ci			goto err_register;
334062306a36Sopenharmony_ci
334162306a36Sopenharmony_ci		instances[i] = nfit_test;
334262306a36Sopenharmony_ci		INIT_WORK(&nfit_test->work, uc_error_notify);
334362306a36Sopenharmony_ci	}
334462306a36Sopenharmony_ci
334562306a36Sopenharmony_ci	rc = platform_driver_register(&nfit_test_driver);
334662306a36Sopenharmony_ci	if (rc)
334762306a36Sopenharmony_ci		goto err_register;
334862306a36Sopenharmony_ci	return 0;
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci err_register:
335162306a36Sopenharmony_ci	if (nfit_pool)
335262306a36Sopenharmony_ci		gen_pool_destroy(nfit_pool);
335362306a36Sopenharmony_ci
335462306a36Sopenharmony_ci	destroy_workqueue(nfit_wq);
335562306a36Sopenharmony_ci	for (i = 0; i < NUM_NFITS; i++)
335662306a36Sopenharmony_ci		if (instances[i])
335762306a36Sopenharmony_ci			platform_device_unregister(&instances[i]->pdev);
335862306a36Sopenharmony_ci	nfit_test_teardown();
335962306a36Sopenharmony_ci	for (i = 0; i < NUM_NFITS; i++)
336062306a36Sopenharmony_ci		if (instances[i])
336162306a36Sopenharmony_ci			put_device(&instances[i]->pdev.dev);
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_ci	return rc;
336462306a36Sopenharmony_ci}
336562306a36Sopenharmony_ci
336662306a36Sopenharmony_cistatic __exit void nfit_test_exit(void)
336762306a36Sopenharmony_ci{
336862306a36Sopenharmony_ci	int i;
336962306a36Sopenharmony_ci
337062306a36Sopenharmony_ci	destroy_workqueue(nfit_wq);
337162306a36Sopenharmony_ci	for (i = 0; i < NUM_NFITS; i++)
337262306a36Sopenharmony_ci		platform_device_unregister(&instances[i]->pdev);
337362306a36Sopenharmony_ci	platform_driver_unregister(&nfit_test_driver);
337462306a36Sopenharmony_ci	nfit_test_teardown();
337562306a36Sopenharmony_ci
337662306a36Sopenharmony_ci	gen_pool_destroy(nfit_pool);
337762306a36Sopenharmony_ci
337862306a36Sopenharmony_ci	for (i = 0; i < NUM_NFITS; i++)
337962306a36Sopenharmony_ci		put_device(&instances[i]->pdev.dev);
338062306a36Sopenharmony_ci	class_destroy(nfit_test_dimm);
338162306a36Sopenharmony_ci}
338262306a36Sopenharmony_ci
338362306a36Sopenharmony_cimodule_init(nfit_test_init);
338462306a36Sopenharmony_cimodule_exit(nfit_test_exit);
338562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
338662306a36Sopenharmony_ciMODULE_AUTHOR("Intel Corporation");
3387