xref: /openbmc/linux/drivers/acpi/nfit/core.c (revision 16f6ccde74a6f8538c62f127f17207c75f4dba7a)
15b497af4SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2bdf97013SDan Williams /*
3bdf97013SDan Williams  * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
4bdf97013SDan Williams  */
5bdf97013SDan Williams #include <linux/list_sort.h>
6bdf97013SDan Williams #include <linux/libnvdimm.h>
7bdf97013SDan Williams #include <linux/module.h>
89a7e3d7fSDan Williams #include <linux/nospec.h>
9bdf97013SDan Williams #include <linux/mutex.h>
10bdf97013SDan Williams #include <linux/ndctl.h>
11bdf97013SDan Williams #include <linux/sysfs.h>
12bdf97013SDan Williams #include <linux/delay.h>
13bdf97013SDan Williams #include <linux/list.h>
14bdf97013SDan Williams #include <linux/acpi.h>
15bdf97013SDan Williams #include <linux/sort.h>
16bdf97013SDan Williams #include <linux/io.h>
17bdf97013SDan Williams #include <linux/nd.h>
18bdf97013SDan Williams #include <asm/cacheflush.h>
1923222f8fSTony Luck #include <acpi/nfit.h>
20b3ed2ce0SDave Jiang #include "intel.h"
21bdf97013SDan Williams #include "nfit.h"
22bdf97013SDan Williams 
23bdf97013SDan Williams /*
24bdf97013SDan Williams  * For readq() and writeq() on 32-bit builds, the hi-lo, lo-hi order is
25bdf97013SDan Williams  * irrelevant.
26bdf97013SDan Williams  */
27bdf97013SDan Williams #include <linux/io-64-nonatomic-hi-lo.h>
28bdf97013SDan Williams 
29bdf97013SDan Williams static bool force_enable_dimms;
30bdf97013SDan Williams module_param(force_enable_dimms, bool, S_IRUGO|S_IWUSR);
31bdf97013SDan Williams MODULE_PARM_DESC(force_enable_dimms, "Ignore _STA (ACPI DIMM device) status");
32bdf97013SDan Williams 
33bdf97013SDan Williams static bool disable_vendor_specific;
34bdf97013SDan Williams module_param(disable_vendor_specific, bool, S_IRUGO);
35bdf97013SDan Williams MODULE_PARM_DESC(disable_vendor_specific,
36f2668fa7SLinda Knippers 		"Limit commands to the publicly specified set");
37bdf97013SDan Williams 
38095ab4b3SLinda Knippers static unsigned long override_dsm_mask;
39095ab4b3SLinda Knippers module_param(override_dsm_mask, ulong, S_IRUGO);
40095ab4b3SLinda Knippers MODULE_PARM_DESC(override_dsm_mask, "Bitmask of allowed NVDIMM DSM functions");
41095ab4b3SLinda Knippers 
42ba650cfcSLinda Knippers static int default_dsm_family = -1;
43ba650cfcSLinda Knippers module_param(default_dsm_family, int, S_IRUGO);
44ba650cfcSLinda Knippers MODULE_PARM_DESC(default_dsm_family,
45ba650cfcSLinda Knippers 		"Try this DSM type first when identifying NVDIMM family");
46ba650cfcSLinda Knippers 
47bca811a7SDan Williams static bool no_init_ars;
48bca811a7SDan Williams module_param(no_init_ars, bool, 0644);
49bca811a7SDan Williams MODULE_PARM_DESC(no_init_ars, "Skip ARS run at nfit init time");
50bca811a7SDan Williams 
510171b6b7SDan Williams static bool force_labels;
520171b6b7SDan Williams module_param(force_labels, bool, 0444);
530171b6b7SDan Williams MODULE_PARM_DESC(force_labels, "Opt-in to labels despite missing methods");
540171b6b7SDan Williams 
556839a6d9SVishal Verma LIST_HEAD(acpi_descs);
566839a6d9SVishal Verma DEFINE_MUTEX(acpi_desc_lock);
576839a6d9SVishal Verma 
58bdf97013SDan Williams static struct workqueue_struct *nfit_wq;
59bdf97013SDan Williams 
60bdf97013SDan Williams struct nfit_table_prev {
61bdf97013SDan Williams 	struct list_head spas;
62bdf97013SDan Williams 	struct list_head memdevs;
63bdf97013SDan Williams 	struct list_head dcrs;
64bdf97013SDan Williams 	struct list_head bdws;
65bdf97013SDan Williams 	struct list_head idts;
66bdf97013SDan Williams 	struct list_head flushes;
67bdf97013SDan Williams };
68bdf97013SDan Williams 
6941c8bdb3SAndy Shevchenko static guid_t nfit_uuid[NFIT_UUID_MAX];
70bdf97013SDan Williams 
to_nfit_uuid(enum nfit_uuids id)7141c8bdb3SAndy Shevchenko const guid_t *to_nfit_uuid(enum nfit_uuids id)
72bdf97013SDan Williams {
7341c8bdb3SAndy Shevchenko 	return &nfit_uuid[id];
74bdf97013SDan Williams }
75bdf97013SDan Williams EXPORT_SYMBOL(to_nfit_uuid);
76bdf97013SDan Williams 
to_nfit_bus_uuid(int family)776450ddbdSDan Williams static const guid_t *to_nfit_bus_uuid(int family)
786450ddbdSDan Williams {
796450ddbdSDan Williams 	if (WARN_ONCE(family == NVDIMM_BUS_FAMILY_NFIT,
806450ddbdSDan Williams 			"only secondary bus families can be translated\n"))
816450ddbdSDan Williams 		return NULL;
826450ddbdSDan Williams 	/*
836450ddbdSDan Williams 	 * The index of bus UUIDs starts immediately following the last
846450ddbdSDan Williams 	 * NVDIMM/leaf family.
856450ddbdSDan Williams 	 */
866450ddbdSDan Williams 	return to_nfit_uuid(family + NVDIMM_FAMILY_MAX);
876450ddbdSDan Williams }
886450ddbdSDan Williams 
to_acpi_dev(struct acpi_nfit_desc * acpi_desc)89bdf97013SDan Williams static struct acpi_device *to_acpi_dev(struct acpi_nfit_desc *acpi_desc)
90bdf97013SDan Williams {
91bdf97013SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
92bdf97013SDan Williams 
93bdf97013SDan Williams 	/*
94bdf97013SDan Williams 	 * If provider == 'ACPI.NFIT' we can assume 'dev' is a struct
95bdf97013SDan Williams 	 * acpi_device.
96bdf97013SDan Williams 	 */
97bdf97013SDan Williams 	if (!nd_desc->provider_name
98bdf97013SDan Williams 			|| strcmp(nd_desc->provider_name, "ACPI.NFIT") != 0)
99bdf97013SDan Williams 		return NULL;
100bdf97013SDan Williams 
101bdf97013SDan Williams 	return to_acpi_device(acpi_desc->dev);
102bdf97013SDan Williams }
103bdf97013SDan Williams 
xlat_bus_status(void * buf,unsigned int cmd,u32 status)104d6eb270cSDan Williams static int xlat_bus_status(void *buf, unsigned int cmd, u32 status)
105bdf97013SDan Williams {
106bdf97013SDan Williams 	struct nd_cmd_clear_error *clear_err;
107bdf97013SDan Williams 	struct nd_cmd_ars_status *ars_status;
108bdf97013SDan Williams 	u16 flags;
109bdf97013SDan Williams 
110bdf97013SDan Williams 	switch (cmd) {
111bdf97013SDan Williams 	case ND_CMD_ARS_CAP:
11211294d63SDan Williams 		if ((status & 0xffff) == NFIT_ARS_CAP_NONE)
113bdf97013SDan Williams 			return -ENOTTY;
114bdf97013SDan Williams 
115bdf97013SDan Williams 		/* Command failed */
11611294d63SDan Williams 		if (status & 0xffff)
117bdf97013SDan Williams 			return -EIO;
118bdf97013SDan Williams 
119bdf97013SDan Williams 		/* No supported scan types for this range */
120bdf97013SDan Williams 		flags = ND_ARS_PERSISTENT | ND_ARS_VOLATILE;
12111294d63SDan Williams 		if ((status >> 16 & flags) == 0)
122bdf97013SDan Williams 			return -ENOTTY;
1239a901f54SVishal Verma 		return 0;
124bdf97013SDan Williams 	case ND_CMD_ARS_START:
125bdf97013SDan Williams 		/* ARS is in progress */
12611294d63SDan Williams 		if ((status & 0xffff) == NFIT_ARS_START_BUSY)
127bdf97013SDan Williams 			return -EBUSY;
128bdf97013SDan Williams 
129bdf97013SDan Williams 		/* Command failed */
13011294d63SDan Williams 		if (status & 0xffff)
131bdf97013SDan Williams 			return -EIO;
1329a901f54SVishal Verma 		return 0;
133bdf97013SDan Williams 	case ND_CMD_ARS_STATUS:
134bdf97013SDan Williams 		ars_status = buf;
135bdf97013SDan Williams 		/* Command failed */
13611294d63SDan Williams 		if (status & 0xffff)
137bdf97013SDan Williams 			return -EIO;
138bdf97013SDan Williams 		/* Check extended status (Upper two bytes) */
13911294d63SDan Williams 		if (status == NFIT_ARS_STATUS_DONE)
140bdf97013SDan Williams 			return 0;
141bdf97013SDan Williams 
142bdf97013SDan Williams 		/* ARS is in progress */
14311294d63SDan Williams 		if (status == NFIT_ARS_STATUS_BUSY)
144bdf97013SDan Williams 			return -EBUSY;
145bdf97013SDan Williams 
146bdf97013SDan Williams 		/* No ARS performed for the current boot */
14711294d63SDan Williams 		if (status == NFIT_ARS_STATUS_NONE)
148bdf97013SDan Williams 			return -EAGAIN;
149bdf97013SDan Williams 
150bdf97013SDan Williams 		/*
151bdf97013SDan Williams 		 * ARS interrupted, either we overflowed or some other
152bdf97013SDan Williams 		 * agent wants the scan to stop.  If we didn't overflow
153bdf97013SDan Williams 		 * then just continue with the returned results.
154bdf97013SDan Williams 		 */
15511294d63SDan Williams 		if (status == NFIT_ARS_STATUS_INTR) {
15682aa37cfSDan Williams 			if (ars_status->out_length >= 40 && (ars_status->flags
15782aa37cfSDan Williams 						& NFIT_ARS_F_OVERFLOW))
158bdf97013SDan Williams 				return -ENOSPC;
159bdf97013SDan Williams 			return 0;
160bdf97013SDan Williams 		}
161bdf97013SDan Williams 
162bdf97013SDan Williams 		/* Unknown status */
16311294d63SDan Williams 		if (status >> 16)
164bdf97013SDan Williams 			return -EIO;
1659a901f54SVishal Verma 		return 0;
166bdf97013SDan Williams 	case ND_CMD_CLEAR_ERROR:
167bdf97013SDan Williams 		clear_err = buf;
16811294d63SDan Williams 		if (status & 0xffff)
169bdf97013SDan Williams 			return -EIO;
170bdf97013SDan Williams 		if (!clear_err->cleared)
171bdf97013SDan Williams 			return -EIO;
172bdf97013SDan Williams 		if (clear_err->length > clear_err->cleared)
173bdf97013SDan Williams 			return clear_err->cleared;
1749a901f54SVishal Verma 		return 0;
175bdf97013SDan Williams 	default:
176bdf97013SDan Williams 		break;
177bdf97013SDan Williams 	}
178bdf97013SDan Williams 
17911294d63SDan Williams 	/* all other non-zero status results in an error */
18011294d63SDan Williams 	if (status)
18111294d63SDan Williams 		return -EIO;
182bdf97013SDan Williams 	return 0;
183bdf97013SDan Williams }
184bdf97013SDan Williams 
1854b27db7eSDan Williams #define ACPI_LABELS_LOCKED 3
1864b27db7eSDan Williams 
xlat_nvdimm_status(struct nvdimm * nvdimm,void * buf,unsigned int cmd,u32 status)1874b27db7eSDan Williams static int xlat_nvdimm_status(struct nvdimm *nvdimm, void *buf, unsigned int cmd,
1884b27db7eSDan Williams 		u32 status)
1899d62ed96SDan Williams {
1904b27db7eSDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
1914b27db7eSDan Williams 
1929d62ed96SDan Williams 	switch (cmd) {
1939d62ed96SDan Williams 	case ND_CMD_GET_CONFIG_SIZE:
1944b27db7eSDan Williams 		/*
1954b27db7eSDan Williams 		 * In the _LSI, _LSR, _LSW case the locked status is
1964b27db7eSDan Williams 		 * communicated via the read/write commands
1974b27db7eSDan Williams 		 */
1986f07f86cSDan Williams 		if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags))
1994b27db7eSDan Williams 			break;
2004b27db7eSDan Williams 
2019d62ed96SDan Williams 		if (status >> 16 & ND_CONFIG_LOCKED)
2029d62ed96SDan Williams 			return -EACCES;
2039d62ed96SDan Williams 		break;
2044b27db7eSDan Williams 	case ND_CMD_GET_CONFIG_DATA:
2056f07f86cSDan Williams 		if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)
2066f07f86cSDan Williams 				&& status == ACPI_LABELS_LOCKED)
2074b27db7eSDan Williams 			return -EACCES;
2084b27db7eSDan Williams 		break;
2094b27db7eSDan Williams 	case ND_CMD_SET_CONFIG_DATA:
2106f07f86cSDan Williams 		if (test_bit(NFIT_MEM_LSW, &nfit_mem->flags)
2116f07f86cSDan Williams 				&& status == ACPI_LABELS_LOCKED)
2124b27db7eSDan Williams 			return -EACCES;
2134b27db7eSDan Williams 		break;
2149d62ed96SDan Williams 	default:
2159d62ed96SDan Williams 		break;
2169d62ed96SDan Williams 	}
2179d62ed96SDan Williams 
2189d62ed96SDan Williams 	/* all other non-zero status results in an error */
2199d62ed96SDan Williams 	if (status)
2209d62ed96SDan Williams 		return -EIO;
2219d62ed96SDan Williams 	return 0;
2229d62ed96SDan Williams }
2239d62ed96SDan Williams 
xlat_status(struct nvdimm * nvdimm,void * buf,unsigned int cmd,u32 status)224d6eb270cSDan Williams static int xlat_status(struct nvdimm *nvdimm, void *buf, unsigned int cmd,
225d6eb270cSDan Williams 		u32 status)
226d6eb270cSDan Williams {
227d6eb270cSDan Williams 	if (!nvdimm)
228d6eb270cSDan Williams 		return xlat_bus_status(buf, cmd, status);
2294b27db7eSDan Williams 	return xlat_nvdimm_status(nvdimm, buf, cmd, status);
2304b27db7eSDan Williams }
2314b27db7eSDan Williams 
2324b27db7eSDan Williams /* convert _LS{I,R} packages to the buffer object acpi_nfit_ctl expects */
pkg_to_buf(union acpi_object * pkg)2334b27db7eSDan Williams static union acpi_object *pkg_to_buf(union acpi_object *pkg)
2344b27db7eSDan Williams {
2354b27db7eSDan Williams 	int i;
2364b27db7eSDan Williams 	void *dst;
2374b27db7eSDan Williams 	size_t size = 0;
2384b27db7eSDan Williams 	union acpi_object *buf = NULL;
2394b27db7eSDan Williams 
2404b27db7eSDan Williams 	if (pkg->type != ACPI_TYPE_PACKAGE) {
2414b27db7eSDan Williams 		WARN_ONCE(1, "BIOS bug, unexpected element type: %d\n",
2424b27db7eSDan Williams 				pkg->type);
2434b27db7eSDan Williams 		goto err;
2444b27db7eSDan Williams 	}
2454b27db7eSDan Williams 
2464b27db7eSDan Williams 	for (i = 0; i < pkg->package.count; i++) {
2474b27db7eSDan Williams 		union acpi_object *obj = &pkg->package.elements[i];
2484b27db7eSDan Williams 
2494b27db7eSDan Williams 		if (obj->type == ACPI_TYPE_INTEGER)
2504b27db7eSDan Williams 			size += 4;
2514b27db7eSDan Williams 		else if (obj->type == ACPI_TYPE_BUFFER)
2524b27db7eSDan Williams 			size += obj->buffer.length;
2534b27db7eSDan Williams 		else {
2544b27db7eSDan Williams 			WARN_ONCE(1, "BIOS bug, unexpected element type: %d\n",
2554b27db7eSDan Williams 					obj->type);
2564b27db7eSDan Williams 			goto err;
2574b27db7eSDan Williams 		}
2584b27db7eSDan Williams 	}
2594b27db7eSDan Williams 
2604b27db7eSDan Williams 	buf = ACPI_ALLOCATE(sizeof(*buf) + size);
2614b27db7eSDan Williams 	if (!buf)
2624b27db7eSDan Williams 		goto err;
2634b27db7eSDan Williams 
2644b27db7eSDan Williams 	dst = buf + 1;
2654b27db7eSDan Williams 	buf->type = ACPI_TYPE_BUFFER;
2664b27db7eSDan Williams 	buf->buffer.length = size;
2674b27db7eSDan Williams 	buf->buffer.pointer = dst;
2684b27db7eSDan Williams 	for (i = 0; i < pkg->package.count; i++) {
2694b27db7eSDan Williams 		union acpi_object *obj = &pkg->package.elements[i];
2704b27db7eSDan Williams 
2714b27db7eSDan Williams 		if (obj->type == ACPI_TYPE_INTEGER) {
2724b27db7eSDan Williams 			memcpy(dst, &obj->integer.value, 4);
2734b27db7eSDan Williams 			dst += 4;
2744b27db7eSDan Williams 		} else if (obj->type == ACPI_TYPE_BUFFER) {
2754b27db7eSDan Williams 			memcpy(dst, obj->buffer.pointer, obj->buffer.length);
2764b27db7eSDan Williams 			dst += obj->buffer.length;
2774b27db7eSDan Williams 		}
2784b27db7eSDan Williams 	}
2794b27db7eSDan Williams err:
2804b27db7eSDan Williams 	ACPI_FREE(pkg);
2814b27db7eSDan Williams 	return buf;
2824b27db7eSDan Williams }
2834b27db7eSDan Williams 
int_to_buf(union acpi_object * integer)2844b27db7eSDan Williams static union acpi_object *int_to_buf(union acpi_object *integer)
2854b27db7eSDan Williams {
2861a57b1a3SZhen Lei 	union acpi_object *buf = NULL;
2874b27db7eSDan Williams 	void *dst = NULL;
2884b27db7eSDan Williams 
2894b27db7eSDan Williams 	if (integer->type != ACPI_TYPE_INTEGER) {
2904b27db7eSDan Williams 		WARN_ONCE(1, "BIOS bug, unexpected element type: %d\n",
2914b27db7eSDan Williams 				integer->type);
2924b27db7eSDan Williams 		goto err;
2934b27db7eSDan Williams 	}
2944b27db7eSDan Williams 
2951a57b1a3SZhen Lei 	buf = ACPI_ALLOCATE(sizeof(*buf) + 4);
2961a57b1a3SZhen Lei 	if (!buf)
2971a57b1a3SZhen Lei 		goto err;
2981a57b1a3SZhen Lei 
2994b27db7eSDan Williams 	dst = buf + 1;
3004b27db7eSDan Williams 	buf->type = ACPI_TYPE_BUFFER;
3014b27db7eSDan Williams 	buf->buffer.length = 4;
3024b27db7eSDan Williams 	buf->buffer.pointer = dst;
3034b27db7eSDan Williams 	memcpy(dst, &integer->integer.value, 4);
3044b27db7eSDan Williams err:
3054b27db7eSDan Williams 	ACPI_FREE(integer);
3064b27db7eSDan Williams 	return buf;
3074b27db7eSDan Williams }
3084b27db7eSDan Williams 
acpi_label_write(acpi_handle handle,u32 offset,u32 len,void * data)3094b27db7eSDan Williams static union acpi_object *acpi_label_write(acpi_handle handle, u32 offset,
3104b27db7eSDan Williams 		u32 len, void *data)
3114b27db7eSDan Williams {
3124b27db7eSDan Williams 	acpi_status rc;
3134b27db7eSDan Williams 	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
3144b27db7eSDan Williams 	struct acpi_object_list input = {
3154b27db7eSDan Williams 		.count = 3,
3164b27db7eSDan Williams 		.pointer = (union acpi_object []) {
3174b27db7eSDan Williams 			[0] = {
3184b27db7eSDan Williams 				.integer.type = ACPI_TYPE_INTEGER,
3194b27db7eSDan Williams 				.integer.value = offset,
3204b27db7eSDan Williams 			},
3214b27db7eSDan Williams 			[1] = {
3224b27db7eSDan Williams 				.integer.type = ACPI_TYPE_INTEGER,
3234b27db7eSDan Williams 				.integer.value = len,
3244b27db7eSDan Williams 			},
3254b27db7eSDan Williams 			[2] = {
3264b27db7eSDan Williams 				.buffer.type = ACPI_TYPE_BUFFER,
3274b27db7eSDan Williams 				.buffer.pointer = data,
3284b27db7eSDan Williams 				.buffer.length = len,
3294b27db7eSDan Williams 			},
3304b27db7eSDan Williams 		},
3314b27db7eSDan Williams 	};
3324b27db7eSDan Williams 
3334b27db7eSDan Williams 	rc = acpi_evaluate_object(handle, "_LSW", &input, &buf);
3344b27db7eSDan Williams 	if (ACPI_FAILURE(rc))
3354b27db7eSDan Williams 		return NULL;
3364b27db7eSDan Williams 	return int_to_buf(buf.pointer);
3374b27db7eSDan Williams }
3384b27db7eSDan Williams 
acpi_label_read(acpi_handle handle,u32 offset,u32 len)3394b27db7eSDan Williams static union acpi_object *acpi_label_read(acpi_handle handle, u32 offset,
3404b27db7eSDan Williams 		u32 len)
3414b27db7eSDan Williams {
3424b27db7eSDan Williams 	acpi_status rc;
3434b27db7eSDan Williams 	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
3444b27db7eSDan Williams 	struct acpi_object_list input = {
3454b27db7eSDan Williams 		.count = 2,
3464b27db7eSDan Williams 		.pointer = (union acpi_object []) {
3474b27db7eSDan Williams 			[0] = {
3484b27db7eSDan Williams 				.integer.type = ACPI_TYPE_INTEGER,
3494b27db7eSDan Williams 				.integer.value = offset,
3504b27db7eSDan Williams 			},
3514b27db7eSDan Williams 			[1] = {
3524b27db7eSDan Williams 				.integer.type = ACPI_TYPE_INTEGER,
3534b27db7eSDan Williams 				.integer.value = len,
3544b27db7eSDan Williams 			},
3554b27db7eSDan Williams 		},
3564b27db7eSDan Williams 	};
3574b27db7eSDan Williams 
3584b27db7eSDan Williams 	rc = acpi_evaluate_object(handle, "_LSR", &input, &buf);
3594b27db7eSDan Williams 	if (ACPI_FAILURE(rc))
3604b27db7eSDan Williams 		return NULL;
3614b27db7eSDan Williams 	return pkg_to_buf(buf.pointer);
3624b27db7eSDan Williams }
3634b27db7eSDan Williams 
acpi_label_info(acpi_handle handle)3644b27db7eSDan Williams static union acpi_object *acpi_label_info(acpi_handle handle)
3654b27db7eSDan Williams {
3664b27db7eSDan Williams 	acpi_status rc;
3674b27db7eSDan Williams 	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
3684b27db7eSDan Williams 
3694b27db7eSDan Williams 	rc = acpi_evaluate_object(handle, "_LSI", NULL, &buf);
3704b27db7eSDan Williams 	if (ACPI_FAILURE(rc))
3714b27db7eSDan Williams 		return NULL;
3724b27db7eSDan Williams 	return pkg_to_buf(buf.pointer);
373d6eb270cSDan Williams }
374d6eb270cSDan Williams 
nfit_dsm_revid(unsigned family,unsigned func)37511e14270SDan Williams static u8 nfit_dsm_revid(unsigned family, unsigned func)
37611e14270SDan Williams {
37701091c49SDan Carpenter 	static const u8 revid_table[NVDIMM_FAMILY_MAX+1][NVDIMM_CMD_MAX+1] = {
37811e14270SDan Williams 		[NVDIMM_FAMILY_INTEL] = {
3796450ddbdSDan Williams 			[NVDIMM_INTEL_GET_MODES ...
3806450ddbdSDan Williams 				NVDIMM_INTEL_FW_ACTIVATE_ARM] = 2,
38111e14270SDan Williams 		},
38211e14270SDan Williams 	};
38311e14270SDan Williams 	u8 id;
38411e14270SDan Williams 
38511e14270SDan Williams 	if (family > NVDIMM_FAMILY_MAX)
38611e14270SDan Williams 		return 0;
38701091c49SDan Carpenter 	if (func > NVDIMM_CMD_MAX)
38811e14270SDan Williams 		return 0;
38911e14270SDan Williams 	id = revid_table[family][func];
39011e14270SDan Williams 	if (id == 0)
39111e14270SDan Williams 		return 1; /* default */
39211e14270SDan Williams 	return id;
39311e14270SDan Williams }
39411e14270SDan Williams 
payload_dumpable(struct nvdimm * nvdimm,unsigned int func)395b3ed2ce0SDave Jiang static bool payload_dumpable(struct nvdimm *nvdimm, unsigned int func)
396b3ed2ce0SDave Jiang {
397b3ed2ce0SDave Jiang 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
398b3ed2ce0SDave Jiang 
399b3ed2ce0SDave Jiang 	if (nfit_mem && nfit_mem->family == NVDIMM_FAMILY_INTEL
400b3ed2ce0SDave Jiang 			&& func >= NVDIMM_INTEL_GET_SECURITY_STATE
401b3ed2ce0SDave Jiang 			&& func <= NVDIMM_INTEL_MASTER_SECURE_ERASE)
402b3ed2ce0SDave Jiang 		return IS_ENABLED(CONFIG_NFIT_SECURITY_DEBUG);
403b3ed2ce0SDave Jiang 	return true;
404b3ed2ce0SDave Jiang }
405b3ed2ce0SDave Jiang 
cmd_to_func(struct nfit_mem * nfit_mem,unsigned int cmd,struct nd_cmd_pkg * call_pkg,int * family)40611189c10SDan Williams static int cmd_to_func(struct nfit_mem *nfit_mem, unsigned int cmd,
4076450ddbdSDan Williams 		struct nd_cmd_pkg *call_pkg, int *family)
40811189c10SDan Williams {
40911189c10SDan Williams 	if (call_pkg) {
41011189c10SDan Williams 		int i;
41111189c10SDan Williams 
412ebe9f6f1SDan Williams 		if (nfit_mem && nfit_mem->family != call_pkg->nd_family)
41311189c10SDan Williams 			return -ENOTTY;
41411189c10SDan Williams 
41511189c10SDan Williams 		for (i = 0; i < ARRAY_SIZE(call_pkg->nd_reserved2); i++)
41611189c10SDan Williams 			if (call_pkg->nd_reserved2[i])
41711189c10SDan Williams 				return -EINVAL;
4186450ddbdSDan Williams 		*family = call_pkg->nd_family;
41911189c10SDan Williams 		return call_pkg->nd_command;
42011189c10SDan Williams 	}
42111189c10SDan Williams 
422ebe9f6f1SDan Williams 	/* In the !call_pkg case, bus commands == bus functions */
423ebe9f6f1SDan Williams 	if (!nfit_mem)
424ebe9f6f1SDan Williams 		return cmd;
425ebe9f6f1SDan Williams 
42611189c10SDan Williams 	/* Linux ND commands == NVDIMM_FAMILY_INTEL function numbers */
42711189c10SDan Williams 	if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
42811189c10SDan Williams 		return cmd;
42911189c10SDan Williams 
43011189c10SDan Williams 	/*
43111189c10SDan Williams 	 * Force function number validation to fail since 0 is never
43211189c10SDan Williams 	 * published as a valid function in dsm_mask.
43311189c10SDan Williams 	 */
43411189c10SDan Williams 	return 0;
43511189c10SDan Williams }
43611189c10SDan Williams 
acpi_nfit_ctl(struct nvdimm_bus_descriptor * nd_desc,struct nvdimm * nvdimm,unsigned int cmd,void * buf,unsigned int buf_len,int * cmd_rc)437a7de92daSDan Williams int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
438a7de92daSDan Williams 		unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc)
439bdf97013SDan Williams {
4408a7f02f6SXiaochun Lee 	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
4414b27db7eSDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
442bdf97013SDan Williams 	union acpi_object in_obj, in_buf, *out_obj;
443bdf97013SDan Williams 	const struct nd_cmd_desc *desc = NULL;
444bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
445bdf97013SDan Williams 	struct nd_cmd_pkg *call_pkg = NULL;
446bdf97013SDan Williams 	const char *cmd_name, *dimm_name;
447bdf97013SDan Williams 	unsigned long cmd_mask, dsm_mask;
44811294d63SDan Williams 	u32 offset, fw_status = 0;
449bdf97013SDan Williams 	acpi_handle handle;
45041c8bdb3SAndy Shevchenko 	const guid_t *guid;
45111189c10SDan Williams 	int func, rc, i;
4526450ddbdSDan Williams 	int family = 0;
453bdf97013SDan Williams 
454ee6581ceSDave Jiang 	if (cmd_rc)
455c1985cefSDave Jiang 		*cmd_rc = -EINVAL;
456bdf97013SDan Williams 
457*e08dc2dcSSuraj Sonawane 	if (cmd == ND_CMD_CALL) {
458*e08dc2dcSSuraj Sonawane 		if (!buf || buf_len < sizeof(*call_pkg))
459*e08dc2dcSSuraj Sonawane 			return -EINVAL;
460*e08dc2dcSSuraj Sonawane 
461ebe9f6f1SDan Williams 		call_pkg = buf;
462*e08dc2dcSSuraj Sonawane 	}
463*e08dc2dcSSuraj Sonawane 
4646450ddbdSDan Williams 	func = cmd_to_func(nfit_mem, cmd, call_pkg, &family);
465ebe9f6f1SDan Williams 	if (func < 0)
466ebe9f6f1SDan Williams 		return func;
467ebe9f6f1SDan Williams 
468bdf97013SDan Williams 	if (nvdimm) {
469bdf97013SDan Williams 		struct acpi_device *adev = nfit_mem->adev;
470bdf97013SDan Williams 
471bdf97013SDan Williams 		if (!adev)
472bdf97013SDan Williams 			return -ENOTTY;
473bdf97013SDan Williams 
474bdf97013SDan Williams 		dimm_name = nvdimm_name(nvdimm);
475bdf97013SDan Williams 		cmd_name = nvdimm_cmd_name(cmd);
476bdf97013SDan Williams 		cmd_mask = nvdimm_cmd_mask(nvdimm);
477bdf97013SDan Williams 		dsm_mask = nfit_mem->dsm_mask;
478bdf97013SDan Williams 		desc = nd_cmd_dimm_desc(cmd);
47941c8bdb3SAndy Shevchenko 		guid = to_nfit_uuid(nfit_mem->family);
480bdf97013SDan Williams 		handle = adev->handle;
481bdf97013SDan Williams 	} else {
482bdf97013SDan Williams 		struct acpi_device *adev = to_acpi_dev(acpi_desc);
483bdf97013SDan Williams 
484bdf97013SDan Williams 		cmd_name = nvdimm_bus_cmd_name(cmd);
485bdf97013SDan Williams 		cmd_mask = nd_desc->cmd_mask;
4866450ddbdSDan Williams 		if (cmd == ND_CMD_CALL && call_pkg->nd_family) {
4876450ddbdSDan Williams 			family = call_pkg->nd_family;
4889a7e3d7fSDan Williams 			if (family > NVDIMM_BUS_FAMILY_MAX ||
4899a7e3d7fSDan Williams 			    !test_bit(family, &nd_desc->bus_family_mask))
4906450ddbdSDan Williams 				return -EINVAL;
4919a7e3d7fSDan Williams 			family = array_index_nospec(family,
4929a7e3d7fSDan Williams 						    NVDIMM_BUS_FAMILY_MAX + 1);
4936450ddbdSDan Williams 			dsm_mask = acpi_desc->family_dsm_mask[family];
4946450ddbdSDan Williams 			guid = to_nfit_bus_uuid(family);
4956450ddbdSDan Williams 		} else {
496d46e6a21SDan Williams 			dsm_mask = acpi_desc->bus_dsm_mask;
49741c8bdb3SAndy Shevchenko 			guid = to_nfit_uuid(NFIT_DEV_BUS);
4986450ddbdSDan Williams 		}
4996450ddbdSDan Williams 		desc = nd_cmd_bus_desc(cmd);
500bdf97013SDan Williams 		handle = adev->handle;
501bdf97013SDan Williams 		dimm_name = "bus";
502bdf97013SDan Williams 	}
503bdf97013SDan Williams 
504bdf97013SDan Williams 	if (!desc || (cmd && (desc->out_num + desc->in_num == 0)))
505bdf97013SDan Williams 		return -ENOTTY;
506bdf97013SDan Williams 
50711189c10SDan Williams 	/*
50811189c10SDan Williams 	 * Check for a valid command.  For ND_CMD_CALL, we also have to
50911189c10SDan Williams 	 * make sure that the DSM function is supported.
51011189c10SDan Williams 	 */
51101091c49SDan Carpenter 	if (cmd == ND_CMD_CALL &&
51201091c49SDan Carpenter 	    (func > NVDIMM_CMD_MAX || !test_bit(func, &dsm_mask)))
51311189c10SDan Williams 		return -ENOTTY;
51411189c10SDan Williams 	else if (!test_bit(cmd, &cmd_mask))
515bdf97013SDan Williams 		return -ENOTTY;
516bdf97013SDan Williams 
517bdf97013SDan Williams 	in_obj.type = ACPI_TYPE_PACKAGE;
518bdf97013SDan Williams 	in_obj.package.count = 1;
519bdf97013SDan Williams 	in_obj.package.elements = &in_buf;
520bdf97013SDan Williams 	in_buf.type = ACPI_TYPE_BUFFER;
521bdf97013SDan Williams 	in_buf.buffer.pointer = buf;
522bdf97013SDan Williams 	in_buf.buffer.length = 0;
523bdf97013SDan Williams 
524bdf97013SDan Williams 	/* libnvdimm has already validated the input envelope */
525bdf97013SDan Williams 	for (i = 0; i < desc->in_num; i++)
526bdf97013SDan Williams 		in_buf.buffer.length += nd_cmd_in_size(nvdimm, cmd, desc,
527bdf97013SDan Williams 				i, buf);
528bdf97013SDan Williams 
529bdf97013SDan Williams 	if (call_pkg) {
530bdf97013SDan Williams 		/* skip over package wrapper */
531bdf97013SDan Williams 		in_buf.buffer.pointer = (void *) &call_pkg->nd_payload;
532bdf97013SDan Williams 		in_buf.buffer.length = call_pkg->nd_size_in;
533bdf97013SDan Williams 	}
534bdf97013SDan Williams 
5356450ddbdSDan Williams 	dev_dbg(dev, "%s cmd: %d: family: %d func: %d input length: %d\n",
5366450ddbdSDan Williams 		dimm_name, cmd, family, func, in_buf.buffer.length);
537b3ed2ce0SDave Jiang 	if (payload_dumpable(nvdimm, func))
538bdf97013SDan Williams 		print_hex_dump_debug("nvdimm in  ", DUMP_PREFIX_OFFSET, 4, 4,
539bdf97013SDan Williams 				in_buf.buffer.pointer,
540bdf97013SDan Williams 				min_t(u32, 256, in_buf.buffer.length), true);
541bdf97013SDan Williams 
5424b27db7eSDan Williams 	/* call the BIOS, prefer the named methods over _DSM if available */
5436f07f86cSDan Williams 	if (nvdimm && cmd == ND_CMD_GET_CONFIG_SIZE
5446f07f86cSDan Williams 			&& test_bit(NFIT_MEM_LSR, &nfit_mem->flags))
5454b27db7eSDan Williams 		out_obj = acpi_label_info(handle);
5466f07f86cSDan Williams 	else if (nvdimm && cmd == ND_CMD_GET_CONFIG_DATA
5476f07f86cSDan Williams 			&& test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) {
5484b27db7eSDan Williams 		struct nd_cmd_get_config_data_hdr *p = buf;
5494b27db7eSDan Williams 
5504b27db7eSDan Williams 		out_obj = acpi_label_read(handle, p->in_offset, p->in_length);
5510e7f0741SDan Williams 	} else if (nvdimm && cmd == ND_CMD_SET_CONFIG_DATA
5526f07f86cSDan Williams 			&& test_bit(NFIT_MEM_LSW, &nfit_mem->flags)) {
5534b27db7eSDan Williams 		struct nd_cmd_set_config_hdr *p = buf;
5544b27db7eSDan Williams 
5554b27db7eSDan Williams 		out_obj = acpi_label_write(handle, p->in_offset, p->in_length,
5564b27db7eSDan Williams 				p->in_buf);
55711e14270SDan Williams 	} else {
55811e14270SDan Williams 		u8 revid;
55911e14270SDan Williams 
5600e7f0741SDan Williams 		if (nvdimm)
56111e14270SDan Williams 			revid = nfit_dsm_revid(nfit_mem->family, func);
56211e14270SDan Williams 		else
56311e14270SDan Williams 			revid = 1;
56411e14270SDan Williams 		out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj);
56511e14270SDan Williams 	}
5664b27db7eSDan Williams 
567bdf97013SDan Williams 	if (!out_obj) {
568b814735fSJohannes Thumshirn 		dev_dbg(dev, "%s _DSM failed cmd: %s\n", dimm_name, cmd_name);
569bdf97013SDan Williams 		return -EINVAL;
570bdf97013SDan Williams 	}
571bdf97013SDan Williams 
57243f89877SDexuan Cui 	if (out_obj->type != ACPI_TYPE_BUFFER) {
57343f89877SDexuan Cui 		dev_dbg(dev, "%s unexpected output object type cmd: %s type: %d\n",
57443f89877SDexuan Cui 				dimm_name, cmd_name, out_obj->type);
57543f89877SDexuan Cui 		rc = -EINVAL;
57643f89877SDexuan Cui 		goto out;
57743f89877SDexuan Cui 	}
57843f89877SDexuan Cui 
579351f339fSDan Williams 	dev_dbg(dev, "%s cmd: %s output length: %d\n", dimm_name,
580351f339fSDan Williams 			cmd_name, out_obj->buffer.length);
581351f339fSDan Williams 	print_hex_dump_debug(cmd_name, DUMP_PREFIX_OFFSET, 4, 4,
582351f339fSDan Williams 			out_obj->buffer.pointer,
583351f339fSDan Williams 			min_t(u32, 128, out_obj->buffer.length), true);
584351f339fSDan Williams 
585bdf97013SDan Williams 	if (call_pkg) {
586bdf97013SDan Williams 		call_pkg->nd_fw_size = out_obj->buffer.length;
587bdf97013SDan Williams 		memcpy(call_pkg->nd_payload + call_pkg->nd_size_in,
588bdf97013SDan Williams 			out_obj->buffer.pointer,
589bdf97013SDan Williams 			min(call_pkg->nd_fw_size, call_pkg->nd_size_out));
590bdf97013SDan Williams 
591bdf97013SDan Williams 		ACPI_FREE(out_obj);
592bdf97013SDan Williams 		/*
593bdf97013SDan Williams 		 * Need to support FW function w/o known size in advance.
594bdf97013SDan Williams 		 * Caller can determine required size based upon nd_fw_size.
595bdf97013SDan Williams 		 * If we return an error (like elsewhere) then caller wouldn't
596bdf97013SDan Williams 		 * be able to rely upon data returned to make calculation.
597bdf97013SDan Williams 		 */
598ee6581ceSDave Jiang 		if (cmd_rc)
599c1985cefSDave Jiang 			*cmd_rc = 0;
600bdf97013SDan Williams 		return 0;
601bdf97013SDan Williams 	}
602bdf97013SDan Williams 
603bdf97013SDan Williams 	for (i = 0, offset = 0; i < desc->out_num; i++) {
604bdf97013SDan Williams 		u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i, buf,
605efda1b5dSDan Williams 				(u32 *) out_obj->buffer.pointer,
606efda1b5dSDan Williams 				out_obj->buffer.length - offset);
607bdf97013SDan Williams 
608bdf97013SDan Williams 		if (offset + out_size > out_obj->buffer.length) {
609b814735fSJohannes Thumshirn 			dev_dbg(dev, "%s output object underflow cmd: %s field: %d\n",
610b814735fSJohannes Thumshirn 					dimm_name, cmd_name, i);
611bdf97013SDan Williams 			break;
612bdf97013SDan Williams 		}
613bdf97013SDan Williams 
614bdf97013SDan Williams 		if (in_buf.buffer.length + offset + out_size > buf_len) {
615b814735fSJohannes Thumshirn 			dev_dbg(dev, "%s output overrun cmd: %s field: %d\n",
616b814735fSJohannes Thumshirn 					dimm_name, cmd_name, i);
617bdf97013SDan Williams 			rc = -ENXIO;
618bdf97013SDan Williams 			goto out;
619bdf97013SDan Williams 		}
620bdf97013SDan Williams 		memcpy(buf + in_buf.buffer.length + offset,
621bdf97013SDan Williams 				out_obj->buffer.pointer + offset, out_size);
622bdf97013SDan Williams 		offset += out_size;
623bdf97013SDan Williams 	}
62411294d63SDan Williams 
62511294d63SDan Williams 	/*
62611294d63SDan Williams 	 * Set fw_status for all the commands with a known format to be
62711294d63SDan Williams 	 * later interpreted by xlat_status().
62811294d63SDan Williams 	 */
6290e7f0741SDan Williams 	if (i >= 1 && ((!nvdimm && cmd >= ND_CMD_ARS_CAP
6300e7f0741SDan Williams 					&& cmd <= ND_CMD_CLEAR_ERROR)
6310e7f0741SDan Williams 				|| (nvdimm && cmd >= ND_CMD_SMART
6320e7f0741SDan Williams 					&& cmd <= ND_CMD_VENDOR)))
63311294d63SDan Williams 		fw_status = *(u32 *) out_obj->buffer.pointer;
63411294d63SDan Williams 
635bdf97013SDan Williams 	if (offset + in_buf.buffer.length < buf_len) {
636bdf97013SDan Williams 		if (i >= 1) {
637bdf97013SDan Williams 			/*
638bdf97013SDan Williams 			 * status valid, return the number of bytes left
639bdf97013SDan Williams 			 * unfilled in the output buffer
640bdf97013SDan Williams 			 */
641bdf97013SDan Williams 			rc = buf_len - offset - in_buf.buffer.length;
642bdf97013SDan Williams 			if (cmd_rc)
643d6eb270cSDan Williams 				*cmd_rc = xlat_status(nvdimm, buf, cmd,
644d6eb270cSDan Williams 						fw_status);
645bdf97013SDan Williams 		} else {
646bdf97013SDan Williams 			dev_err(dev, "%s:%s underrun cmd: %s buf_len: %d out_len: %d\n",
647bdf97013SDan Williams 					__func__, dimm_name, cmd_name, buf_len,
648bdf97013SDan Williams 					offset);
649bdf97013SDan Williams 			rc = -ENXIO;
650bdf97013SDan Williams 		}
651bdf97013SDan Williams 	} else {
652bdf97013SDan Williams 		rc = 0;
653bdf97013SDan Williams 		if (cmd_rc)
654d6eb270cSDan Williams 			*cmd_rc = xlat_status(nvdimm, buf, cmd, fw_status);
655bdf97013SDan Williams 	}
656bdf97013SDan Williams 
657bdf97013SDan Williams  out:
658bdf97013SDan Williams 	ACPI_FREE(out_obj);
659bdf97013SDan Williams 
660bdf97013SDan Williams 	return rc;
661bdf97013SDan Williams }
662a7de92daSDan Williams EXPORT_SYMBOL_GPL(acpi_nfit_ctl);
663bdf97013SDan Williams 
spa_type_name(u16 type)664bdf97013SDan Williams static const char *spa_type_name(u16 type)
665bdf97013SDan Williams {
666bdf97013SDan Williams 	static const char *to_name[] = {
667bdf97013SDan Williams 		[NFIT_SPA_VOLATILE] = "volatile",
668bdf97013SDan Williams 		[NFIT_SPA_PM] = "pmem",
669bdf97013SDan Williams 		[NFIT_SPA_DCR] = "dimm-control-region",
670bdf97013SDan Williams 		[NFIT_SPA_BDW] = "block-data-window",
671bdf97013SDan Williams 		[NFIT_SPA_VDISK] = "volatile-disk",
672bdf97013SDan Williams 		[NFIT_SPA_VCD] = "volatile-cd",
673bdf97013SDan Williams 		[NFIT_SPA_PDISK] = "persistent-disk",
674bdf97013SDan Williams 		[NFIT_SPA_PCD] = "persistent-cd",
675bdf97013SDan Williams 
676bdf97013SDan Williams 	};
677bdf97013SDan Williams 
678bdf97013SDan Williams 	if (type > NFIT_SPA_PCD)
679bdf97013SDan Williams 		return "unknown";
680bdf97013SDan Williams 
681bdf97013SDan Williams 	return to_name[type];
682bdf97013SDan Williams }
683bdf97013SDan Williams 
nfit_spa_type(struct acpi_nfit_system_address * spa)6846839a6d9SVishal Verma int nfit_spa_type(struct acpi_nfit_system_address *spa)
685bdf97013SDan Williams {
686abc14eb1SAndy Shevchenko 	guid_t guid;
687bdf97013SDan Williams 	int i;
688bdf97013SDan Williams 
689abc14eb1SAndy Shevchenko 	import_guid(&guid, spa->range_guid);
690bdf97013SDan Williams 	for (i = 0; i < NFIT_UUID_MAX; i++)
691abc14eb1SAndy Shevchenko 		if (guid_equal(to_nfit_uuid(i), &guid))
692bdf97013SDan Williams 			return i;
693bdf97013SDan Williams 	return -1;
694bdf97013SDan Williams }
695bdf97013SDan Williams 
sizeof_spa(struct acpi_nfit_system_address * spa)696e9cfd259SDan Williams static size_t sizeof_spa(struct acpi_nfit_system_address *spa)
697e9cfd259SDan Williams {
698e9cfd259SDan Williams 	if (spa->flags & ACPI_NFIT_LOCATION_COOKIE_VALID)
699e9cfd259SDan Williams 		return sizeof(*spa);
700e9cfd259SDan Williams 	return sizeof(*spa) - 8;
701e9cfd259SDan Williams }
702e9cfd259SDan Williams 
add_spa(struct acpi_nfit_desc * acpi_desc,struct nfit_table_prev * prev,struct acpi_nfit_system_address * spa)703bdf97013SDan Williams static bool add_spa(struct acpi_nfit_desc *acpi_desc,
704bdf97013SDan Williams 		struct nfit_table_prev *prev,
705bdf97013SDan Williams 		struct acpi_nfit_system_address *spa)
706bdf97013SDan Williams {
707bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
708bdf97013SDan Williams 	struct nfit_spa *nfit_spa;
709bdf97013SDan Williams 
710e9cfd259SDan Williams 	if (spa->header.length != sizeof_spa(spa))
711bdf97013SDan Williams 		return false;
712bdf97013SDan Williams 
713bdf97013SDan Williams 	list_for_each_entry(nfit_spa, &prev->spas, list) {
714e9cfd259SDan Williams 		if (memcmp(nfit_spa->spa, spa, sizeof_spa(spa)) == 0) {
715bdf97013SDan Williams 			list_move_tail(&nfit_spa->list, &acpi_desc->spas);
716bdf97013SDan Williams 			return true;
717bdf97013SDan Williams 		}
718bdf97013SDan Williams 	}
719bdf97013SDan Williams 
720e9cfd259SDan Williams 	nfit_spa = devm_kzalloc(dev, sizeof(*nfit_spa) + sizeof_spa(spa),
721bdf97013SDan Williams 			GFP_KERNEL);
722bdf97013SDan Williams 	if (!nfit_spa)
723bdf97013SDan Williams 		return false;
724bdf97013SDan Williams 	INIT_LIST_HEAD(&nfit_spa->list);
725e9cfd259SDan Williams 	memcpy(nfit_spa->spa, spa, sizeof_spa(spa));
726bdf97013SDan Williams 	list_add_tail(&nfit_spa->list, &acpi_desc->spas);
727b814735fSJohannes Thumshirn 	dev_dbg(dev, "spa index: %d type: %s\n",
728bdf97013SDan Williams 			spa->range_index,
729bdf97013SDan Williams 			spa_type_name(nfit_spa_type(spa)));
730bdf97013SDan Williams 	return true;
731bdf97013SDan Williams }
732bdf97013SDan Williams 
add_memdev(struct acpi_nfit_desc * acpi_desc,struct nfit_table_prev * prev,struct acpi_nfit_memory_map * memdev)733bdf97013SDan Williams static bool add_memdev(struct acpi_nfit_desc *acpi_desc,
734bdf97013SDan Williams 		struct nfit_table_prev *prev,
735bdf97013SDan Williams 		struct acpi_nfit_memory_map *memdev)
736bdf97013SDan Williams {
737bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
738bdf97013SDan Williams 	struct nfit_memdev *nfit_memdev;
739bdf97013SDan Williams 
740bdf97013SDan Williams 	if (memdev->header.length != sizeof(*memdev))
741bdf97013SDan Williams 		return false;
742bdf97013SDan Williams 
743bdf97013SDan Williams 	list_for_each_entry(nfit_memdev, &prev->memdevs, list)
744bdf97013SDan Williams 		if (memcmp(nfit_memdev->memdev, memdev, sizeof(*memdev)) == 0) {
745bdf97013SDan Williams 			list_move_tail(&nfit_memdev->list, &acpi_desc->memdevs);
746bdf97013SDan Williams 			return true;
747bdf97013SDan Williams 		}
748bdf97013SDan Williams 
749bdf97013SDan Williams 	nfit_memdev = devm_kzalloc(dev, sizeof(*nfit_memdev) + sizeof(*memdev),
750bdf97013SDan Williams 			GFP_KERNEL);
751bdf97013SDan Williams 	if (!nfit_memdev)
752bdf97013SDan Williams 		return false;
753bdf97013SDan Williams 	INIT_LIST_HEAD(&nfit_memdev->list);
754bdf97013SDan Williams 	memcpy(nfit_memdev->memdev, memdev, sizeof(*memdev));
755bdf97013SDan Williams 	list_add_tail(&nfit_memdev->list, &acpi_desc->memdevs);
756b814735fSJohannes Thumshirn 	dev_dbg(dev, "memdev handle: %#x spa: %d dcr: %d flags: %#x\n",
757b814735fSJohannes Thumshirn 			memdev->device_handle, memdev->range_index,
758caa603aaSDan Williams 			memdev->region_index, memdev->flags);
759bdf97013SDan Williams 	return true;
760bdf97013SDan Williams }
761bdf97013SDan Williams 
nfit_get_smbios_id(u32 device_handle,u16 * flags)76223222f8fSTony Luck int nfit_get_smbios_id(u32 device_handle, u16 *flags)
76323222f8fSTony Luck {
76423222f8fSTony Luck 	struct acpi_nfit_memory_map *memdev;
76523222f8fSTony Luck 	struct acpi_nfit_desc *acpi_desc;
76623222f8fSTony Luck 	struct nfit_mem *nfit_mem;
7670919871aSTony Luck 	u16 physical_id;
76823222f8fSTony Luck 
76923222f8fSTony Luck 	mutex_lock(&acpi_desc_lock);
77023222f8fSTony Luck 	list_for_each_entry(acpi_desc, &acpi_descs, list) {
77123222f8fSTony Luck 		mutex_lock(&acpi_desc->init_mutex);
77223222f8fSTony Luck 		list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
77323222f8fSTony Luck 			memdev = __to_nfit_memdev(nfit_mem);
77423222f8fSTony Luck 			if (memdev->device_handle == device_handle) {
7750919871aSTony Luck 				*flags = memdev->flags;
7760919871aSTony Luck 				physical_id = memdev->physical_id;
77723222f8fSTony Luck 				mutex_unlock(&acpi_desc->init_mutex);
77823222f8fSTony Luck 				mutex_unlock(&acpi_desc_lock);
7790919871aSTony Luck 				return physical_id;
78023222f8fSTony Luck 			}
78123222f8fSTony Luck 		}
78223222f8fSTony Luck 		mutex_unlock(&acpi_desc->init_mutex);
78323222f8fSTony Luck 	}
78423222f8fSTony Luck 	mutex_unlock(&acpi_desc_lock);
78523222f8fSTony Luck 
78623222f8fSTony Luck 	return -ENODEV;
78723222f8fSTony Luck }
78823222f8fSTony Luck EXPORT_SYMBOL_GPL(nfit_get_smbios_id);
78923222f8fSTony Luck 
790bdf97013SDan Williams /*
791bdf97013SDan Williams  * An implementation may provide a truncated control region if no block windows
792bdf97013SDan Williams  * are defined.
793bdf97013SDan Williams  */
sizeof_dcr(struct acpi_nfit_control_region * dcr)794bdf97013SDan Williams static size_t sizeof_dcr(struct acpi_nfit_control_region *dcr)
795bdf97013SDan Williams {
796bdf97013SDan Williams 	if (dcr->header.length < offsetof(struct acpi_nfit_control_region,
797bdf97013SDan Williams 				window_size))
798bdf97013SDan Williams 		return 0;
799bdf97013SDan Williams 	if (dcr->windows)
800bdf97013SDan Williams 		return sizeof(*dcr);
801bdf97013SDan Williams 	return offsetof(struct acpi_nfit_control_region, window_size);
802bdf97013SDan Williams }
803bdf97013SDan Williams 
add_dcr(struct acpi_nfit_desc * acpi_desc,struct nfit_table_prev * prev,struct acpi_nfit_control_region * dcr)804bdf97013SDan Williams static bool add_dcr(struct acpi_nfit_desc *acpi_desc,
805bdf97013SDan Williams 		struct nfit_table_prev *prev,
806bdf97013SDan Williams 		struct acpi_nfit_control_region *dcr)
807bdf97013SDan Williams {
808bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
809bdf97013SDan Williams 	struct nfit_dcr *nfit_dcr;
810bdf97013SDan Williams 
811bdf97013SDan Williams 	if (!sizeof_dcr(dcr))
812bdf97013SDan Williams 		return false;
813bdf97013SDan Williams 
814bdf97013SDan Williams 	list_for_each_entry(nfit_dcr, &prev->dcrs, list)
815bdf97013SDan Williams 		if (memcmp(nfit_dcr->dcr, dcr, sizeof_dcr(dcr)) == 0) {
816bdf97013SDan Williams 			list_move_tail(&nfit_dcr->list, &acpi_desc->dcrs);
817bdf97013SDan Williams 			return true;
818bdf97013SDan Williams 		}
819bdf97013SDan Williams 
820bdf97013SDan Williams 	nfit_dcr = devm_kzalloc(dev, sizeof(*nfit_dcr) + sizeof(*dcr),
821bdf97013SDan Williams 			GFP_KERNEL);
822bdf97013SDan Williams 	if (!nfit_dcr)
823bdf97013SDan Williams 		return false;
824bdf97013SDan Williams 	INIT_LIST_HEAD(&nfit_dcr->list);
825bdf97013SDan Williams 	memcpy(nfit_dcr->dcr, dcr, sizeof_dcr(dcr));
826bdf97013SDan Williams 	list_add_tail(&nfit_dcr->list, &acpi_desc->dcrs);
827b814735fSJohannes Thumshirn 	dev_dbg(dev, "dcr index: %d windows: %d\n",
828bdf97013SDan Williams 			dcr->region_index, dcr->windows);
829bdf97013SDan Williams 	return true;
830bdf97013SDan Williams }
831bdf97013SDan Williams 
add_bdw(struct acpi_nfit_desc * acpi_desc,struct nfit_table_prev * prev,struct acpi_nfit_data_region * bdw)832bdf97013SDan Williams static bool add_bdw(struct acpi_nfit_desc *acpi_desc,
833bdf97013SDan Williams 		struct nfit_table_prev *prev,
834bdf97013SDan Williams 		struct acpi_nfit_data_region *bdw)
835bdf97013SDan Williams {
836bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
837bdf97013SDan Williams 	struct nfit_bdw *nfit_bdw;
838bdf97013SDan Williams 
839bdf97013SDan Williams 	if (bdw->header.length != sizeof(*bdw))
840bdf97013SDan Williams 		return false;
841bdf97013SDan Williams 	list_for_each_entry(nfit_bdw, &prev->bdws, list)
842bdf97013SDan Williams 		if (memcmp(nfit_bdw->bdw, bdw, sizeof(*bdw)) == 0) {
843bdf97013SDan Williams 			list_move_tail(&nfit_bdw->list, &acpi_desc->bdws);
844bdf97013SDan Williams 			return true;
845bdf97013SDan Williams 		}
846bdf97013SDan Williams 
847bdf97013SDan Williams 	nfit_bdw = devm_kzalloc(dev, sizeof(*nfit_bdw) + sizeof(*bdw),
848bdf97013SDan Williams 			GFP_KERNEL);
849bdf97013SDan Williams 	if (!nfit_bdw)
850bdf97013SDan Williams 		return false;
851bdf97013SDan Williams 	INIT_LIST_HEAD(&nfit_bdw->list);
852bdf97013SDan Williams 	memcpy(nfit_bdw->bdw, bdw, sizeof(*bdw));
853bdf97013SDan Williams 	list_add_tail(&nfit_bdw->list, &acpi_desc->bdws);
854b814735fSJohannes Thumshirn 	dev_dbg(dev, "bdw dcr: %d windows: %d\n",
855bdf97013SDan Williams 			bdw->region_index, bdw->windows);
856bdf97013SDan Williams 	return true;
857bdf97013SDan Williams }
858bdf97013SDan Williams 
sizeof_idt(struct acpi_nfit_interleave * idt)859bdf97013SDan Williams static size_t sizeof_idt(struct acpi_nfit_interleave *idt)
860bdf97013SDan Williams {
861bdf97013SDan Williams 	if (idt->header.length < sizeof(*idt))
862bdf97013SDan Williams 		return 0;
86333908660SYu Liao 	return sizeof(*idt) + sizeof(u32) * idt->line_count;
864bdf97013SDan Williams }
865bdf97013SDan Williams 
add_idt(struct acpi_nfit_desc * acpi_desc,struct nfit_table_prev * prev,struct acpi_nfit_interleave * idt)866bdf97013SDan Williams static bool add_idt(struct acpi_nfit_desc *acpi_desc,
867bdf97013SDan Williams 		struct nfit_table_prev *prev,
868bdf97013SDan Williams 		struct acpi_nfit_interleave *idt)
869bdf97013SDan Williams {
870bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
871bdf97013SDan Williams 	struct nfit_idt *nfit_idt;
872bdf97013SDan Williams 
873bdf97013SDan Williams 	if (!sizeof_idt(idt))
874bdf97013SDan Williams 		return false;
875bdf97013SDan Williams 
876bdf97013SDan Williams 	list_for_each_entry(nfit_idt, &prev->idts, list) {
877bdf97013SDan Williams 		if (sizeof_idt(nfit_idt->idt) != sizeof_idt(idt))
878bdf97013SDan Williams 			continue;
879bdf97013SDan Williams 
880bdf97013SDan Williams 		if (memcmp(nfit_idt->idt, idt, sizeof_idt(idt)) == 0) {
881bdf97013SDan Williams 			list_move_tail(&nfit_idt->list, &acpi_desc->idts);
882bdf97013SDan Williams 			return true;
883bdf97013SDan Williams 		}
884bdf97013SDan Williams 	}
885bdf97013SDan Williams 
886bdf97013SDan Williams 	nfit_idt = devm_kzalloc(dev, sizeof(*nfit_idt) + sizeof_idt(idt),
887bdf97013SDan Williams 			GFP_KERNEL);
888bdf97013SDan Williams 	if (!nfit_idt)
889bdf97013SDan Williams 		return false;
890bdf97013SDan Williams 	INIT_LIST_HEAD(&nfit_idt->list);
891bdf97013SDan Williams 	memcpy(nfit_idt->idt, idt, sizeof_idt(idt));
892bdf97013SDan Williams 	list_add_tail(&nfit_idt->list, &acpi_desc->idts);
893b814735fSJohannes Thumshirn 	dev_dbg(dev, "idt index: %d num_lines: %d\n",
894bdf97013SDan Williams 			idt->interleave_index, idt->line_count);
895bdf97013SDan Williams 	return true;
896bdf97013SDan Williams }
897bdf97013SDan Williams 
sizeof_flush(struct acpi_nfit_flush_address * flush)898bdf97013SDan Williams static size_t sizeof_flush(struct acpi_nfit_flush_address *flush)
899bdf97013SDan Williams {
900bdf97013SDan Williams 	if (flush->header.length < sizeof(*flush))
901bdf97013SDan Williams 		return 0;
90274522feaSKees Cook 	return struct_size(flush, hint_address, flush->hint_count);
903bdf97013SDan Williams }
904bdf97013SDan Williams 
add_flush(struct acpi_nfit_desc * acpi_desc,struct nfit_table_prev * prev,struct acpi_nfit_flush_address * flush)905bdf97013SDan Williams static bool add_flush(struct acpi_nfit_desc *acpi_desc,
906bdf97013SDan Williams 		struct nfit_table_prev *prev,
907bdf97013SDan Williams 		struct acpi_nfit_flush_address *flush)
908bdf97013SDan Williams {
909bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
910bdf97013SDan Williams 	struct nfit_flush *nfit_flush;
911bdf97013SDan Williams 
912bdf97013SDan Williams 	if (!sizeof_flush(flush))
913bdf97013SDan Williams 		return false;
914bdf97013SDan Williams 
915bdf97013SDan Williams 	list_for_each_entry(nfit_flush, &prev->flushes, list) {
916bdf97013SDan Williams 		if (sizeof_flush(nfit_flush->flush) != sizeof_flush(flush))
917bdf97013SDan Williams 			continue;
918bdf97013SDan Williams 
919bdf97013SDan Williams 		if (memcmp(nfit_flush->flush, flush,
920bdf97013SDan Williams 					sizeof_flush(flush)) == 0) {
921bdf97013SDan Williams 			list_move_tail(&nfit_flush->list, &acpi_desc->flushes);
922bdf97013SDan Williams 			return true;
923bdf97013SDan Williams 		}
924bdf97013SDan Williams 	}
925bdf97013SDan Williams 
926bdf97013SDan Williams 	nfit_flush = devm_kzalloc(dev, sizeof(*nfit_flush)
927bdf97013SDan Williams 			+ sizeof_flush(flush), GFP_KERNEL);
928bdf97013SDan Williams 	if (!nfit_flush)
929bdf97013SDan Williams 		return false;
930bdf97013SDan Williams 	INIT_LIST_HEAD(&nfit_flush->list);
931bdf97013SDan Williams 	memcpy(nfit_flush->flush, flush, sizeof_flush(flush));
932bdf97013SDan Williams 	list_add_tail(&nfit_flush->list, &acpi_desc->flushes);
933b814735fSJohannes Thumshirn 	dev_dbg(dev, "nfit_flush handle: %d hint_count: %d\n",
934bdf97013SDan Williams 			flush->device_handle, flush->hint_count);
935bdf97013SDan Williams 	return true;
936bdf97013SDan Williams }
937bdf97013SDan Williams 
add_platform_cap(struct acpi_nfit_desc * acpi_desc,struct acpi_nfit_capabilities * pcap)93806e8ccdaSDave Jiang static bool add_platform_cap(struct acpi_nfit_desc *acpi_desc,
93906e8ccdaSDave Jiang 		struct acpi_nfit_capabilities *pcap)
94006e8ccdaSDave Jiang {
94106e8ccdaSDave Jiang 	struct device *dev = acpi_desc->dev;
94206e8ccdaSDave Jiang 	u32 mask;
94306e8ccdaSDave Jiang 
94406e8ccdaSDave Jiang 	mask = (1 << (pcap->highest_capability + 1)) - 1;
94506e8ccdaSDave Jiang 	acpi_desc->platform_cap = pcap->capabilities & mask;
946b814735fSJohannes Thumshirn 	dev_dbg(dev, "cap: %#x\n", acpi_desc->platform_cap);
94706e8ccdaSDave Jiang 	return true;
94806e8ccdaSDave Jiang }
94906e8ccdaSDave Jiang 
add_table(struct acpi_nfit_desc * acpi_desc,struct nfit_table_prev * prev,void * table,const void * end)950bdf97013SDan Williams static void *add_table(struct acpi_nfit_desc *acpi_desc,
951bdf97013SDan Williams 		struct nfit_table_prev *prev, void *table, const void *end)
952bdf97013SDan Williams {
953bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
954bdf97013SDan Williams 	struct acpi_nfit_header *hdr;
955bdf97013SDan Williams 	void *err = ERR_PTR(-ENOMEM);
956bdf97013SDan Williams 
957bdf97013SDan Williams 	if (table >= end)
958bdf97013SDan Williams 		return NULL;
959bdf97013SDan Williams 
960bdf97013SDan Williams 	hdr = table;
961bdf97013SDan Williams 	if (!hdr->length) {
962bdf97013SDan Williams 		dev_warn(dev, "found a zero length table '%d' parsing nfit\n",
963bdf97013SDan Williams 			hdr->type);
964bdf97013SDan Williams 		return NULL;
965bdf97013SDan Williams 	}
966bdf97013SDan Williams 
967bdf97013SDan Williams 	switch (hdr->type) {
968bdf97013SDan Williams 	case ACPI_NFIT_TYPE_SYSTEM_ADDRESS:
969bdf97013SDan Williams 		if (!add_spa(acpi_desc, prev, table))
970bdf97013SDan Williams 			return err;
971bdf97013SDan Williams 		break;
972bdf97013SDan Williams 	case ACPI_NFIT_TYPE_MEMORY_MAP:
973bdf97013SDan Williams 		if (!add_memdev(acpi_desc, prev, table))
974bdf97013SDan Williams 			return err;
975bdf97013SDan Williams 		break;
976bdf97013SDan Williams 	case ACPI_NFIT_TYPE_CONTROL_REGION:
977bdf97013SDan Williams 		if (!add_dcr(acpi_desc, prev, table))
978bdf97013SDan Williams 			return err;
979bdf97013SDan Williams 		break;
980bdf97013SDan Williams 	case ACPI_NFIT_TYPE_DATA_REGION:
981bdf97013SDan Williams 		if (!add_bdw(acpi_desc, prev, table))
982bdf97013SDan Williams 			return err;
983bdf97013SDan Williams 		break;
984bdf97013SDan Williams 	case ACPI_NFIT_TYPE_INTERLEAVE:
985bdf97013SDan Williams 		if (!add_idt(acpi_desc, prev, table))
986bdf97013SDan Williams 			return err;
987bdf97013SDan Williams 		break;
988bdf97013SDan Williams 	case ACPI_NFIT_TYPE_FLUSH_ADDRESS:
989bdf97013SDan Williams 		if (!add_flush(acpi_desc, prev, table))
990bdf97013SDan Williams 			return err;
991bdf97013SDan Williams 		break;
992bdf97013SDan Williams 	case ACPI_NFIT_TYPE_SMBIOS:
993b814735fSJohannes Thumshirn 		dev_dbg(dev, "smbios\n");
994bdf97013SDan Williams 		break;
99506e8ccdaSDave Jiang 	case ACPI_NFIT_TYPE_CAPABILITIES:
99606e8ccdaSDave Jiang 		if (!add_platform_cap(acpi_desc, table))
99706e8ccdaSDave Jiang 			return err;
99806e8ccdaSDave Jiang 		break;
999bdf97013SDan Williams 	default:
1000bdf97013SDan Williams 		dev_err(dev, "unknown table '%d' parsing nfit\n", hdr->type);
1001bdf97013SDan Williams 		break;
1002bdf97013SDan Williams 	}
1003bdf97013SDan Williams 
1004bdf97013SDan Williams 	return table + hdr->length;
1005bdf97013SDan Williams }
1006bdf97013SDan Williams 
__nfit_mem_init(struct acpi_nfit_desc * acpi_desc,struct acpi_nfit_system_address * spa)10071499934dSDan Williams static int __nfit_mem_init(struct acpi_nfit_desc *acpi_desc,
1008bdf97013SDan Williams 		struct acpi_nfit_system_address *spa)
1009bdf97013SDan Williams {
1010bdf97013SDan Williams 	struct nfit_mem *nfit_mem, *found;
1011bdf97013SDan Williams 	struct nfit_memdev *nfit_memdev;
10121499934dSDan Williams 	int type = spa ? nfit_spa_type(spa) : 0;
1013bdf97013SDan Williams 
1014bdf97013SDan Williams 	switch (type) {
1015bdf97013SDan Williams 	case NFIT_SPA_DCR:
1016bdf97013SDan Williams 	case NFIT_SPA_PM:
1017bdf97013SDan Williams 		break;
1018bdf97013SDan Williams 	default:
10191499934dSDan Williams 		if (spa)
1020bdf97013SDan Williams 			return 0;
1021bdf97013SDan Williams 	}
1022bdf97013SDan Williams 
10231499934dSDan Williams 	/*
10241499934dSDan Williams 	 * This loop runs in two modes, when a dimm is mapped the loop
10251499934dSDan Williams 	 * adds memdev associations to an existing dimm, or creates a
10261499934dSDan Williams 	 * dimm. In the unmapped dimm case this loop sweeps for memdev
10271499934dSDan Williams 	 * instances with an invalid / zero range_index and adds those
10281499934dSDan Williams 	 * dimms without spa associations.
10291499934dSDan Williams 	 */
1030bdf97013SDan Williams 	list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
1031bdf97013SDan Williams 		struct nfit_flush *nfit_flush;
1032bdf97013SDan Williams 		struct nfit_dcr *nfit_dcr;
1033bdf97013SDan Williams 		u32 device_handle;
1034bdf97013SDan Williams 		u16 dcr;
1035bdf97013SDan Williams 
10361499934dSDan Williams 		if (spa && nfit_memdev->memdev->range_index != spa->range_index)
10371499934dSDan Williams 			continue;
10381499934dSDan Williams 		if (!spa && nfit_memdev->memdev->range_index)
1039bdf97013SDan Williams 			continue;
1040bdf97013SDan Williams 		found = NULL;
1041bdf97013SDan Williams 		dcr = nfit_memdev->memdev->region_index;
1042bdf97013SDan Williams 		device_handle = nfit_memdev->memdev->device_handle;
1043bdf97013SDan Williams 		list_for_each_entry(nfit_mem, &acpi_desc->dimms, list)
1044bdf97013SDan Williams 			if (__to_nfit_memdev(nfit_mem)->device_handle
1045bdf97013SDan Williams 					== device_handle) {
1046bdf97013SDan Williams 				found = nfit_mem;
1047bdf97013SDan Williams 				break;
1048bdf97013SDan Williams 			}
1049bdf97013SDan Williams 
1050bdf97013SDan Williams 		if (found)
1051bdf97013SDan Williams 			nfit_mem = found;
1052bdf97013SDan Williams 		else {
1053bdf97013SDan Williams 			nfit_mem = devm_kzalloc(acpi_desc->dev,
1054bdf97013SDan Williams 					sizeof(*nfit_mem), GFP_KERNEL);
1055bdf97013SDan Williams 			if (!nfit_mem)
1056bdf97013SDan Williams 				return -ENOMEM;
1057bdf97013SDan Williams 			INIT_LIST_HEAD(&nfit_mem->list);
1058bdf97013SDan Williams 			nfit_mem->acpi_desc = acpi_desc;
1059bdf97013SDan Williams 			list_add(&nfit_mem->list, &acpi_desc->dimms);
1060bdf97013SDan Williams 		}
1061bdf97013SDan Williams 
1062bdf97013SDan Williams 		list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) {
1063bdf97013SDan Williams 			if (nfit_dcr->dcr->region_index != dcr)
1064bdf97013SDan Williams 				continue;
1065bdf97013SDan Williams 			/*
1066bdf97013SDan Williams 			 * Record the control region for the dimm.  For
1067bdf97013SDan Williams 			 * the ACPI 6.1 case, where there are separate
1068bdf97013SDan Williams 			 * control regions for the pmem vs blk
1069bdf97013SDan Williams 			 * interfaces, be sure to record the extended
1070bdf97013SDan Williams 			 * blk details.
1071bdf97013SDan Williams 			 */
1072bdf97013SDan Williams 			if (!nfit_mem->dcr)
1073bdf97013SDan Williams 				nfit_mem->dcr = nfit_dcr->dcr;
1074bdf97013SDan Williams 			else if (nfit_mem->dcr->windows == 0
1075bdf97013SDan Williams 					&& nfit_dcr->dcr->windows)
1076bdf97013SDan Williams 				nfit_mem->dcr = nfit_dcr->dcr;
1077bdf97013SDan Williams 			break;
1078bdf97013SDan Williams 		}
1079bdf97013SDan Williams 
1080bdf97013SDan Williams 		list_for_each_entry(nfit_flush, &acpi_desc->flushes, list) {
1081bdf97013SDan Williams 			struct acpi_nfit_flush_address *flush;
1082bdf97013SDan Williams 			u16 i;
1083bdf97013SDan Williams 
1084bdf97013SDan Williams 			if (nfit_flush->flush->device_handle != device_handle)
1085bdf97013SDan Williams 				continue;
1086bdf97013SDan Williams 			nfit_mem->nfit_flush = nfit_flush;
1087bdf97013SDan Williams 			flush = nfit_flush->flush;
1088a86854d0SKees Cook 			nfit_mem->flush_wpq = devm_kcalloc(acpi_desc->dev,
1089a86854d0SKees Cook 					flush->hint_count,
1090a86854d0SKees Cook 					sizeof(struct resource),
1091a86854d0SKees Cook 					GFP_KERNEL);
1092bdf97013SDan Williams 			if (!nfit_mem->flush_wpq)
1093bdf97013SDan Williams 				return -ENOMEM;
1094bdf97013SDan Williams 			for (i = 0; i < flush->hint_count; i++) {
1095bdf97013SDan Williams 				struct resource *res = &nfit_mem->flush_wpq[i];
1096bdf97013SDan Williams 
1097bdf97013SDan Williams 				res->start = flush->hint_address[i];
1098bdf97013SDan Williams 				res->end = res->start + 8 - 1;
1099bdf97013SDan Williams 			}
1100bdf97013SDan Williams 			break;
1101bdf97013SDan Williams 		}
1102bdf97013SDan Williams 
1103bdf97013SDan Williams 		if (dcr && !nfit_mem->dcr) {
1104bdf97013SDan Williams 			dev_err(acpi_desc->dev, "SPA %d missing DCR %d\n",
1105bdf97013SDan Williams 					spa->range_index, dcr);
1106bdf97013SDan Williams 			return -ENODEV;
1107bdf97013SDan Williams 		}
1108bdf97013SDan Williams 
1109bdf97013SDan Williams 		if (type == NFIT_SPA_DCR) {
1110bdf97013SDan Williams 			struct nfit_idt *nfit_idt;
1111bdf97013SDan Williams 			u16 idt_idx;
1112bdf97013SDan Williams 
1113bdf97013SDan Williams 			/* multiple dimms may share a SPA when interleaved */
1114bdf97013SDan Williams 			nfit_mem->spa_dcr = spa;
1115bdf97013SDan Williams 			nfit_mem->memdev_dcr = nfit_memdev->memdev;
1116bdf97013SDan Williams 			idt_idx = nfit_memdev->memdev->interleave_index;
1117bdf97013SDan Williams 			list_for_each_entry(nfit_idt, &acpi_desc->idts, list) {
1118bdf97013SDan Williams 				if (nfit_idt->idt->interleave_index != idt_idx)
1119bdf97013SDan Williams 					continue;
1120bdf97013SDan Williams 				nfit_mem->idt_dcr = nfit_idt->idt;
1121bdf97013SDan Williams 				break;
1122bdf97013SDan Williams 			}
11231499934dSDan Williams 		} else if (type == NFIT_SPA_PM) {
1124bdf97013SDan Williams 			/*
1125bdf97013SDan Williams 			 * A single dimm may belong to multiple SPA-PM
1126bdf97013SDan Williams 			 * ranges, record at least one in addition to
1127bdf97013SDan Williams 			 * any SPA-DCR range.
1128bdf97013SDan Williams 			 */
1129bdf97013SDan Williams 			nfit_mem->memdev_pmem = nfit_memdev->memdev;
11301499934dSDan Williams 		} else
11311499934dSDan Williams 			nfit_mem->memdev_dcr = nfit_memdev->memdev;
1132bdf97013SDan Williams 	}
1133bdf97013SDan Williams 
1134bdf97013SDan Williams 	return 0;
1135bdf97013SDan Williams }
1136bdf97013SDan Williams 
nfit_mem_cmp(void * priv,const struct list_head * _a,const struct list_head * _b)11374f0f586bSSami Tolvanen static int nfit_mem_cmp(void *priv, const struct list_head *_a,
11384f0f586bSSami Tolvanen 		const struct list_head *_b)
1139bdf97013SDan Williams {
1140bdf97013SDan Williams 	struct nfit_mem *a = container_of(_a, typeof(*a), list);
1141bdf97013SDan Williams 	struct nfit_mem *b = container_of(_b, typeof(*b), list);
1142bdf97013SDan Williams 	u32 handleA, handleB;
1143bdf97013SDan Williams 
1144bdf97013SDan Williams 	handleA = __to_nfit_memdev(a)->device_handle;
1145bdf97013SDan Williams 	handleB = __to_nfit_memdev(b)->device_handle;
1146bdf97013SDan Williams 	if (handleA < handleB)
1147bdf97013SDan Williams 		return -1;
1148bdf97013SDan Williams 	else if (handleA > handleB)
1149bdf97013SDan Williams 		return 1;
1150bdf97013SDan Williams 	return 0;
1151bdf97013SDan Williams }
1152bdf97013SDan Williams 
nfit_mem_init(struct acpi_nfit_desc * acpi_desc)1153bdf97013SDan Williams static int nfit_mem_init(struct acpi_nfit_desc *acpi_desc)
1154bdf97013SDan Williams {
1155bdf97013SDan Williams 	struct nfit_spa *nfit_spa;
11561499934dSDan Williams 	int rc;
11571499934dSDan Williams 
1158bdf97013SDan Williams 
1159bdf97013SDan Williams 	/*
1160bdf97013SDan Williams 	 * For each SPA-DCR or SPA-PMEM address range find its
1161bdf97013SDan Williams 	 * corresponding MEMDEV(s).  From each MEMDEV find the
1162bdf97013SDan Williams 	 * corresponding DCR.  Then, if we're operating on a SPA-DCR,
1163bdf97013SDan Williams 	 * try to find a SPA-BDW and a corresponding BDW that references
1164bdf97013SDan Williams 	 * the DCR.  Throw it all into an nfit_mem object.  Note, that
1165bdf97013SDan Williams 	 * BDWs are optional.
1166bdf97013SDan Williams 	 */
1167bdf97013SDan Williams 	list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
11681499934dSDan Williams 		rc = __nfit_mem_init(acpi_desc, nfit_spa->spa);
1169bdf97013SDan Williams 		if (rc)
1170bdf97013SDan Williams 			return rc;
1171bdf97013SDan Williams 	}
1172bdf97013SDan Williams 
11731499934dSDan Williams 	/*
11741499934dSDan Williams 	 * If a DIMM has failed to be mapped into SPA there will be no
11751499934dSDan Williams 	 * SPA entries above. Find and register all the unmapped DIMMs
11761499934dSDan Williams 	 * for reporting and recovery purposes.
11771499934dSDan Williams 	 */
11781499934dSDan Williams 	rc = __nfit_mem_init(acpi_desc, NULL);
11791499934dSDan Williams 	if (rc)
11801499934dSDan Williams 		return rc;
11811499934dSDan Williams 
1182bdf97013SDan Williams 	list_sort(NULL, &acpi_desc->dimms, nfit_mem_cmp);
1183bdf97013SDan Williams 
1184bdf97013SDan Williams 	return 0;
1185bdf97013SDan Williams }
1186bdf97013SDan Williams 
bus_dsm_mask_show(struct device * dev,struct device_attribute * attr,char * buf)118741f95db7SJerry Hoemann static ssize_t bus_dsm_mask_show(struct device *dev,
118841f95db7SJerry Hoemann 		struct device_attribute *attr, char *buf)
118941f95db7SJerry Hoemann {
119041f95db7SJerry Hoemann 	struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
119141f95db7SJerry Hoemann 	struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
1192d46e6a21SDan Williams 	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
119341f95db7SJerry Hoemann 
1194d46e6a21SDan Williams 	return sprintf(buf, "%#lx\n", acpi_desc->bus_dsm_mask);
119541f95db7SJerry Hoemann }
119641f95db7SJerry Hoemann static struct device_attribute dev_attr_bus_dsm_mask =
119741f95db7SJerry Hoemann 		__ATTR(dsm_mask, 0444, bus_dsm_mask_show, NULL);
119841f95db7SJerry Hoemann 
revision_show(struct device * dev,struct device_attribute * attr,char * buf)1199bdf97013SDan Williams static ssize_t revision_show(struct device *dev,
1200bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1201bdf97013SDan Williams {
1202bdf97013SDan Williams 	struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
1203bdf97013SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
1204bdf97013SDan Williams 	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
1205bdf97013SDan Williams 
1206bdf97013SDan Williams 	return sprintf(buf, "%d\n", acpi_desc->acpi_header.revision);
1207bdf97013SDan Williams }
1208bdf97013SDan Williams static DEVICE_ATTR_RO(revision);
1209bdf97013SDan Williams 
hw_error_scrub_show(struct device * dev,struct device_attribute * attr,char * buf)12109ffd6350SVishal Verma static ssize_t hw_error_scrub_show(struct device *dev,
12119ffd6350SVishal Verma 		struct device_attribute *attr, char *buf)
12129ffd6350SVishal Verma {
12139ffd6350SVishal Verma 	struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
12149ffd6350SVishal Verma 	struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
12159ffd6350SVishal Verma 	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
12169ffd6350SVishal Verma 
12179ffd6350SVishal Verma 	return sprintf(buf, "%d\n", acpi_desc->scrub_mode);
12189ffd6350SVishal Verma }
12199ffd6350SVishal Verma 
12209ffd6350SVishal Verma /*
12219ffd6350SVishal Verma  * The 'hw_error_scrub' attribute can have the following values written to it:
12229ffd6350SVishal Verma  * '0': Switch to the default mode where an exception will only insert
12239ffd6350SVishal Verma  *      the address of the memory error into the poison and badblocks lists.
12249ffd6350SVishal Verma  * '1': Enable a full scrub to happen if an exception for a memory error is
12259ffd6350SVishal Verma  *      received.
12269ffd6350SVishal Verma  */
hw_error_scrub_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)12279ffd6350SVishal Verma static ssize_t hw_error_scrub_store(struct device *dev,
12289ffd6350SVishal Verma 		struct device_attribute *attr, const char *buf, size_t size)
12299ffd6350SVishal Verma {
12309ffd6350SVishal Verma 	struct nvdimm_bus_descriptor *nd_desc;
12319ffd6350SVishal Verma 	ssize_t rc;
12329ffd6350SVishal Verma 	long val;
12339ffd6350SVishal Verma 
12349ffd6350SVishal Verma 	rc = kstrtol(buf, 0, &val);
12359ffd6350SVishal Verma 	if (rc)
12369ffd6350SVishal Verma 		return rc;
12379ffd6350SVishal Verma 
12381550a17aSDan Williams 	device_lock(dev);
12399ffd6350SVishal Verma 	nd_desc = dev_get_drvdata(dev);
12409ffd6350SVishal Verma 	if (nd_desc) {
12419ffd6350SVishal Verma 		struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
12429ffd6350SVishal Verma 
12439ffd6350SVishal Verma 		switch (val) {
12449ffd6350SVishal Verma 		case HW_ERROR_SCRUB_ON:
12459ffd6350SVishal Verma 			acpi_desc->scrub_mode = HW_ERROR_SCRUB_ON;
12469ffd6350SVishal Verma 			break;
12479ffd6350SVishal Verma 		case HW_ERROR_SCRUB_OFF:
12489ffd6350SVishal Verma 			acpi_desc->scrub_mode = HW_ERROR_SCRUB_OFF;
12499ffd6350SVishal Verma 			break;
12509ffd6350SVishal Verma 		default:
12519ffd6350SVishal Verma 			rc = -EINVAL;
12529ffd6350SVishal Verma 			break;
12539ffd6350SVishal Verma 		}
12549ffd6350SVishal Verma 	}
12551550a17aSDan Williams 	device_unlock(dev);
12569ffd6350SVishal Verma 	if (rc)
12579ffd6350SVishal Verma 		return rc;
12589ffd6350SVishal Verma 	return size;
12599ffd6350SVishal Verma }
12609ffd6350SVishal Verma static DEVICE_ATTR_RW(hw_error_scrub);
12619ffd6350SVishal Verma 
1262bdf97013SDan Williams /*
1263bdf97013SDan Williams  * This shows the number of full Address Range Scrubs that have been
1264bdf97013SDan Williams  * completed since driver load time. Userspace can wait on this using
1265bdf97013SDan Williams  * select/poll etc. A '+' at the end indicates an ARS is in progress
1266bdf97013SDan Williams  */
scrub_show(struct device * dev,struct device_attribute * attr,char * buf)1267bdf97013SDan Williams static ssize_t scrub_show(struct device *dev,
1268bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1269bdf97013SDan Williams {
1270bdf97013SDan Williams 	struct nvdimm_bus_descriptor *nd_desc;
1271e34b8252SDan Williams 	struct acpi_nfit_desc *acpi_desc;
1272bdf97013SDan Williams 	ssize_t rc = -ENXIO;
1273e34b8252SDan Williams 	bool busy;
1274bdf97013SDan Williams 
12751550a17aSDan Williams 	device_lock(dev);
1276bdf97013SDan Williams 	nd_desc = dev_get_drvdata(dev);
1277e34b8252SDan Williams 	if (!nd_desc) {
12781550a17aSDan Williams 		device_unlock(dev);
1279e34b8252SDan Williams 		return rc;
1280e34b8252SDan Williams 	}
1281e34b8252SDan Williams 	acpi_desc = to_acpi_desc(nd_desc);
1282bdf97013SDan Williams 
128378727137SDan Williams 	mutex_lock(&acpi_desc->init_mutex);
1284e34b8252SDan Williams 	busy = test_bit(ARS_BUSY, &acpi_desc->scrub_flags)
1285e34b8252SDan Williams 		&& !test_bit(ARS_CANCEL, &acpi_desc->scrub_flags);
1286e34b8252SDan Williams 	rc = sprintf(buf, "%d%s", acpi_desc->scrub_count, busy ? "+\n" : "\n");
12875479b275SDan Williams 	/* Allow an admin to poll the busy state at a higher rate */
12885479b275SDan Williams 	if (busy && capable(CAP_SYS_RAWIO) && !test_and_set_bit(ARS_POLL,
12895479b275SDan Williams 				&acpi_desc->scrub_flags)) {
12905479b275SDan Williams 		acpi_desc->scrub_tmo = 1;
12915479b275SDan Williams 		mod_delayed_work(nfit_wq, &acpi_desc->dwork, HZ);
1292bdf97013SDan Williams 	}
12935479b275SDan Williams 
1294bdf97013SDan Williams 	mutex_unlock(&acpi_desc->init_mutex);
12951550a17aSDan Williams 	device_unlock(dev);
1296bdf97013SDan Williams 	return rc;
1297bdf97013SDan Williams }
1298bdf97013SDan Williams 
scrub_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)1299bdf97013SDan Williams static ssize_t scrub_store(struct device *dev,
1300bdf97013SDan Williams 		struct device_attribute *attr, const char *buf, size_t size)
1301bdf97013SDan Williams {
1302bdf97013SDan Williams 	struct nvdimm_bus_descriptor *nd_desc;
1303bdf97013SDan Williams 	ssize_t rc;
1304bdf97013SDan Williams 	long val;
1305bdf97013SDan Williams 
1306bdf97013SDan Williams 	rc = kstrtol(buf, 0, &val);
1307bdf97013SDan Williams 	if (rc)
1308bdf97013SDan Williams 		return rc;
1309bdf97013SDan Williams 	if (val != 1)
1310bdf97013SDan Williams 		return -EINVAL;
1311bdf97013SDan Williams 
13121550a17aSDan Williams 	device_lock(dev);
1313bdf97013SDan Williams 	nd_desc = dev_get_drvdata(dev);
1314bdf97013SDan Williams 	if (nd_desc) {
1315bdf97013SDan Williams 		struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
1316bdf97013SDan Williams 
1317b5fd2e00SDan Williams 		rc = acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_LONG);
1318bdf97013SDan Williams 	}
13191550a17aSDan Williams 	device_unlock(dev);
1320bdf97013SDan Williams 	if (rc)
1321bdf97013SDan Williams 		return rc;
1322bdf97013SDan Williams 	return size;
1323bdf97013SDan Williams }
1324bdf97013SDan Williams static DEVICE_ATTR_RW(scrub);
1325bdf97013SDan Williams 
ars_supported(struct nvdimm_bus * nvdimm_bus)1326bdf97013SDan Williams static bool ars_supported(struct nvdimm_bus *nvdimm_bus)
1327bdf97013SDan Williams {
1328bdf97013SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
1329bdf97013SDan Williams 	const unsigned long mask = 1 << ND_CMD_ARS_CAP | 1 << ND_CMD_ARS_START
1330bdf97013SDan Williams 		| 1 << ND_CMD_ARS_STATUS;
1331bdf97013SDan Williams 
1332bdf97013SDan Williams 	return (nd_desc->cmd_mask & mask) == mask;
1333bdf97013SDan Williams }
1334bdf97013SDan Williams 
nfit_visible(struct kobject * kobj,struct attribute * a,int n)1335bdf97013SDan Williams static umode_t nfit_visible(struct kobject *kobj, struct attribute *a, int n)
1336bdf97013SDan Williams {
13375f155515SWang Qing 	struct device *dev = kobj_to_dev(kobj);
1338bdf97013SDan Williams 	struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
1339bdf97013SDan Williams 
1340a1facc1fSDan Williams 	if (a == &dev_attr_scrub.attr)
1341a1facc1fSDan Williams 		return ars_supported(nvdimm_bus) ? a->mode : 0;
1342a1facc1fSDan Williams 
1343a1facc1fSDan Williams 	if (a == &dev_attr_firmware_activate_noidle.attr)
1344a1facc1fSDan Williams 		return intel_fwa_supported(nvdimm_bus) ? a->mode : 0;
1345a1facc1fSDan Williams 
1346bdf97013SDan Williams 	return a->mode;
1347bdf97013SDan Williams }
1348bdf97013SDan Williams 
1349bdf97013SDan Williams static struct attribute *acpi_nfit_attributes[] = {
1350bdf97013SDan Williams 	&dev_attr_revision.attr,
1351bdf97013SDan Williams 	&dev_attr_scrub.attr,
13529ffd6350SVishal Verma 	&dev_attr_hw_error_scrub.attr,
135341f95db7SJerry Hoemann 	&dev_attr_bus_dsm_mask.attr,
1354a1facc1fSDan Williams 	&dev_attr_firmware_activate_noidle.attr,
1355bdf97013SDan Williams 	NULL,
1356bdf97013SDan Williams };
1357bdf97013SDan Williams 
13585e93746fSArvind Yadav static const struct attribute_group acpi_nfit_attribute_group = {
1359bdf97013SDan Williams 	.name = "nfit",
1360bdf97013SDan Williams 	.attrs = acpi_nfit_attributes,
1361bdf97013SDan Williams 	.is_visible = nfit_visible,
1362bdf97013SDan Williams };
1363bdf97013SDan Williams 
1364bdf97013SDan Williams static const struct attribute_group *acpi_nfit_attribute_groups[] = {
1365bdf97013SDan Williams 	&acpi_nfit_attribute_group,
1366bdf97013SDan Williams 	NULL,
1367bdf97013SDan Williams };
1368bdf97013SDan Williams 
to_nfit_memdev(struct device * dev)1369bdf97013SDan Williams static struct acpi_nfit_memory_map *to_nfit_memdev(struct device *dev)
1370bdf97013SDan Williams {
1371bdf97013SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
1372bdf97013SDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
1373bdf97013SDan Williams 
1374bdf97013SDan Williams 	return __to_nfit_memdev(nfit_mem);
1375bdf97013SDan Williams }
1376bdf97013SDan Williams 
to_nfit_dcr(struct device * dev)1377bdf97013SDan Williams static struct acpi_nfit_control_region *to_nfit_dcr(struct device *dev)
1378bdf97013SDan Williams {
1379bdf97013SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
1380bdf97013SDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
1381bdf97013SDan Williams 
1382bdf97013SDan Williams 	return nfit_mem->dcr;
1383bdf97013SDan Williams }
1384bdf97013SDan Williams 
handle_show(struct device * dev,struct device_attribute * attr,char * buf)1385bdf97013SDan Williams static ssize_t handle_show(struct device *dev,
1386bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1387bdf97013SDan Williams {
1388bdf97013SDan Williams 	struct acpi_nfit_memory_map *memdev = to_nfit_memdev(dev);
1389bdf97013SDan Williams 
1390bdf97013SDan Williams 	return sprintf(buf, "%#x\n", memdev->device_handle);
1391bdf97013SDan Williams }
1392bdf97013SDan Williams static DEVICE_ATTR_RO(handle);
1393bdf97013SDan Williams 
phys_id_show(struct device * dev,struct device_attribute * attr,char * buf)1394bdf97013SDan Williams static ssize_t phys_id_show(struct device *dev,
1395bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1396bdf97013SDan Williams {
1397bdf97013SDan Williams 	struct acpi_nfit_memory_map *memdev = to_nfit_memdev(dev);
1398bdf97013SDan Williams 
1399bdf97013SDan Williams 	return sprintf(buf, "%#x\n", memdev->physical_id);
1400bdf97013SDan Williams }
1401bdf97013SDan Williams static DEVICE_ATTR_RO(phys_id);
1402bdf97013SDan Williams 
vendor_show(struct device * dev,struct device_attribute * attr,char * buf)1403bdf97013SDan Williams static ssize_t vendor_show(struct device *dev,
1404bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1405bdf97013SDan Williams {
1406bdf97013SDan Williams 	struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
1407bdf97013SDan Williams 
1408bdf97013SDan Williams 	return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->vendor_id));
1409bdf97013SDan Williams }
1410bdf97013SDan Williams static DEVICE_ATTR_RO(vendor);
1411bdf97013SDan Williams 
rev_id_show(struct device * dev,struct device_attribute * attr,char * buf)1412bdf97013SDan Williams static ssize_t rev_id_show(struct device *dev,
1413bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1414bdf97013SDan Williams {
1415bdf97013SDan Williams 	struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
1416bdf97013SDan Williams 
1417bdf97013SDan Williams 	return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->revision_id));
1418bdf97013SDan Williams }
1419bdf97013SDan Williams static DEVICE_ATTR_RO(rev_id);
1420bdf97013SDan Williams 
device_show(struct device * dev,struct device_attribute * attr,char * buf)1421bdf97013SDan Williams static ssize_t device_show(struct device *dev,
1422bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1423bdf97013SDan Williams {
1424bdf97013SDan Williams 	struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
1425bdf97013SDan Williams 
1426bdf97013SDan Williams 	return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->device_id));
1427bdf97013SDan Williams }
1428bdf97013SDan Williams static DEVICE_ATTR_RO(device);
1429bdf97013SDan Williams 
subsystem_vendor_show(struct device * dev,struct device_attribute * attr,char * buf)1430bdf97013SDan Williams static ssize_t subsystem_vendor_show(struct device *dev,
1431bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1432bdf97013SDan Williams {
1433bdf97013SDan Williams 	struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
1434bdf97013SDan Williams 
1435bdf97013SDan Williams 	return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->subsystem_vendor_id));
1436bdf97013SDan Williams }
1437bdf97013SDan Williams static DEVICE_ATTR_RO(subsystem_vendor);
1438bdf97013SDan Williams 
subsystem_rev_id_show(struct device * dev,struct device_attribute * attr,char * buf)1439bdf97013SDan Williams static ssize_t subsystem_rev_id_show(struct device *dev,
1440bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1441bdf97013SDan Williams {
1442bdf97013SDan Williams 	struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
1443bdf97013SDan Williams 
1444bdf97013SDan Williams 	return sprintf(buf, "0x%04x\n",
1445bdf97013SDan Williams 			be16_to_cpu(dcr->subsystem_revision_id));
1446bdf97013SDan Williams }
1447bdf97013SDan Williams static DEVICE_ATTR_RO(subsystem_rev_id);
1448bdf97013SDan Williams 
subsystem_device_show(struct device * dev,struct device_attribute * attr,char * buf)1449bdf97013SDan Williams static ssize_t subsystem_device_show(struct device *dev,
1450bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1451bdf97013SDan Williams {
1452bdf97013SDan Williams 	struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
1453bdf97013SDan Williams 
1454bdf97013SDan Williams 	return sprintf(buf, "0x%04x\n", be16_to_cpu(dcr->subsystem_device_id));
1455bdf97013SDan Williams }
1456bdf97013SDan Williams static DEVICE_ATTR_RO(subsystem_device);
1457bdf97013SDan Williams 
num_nvdimm_formats(struct nvdimm * nvdimm)1458bdf97013SDan Williams static int num_nvdimm_formats(struct nvdimm *nvdimm)
1459bdf97013SDan Williams {
1460bdf97013SDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
1461bdf97013SDan Williams 	int formats = 0;
1462bdf97013SDan Williams 
1463bdf97013SDan Williams 	if (nfit_mem->memdev_pmem)
1464bdf97013SDan Williams 		formats++;
1465bdf97013SDan Williams 	return formats;
1466bdf97013SDan Williams }
1467bdf97013SDan Williams 
format_show(struct device * dev,struct device_attribute * attr,char * buf)1468bdf97013SDan Williams static ssize_t format_show(struct device *dev,
1469bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1470bdf97013SDan Williams {
1471bdf97013SDan Williams 	struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
1472bdf97013SDan Williams 
14730606263fSDan Williams 	return sprintf(buf, "0x%04x\n", le16_to_cpu(dcr->code));
1474bdf97013SDan Williams }
1475bdf97013SDan Williams static DEVICE_ATTR_RO(format);
1476bdf97013SDan Williams 
format1_show(struct device * dev,struct device_attribute * attr,char * buf)1477bdf97013SDan Williams static ssize_t format1_show(struct device *dev,
1478bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1479bdf97013SDan Williams {
1480bdf97013SDan Williams 	u32 handle;
1481bdf97013SDan Williams 	ssize_t rc = -ENXIO;
1482bdf97013SDan Williams 	struct nfit_mem *nfit_mem;
1483bdf97013SDan Williams 	struct nfit_memdev *nfit_memdev;
1484bdf97013SDan Williams 	struct acpi_nfit_desc *acpi_desc;
1485bdf97013SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
1486bdf97013SDan Williams 	struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
1487bdf97013SDan Williams 
1488bdf97013SDan Williams 	nfit_mem = nvdimm_provider_data(nvdimm);
1489bdf97013SDan Williams 	acpi_desc = nfit_mem->acpi_desc;
1490bdf97013SDan Williams 	handle = to_nfit_memdev(dev)->device_handle;
1491bdf97013SDan Williams 
1492bdf97013SDan Williams 	/* assumes DIMMs have at most 2 published interface codes */
1493bdf97013SDan Williams 	mutex_lock(&acpi_desc->init_mutex);
1494bdf97013SDan Williams 	list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
1495bdf97013SDan Williams 		struct acpi_nfit_memory_map *memdev = nfit_memdev->memdev;
1496bdf97013SDan Williams 		struct nfit_dcr *nfit_dcr;
1497bdf97013SDan Williams 
1498bdf97013SDan Williams 		if (memdev->device_handle != handle)
1499bdf97013SDan Williams 			continue;
1500bdf97013SDan Williams 
1501bdf97013SDan Williams 		list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) {
1502bdf97013SDan Williams 			if (nfit_dcr->dcr->region_index != memdev->region_index)
1503bdf97013SDan Williams 				continue;
1504bdf97013SDan Williams 			if (nfit_dcr->dcr->code == dcr->code)
1505bdf97013SDan Williams 				continue;
15060606263fSDan Williams 			rc = sprintf(buf, "0x%04x\n",
15070606263fSDan Williams 					le16_to_cpu(nfit_dcr->dcr->code));
1508bdf97013SDan Williams 			break;
1509bdf97013SDan Williams 		}
151085f971b6SZhang Qilong 		if (rc != -ENXIO)
1511bdf97013SDan Williams 			break;
1512bdf97013SDan Williams 	}
1513bdf97013SDan Williams 	mutex_unlock(&acpi_desc->init_mutex);
1514bdf97013SDan Williams 	return rc;
1515bdf97013SDan Williams }
1516bdf97013SDan Williams static DEVICE_ATTR_RO(format1);
1517bdf97013SDan Williams 
formats_show(struct device * dev,struct device_attribute * attr,char * buf)1518bdf97013SDan Williams static ssize_t formats_show(struct device *dev,
1519bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1520bdf97013SDan Williams {
1521bdf97013SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
1522bdf97013SDan Williams 
1523bdf97013SDan Williams 	return sprintf(buf, "%d\n", num_nvdimm_formats(nvdimm));
1524bdf97013SDan Williams }
1525bdf97013SDan Williams static DEVICE_ATTR_RO(formats);
1526bdf97013SDan Williams 
serial_show(struct device * dev,struct device_attribute * attr,char * buf)1527bdf97013SDan Williams static ssize_t serial_show(struct device *dev,
1528bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1529bdf97013SDan Williams {
1530bdf97013SDan Williams 	struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
1531bdf97013SDan Williams 
1532bdf97013SDan Williams 	return sprintf(buf, "0x%08x\n", be32_to_cpu(dcr->serial_number));
1533bdf97013SDan Williams }
1534bdf97013SDan Williams static DEVICE_ATTR_RO(serial);
1535bdf97013SDan Williams 
family_show(struct device * dev,struct device_attribute * attr,char * buf)1536bdf97013SDan Williams static ssize_t family_show(struct device *dev,
1537bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1538bdf97013SDan Williams {
1539bdf97013SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
1540bdf97013SDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
1541bdf97013SDan Williams 
1542bdf97013SDan Williams 	if (nfit_mem->family < 0)
1543bdf97013SDan Williams 		return -ENXIO;
1544bdf97013SDan Williams 	return sprintf(buf, "%d\n", nfit_mem->family);
1545bdf97013SDan Williams }
1546bdf97013SDan Williams static DEVICE_ATTR_RO(family);
1547bdf97013SDan Williams 
dsm_mask_show(struct device * dev,struct device_attribute * attr,char * buf)1548bdf97013SDan Williams static ssize_t dsm_mask_show(struct device *dev,
1549bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1550bdf97013SDan Williams {
1551bdf97013SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
1552bdf97013SDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
1553bdf97013SDan Williams 
1554bdf97013SDan Williams 	if (nfit_mem->family < 0)
1555bdf97013SDan Williams 		return -ENXIO;
1556bdf97013SDan Williams 	return sprintf(buf, "%#lx\n", nfit_mem->dsm_mask);
1557bdf97013SDan Williams }
1558bdf97013SDan Williams static DEVICE_ATTR_RO(dsm_mask);
1559bdf97013SDan Williams 
flags_show(struct device * dev,struct device_attribute * attr,char * buf)1560bdf97013SDan Williams static ssize_t flags_show(struct device *dev,
1561bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1562bdf97013SDan Williams {
15630ead1118SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
15640ead1118SDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
15650ead1118SDan Williams 	u16 flags = __to_nfit_memdev(nfit_mem)->flags;
15660ead1118SDan Williams 
15670ead1118SDan Williams 	if (test_bit(NFIT_MEM_DIRTY, &nfit_mem->flags))
15680ead1118SDan Williams 		flags |= ACPI_NFIT_MEM_FLUSH_FAILED;
1569bdf97013SDan Williams 
1570ffab9385SDan Williams 	return sprintf(buf, "%s%s%s%s%s%s%s\n",
1571bdf97013SDan Williams 		flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save_fail " : "",
1572bdf97013SDan Williams 		flags & ACPI_NFIT_MEM_RESTORE_FAILED ? "restore_fail " : "",
1573bdf97013SDan Williams 		flags & ACPI_NFIT_MEM_FLUSH_FAILED ? "flush_fail " : "",
1574bdf97013SDan Williams 		flags & ACPI_NFIT_MEM_NOT_ARMED ? "not_armed " : "",
1575ffab9385SDan Williams 		flags & ACPI_NFIT_MEM_HEALTH_OBSERVED ? "smart_event " : "",
1576ffab9385SDan Williams 		flags & ACPI_NFIT_MEM_MAP_FAILED ? "map_fail " : "",
1577ffab9385SDan Williams 		flags & ACPI_NFIT_MEM_HEALTH_ENABLED ? "smart_notify " : "");
1578bdf97013SDan Williams }
1579bdf97013SDan Williams static DEVICE_ATTR_RO(flags);
1580bdf97013SDan Williams 
id_show(struct device * dev,struct device_attribute * attr,char * buf)1581bdf97013SDan Williams static ssize_t id_show(struct device *dev,
1582bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
1583bdf97013SDan Williams {
1584d6548ae4SDave Jiang 	struct nvdimm *nvdimm = to_nvdimm(dev);
1585d6548ae4SDave Jiang 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
1586bdf97013SDan Williams 
1587d6548ae4SDave Jiang 	return sprintf(buf, "%s\n", nfit_mem->id);
1588bdf97013SDan Williams }
1589bdf97013SDan Williams static DEVICE_ATTR_RO(id);
1590bdf97013SDan Williams 
dirty_shutdown_show(struct device * dev,struct device_attribute * attr,char * buf)15910ead1118SDan Williams static ssize_t dirty_shutdown_show(struct device *dev,
15920ead1118SDan Williams 		struct device_attribute *attr, char *buf)
15930ead1118SDan Williams {
15940ead1118SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
15950ead1118SDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
15960ead1118SDan Williams 
15970ead1118SDan Williams 	return sprintf(buf, "%d\n", nfit_mem->dirty_shutdown);
15980ead1118SDan Williams }
15990ead1118SDan Williams static DEVICE_ATTR_RO(dirty_shutdown);
16000ead1118SDan Williams 
1601bdf97013SDan Williams static struct attribute *acpi_nfit_dimm_attributes[] = {
1602bdf97013SDan Williams 	&dev_attr_handle.attr,
1603bdf97013SDan Williams 	&dev_attr_phys_id.attr,
1604bdf97013SDan Williams 	&dev_attr_vendor.attr,
1605bdf97013SDan Williams 	&dev_attr_device.attr,
1606bdf97013SDan Williams 	&dev_attr_rev_id.attr,
1607bdf97013SDan Williams 	&dev_attr_subsystem_vendor.attr,
1608bdf97013SDan Williams 	&dev_attr_subsystem_device.attr,
1609bdf97013SDan Williams 	&dev_attr_subsystem_rev_id.attr,
1610bdf97013SDan Williams 	&dev_attr_format.attr,
1611bdf97013SDan Williams 	&dev_attr_formats.attr,
1612bdf97013SDan Williams 	&dev_attr_format1.attr,
1613bdf97013SDan Williams 	&dev_attr_serial.attr,
1614bdf97013SDan Williams 	&dev_attr_flags.attr,
1615bdf97013SDan Williams 	&dev_attr_id.attr,
1616bdf97013SDan Williams 	&dev_attr_family.attr,
1617bdf97013SDan Williams 	&dev_attr_dsm_mask.attr,
16180ead1118SDan Williams 	&dev_attr_dirty_shutdown.attr,
1619bdf97013SDan Williams 	NULL,
1620bdf97013SDan Williams };
1621bdf97013SDan Williams 
acpi_nfit_dimm_attr_visible(struct kobject * kobj,struct attribute * a,int n)1622bdf97013SDan Williams static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj,
1623bdf97013SDan Williams 		struct attribute *a, int n)
1624bdf97013SDan Williams {
16255f155515SWang Qing 	struct device *dev = kobj_to_dev(kobj);
1626bdf97013SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
16270ead1118SDan Williams 	struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
1628bdf97013SDan Williams 
16291499934dSDan Williams 	if (!to_nfit_dcr(dev)) {
16301499934dSDan Williams 		/* Without a dcr only the memdev attributes can be surfaced */
16311499934dSDan Williams 		if (a == &dev_attr_handle.attr || a == &dev_attr_phys_id.attr
16321499934dSDan Williams 				|| a == &dev_attr_flags.attr
16331499934dSDan Williams 				|| a == &dev_attr_family.attr
16341499934dSDan Williams 				|| a == &dev_attr_dsm_mask.attr)
16351499934dSDan Williams 			return a->mode;
1636bdf97013SDan Williams 		return 0;
16371499934dSDan Williams 	}
16381499934dSDan Williams 
1639bdf97013SDan Williams 	if (a == &dev_attr_format1.attr && num_nvdimm_formats(nvdimm) <= 1)
1640bdf97013SDan Williams 		return 0;
16410ead1118SDan Williams 
16420ead1118SDan Williams 	if (!test_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags)
16430ead1118SDan Williams 			&& a == &dev_attr_dirty_shutdown.attr)
16440ead1118SDan Williams 		return 0;
16450ead1118SDan Williams 
1646bdf97013SDan Williams 	return a->mode;
1647bdf97013SDan Williams }
1648bdf97013SDan Williams 
16495e93746fSArvind Yadav static const struct attribute_group acpi_nfit_dimm_attribute_group = {
1650bdf97013SDan Williams 	.name = "nfit",
1651bdf97013SDan Williams 	.attrs = acpi_nfit_dimm_attributes,
1652bdf97013SDan Williams 	.is_visible = acpi_nfit_dimm_attr_visible,
1653bdf97013SDan Williams };
1654bdf97013SDan Williams 
1655bdf97013SDan Williams static const struct attribute_group *acpi_nfit_dimm_attribute_groups[] = {
1656bdf97013SDan Williams 	&acpi_nfit_dimm_attribute_group,
1657bdf97013SDan Williams 	NULL,
1658bdf97013SDan Williams };
1659bdf97013SDan Williams 
acpi_nfit_dimm_by_handle(struct acpi_nfit_desc * acpi_desc,u32 device_handle)1660bdf97013SDan Williams static struct nvdimm *acpi_nfit_dimm_by_handle(struct acpi_nfit_desc *acpi_desc,
1661bdf97013SDan Williams 		u32 device_handle)
1662bdf97013SDan Williams {
1663bdf97013SDan Williams 	struct nfit_mem *nfit_mem;
1664bdf97013SDan Williams 
1665bdf97013SDan Williams 	list_for_each_entry(nfit_mem, &acpi_desc->dimms, list)
1666bdf97013SDan Williams 		if (__to_nfit_memdev(nfit_mem)->device_handle == device_handle)
1667bdf97013SDan Williams 			return nfit_mem->nvdimm;
1668bdf97013SDan Williams 
1669bdf97013SDan Williams 	return NULL;
1670bdf97013SDan Williams }
1671bdf97013SDan Williams 
__acpi_nvdimm_notify(struct device * dev,u32 event)1672231bf117SDan Williams void __acpi_nvdimm_notify(struct device *dev, u32 event)
1673ba9c8dd3SDan Williams {
1674ba9c8dd3SDan Williams 	struct nfit_mem *nfit_mem;
1675ba9c8dd3SDan Williams 	struct acpi_nfit_desc *acpi_desc;
1676ba9c8dd3SDan Williams 
1677b814735fSJohannes Thumshirn 	dev_dbg(dev->parent, "%s: event: %d\n", dev_name(dev),
1678ba9c8dd3SDan Williams 			event);
1679ba9c8dd3SDan Williams 
1680ba9c8dd3SDan Williams 	if (event != NFIT_NOTIFY_DIMM_HEALTH) {
1681ba9c8dd3SDan Williams 		dev_dbg(dev->parent, "%s: unknown event: %d\n", dev_name(dev),
1682ba9c8dd3SDan Williams 				event);
1683ba9c8dd3SDan Williams 		return;
1684ba9c8dd3SDan Williams 	}
1685ba9c8dd3SDan Williams 
1686ba9c8dd3SDan Williams 	acpi_desc = dev_get_drvdata(dev->parent);
1687ba9c8dd3SDan Williams 	if (!acpi_desc)
1688ba9c8dd3SDan Williams 		return;
1689ba9c8dd3SDan Williams 
1690ba9c8dd3SDan Williams 	/*
1691ba9c8dd3SDan Williams 	 * If we successfully retrieved acpi_desc, then we know nfit_mem data
1692ba9c8dd3SDan Williams 	 * is still valid.
1693ba9c8dd3SDan Williams 	 */
1694ba9c8dd3SDan Williams 	nfit_mem = dev_get_drvdata(dev);
1695ba9c8dd3SDan Williams 	if (nfit_mem && nfit_mem->flags_attr)
1696ba9c8dd3SDan Williams 		sysfs_notify_dirent(nfit_mem->flags_attr);
1697ba9c8dd3SDan Williams }
1698231bf117SDan Williams EXPORT_SYMBOL_GPL(__acpi_nvdimm_notify);
1699ba9c8dd3SDan Williams 
acpi_nvdimm_notify(acpi_handle handle,u32 event,void * data)1700ba9c8dd3SDan Williams static void acpi_nvdimm_notify(acpi_handle handle, u32 event, void *data)
1701ba9c8dd3SDan Williams {
1702ba9c8dd3SDan Williams 	struct acpi_device *adev = data;
1703ba9c8dd3SDan Williams 	struct device *dev = &adev->dev;
1704ba9c8dd3SDan Williams 
17051550a17aSDan Williams 	device_lock(dev->parent);
1706ba9c8dd3SDan Williams 	__acpi_nvdimm_notify(dev, event);
17071550a17aSDan Williams 	device_unlock(dev->parent);
1708ba9c8dd3SDan Williams }
1709ba9c8dd3SDan Williams 
acpi_nvdimm_has_method(struct acpi_device * adev,char * method)1710466d1493SDan Williams static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method)
1711466d1493SDan Williams {
1712466d1493SDan Williams 	acpi_handle handle;
1713466d1493SDan Williams 	acpi_status status;
1714466d1493SDan Williams 
1715466d1493SDan Williams 	status = acpi_get_handle(adev->handle, method, &handle);
1716466d1493SDan Williams 
1717466d1493SDan Williams 	if (ACPI_SUCCESS(status))
1718466d1493SDan Williams 		return true;
1719466d1493SDan Williams 	return false;
1720466d1493SDan Williams }
1721466d1493SDan Williams 
nfit_intel_shutdown_status(struct nfit_mem * nfit_mem)1722f1101766SDan Williams __weak void nfit_intel_shutdown_status(struct nfit_mem *nfit_mem)
17230ead1118SDan Williams {
1724f596c884SDan Williams 	struct device *dev = &nfit_mem->adev->dev;
17250ead1118SDan Williams 	struct nd_intel_smart smart = { 0 };
17260ead1118SDan Williams 	union acpi_object in_buf = {
1727f596c884SDan Williams 		.buffer.type = ACPI_TYPE_BUFFER,
1728f596c884SDan Williams 		.buffer.length = 0,
17290ead1118SDan Williams 	};
17300ead1118SDan Williams 	union acpi_object in_obj = {
1731f596c884SDan Williams 		.package.type = ACPI_TYPE_PACKAGE,
17320ead1118SDan Williams 		.package.count = 1,
17330ead1118SDan Williams 		.package.elements = &in_buf,
17340ead1118SDan Williams 	};
17350ead1118SDan Williams 	const u8 func = ND_INTEL_SMART;
17360ead1118SDan Williams 	const guid_t *guid = to_nfit_uuid(nfit_mem->family);
17370ead1118SDan Williams 	u8 revid = nfit_dsm_revid(nfit_mem->family, func);
17380ead1118SDan Williams 	struct acpi_device *adev = nfit_mem->adev;
17390ead1118SDan Williams 	acpi_handle handle = adev->handle;
17400ead1118SDan Williams 	union acpi_object *out_obj;
17410ead1118SDan Williams 
17420ead1118SDan Williams 	if ((nfit_mem->dsm_mask & (1 << func)) == 0)
17430ead1118SDan Williams 		return;
17440ead1118SDan Williams 
17450ead1118SDan Williams 	out_obj = acpi_evaluate_dsm(handle, guid, revid, func, &in_obj);
1746f596c884SDan Williams 	if (!out_obj || out_obj->type != ACPI_TYPE_BUFFER
1747f596c884SDan Williams 			|| out_obj->buffer.length < sizeof(smart)) {
1748f596c884SDan Williams 		dev_dbg(dev->parent, "%s: failed to retrieve initial health\n",
1749f596c884SDan Williams 				dev_name(dev));
1750f596c884SDan Williams 		ACPI_FREE(out_obj);
17510ead1118SDan Williams 		return;
1752f596c884SDan Williams 	}
1753f596c884SDan Williams 	memcpy(&smart, out_obj->buffer.pointer, sizeof(smart));
1754f596c884SDan Williams 	ACPI_FREE(out_obj);
17550ead1118SDan Williams 
17560ead1118SDan Williams 	if (smart.flags & ND_INTEL_SMART_SHUTDOWN_VALID) {
17570ead1118SDan Williams 		if (smart.shutdown_state)
17580ead1118SDan Williams 			set_bit(NFIT_MEM_DIRTY, &nfit_mem->flags);
17590ead1118SDan Williams 	}
17600ead1118SDan Williams 
17610ead1118SDan Williams 	if (smart.flags & ND_INTEL_SMART_SHUTDOWN_COUNT_VALID) {
17620ead1118SDan Williams 		set_bit(NFIT_MEM_DIRTY_COUNT, &nfit_mem->flags);
17630ead1118SDan Williams 		nfit_mem->dirty_shutdown = smart.shutdown_count;
17640ead1118SDan Williams 	}
17650ead1118SDan Williams }
17660ead1118SDan Williams 
populate_shutdown_status(struct nfit_mem * nfit_mem)17670ead1118SDan Williams static void populate_shutdown_status(struct nfit_mem *nfit_mem)
17680ead1118SDan Williams {
17690ead1118SDan Williams 	/*
17700ead1118SDan Williams 	 * For DIMMs that provide a dynamic facility to retrieve a
17710ead1118SDan Williams 	 * dirty-shutdown status and/or a dirty-shutdown count, cache
17720ead1118SDan Williams 	 * these values in nfit_mem.
17730ead1118SDan Williams 	 */
17740ead1118SDan Williams 	if (nfit_mem->family == NVDIMM_FAMILY_INTEL)
17750ead1118SDan Williams 		nfit_intel_shutdown_status(nfit_mem);
17760ead1118SDan Williams }
17770ead1118SDan Williams 
acpi_nfit_add_dimm(struct acpi_nfit_desc * acpi_desc,struct nfit_mem * nfit_mem,u32 device_handle)1778bdf97013SDan Williams static int acpi_nfit_add_dimm(struct acpi_nfit_desc *acpi_desc,
1779bdf97013SDan Williams 		struct nfit_mem *nfit_mem, u32 device_handle)
1780bdf97013SDan Williams {
178192fe2aa8SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
1782bdf97013SDan Williams 	struct acpi_device *adev, *adev_dimm;
1783bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
1784099b07a2SDan Williams 	unsigned long dsm_mask, label_mask;
178541c8bdb3SAndy Shevchenko 	const guid_t *guid;
1786bdf97013SDan Williams 	int i;
1787ba650cfcSLinda Knippers 	int family = -1;
1788d6548ae4SDave Jiang 	struct acpi_nfit_control_region *dcr = nfit_mem->dcr;
1789bdf97013SDan Williams 
1790bdf97013SDan Williams 	/* nfit test assumes 1:1 relationship between commands and dsms */
1791bdf97013SDan Williams 	nfit_mem->dsm_mask = acpi_desc->dimm_cmd_force_en;
1792bdf97013SDan Williams 	nfit_mem->family = NVDIMM_FAMILY_INTEL;
179392fe2aa8SDan Williams 	set_bit(NVDIMM_FAMILY_INTEL, &nd_desc->dimm_family_mask);
1794d6548ae4SDave Jiang 
1795d6548ae4SDave Jiang 	if (dcr->valid_fields & ACPI_NFIT_CONTROL_MFG_INFO_VALID)
1796d6548ae4SDave Jiang 		sprintf(nfit_mem->id, "%04x-%02x-%04x-%08x",
1797d6548ae4SDave Jiang 				be16_to_cpu(dcr->vendor_id),
1798d6548ae4SDave Jiang 				dcr->manufacturing_location,
1799d6548ae4SDave Jiang 				be16_to_cpu(dcr->manufacturing_date),
1800d6548ae4SDave Jiang 				be32_to_cpu(dcr->serial_number));
1801d6548ae4SDave Jiang 	else
1802d6548ae4SDave Jiang 		sprintf(nfit_mem->id, "%04x-%08x",
1803d6548ae4SDave Jiang 				be16_to_cpu(dcr->vendor_id),
1804d6548ae4SDave Jiang 				be32_to_cpu(dcr->serial_number));
1805d6548ae4SDave Jiang 
1806bdf97013SDan Williams 	adev = to_acpi_dev(acpi_desc);
1807f1101766SDan Williams 	if (!adev) {
1808f1101766SDan Williams 		/* unit test case */
1809f1101766SDan Williams 		populate_shutdown_status(nfit_mem);
1810bdf97013SDan Williams 		return 0;
1811f1101766SDan Williams 	}
1812bdf97013SDan Williams 
1813bdf97013SDan Williams 	adev_dimm = acpi_find_child_device(adev, device_handle, false);
1814bdf97013SDan Williams 	nfit_mem->adev = adev_dimm;
1815bdf97013SDan Williams 	if (!adev_dimm) {
1816bdf97013SDan Williams 		dev_err(dev, "no ACPI.NFIT device with _ADR %#x, disabling...\n",
1817bdf97013SDan Williams 				device_handle);
1818bdf97013SDan Williams 		return force_enable_dimms ? 0 : -ENODEV;
1819bdf97013SDan Williams 	}
1820bdf97013SDan Williams 
1821ba9c8dd3SDan Williams 	if (ACPI_FAILURE(acpi_install_notify_handler(adev_dimm->handle,
1822ba9c8dd3SDan Williams 		ACPI_DEVICE_NOTIFY, acpi_nvdimm_notify, adev_dimm))) {
1823ba9c8dd3SDan Williams 		dev_err(dev, "%s: notification registration failed\n",
1824ba9c8dd3SDan Williams 				dev_name(&adev_dimm->dev));
1825ba9c8dd3SDan Williams 		return -ENXIO;
1826ba9c8dd3SDan Williams 	}
1827adf68957SDan Williams 	/*
1828adf68957SDan Williams 	 * Record nfit_mem for the notification path to track back to
1829adf68957SDan Williams 	 * the nfit sysfs attributes for this dimm device object.
1830adf68957SDan Williams 	 */
1831adf68957SDan Williams 	dev_set_drvdata(&adev_dimm->dev, nfit_mem);
1832ba9c8dd3SDan Williams 
1833bdf97013SDan Williams 	/*
18341194c413SDexuan Cui 	 * There are 4 "legacy" NVDIMM command sets
18351194c413SDexuan Cui 	 * (NVDIMM_FAMILY_{INTEL,MSFT,HPE1,HPE2}) that were created before
18361194c413SDexuan Cui 	 * an EFI working group was established to constrain this
18371194c413SDexuan Cui 	 * proliferation. The nfit driver probes for the supported command
18381194c413SDexuan Cui 	 * set by GUID. Note, if you're a platform developer looking to add
18391194c413SDexuan Cui 	 * a new command set to this probe, consider using an existing set,
18401194c413SDexuan Cui 	 * or otherwise seek approval to publish the command set at
18411194c413SDexuan Cui 	 * http://www.uefi.org/RFIC_LIST.
18421194c413SDexuan Cui 	 *
18431194c413SDexuan Cui 	 * Note, that checking for function0 (bit0) tells us if any commands
18441194c413SDexuan Cui 	 * are reachable through this GUID.
1845bdf97013SDan Williams 	 */
184692fe2aa8SDan Williams 	clear_bit(NVDIMM_FAMILY_INTEL, &nd_desc->dimm_family_mask);
184711e14270SDan Williams 	for (i = 0; i <= NVDIMM_FAMILY_MAX; i++)
184892fe2aa8SDan Williams 		if (acpi_check_dsm(adev_dimm->handle, to_nfit_uuid(i), 1, 1)) {
184992fe2aa8SDan Williams 			set_bit(i, &nd_desc->dimm_family_mask);
1850ba650cfcSLinda Knippers 			if (family < 0 || i == default_dsm_family)
1851ba650cfcSLinda Knippers 				family = i;
185292fe2aa8SDan Williams 		}
1853bdf97013SDan Williams 
1854bdf97013SDan Williams 	/* limit the supported commands to those that are publicly documented */
1855ba650cfcSLinda Knippers 	nfit_mem->family = family;
1856095ab4b3SLinda Knippers 	if (override_dsm_mask && !disable_vendor_specific)
1857095ab4b3SLinda Knippers 		dsm_mask = override_dsm_mask;
1858095ab4b3SLinda Knippers 	else if (nfit_mem->family == NVDIMM_FAMILY_INTEL) {
185911e14270SDan Williams 		dsm_mask = NVDIMM_INTEL_CMDMASK;
1860bdf97013SDan Williams 		if (disable_vendor_specific)
1861bdf97013SDan Williams 			dsm_mask &= ~(1 << ND_CMD_VENDOR);
1862bdf97013SDan Williams 	} else if (nfit_mem->family == NVDIMM_FAMILY_HPE1) {
1863bdf97013SDan Williams 		dsm_mask = 0x1c3c76;
1864bdf97013SDan Williams 	} else if (nfit_mem->family == NVDIMM_FAMILY_HPE2) {
1865bdf97013SDan Williams 		dsm_mask = 0x1fe;
1866bdf97013SDan Williams 		if (disable_vendor_specific)
1867bdf97013SDan Williams 			dsm_mask &= ~(1 << 8);
1868bdf97013SDan Williams 	} else if (nfit_mem->family == NVDIMM_FAMILY_MSFT) {
1869bdf97013SDan Williams 		dsm_mask = 0xffffffff;
18701194c413SDexuan Cui 	} else if (nfit_mem->family == NVDIMM_FAMILY_HYPERV) {
18711194c413SDexuan Cui 		dsm_mask = 0x1f;
1872bdf97013SDan Williams 	} else {
18730606263fSDan Williams 		dev_dbg(dev, "unknown dimm command family\n");
1874bdf97013SDan Williams 		nfit_mem->family = -1;
18750606263fSDan Williams 		/* DSMs are optional, continue loading the driver... */
18760606263fSDan Williams 		return 0;
1877bdf97013SDan Williams 	}
1878bdf97013SDan Williams 
18795e9e38d0SDan Williams 	/*
18805e9e38d0SDan Williams 	 * Function 0 is the command interrogation function, don't
18815e9e38d0SDan Williams 	 * export it to potential userspace use, and enable it to be
18825e9e38d0SDan Williams 	 * used as an error value in acpi_nfit_ctl().
18835e9e38d0SDan Williams 	 */
18845e9e38d0SDan Williams 	dsm_mask &= ~1UL;
18855e9e38d0SDan Williams 
188641c8bdb3SAndy Shevchenko 	guid = to_nfit_uuid(nfit_mem->family);
1887bdf97013SDan Williams 	for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
188811e14270SDan Williams 		if (acpi_check_dsm(adev_dimm->handle, guid,
188911e14270SDan Williams 					nfit_dsm_revid(nfit_mem->family, i),
189011e14270SDan Williams 					1ULL << i))
1891bdf97013SDan Williams 			set_bit(i, &nfit_mem->dsm_mask);
1892bdf97013SDan Williams 
1893099b07a2SDan Williams 	/*
1894099b07a2SDan Williams 	 * Prefer the NVDIMM_FAMILY_INTEL label read commands if present
1895099b07a2SDan Williams 	 * due to their better semantics handling locked capacity.
1896099b07a2SDan Williams 	 */
1897099b07a2SDan Williams 	label_mask = 1 << ND_CMD_GET_CONFIG_SIZE | 1 << ND_CMD_GET_CONFIG_DATA
1898099b07a2SDan Williams 		| 1 << ND_CMD_SET_CONFIG_DATA;
1899099b07a2SDan Williams 	if (family == NVDIMM_FAMILY_INTEL
1900099b07a2SDan Williams 			&& (dsm_mask & label_mask) == label_mask)
1901f596c884SDan Williams 		/* skip _LS{I,R,W} enabling */;
1902f596c884SDan Williams 	else {
1903466d1493SDan Williams 		if (acpi_nvdimm_has_method(adev_dimm, "_LSI")
1904466d1493SDan Williams 				&& acpi_nvdimm_has_method(adev_dimm, "_LSR")) {
19054b27db7eSDan Williams 			dev_dbg(dev, "%s: has _LSR\n", dev_name(&adev_dimm->dev));
19066f07f86cSDan Williams 			set_bit(NFIT_MEM_LSR, &nfit_mem->flags);
19074b27db7eSDan Williams 		}
19084b27db7eSDan Williams 
19096f07f86cSDan Williams 		if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)
19106f07f86cSDan Williams 				&& acpi_nvdimm_has_method(adev_dimm, "_LSW")) {
19114b27db7eSDan Williams 			dev_dbg(dev, "%s: has _LSW\n", dev_name(&adev_dimm->dev));
19126f07f86cSDan Williams 			set_bit(NFIT_MEM_LSW, &nfit_mem->flags);
19134b27db7eSDan Williams 		}
19144b27db7eSDan Williams 
19150171b6b7SDan Williams 		/*
19160171b6b7SDan Williams 		 * Quirk read-only label configurations to preserve
19170171b6b7SDan Williams 		 * access to label-less namespaces by default.
19180171b6b7SDan Williams 		 */
19190171b6b7SDan Williams 		if (!test_bit(NFIT_MEM_LSW, &nfit_mem->flags)
19200171b6b7SDan Williams 				&& !force_labels) {
19210171b6b7SDan Williams 			dev_dbg(dev, "%s: No _LSW, disable labels\n",
19220171b6b7SDan Williams 					dev_name(&adev_dimm->dev));
19230171b6b7SDan Williams 			clear_bit(NFIT_MEM_LSR, &nfit_mem->flags);
19240171b6b7SDan Williams 		} else
19250171b6b7SDan Williams 			dev_dbg(dev, "%s: Force enable labels\n",
19260171b6b7SDan Williams 					dev_name(&adev_dimm->dev));
1927f596c884SDan Williams 	}
1928bdf97013SDan Williams 
19290ead1118SDan Williams 	populate_shutdown_status(nfit_mem);
19300ead1118SDan Williams 
1931bdf97013SDan Williams 	return 0;
1932bdf97013SDan Williams }
1933bdf97013SDan Williams 
shutdown_dimm_notify(void * data)1934ba9c8dd3SDan Williams static void shutdown_dimm_notify(void *data)
1935ba9c8dd3SDan Williams {
1936ba9c8dd3SDan Williams 	struct acpi_nfit_desc *acpi_desc = data;
1937ba9c8dd3SDan Williams 	struct nfit_mem *nfit_mem;
1938ba9c8dd3SDan Williams 
1939ba9c8dd3SDan Williams 	mutex_lock(&acpi_desc->init_mutex);
1940ba9c8dd3SDan Williams 	/*
1941ba9c8dd3SDan Williams 	 * Clear out the nfit_mem->flags_attr and shut down dimm event
1942ba9c8dd3SDan Williams 	 * notifications.
1943ba9c8dd3SDan Williams 	 */
1944ba9c8dd3SDan Williams 	list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
1945231bf117SDan Williams 		struct acpi_device *adev_dimm = nfit_mem->adev;
1946231bf117SDan Williams 
1947ba9c8dd3SDan Williams 		if (nfit_mem->flags_attr) {
1948ba9c8dd3SDan Williams 			sysfs_put(nfit_mem->flags_attr);
1949ba9c8dd3SDan Williams 			nfit_mem->flags_attr = NULL;
1950ba9c8dd3SDan Williams 		}
1951adf68957SDan Williams 		if (adev_dimm) {
1952231bf117SDan Williams 			acpi_remove_notify_handler(adev_dimm->handle,
1953ba9c8dd3SDan Williams 					ACPI_DEVICE_NOTIFY, acpi_nvdimm_notify);
1954adf68957SDan Williams 			dev_set_drvdata(&adev_dimm->dev, NULL);
1955adf68957SDan Williams 		}
1956ba9c8dd3SDan Williams 	}
1957ba9c8dd3SDan Williams 	mutex_unlock(&acpi_desc->init_mutex);
1958ba9c8dd3SDan Williams }
1959ba9c8dd3SDan Williams 
acpi_nfit_get_security_ops(int family)1960f2989396SDave Jiang static const struct nvdimm_security_ops *acpi_nfit_get_security_ops(int family)
1961f2989396SDave Jiang {
1962f2989396SDave Jiang 	switch (family) {
1963f2989396SDave Jiang 	case NVDIMM_FAMILY_INTEL:
1964f2989396SDave Jiang 		return intel_security_ops;
1965f2989396SDave Jiang 	default:
1966f2989396SDave Jiang 		return NULL;
1967f2989396SDave Jiang 	}
1968f2989396SDave Jiang }
1969f2989396SDave Jiang 
acpi_nfit_get_fw_ops(struct nfit_mem * nfit_mem)1970a1facc1fSDan Williams static const struct nvdimm_fw_ops *acpi_nfit_get_fw_ops(
1971a1facc1fSDan Williams 		struct nfit_mem *nfit_mem)
1972a1facc1fSDan Williams {
1973a1facc1fSDan Williams 	unsigned long mask;
1974a1facc1fSDan Williams 	struct acpi_nfit_desc *acpi_desc = nfit_mem->acpi_desc;
1975a1facc1fSDan Williams 	struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
1976a1facc1fSDan Williams 
1977a1facc1fSDan Williams 	if (!nd_desc->fw_ops)
1978a1facc1fSDan Williams 		return NULL;
1979a1facc1fSDan Williams 
1980a1facc1fSDan Williams 	if (nfit_mem->family != NVDIMM_FAMILY_INTEL)
1981a1facc1fSDan Williams 		return NULL;
1982a1facc1fSDan Williams 
1983a1facc1fSDan Williams 	mask = nfit_mem->dsm_mask & NVDIMM_INTEL_FW_ACTIVATE_CMDMASK;
1984a1facc1fSDan Williams 	if (mask != NVDIMM_INTEL_FW_ACTIVATE_CMDMASK)
1985a1facc1fSDan Williams 		return NULL;
1986a1facc1fSDan Williams 
1987a1facc1fSDan Williams 	return intel_fw_ops;
1988a1facc1fSDan Williams }
1989a1facc1fSDan Williams 
acpi_nfit_register_dimms(struct acpi_nfit_desc * acpi_desc)1990bdf97013SDan Williams static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
1991bdf97013SDan Williams {
1992bdf97013SDan Williams 	struct nfit_mem *nfit_mem;
1993ba9c8dd3SDan Williams 	int dimm_count = 0, rc;
1994ba9c8dd3SDan Williams 	struct nvdimm *nvdimm;
1995bdf97013SDan Williams 
1996bdf97013SDan Williams 	list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
1997bdf97013SDan Williams 		struct acpi_nfit_flush_address *flush;
1998bdf97013SDan Williams 		unsigned long flags = 0, cmd_mask;
1999caa603aaSDan Williams 		struct nfit_memdev *nfit_memdev;
2000bdf97013SDan Williams 		u32 device_handle;
2001bdf97013SDan Williams 		u16 mem_flags;
2002bdf97013SDan Williams 
2003bdf97013SDan Williams 		device_handle = __to_nfit_memdev(nfit_mem)->device_handle;
2004bdf97013SDan Williams 		nvdimm = acpi_nfit_dimm_by_handle(acpi_desc, device_handle);
2005bdf97013SDan Williams 		if (nvdimm) {
2006bdf97013SDan Williams 			dimm_count++;
2007bdf97013SDan Williams 			continue;
2008bdf97013SDan Williams 		}
2009bdf97013SDan Williams 
2010caa603aaSDan Williams 		/* collate flags across all memdevs for this dimm */
2011caa603aaSDan Williams 		list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
2012caa603aaSDan Williams 			struct acpi_nfit_memory_map *dimm_memdev;
2013caa603aaSDan Williams 
2014caa603aaSDan Williams 			dimm_memdev = __to_nfit_memdev(nfit_mem);
2015caa603aaSDan Williams 			if (dimm_memdev->device_handle
2016caa603aaSDan Williams 					!= nfit_memdev->memdev->device_handle)
2017caa603aaSDan Williams 				continue;
2018caa603aaSDan Williams 			dimm_memdev->flags |= nfit_memdev->memdev->flags;
2019caa603aaSDan Williams 		}
2020caa603aaSDan Williams 
2021bdf97013SDan Williams 		mem_flags = __to_nfit_memdev(nfit_mem)->flags;
2022bdf97013SDan Williams 		if (mem_flags & ACPI_NFIT_MEM_NOT_ARMED)
20238f078b38SDan Williams 			set_bit(NDD_UNARMED, &flags);
2024bdf97013SDan Williams 
2025bdf97013SDan Williams 		rc = acpi_nfit_add_dimm(acpi_desc, nfit_mem, device_handle);
2026bdf97013SDan Williams 		if (rc)
2027bdf97013SDan Williams 			continue;
2028bdf97013SDan Williams 
2029bdf97013SDan Williams 		/*
2030bdf97013SDan Williams 		 * TODO: provide translation for non-NVDIMM_FAMILY_INTEL
2031bdf97013SDan Williams 		 * devices (i.e. from nd_cmd to acpi_dsm) to standardize the
2032bdf97013SDan Williams 		 * userspace interface.
2033bdf97013SDan Williams 		 */
2034bdf97013SDan Williams 		cmd_mask = 1UL << ND_CMD_CALL;
2035b9b1504dSDan Williams 		if (nfit_mem->family == NVDIMM_FAMILY_INTEL) {
2036b9b1504dSDan Williams 			/*
2037b9b1504dSDan Williams 			 * These commands have a 1:1 correspondence
2038b9b1504dSDan Williams 			 * between DSM payload and libnvdimm ioctl
2039b9b1504dSDan Williams 			 * payload format.
2040b9b1504dSDan Williams 			 */
2041b9b1504dSDan Williams 			cmd_mask |= nfit_mem->dsm_mask & NVDIMM_STANDARD_CMDMASK;
2042b9b1504dSDan Williams 		}
2043bdf97013SDan Williams 
20446f07f86cSDan Williams 		if (test_bit(NFIT_MEM_LSR, &nfit_mem->flags)) {
20454b27db7eSDan Williams 			set_bit(ND_CMD_GET_CONFIG_SIZE, &cmd_mask);
20464b27db7eSDan Williams 			set_bit(ND_CMD_GET_CONFIG_DATA, &cmd_mask);
2047466d1493SDan Williams 		}
20486f07f86cSDan Williams 		if (test_bit(NFIT_MEM_LSW, &nfit_mem->flags))
20494b27db7eSDan Williams 			set_bit(ND_CMD_SET_CONFIG_DATA, &cmd_mask);
20504b27db7eSDan Williams 
2051bdf97013SDan Williams 		flush = nfit_mem->nfit_flush ? nfit_mem->nfit_flush->flush
2052bdf97013SDan Williams 			: NULL;
2053d6548ae4SDave Jiang 		nvdimm = __nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem,
2054bdf97013SDan Williams 				acpi_nfit_dimm_attribute_groups,
2055bdf97013SDan Williams 				flags, cmd_mask, flush ? flush->hint_count : 0,
2056f2989396SDave Jiang 				nfit_mem->flush_wpq, &nfit_mem->id[0],
2057a1facc1fSDan Williams 				acpi_nfit_get_security_ops(nfit_mem->family),
2058a1facc1fSDan Williams 				acpi_nfit_get_fw_ops(nfit_mem));
2059bdf97013SDan Williams 		if (!nvdimm)
2060bdf97013SDan Williams 			return -ENOMEM;
2061bdf97013SDan Williams 
2062bdf97013SDan Williams 		nfit_mem->nvdimm = nvdimm;
2063bdf97013SDan Williams 		dimm_count++;
2064bdf97013SDan Williams 
2065bdf97013SDan Williams 		if ((mem_flags & ACPI_NFIT_MEM_FAILED_MASK) == 0)
2066bdf97013SDan Williams 			continue;
2067bdf97013SDan Williams 
20685c9d62d0SToshi Kani 		dev_err(acpi_desc->dev, "Error found in NVDIMM %s flags:%s%s%s%s%s\n",
2069bdf97013SDan Williams 				nvdimm_name(nvdimm),
2070bdf97013SDan Williams 		  mem_flags & ACPI_NFIT_MEM_SAVE_FAILED ? " save_fail" : "",
2071bdf97013SDan Williams 		  mem_flags & ACPI_NFIT_MEM_RESTORE_FAILED ? " restore_fail":"",
2072bdf97013SDan Williams 		  mem_flags & ACPI_NFIT_MEM_FLUSH_FAILED ? " flush_fail" : "",
20731499934dSDan Williams 		  mem_flags & ACPI_NFIT_MEM_NOT_ARMED ? " not_armed" : "",
20741499934dSDan Williams 		  mem_flags & ACPI_NFIT_MEM_MAP_FAILED ? " map_fail" : "");
2075bdf97013SDan Williams 
2076bdf97013SDan Williams 	}
2077bdf97013SDan Williams 
2078ba9c8dd3SDan Williams 	rc = nvdimm_bus_check_dimm_count(acpi_desc->nvdimm_bus, dimm_count);
2079ba9c8dd3SDan Williams 	if (rc)
2080ba9c8dd3SDan Williams 		return rc;
2081ba9c8dd3SDan Williams 
2082ba9c8dd3SDan Williams 	/*
2083ba9c8dd3SDan Williams 	 * Now that dimms are successfully registered, and async registration
2084ba9c8dd3SDan Williams 	 * is flushed, attempt to enable event notification.
2085ba9c8dd3SDan Williams 	 */
2086ba9c8dd3SDan Williams 	list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
2087ba9c8dd3SDan Williams 		struct kernfs_node *nfit_kernfs;
2088ba9c8dd3SDan Williams 
2089ba9c8dd3SDan Williams 		nvdimm = nfit_mem->nvdimm;
209023fbd7c7SToshi Kani 		if (!nvdimm)
209123fbd7c7SToshi Kani 			continue;
209223fbd7c7SToshi Kani 
2093ba9c8dd3SDan Williams 		nfit_kernfs = sysfs_get_dirent(nvdimm_kobj(nvdimm)->sd, "nfit");
2094ba9c8dd3SDan Williams 		if (nfit_kernfs)
2095ba9c8dd3SDan Williams 			nfit_mem->flags_attr = sysfs_get_dirent(nfit_kernfs,
2096ba9c8dd3SDan Williams 					"flags");
2097ba9c8dd3SDan Williams 		sysfs_put(nfit_kernfs);
2098ba9c8dd3SDan Williams 		if (!nfit_mem->flags_attr)
2099ba9c8dd3SDan Williams 			dev_warn(acpi_desc->dev, "%s: notifications disabled\n",
2100ba9c8dd3SDan Williams 					nvdimm_name(nvdimm));
2101ba9c8dd3SDan Williams 	}
2102ba9c8dd3SDan Williams 
2103ba9c8dd3SDan Williams 	return devm_add_action_or_reset(acpi_desc->dev, shutdown_dimm_notify,
2104ba9c8dd3SDan Williams 			acpi_desc);
2105bdf97013SDan Williams }
2106bdf97013SDan Williams 
21077db5bb33SJerry Hoemann /*
21087db5bb33SJerry Hoemann  * These constants are private because there are no kernel consumers of
21097db5bb33SJerry Hoemann  * these commands.
21107db5bb33SJerry Hoemann  */
21117db5bb33SJerry Hoemann enum nfit_aux_cmds {
21127db5bb33SJerry Hoemann 	NFIT_CMD_TRANSLATE_SPA = 5,
21137db5bb33SJerry Hoemann 	NFIT_CMD_ARS_INJECT_SET = 7,
21147db5bb33SJerry Hoemann 	NFIT_CMD_ARS_INJECT_CLEAR = 8,
21157db5bb33SJerry Hoemann 	NFIT_CMD_ARS_INJECT_GET = 9,
21167db5bb33SJerry Hoemann };
21177db5bb33SJerry Hoemann 
acpi_nfit_init_dsms(struct acpi_nfit_desc * acpi_desc)2118bdf97013SDan Williams static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
2119bdf97013SDan Williams {
2120bdf97013SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
212141c8bdb3SAndy Shevchenko 	const guid_t *guid = to_nfit_uuid(NFIT_DEV_BUS);
21226450ddbdSDan Williams 	unsigned long dsm_mask, *mask;
2123bdf97013SDan Williams 	struct acpi_device *adev;
2124bdf97013SDan Williams 	int i;
2125bdf97013SDan Williams 
212692fe2aa8SDan Williams 	set_bit(ND_CMD_CALL, &nd_desc->cmd_mask);
212792fe2aa8SDan Williams 	set_bit(NVDIMM_BUS_FAMILY_NFIT, &nd_desc->bus_family_mask);
212892fe2aa8SDan Williams 
21296450ddbdSDan Williams 	/* enable nfit_test to inject bus command emulation */
21306450ddbdSDan Williams 	if (acpi_desc->bus_cmd_force_en) {
21316450ddbdSDan Williams 		nd_desc->cmd_mask = acpi_desc->bus_cmd_force_en;
21326450ddbdSDan Williams 		mask = &nd_desc->bus_family_mask;
2133a1facc1fSDan Williams 		if (acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL]) {
21346450ddbdSDan Williams 			set_bit(NVDIMM_BUS_FAMILY_INTEL, mask);
2135a1facc1fSDan Williams 			nd_desc->fw_ops = intel_bus_fw_ops;
2136a1facc1fSDan Williams 		}
21376450ddbdSDan Williams 	}
21386450ddbdSDan Williams 
2139bdf97013SDan Williams 	adev = to_acpi_dev(acpi_desc);
2140bdf97013SDan Williams 	if (!adev)
2141bdf97013SDan Williams 		return;
2142bdf97013SDan Williams 
2143bdf97013SDan Williams 	for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; i++)
214494116f81SAndy Shevchenko 		if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i))
2145bdf97013SDan Williams 			set_bit(i, &nd_desc->cmd_mask);
21467db5bb33SJerry Hoemann 
21477db5bb33SJerry Hoemann 	dsm_mask =
21487db5bb33SJerry Hoemann 		(1 << ND_CMD_ARS_CAP) |
21497db5bb33SJerry Hoemann 		(1 << ND_CMD_ARS_START) |
21507db5bb33SJerry Hoemann 		(1 << ND_CMD_ARS_STATUS) |
21517db5bb33SJerry Hoemann 		(1 << ND_CMD_CLEAR_ERROR) |
21527db5bb33SJerry Hoemann 		(1 << NFIT_CMD_TRANSLATE_SPA) |
21537db5bb33SJerry Hoemann 		(1 << NFIT_CMD_ARS_INJECT_SET) |
21547db5bb33SJerry Hoemann 		(1 << NFIT_CMD_ARS_INJECT_CLEAR) |
21557db5bb33SJerry Hoemann 		(1 << NFIT_CMD_ARS_INJECT_GET);
21567db5bb33SJerry Hoemann 	for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
21577db5bb33SJerry Hoemann 		if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i))
2158d46e6a21SDan Williams 			set_bit(i, &acpi_desc->bus_dsm_mask);
21596450ddbdSDan Williams 
21606450ddbdSDan Williams 	/* Enumerate allowed NVDIMM_BUS_FAMILY_INTEL commands */
21616450ddbdSDan Williams 	dsm_mask = NVDIMM_BUS_INTEL_FW_ACTIVATE_CMDMASK;
21626450ddbdSDan Williams 	guid = to_nfit_bus_uuid(NVDIMM_BUS_FAMILY_INTEL);
21636450ddbdSDan Williams 	mask = &acpi_desc->family_dsm_mask[NVDIMM_BUS_FAMILY_INTEL];
21646450ddbdSDan Williams 	for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
21656450ddbdSDan Williams 		if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i))
21666450ddbdSDan Williams 			set_bit(i, mask);
2167a1facc1fSDan Williams 
2168a1facc1fSDan Williams 	if (*mask == dsm_mask) {
2169a1facc1fSDan Williams 		set_bit(NVDIMM_BUS_FAMILY_INTEL, &nd_desc->bus_family_mask);
2170a1facc1fSDan Williams 		nd_desc->fw_ops = intel_bus_fw_ops;
2171a1facc1fSDan Williams 	}
2172bdf97013SDan Williams }
2173bdf97013SDan Williams 
range_index_show(struct device * dev,struct device_attribute * attr,char * buf)2174bdf97013SDan Williams static ssize_t range_index_show(struct device *dev,
2175bdf97013SDan Williams 		struct device_attribute *attr, char *buf)
2176bdf97013SDan Williams {
2177bdf97013SDan Williams 	struct nd_region *nd_region = to_nd_region(dev);
2178bdf97013SDan Williams 	struct nfit_spa *nfit_spa = nd_region_provider_data(nd_region);
2179bdf97013SDan Williams 
2180bdf97013SDan Williams 	return sprintf(buf, "%d\n", nfit_spa->spa->range_index);
2181bdf97013SDan Williams }
2182bdf97013SDan Williams static DEVICE_ATTR_RO(range_index);
2183bdf97013SDan Williams 
2184bdf97013SDan Williams static struct attribute *acpi_nfit_region_attributes[] = {
2185bdf97013SDan Williams 	&dev_attr_range_index.attr,
2186bdf97013SDan Williams 	NULL,
2187bdf97013SDan Williams };
2188bdf97013SDan Williams 
21895e93746fSArvind Yadav static const struct attribute_group acpi_nfit_region_attribute_group = {
2190bdf97013SDan Williams 	.name = "nfit",
2191bdf97013SDan Williams 	.attrs = acpi_nfit_region_attributes,
2192bdf97013SDan Williams };
2193bdf97013SDan Williams 
2194bdf97013SDan Williams static const struct attribute_group *acpi_nfit_region_attribute_groups[] = {
2195bdf97013SDan Williams 	&acpi_nfit_region_attribute_group,
2196bdf97013SDan Williams 	NULL,
2197bdf97013SDan Williams };
2198bdf97013SDan Williams 
2199bdf97013SDan Williams /* enough info to uniquely specify an interleave set */
2200bdf97013SDan Williams struct nfit_set_info {
2201bdf97013SDan Williams 	u64 region_offset;
2202bdf97013SDan Williams 	u32 serial_number;
2203bdf97013SDan Williams 	u32 pad;
2204bdf97013SDan Williams };
2205bdf97013SDan Williams 
2206c12c48ceSDan Williams struct nfit_set_info2 {
2207c12c48ceSDan Williams 	u64 region_offset;
2208c12c48ceSDan Williams 	u32 serial_number;
2209c12c48ceSDan Williams 	u16 vendor_id;
2210c12c48ceSDan Williams 	u16 manufacturing_date;
2211c12c48ceSDan Williams 	u8 manufacturing_location;
2212c12c48ceSDan Williams 	u8 reserved[31];
2213c12c48ceSDan Williams };
2214c12c48ceSDan Williams 
cmp_map_compat(const void * m0,const void * m1)221586ef58a4SDan Williams static int cmp_map_compat(const void *m0, const void *m1)
2216bdf97013SDan Williams {
2217637464c5SDan Williams 	const struct nfit_set_info *map0 = m0;
2218637464c5SDan Williams 	const struct nfit_set_info *map1 = m1;
2219bdf97013SDan Williams 
2220bdf97013SDan Williams 	return memcmp(&map0->region_offset, &map1->region_offset,
2221bdf97013SDan Williams 			sizeof(u64));
2222bdf97013SDan Williams }
2223bdf97013SDan Williams 
cmp_map(const void * m0,const void * m1)222486ef58a4SDan Williams static int cmp_map(const void *m0, const void *m1)
222586ef58a4SDan Williams {
2226637464c5SDan Williams 	const struct nfit_set_info *map0 = m0;
2227637464c5SDan Williams 	const struct nfit_set_info *map1 = m1;
222886ef58a4SDan Williams 
2229b03b99a3SDan Williams 	if (map0->region_offset < map1->region_offset)
2230b03b99a3SDan Williams 		return -1;
2231b03b99a3SDan Williams 	else if (map0->region_offset > map1->region_offset)
2232b03b99a3SDan Williams 		return 1;
2233b03b99a3SDan Williams 	return 0;
223486ef58a4SDan Williams }
223586ef58a4SDan Williams 
cmp_map2(const void * m0,const void * m1)2236c12c48ceSDan Williams static int cmp_map2(const void *m0, const void *m1)
2237c12c48ceSDan Williams {
2238637464c5SDan Williams 	const struct nfit_set_info2 *map0 = m0;
2239637464c5SDan Williams 	const struct nfit_set_info2 *map1 = m1;
2240c12c48ceSDan Williams 
2241c12c48ceSDan Williams 	if (map0->region_offset < map1->region_offset)
2242c12c48ceSDan Williams 		return -1;
2243c12c48ceSDan Williams 	else if (map0->region_offset > map1->region_offset)
2244c12c48ceSDan Williams 		return 1;
2245c12c48ceSDan Williams 	return 0;
2246c12c48ceSDan Williams }
2247c12c48ceSDan Williams 
2248bdf97013SDan Williams /* Retrieve the nth entry referencing this spa */
memdev_from_spa(struct acpi_nfit_desc * acpi_desc,u16 range_index,int n)2249bdf97013SDan Williams static struct acpi_nfit_memory_map *memdev_from_spa(
2250bdf97013SDan Williams 		struct acpi_nfit_desc *acpi_desc, u16 range_index, int n)
2251bdf97013SDan Williams {
2252bdf97013SDan Williams 	struct nfit_memdev *nfit_memdev;
2253bdf97013SDan Williams 
2254bdf97013SDan Williams 	list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list)
2255bdf97013SDan Williams 		if (nfit_memdev->memdev->range_index == range_index)
2256bdf97013SDan Williams 			if (n-- == 0)
2257bdf97013SDan Williams 				return nfit_memdev->memdev;
2258bdf97013SDan Williams 	return NULL;
2259bdf97013SDan Williams }
2260bdf97013SDan Williams 
acpi_nfit_init_interleave_set(struct acpi_nfit_desc * acpi_desc,struct nd_region_desc * ndr_desc,struct acpi_nfit_system_address * spa)2261bdf97013SDan Williams static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
2262bdf97013SDan Williams 		struct nd_region_desc *ndr_desc,
2263bdf97013SDan Williams 		struct acpi_nfit_system_address *spa)
2264bdf97013SDan Williams {
2265bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
2266bdf97013SDan Williams 	struct nd_interleave_set *nd_set;
2267bdf97013SDan Williams 	u16 nr = ndr_desc->num_mappings;
2268c12c48ceSDan Williams 	struct nfit_set_info2 *info2;
2269bdf97013SDan Williams 	struct nfit_set_info *info;
22708f2bc243SDan Williams 	int i;
2271bdf97013SDan Williams 
2272faec6f8aSDan Williams 	nd_set = devm_kzalloc(dev, sizeof(*nd_set), GFP_KERNEL);
2273faec6f8aSDan Williams 	if (!nd_set)
2274faec6f8aSDan Williams 		return -ENOMEM;
2275f37b451fSAndy Shevchenko 	import_guid(&nd_set->type_guid, spa->range_guid);
2276faec6f8aSDan Williams 
2277637464c5SDan Williams 	info = devm_kcalloc(dev, nr, sizeof(*info), GFP_KERNEL);
2278bdf97013SDan Williams 	if (!info)
2279bdf97013SDan Williams 		return -ENOMEM;
2280c12c48ceSDan Williams 
2281637464c5SDan Williams 	info2 = devm_kcalloc(dev, nr, sizeof(*info2), GFP_KERNEL);
2282c12c48ceSDan Williams 	if (!info2)
2283c12c48ceSDan Williams 		return -ENOMEM;
2284c12c48ceSDan Williams 
2285bdf97013SDan Williams 	for (i = 0; i < nr; i++) {
228644c462ebSDan Williams 		struct nd_mapping_desc *mapping = &ndr_desc->mapping[i];
228744c462ebSDan Williams 		struct nvdimm *nvdimm = mapping->nvdimm;
2288bdf97013SDan Williams 		struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
2289637464c5SDan Williams 		struct nfit_set_info *map = &info[i];
2290637464c5SDan Williams 		struct nfit_set_info2 *map2 = &info2[i];
2291637464c5SDan Williams 		struct acpi_nfit_memory_map *memdev =
2292637464c5SDan Williams 			memdev_from_spa(acpi_desc, spa->range_index, i);
2293dcb79b15SDan Williams 		struct acpi_nfit_control_region *dcr = nfit_mem->dcr;
2294bdf97013SDan Williams 
2295bdf97013SDan Williams 		if (!memdev || !nfit_mem->dcr) {
2296bdf97013SDan Williams 			dev_err(dev, "%s: failed to find DCR\n", __func__);
2297bdf97013SDan Williams 			return -ENODEV;
2298bdf97013SDan Williams 		}
2299bdf97013SDan Williams 
2300bdf97013SDan Williams 		map->region_offset = memdev->region_offset;
2301dcb79b15SDan Williams 		map->serial_number = dcr->serial_number;
2302c12c48ceSDan Williams 
2303c12c48ceSDan Williams 		map2->region_offset = memdev->region_offset;
2304dcb79b15SDan Williams 		map2->serial_number = dcr->serial_number;
2305dcb79b15SDan Williams 		map2->vendor_id = dcr->vendor_id;
2306dcb79b15SDan Williams 		map2->manufacturing_date = dcr->manufacturing_date;
2307dcb79b15SDan Williams 		map2->manufacturing_location = dcr->manufacturing_location;
2308bdf97013SDan Williams 	}
2309bdf97013SDan Williams 
2310c12c48ceSDan Williams 	/* v1.1 namespaces */
2311637464c5SDan Williams 	sort(info, nr, sizeof(*info), cmp_map, NULL);
2312637464c5SDan Williams 	nd_set->cookie1 = nd_fletcher64(info, sizeof(*info) * nr, 0);
231386ef58a4SDan Williams 
2314c12c48ceSDan Williams 	/* v1.2 namespaces */
2315637464c5SDan Williams 	sort(info2, nr, sizeof(*info2), cmp_map2, NULL);
2316637464c5SDan Williams 	nd_set->cookie2 = nd_fletcher64(info2, sizeof(*info2) * nr, 0);
2317c12c48ceSDan Williams 
2318c12c48ceSDan Williams 	/* support v1.1 namespaces created with the wrong sort order */
2319637464c5SDan Williams 	sort(info, nr, sizeof(*info), cmp_map_compat, NULL);
2320637464c5SDan Williams 	nd_set->altcookie = nd_fletcher64(info, sizeof(*info) * nr, 0);
232186ef58a4SDan Williams 
2322401c0a19SDan Williams 	/* record the result of the sort for the mapping position */
2323401c0a19SDan Williams 	for (i = 0; i < nr; i++) {
2324637464c5SDan Williams 		struct nfit_set_info2 *map2 = &info2[i];
2325401c0a19SDan Williams 		int j;
2326401c0a19SDan Williams 
2327401c0a19SDan Williams 		for (j = 0; j < nr; j++) {
2328401c0a19SDan Williams 			struct nd_mapping_desc *mapping = &ndr_desc->mapping[j];
2329401c0a19SDan Williams 			struct nvdimm *nvdimm = mapping->nvdimm;
2330401c0a19SDan Williams 			struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
2331dcb79b15SDan Williams 			struct acpi_nfit_control_region *dcr = nfit_mem->dcr;
2332401c0a19SDan Williams 
2333dcb79b15SDan Williams 			if (map2->serial_number == dcr->serial_number &&
2334dcb79b15SDan Williams 			    map2->vendor_id == dcr->vendor_id &&
2335dcb79b15SDan Williams 			    map2->manufacturing_date == dcr->manufacturing_date &&
2336401c0a19SDan Williams 			    map2->manufacturing_location
2337dcb79b15SDan Williams 				    == dcr->manufacturing_location) {
2338401c0a19SDan Williams 				mapping->position = i;
2339401c0a19SDan Williams 				break;
2340401c0a19SDan Williams 			}
2341401c0a19SDan Williams 		}
2342401c0a19SDan Williams 	}
2343401c0a19SDan Williams 
2344bdf97013SDan Williams 	ndr_desc->nd_set = nd_set;
2345bdf97013SDan Williams 	devm_kfree(dev, info);
2346c12c48ceSDan Williams 	devm_kfree(dev, info2);
2347bdf97013SDan Williams 
2348bdf97013SDan Williams 	return 0;
2349bdf97013SDan Williams }
2350bdf97013SDan Williams 
ars_get_cap(struct acpi_nfit_desc * acpi_desc,struct nd_cmd_ars_cap * cmd,struct nfit_spa * nfit_spa)2351bdf97013SDan Williams static int ars_get_cap(struct acpi_nfit_desc *acpi_desc,
2352bdf97013SDan Williams 		struct nd_cmd_ars_cap *cmd, struct nfit_spa *nfit_spa)
2353bdf97013SDan Williams {
2354bdf97013SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
2355bdf97013SDan Williams 	struct acpi_nfit_system_address *spa = nfit_spa->spa;
2356bdf97013SDan Williams 	int cmd_rc, rc;
2357bdf97013SDan Williams 
2358bdf97013SDan Williams 	cmd->address = spa->address;
2359bdf97013SDan Williams 	cmd->length = spa->length;
2360bdf97013SDan Williams 	rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_CAP, cmd,
2361bdf97013SDan Williams 			sizeof(*cmd), &cmd_rc);
2362bdf97013SDan Williams 	if (rc < 0)
2363bdf97013SDan Williams 		return rc;
2364bdf97013SDan Williams 	return cmd_rc;
2365bdf97013SDan Williams }
2366bdf97013SDan Williams 
ars_start(struct acpi_nfit_desc * acpi_desc,struct nfit_spa * nfit_spa,enum nfit_ars_state req_type)2367d3abaf43SDan Williams static int ars_start(struct acpi_nfit_desc *acpi_desc,
2368d3abaf43SDan Williams 		struct nfit_spa *nfit_spa, enum nfit_ars_state req_type)
2369bdf97013SDan Williams {
2370bdf97013SDan Williams 	int rc;
2371bdf97013SDan Williams 	int cmd_rc;
2372bdf97013SDan Williams 	struct nd_cmd_ars_start ars_start;
2373bdf97013SDan Williams 	struct acpi_nfit_system_address *spa = nfit_spa->spa;
2374bdf97013SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
2375bdf97013SDan Williams 
2376bdf97013SDan Williams 	memset(&ars_start, 0, sizeof(ars_start));
2377bdf97013SDan Williams 	ars_start.address = spa->address;
2378bdf97013SDan Williams 	ars_start.length = spa->length;
2379d3abaf43SDan Williams 	if (req_type == ARS_REQ_SHORT)
2380bc6ba808SDan Williams 		ars_start.flags = ND_ARS_RETURN_PREV_DATA;
2381bdf97013SDan Williams 	if (nfit_spa_type(spa) == NFIT_SPA_PM)
2382bdf97013SDan Williams 		ars_start.type = ND_ARS_PERSISTENT;
2383bdf97013SDan Williams 	else if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE)
2384bdf97013SDan Williams 		ars_start.type = ND_ARS_VOLATILE;
2385bdf97013SDan Williams 	else
2386bdf97013SDan Williams 		return -ENOTTY;
2387bdf97013SDan Williams 
2388bdf97013SDan Williams 	rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_START, &ars_start,
2389bdf97013SDan Williams 			sizeof(ars_start), &cmd_rc);
2390bdf97013SDan Williams 
2391bdf97013SDan Williams 	if (rc < 0)
2392bdf97013SDan Williams 		return rc;
239378153dd4SDan Williams 	if (cmd_rc < 0)
2394bdf97013SDan Williams 		return cmd_rc;
239578153dd4SDan Williams 	set_bit(ARS_VALID, &acpi_desc->scrub_flags);
239678153dd4SDan Williams 	return 0;
2397bdf97013SDan Williams }
2398bdf97013SDan Williams 
ars_continue(struct acpi_nfit_desc * acpi_desc)2399bdf97013SDan Williams static int ars_continue(struct acpi_nfit_desc *acpi_desc)
2400bdf97013SDan Williams {
2401bdf97013SDan Williams 	int rc, cmd_rc;
2402bdf97013SDan Williams 	struct nd_cmd_ars_start ars_start;
2403bdf97013SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
2404bdf97013SDan Williams 	struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status;
2405bdf97013SDan Williams 
2406317a992aSDan Williams 	ars_start = (struct nd_cmd_ars_start) {
2407317a992aSDan Williams 		.address = ars_status->restart_address,
2408317a992aSDan Williams 		.length = ars_status->restart_length,
2409317a992aSDan Williams 		.type = ars_status->type,
2410317a992aSDan Williams 	};
2411bdf97013SDan Williams 	rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_START, &ars_start,
2412bdf97013SDan Williams 			sizeof(ars_start), &cmd_rc);
2413bdf97013SDan Williams 	if (rc < 0)
2414bdf97013SDan Williams 		return rc;
2415bdf97013SDan Williams 	return cmd_rc;
2416bdf97013SDan Williams }
2417bdf97013SDan Williams 
ars_get_status(struct acpi_nfit_desc * acpi_desc)2418bdf97013SDan Williams static int ars_get_status(struct acpi_nfit_desc *acpi_desc)
2419bdf97013SDan Williams {
2420bdf97013SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
2421bdf97013SDan Williams 	struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status;
2422bdf97013SDan Williams 	int rc, cmd_rc;
2423bdf97013SDan Williams 
2424bdf97013SDan Williams 	rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_STATUS, ars_status,
2425459d0ddbSDan Williams 			acpi_desc->max_ars, &cmd_rc);
2426bdf97013SDan Williams 	if (rc < 0)
2427bdf97013SDan Williams 		return rc;
2428bdf97013SDan Williams 	return cmd_rc;
2429bdf97013SDan Williams }
2430bdf97013SDan Williams 
ars_complete(struct acpi_nfit_desc * acpi_desc,struct nfit_spa * nfit_spa)2431bc6ba808SDan Williams static void ars_complete(struct acpi_nfit_desc *acpi_desc,
2432bc6ba808SDan Williams 		struct nfit_spa *nfit_spa)
2433bc6ba808SDan Williams {
2434bc6ba808SDan Williams 	struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status;
2435bc6ba808SDan Williams 	struct acpi_nfit_system_address *spa = nfit_spa->spa;
2436bc6ba808SDan Williams 	struct nd_region *nd_region = nfit_spa->nd_region;
2437bc6ba808SDan Williams 	struct device *dev;
2438bc6ba808SDan Williams 
2439d3abaf43SDan Williams 	lockdep_assert_held(&acpi_desc->init_mutex);
2440d3abaf43SDan Williams 	/*
2441d3abaf43SDan Williams 	 * Only advance the ARS state for ARS runs initiated by the
2442d3abaf43SDan Williams 	 * kernel, ignore ARS results from BIOS initiated runs for scrub
2443d3abaf43SDan Williams 	 * completion tracking.
2444d3abaf43SDan Williams 	 */
2445d3abaf43SDan Williams 	if (acpi_desc->scrub_spa != nfit_spa)
2446d3abaf43SDan Williams 		return;
2447d3abaf43SDan Williams 
2448bc6ba808SDan Williams 	if ((ars_status->address >= spa->address && ars_status->address
2449bc6ba808SDan Williams 				< spa->address + spa->length)
2450bc6ba808SDan Williams 			|| (ars_status->address < spa->address)) {
2451bc6ba808SDan Williams 		/*
2452bc6ba808SDan Williams 		 * Assume that if a scrub starts at an offset from the
2453bc6ba808SDan Williams 		 * start of nfit_spa that we are in the continuation
2454bc6ba808SDan Williams 		 * case.
2455bc6ba808SDan Williams 		 *
2456bc6ba808SDan Williams 		 * Otherwise, if the scrub covers the spa range, mark
2457bc6ba808SDan Williams 		 * any pending request complete.
2458bc6ba808SDan Williams 		 */
2459bc6ba808SDan Williams 		if (ars_status->address + ars_status->length
2460bc6ba808SDan Williams 				>= spa->address + spa->length)
2461bc6ba808SDan Williams 				/* complete */;
2462bc6ba808SDan Williams 		else
2463bc6ba808SDan Williams 			return;
2464bc6ba808SDan Williams 	} else
2465bc6ba808SDan Williams 		return;
2466bc6ba808SDan Williams 
2467d3abaf43SDan Williams 	acpi_desc->scrub_spa = NULL;
2468bc6ba808SDan Williams 	if (nd_region) {
2469bc6ba808SDan Williams 		dev = nd_region_dev(nd_region);
2470bc6ba808SDan Williams 		nvdimm_region_notify(nd_region, NVDIMM_REVALIDATE_POISON);
2471bc6ba808SDan Williams 	} else
2472bc6ba808SDan Williams 		dev = acpi_desc->dev;
2473d3abaf43SDan Williams 	dev_dbg(dev, "ARS: range %d complete\n", spa->range_index);
2474bc6ba808SDan Williams }
2475bc6ba808SDan Williams 
ars_status_process_records(struct acpi_nfit_desc * acpi_desc)2476459d0ddbSDan Williams static int ars_status_process_records(struct acpi_nfit_desc *acpi_desc)
2477bdf97013SDan Williams {
247882aa37cfSDan Williams 	struct nvdimm_bus *nvdimm_bus = acpi_desc->nvdimm_bus;
2479459d0ddbSDan Williams 	struct nd_cmd_ars_status *ars_status = acpi_desc->ars_status;
2480bdf97013SDan Williams 	int rc;
2481bdf97013SDan Williams 	u32 i;
2482bdf97013SDan Williams 
248382aa37cfSDan Williams 	/*
248482aa37cfSDan Williams 	 * First record starts at 44 byte offset from the start of the
248582aa37cfSDan Williams 	 * payload.
248682aa37cfSDan Williams 	 */
248782aa37cfSDan Williams 	if (ars_status->out_length < 44)
248882aa37cfSDan Williams 		return 0;
248978153dd4SDan Williams 
249078153dd4SDan Williams 	/*
249178153dd4SDan Williams 	 * Ignore potentially stale results that are only refreshed
249278153dd4SDan Williams 	 * after a start-ARS event.
249378153dd4SDan Williams 	 */
249478153dd4SDan Williams 	if (!test_and_clear_bit(ARS_VALID, &acpi_desc->scrub_flags)) {
249578153dd4SDan Williams 		dev_dbg(acpi_desc->dev, "skip %d stale records\n",
249678153dd4SDan Williams 				ars_status->num_records);
249778153dd4SDan Williams 		return 0;
249878153dd4SDan Williams 	}
249978153dd4SDan Williams 
2500bdf97013SDan Williams 	for (i = 0; i < ars_status->num_records; i++) {
250182aa37cfSDan Williams 		/* only process full records */
250282aa37cfSDan Williams 		if (ars_status->out_length
250382aa37cfSDan Williams 				< 44 + sizeof(struct nd_ars_record) * (i + 1))
250482aa37cfSDan Williams 			break;
2505aa9ad44aSDave Jiang 		rc = nvdimm_bus_add_badrange(nvdimm_bus,
2506bdf97013SDan Williams 				ars_status->records[i].err_address,
2507bdf97013SDan Williams 				ars_status->records[i].length);
2508bdf97013SDan Williams 		if (rc)
2509bdf97013SDan Williams 			return rc;
2510bdf97013SDan Williams 	}
251182aa37cfSDan Williams 	if (i < ars_status->num_records)
251282aa37cfSDan Williams 		dev_warn(acpi_desc->dev, "detected truncated ars results\n");
2513bdf97013SDan Williams 
2514bdf97013SDan Williams 	return 0;
2515bdf97013SDan Williams }
2516bdf97013SDan Williams 
acpi_nfit_remove_resource(void * data)2517bdf97013SDan Williams static void acpi_nfit_remove_resource(void *data)
2518bdf97013SDan Williams {
2519bdf97013SDan Williams 	struct resource *res = data;
2520bdf97013SDan Williams 
2521bdf97013SDan Williams 	remove_resource(res);
2522bdf97013SDan Williams }
2523bdf97013SDan Williams 
acpi_nfit_insert_resource(struct acpi_nfit_desc * acpi_desc,struct nd_region_desc * ndr_desc)2524bdf97013SDan Williams static int acpi_nfit_insert_resource(struct acpi_nfit_desc *acpi_desc,
2525bdf97013SDan Williams 		struct nd_region_desc *ndr_desc)
2526bdf97013SDan Williams {
2527bdf97013SDan Williams 	struct resource *res, *nd_res = ndr_desc->res;
2528bdf97013SDan Williams 	int is_pmem, ret;
2529bdf97013SDan Williams 
2530bdf97013SDan Williams 	/* No operation if the region is already registered as PMEM */
2531bdf97013SDan Williams 	is_pmem = region_intersects(nd_res->start, resource_size(nd_res),
2532bdf97013SDan Williams 				IORESOURCE_MEM, IORES_DESC_PERSISTENT_MEMORY);
2533bdf97013SDan Williams 	if (is_pmem == REGION_INTERSECTS)
2534bdf97013SDan Williams 		return 0;
2535bdf97013SDan Williams 
2536bdf97013SDan Williams 	res = devm_kzalloc(acpi_desc->dev, sizeof(*res), GFP_KERNEL);
2537bdf97013SDan Williams 	if (!res)
2538bdf97013SDan Williams 		return -ENOMEM;
2539bdf97013SDan Williams 
2540bdf97013SDan Williams 	res->name = "Persistent Memory";
2541bdf97013SDan Williams 	res->start = nd_res->start;
2542bdf97013SDan Williams 	res->end = nd_res->end;
2543bdf97013SDan Williams 	res->flags = IORESOURCE_MEM;
2544bdf97013SDan Williams 	res->desc = IORES_DESC_PERSISTENT_MEMORY;
2545bdf97013SDan Williams 
2546bdf97013SDan Williams 	ret = insert_resource(&iomem_resource, res);
2547bdf97013SDan Williams 	if (ret)
2548bdf97013SDan Williams 		return ret;
2549bdf97013SDan Williams 
2550bdf97013SDan Williams 	ret = devm_add_action_or_reset(acpi_desc->dev,
2551bdf97013SDan Williams 					acpi_nfit_remove_resource,
2552bdf97013SDan Williams 					res);
2553bdf97013SDan Williams 	if (ret)
2554bdf97013SDan Williams 		return ret;
2555bdf97013SDan Williams 
2556bdf97013SDan Williams 	return 0;
2557bdf97013SDan Williams }
2558bdf97013SDan Williams 
acpi_nfit_init_mapping(struct acpi_nfit_desc * acpi_desc,struct nd_mapping_desc * mapping,struct nd_region_desc * ndr_desc,struct acpi_nfit_memory_map * memdev,struct nfit_spa * nfit_spa)2559bdf97013SDan Williams static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc,
256044c462ebSDan Williams 		struct nd_mapping_desc *mapping, struct nd_region_desc *ndr_desc,
2561bdf97013SDan Williams 		struct acpi_nfit_memory_map *memdev,
2562bdf97013SDan Williams 		struct nfit_spa *nfit_spa)
2563bdf97013SDan Williams {
2564bdf97013SDan Williams 	struct nvdimm *nvdimm = acpi_nfit_dimm_by_handle(acpi_desc,
2565bdf97013SDan Williams 			memdev->device_handle);
2566bdf97013SDan Williams 	struct acpi_nfit_system_address *spa = nfit_spa->spa;
2567bdf97013SDan Williams 
2568bdf97013SDan Williams 	if (!nvdimm) {
2569bdf97013SDan Williams 		dev_err(acpi_desc->dev, "spa%d dimm: %#x not found\n",
2570bdf97013SDan Williams 				spa->range_index, memdev->device_handle);
2571bdf97013SDan Williams 		return -ENODEV;
2572bdf97013SDan Williams 	}
2573bdf97013SDan Williams 
257444c462ebSDan Williams 	mapping->nvdimm = nvdimm;
2575bdf97013SDan Williams 	switch (nfit_spa_type(spa)) {
2576bdf97013SDan Williams 	case NFIT_SPA_PM:
2577bdf97013SDan Williams 	case NFIT_SPA_VOLATILE:
257844c462ebSDan Williams 		mapping->start = memdev->address;
257944c462ebSDan Williams 		mapping->size = memdev->region_size;
2580bdf97013SDan Williams 		break;
2581bdf97013SDan Williams 	}
2582bdf97013SDan Williams 
2583bdf97013SDan Williams 	return 0;
2584bdf97013SDan Williams }
2585bdf97013SDan Williams 
nfit_spa_is_virtual(struct acpi_nfit_system_address * spa)2586bdf97013SDan Williams static bool nfit_spa_is_virtual(struct acpi_nfit_system_address *spa)
2587bdf97013SDan Williams {
2588bdf97013SDan Williams 	return (nfit_spa_type(spa) == NFIT_SPA_VDISK ||
2589bdf97013SDan Williams 		nfit_spa_type(spa) == NFIT_SPA_VCD   ||
2590bdf97013SDan Williams 		nfit_spa_type(spa) == NFIT_SPA_PDISK ||
2591bdf97013SDan Williams 		nfit_spa_type(spa) == NFIT_SPA_PCD);
2592bdf97013SDan Williams }
2593bdf97013SDan Williams 
nfit_spa_is_volatile(struct acpi_nfit_system_address * spa)2594c9e582aaSDan Williams static bool nfit_spa_is_volatile(struct acpi_nfit_system_address *spa)
2595c9e582aaSDan Williams {
2596c9e582aaSDan Williams 	return (nfit_spa_type(spa) == NFIT_SPA_VDISK ||
2597c9e582aaSDan Williams 		nfit_spa_type(spa) == NFIT_SPA_VCD   ||
2598c9e582aaSDan Williams 		nfit_spa_type(spa) == NFIT_SPA_VOLATILE);
2599c9e582aaSDan Williams }
2600c9e582aaSDan Williams 
acpi_nfit_register_region(struct acpi_nfit_desc * acpi_desc,struct nfit_spa * nfit_spa)2601bdf97013SDan Williams static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc,
2602bdf97013SDan Williams 		struct nfit_spa *nfit_spa)
2603bdf97013SDan Williams {
260444c462ebSDan Williams 	static struct nd_mapping_desc mappings[ND_MAX_MAPPINGS];
2605bdf97013SDan Williams 	struct acpi_nfit_system_address *spa = nfit_spa->spa;
26063b6c6c03SDan Williams 	struct nd_region_desc *ndr_desc, _ndr_desc;
2607bdf97013SDan Williams 	struct nfit_memdev *nfit_memdev;
2608bdf97013SDan Williams 	struct nvdimm_bus *nvdimm_bus;
2609bdf97013SDan Williams 	struct resource res;
2610bdf97013SDan Williams 	int count = 0, rc;
2611bdf97013SDan Williams 
2612bdf97013SDan Williams 	if (nfit_spa->nd_region)
2613bdf97013SDan Williams 		return 0;
2614bdf97013SDan Williams 
2615bdf97013SDan Williams 	if (spa->range_index == 0 && !nfit_spa_is_virtual(spa)) {
2616b814735fSJohannes Thumshirn 		dev_dbg(acpi_desc->dev, "detected invalid spa index\n");
2617bdf97013SDan Williams 		return 0;
2618bdf97013SDan Williams 	}
2619bdf97013SDan Williams 
2620bdf97013SDan Williams 	memset(&res, 0, sizeof(res));
262144c462ebSDan Williams 	memset(&mappings, 0, sizeof(mappings));
26223b6c6c03SDan Williams 	memset(&_ndr_desc, 0, sizeof(_ndr_desc));
2623bdf97013SDan Williams 	res.start = spa->address;
2624bdf97013SDan Williams 	res.end = res.start + spa->length - 1;
26253b6c6c03SDan Williams 	ndr_desc = &_ndr_desc;
2626bdf97013SDan Williams 	ndr_desc->res = &res;
2627bdf97013SDan Williams 	ndr_desc->provider_data = nfit_spa;
2628bdf97013SDan Williams 	ndr_desc->attr_groups = acpi_nfit_region_attribute_groups;
26298fc5c735SDan Williams 	if (spa->flags & ACPI_NFIT_PROXIMITY_VALID) {
26304eb3723fSJonathan Cameron 		ndr_desc->numa_node = pxm_to_online_node(spa->proximity_domain);
263101feba59SJonathan Cameron 		ndr_desc->target_node = pxm_to_node(spa->proximity_domain);
26328fc5c735SDan Williams 	} else {
2633bdf97013SDan Williams 		ndr_desc->numa_node = NUMA_NO_NODE;
26348fc5c735SDan Williams 		ndr_desc->target_node = NUMA_NO_NODE;
26358fc5c735SDan Williams 	}
2636bdf97013SDan Williams 
2637f060db99SJia He 	/* Fallback to address based numa information if node lookup failed */
2638f060db99SJia He 	if (ndr_desc->numa_node == NUMA_NO_NODE) {
2639f060db99SJia He 		ndr_desc->numa_node = memory_add_physaddr_to_nid(spa->address);
2640f060db99SJia He 		dev_info(acpi_desc->dev, "changing numa node from %d to %d for nfit region [%pa-%pa]",
2641f060db99SJia He 			NUMA_NO_NODE, ndr_desc->numa_node, &res.start, &res.end);
2642f060db99SJia He 	}
2643f060db99SJia He 	if (ndr_desc->target_node == NUMA_NO_NODE) {
2644f060db99SJia He 		ndr_desc->target_node = phys_to_target_node(spa->address);
2645f060db99SJia He 		dev_info(acpi_desc->dev, "changing target node from %d to %d for nfit region [%pa-%pa]",
2646f060db99SJia He 			NUMA_NO_NODE, ndr_desc->numa_node, &res.start, &res.end);
2647f060db99SJia He 	}
2648f060db99SJia He 
2649fe9a552eSDan Williams 	/*
2650fe9a552eSDan Williams 	 * Persistence domain bits are hierarchical, if
2651fe9a552eSDan Williams 	 * ACPI_NFIT_CAPABILITY_CACHE_FLUSH is set then
2652fe9a552eSDan Williams 	 * ACPI_NFIT_CAPABILITY_MEM_FLUSH is implied.
2653fe9a552eSDan Williams 	 */
265406e8ccdaSDave Jiang 	if (acpi_desc->platform_cap & ACPI_NFIT_CAPABILITY_CACHE_FLUSH)
265506e8ccdaSDave Jiang 		set_bit(ND_REGION_PERSIST_CACHE, &ndr_desc->flags);
2656fe9a552eSDan Williams 	else if (acpi_desc->platform_cap & ACPI_NFIT_CAPABILITY_MEM_FLUSH)
265730e6d7bfSDave Jiang 		set_bit(ND_REGION_PERSIST_MEMCTRL, &ndr_desc->flags);
265830e6d7bfSDave Jiang 
2659bdf97013SDan Williams 	list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) {
2660bdf97013SDan Williams 		struct acpi_nfit_memory_map *memdev = nfit_memdev->memdev;
266144c462ebSDan Williams 		struct nd_mapping_desc *mapping;
2662bdf97013SDan Williams 
2663b93dfa6bSDan Williams 		/* range index 0 == unmapped in SPA or invalid-SPA */
2664b93dfa6bSDan Williams 		if (memdev->range_index == 0 || spa->range_index == 0)
2665b93dfa6bSDan Williams 			continue;
2666bdf97013SDan Williams 		if (memdev->range_index != spa->range_index)
2667bdf97013SDan Williams 			continue;
2668bdf97013SDan Williams 		if (count >= ND_MAX_MAPPINGS) {
2669bdf97013SDan Williams 			dev_err(acpi_desc->dev, "spa%d exceeds max mappings %d\n",
2670bdf97013SDan Williams 					spa->range_index, ND_MAX_MAPPINGS);
2671bdf97013SDan Williams 			return -ENXIO;
2672bdf97013SDan Williams 		}
267344c462ebSDan Williams 		mapping = &mappings[count++];
267444c462ebSDan Williams 		rc = acpi_nfit_init_mapping(acpi_desc, mapping, ndr_desc,
2675bdf97013SDan Williams 				memdev, nfit_spa);
2676bdf97013SDan Williams 		if (rc)
2677bdf97013SDan Williams 			goto out;
2678bdf97013SDan Williams 	}
2679bdf97013SDan Williams 
268044c462ebSDan Williams 	ndr_desc->mapping = mappings;
2681bdf97013SDan Williams 	ndr_desc->num_mappings = count;
2682bdf97013SDan Williams 	rc = acpi_nfit_init_interleave_set(acpi_desc, ndr_desc, spa);
2683bdf97013SDan Williams 	if (rc)
2684bdf97013SDan Williams 		goto out;
2685bdf97013SDan Williams 
2686bdf97013SDan Williams 	nvdimm_bus = acpi_desc->nvdimm_bus;
2687bdf97013SDan Williams 	if (nfit_spa_type(spa) == NFIT_SPA_PM) {
2688bdf97013SDan Williams 		rc = acpi_nfit_insert_resource(acpi_desc, ndr_desc);
2689bdf97013SDan Williams 		if (rc) {
2690bdf97013SDan Williams 			dev_warn(acpi_desc->dev,
2691bdf97013SDan Williams 				"failed to insert pmem resource to iomem: %d\n",
2692bdf97013SDan Williams 				rc);
2693bdf97013SDan Williams 			goto out;
2694bdf97013SDan Williams 		}
2695bdf97013SDan Williams 
2696bdf97013SDan Williams 		nfit_spa->nd_region = nvdimm_pmem_region_create(nvdimm_bus,
2697bdf97013SDan Williams 				ndr_desc);
2698bdf97013SDan Williams 		if (!nfit_spa->nd_region)
2699bdf97013SDan Williams 			rc = -ENOMEM;
2700c9e582aaSDan Williams 	} else if (nfit_spa_is_volatile(spa)) {
2701bdf97013SDan Williams 		nfit_spa->nd_region = nvdimm_volatile_region_create(nvdimm_bus,
2702bdf97013SDan Williams 				ndr_desc);
2703bdf97013SDan Williams 		if (!nfit_spa->nd_region)
2704bdf97013SDan Williams 			rc = -ENOMEM;
2705bdf97013SDan Williams 	} else if (nfit_spa_is_virtual(spa)) {
2706bdf97013SDan Williams 		nfit_spa->nd_region = nvdimm_pmem_region_create(nvdimm_bus,
2707bdf97013SDan Williams 				ndr_desc);
2708bdf97013SDan Williams 		if (!nfit_spa->nd_region)
2709bdf97013SDan Williams 			rc = -ENOMEM;
2710bdf97013SDan Williams 	}
2711bdf97013SDan Williams 
2712bdf97013SDan Williams  out:
2713bdf97013SDan Williams 	if (rc)
2714bdf97013SDan Williams 		dev_err(acpi_desc->dev, "failed to register spa range %d\n",
2715bdf97013SDan Williams 				nfit_spa->spa->range_index);
2716bdf97013SDan Williams 	return rc;
2717bdf97013SDan Williams }
2718bdf97013SDan Williams 
ars_status_alloc(struct acpi_nfit_desc * acpi_desc)2719459d0ddbSDan Williams static int ars_status_alloc(struct acpi_nfit_desc *acpi_desc)
2720bdf97013SDan Williams {
2721bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
2722bdf97013SDan Williams 	struct nd_cmd_ars_status *ars_status;
2723bdf97013SDan Williams 
2724459d0ddbSDan Williams 	if (acpi_desc->ars_status) {
2725459d0ddbSDan Williams 		memset(acpi_desc->ars_status, 0, acpi_desc->max_ars);
2726bdf97013SDan Williams 		return 0;
2727bdf97013SDan Williams 	}
2728bdf97013SDan Williams 
2729459d0ddbSDan Williams 	ars_status = devm_kzalloc(dev, acpi_desc->max_ars, GFP_KERNEL);
2730bdf97013SDan Williams 	if (!ars_status)
2731bdf97013SDan Williams 		return -ENOMEM;
2732bdf97013SDan Williams 	acpi_desc->ars_status = ars_status;
2733bdf97013SDan Williams 	return 0;
2734bdf97013SDan Williams }
2735bdf97013SDan Williams 
acpi_nfit_query_poison(struct acpi_nfit_desc * acpi_desc)2736459d0ddbSDan Williams static int acpi_nfit_query_poison(struct acpi_nfit_desc *acpi_desc)
2737bdf97013SDan Williams {
2738bdf97013SDan Williams 	int rc;
2739bdf97013SDan Williams 
2740459d0ddbSDan Williams 	if (ars_status_alloc(acpi_desc))
2741bdf97013SDan Williams 		return -ENOMEM;
2742bdf97013SDan Williams 
2743bdf97013SDan Williams 	rc = ars_get_status(acpi_desc);
2744bc6ba808SDan Williams 
2745bdf97013SDan Williams 	if (rc < 0 && rc != -ENOSPC)
2746bdf97013SDan Williams 		return rc;
2747bdf97013SDan Williams 
2748459d0ddbSDan Williams 	if (ars_status_process_records(acpi_desc))
27493fa58dcaSDan Williams 		dev_err(acpi_desc->dev, "Failed to process ARS records\n");
2750bdf97013SDan Williams 
27513fa58dcaSDan Williams 	return rc;
2752bdf97013SDan Williams }
2753bdf97013SDan Williams 
ars_register(struct acpi_nfit_desc * acpi_desc,struct nfit_spa * nfit_spa)2754d3abaf43SDan Williams static int ars_register(struct acpi_nfit_desc *acpi_desc,
2755d3abaf43SDan Williams 		struct nfit_spa *nfit_spa)
2756bdf97013SDan Williams {
2757d3abaf43SDan Williams 	int rc;
2758bdf97013SDan Williams 
2759fa3ed4d9SDan Williams 	if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
2760bca811a7SDan Williams 		return acpi_nfit_register_region(acpi_desc, nfit_spa);
2761bdf97013SDan Williams 
2762d3abaf43SDan Williams 	set_bit(ARS_REQ_SHORT, &nfit_spa->ars_state);
2763fa3ed4d9SDan Williams 	if (!no_init_ars)
2764d3abaf43SDan Williams 		set_bit(ARS_REQ_LONG, &nfit_spa->ars_state);
2765bdf97013SDan Williams 
2766d3abaf43SDan Williams 	switch (acpi_nfit_query_poison(acpi_desc)) {
2767bc6ba808SDan Williams 	case 0:
2768c6c5df29SDan Williams 	case -ENOSPC:
2769bc6ba808SDan Williams 	case -EAGAIN:
2770d3abaf43SDan Williams 		rc = ars_start(acpi_desc, nfit_spa, ARS_REQ_SHORT);
2771d3abaf43SDan Williams 		/* shouldn't happen, try again later */
2772d3abaf43SDan Williams 		if (rc == -EBUSY)
2773bc6ba808SDan Williams 			break;
2774d3abaf43SDan Williams 		if (rc) {
2775bc6ba808SDan Williams 			set_bit(ARS_FAILED, &nfit_spa->ars_state);
2776bc6ba808SDan Williams 			break;
2777bc6ba808SDan Williams 		}
2778d3abaf43SDan Williams 		clear_bit(ARS_REQ_SHORT, &nfit_spa->ars_state);
2779d3abaf43SDan Williams 		rc = acpi_nfit_query_poison(acpi_desc);
2780d3abaf43SDan Williams 		if (rc)
2781d3abaf43SDan Williams 			break;
2782d3abaf43SDan Williams 		acpi_desc->scrub_spa = nfit_spa;
2783bc6ba808SDan Williams 		ars_complete(acpi_desc, nfit_spa);
2784d3abaf43SDan Williams 		/*
2785d3abaf43SDan Williams 		 * If ars_complete() says we didn't complete the
2786d3abaf43SDan Williams 		 * short scrub, we'll try again with a long
2787d3abaf43SDan Williams 		 * request.
2788d3abaf43SDan Williams 		 */
2789d3abaf43SDan Williams 		acpi_desc->scrub_spa = NULL;
2790bc6ba808SDan Williams 		break;
2791bc6ba808SDan Williams 	case -EBUSY:
2792d3abaf43SDan Williams 	case -ENOMEM:
2793d3abaf43SDan Williams 		/*
2794d3abaf43SDan Williams 		 * BIOS was using ARS, wait for it to complete (or
2795d3abaf43SDan Williams 		 * resources to become available) and then perform our
2796d3abaf43SDan Williams 		 * own scrubs.
2797d3abaf43SDan Williams 		 */
2798bc6ba808SDan Williams 		break;
2799bc6ba808SDan Williams 	default:
2800bc6ba808SDan Williams 		set_bit(ARS_FAILED, &nfit_spa->ars_state);
2801bc6ba808SDan Williams 		break;
2802bc6ba808SDan Williams 	}
2803bdf97013SDan Williams 
2804bc6ba808SDan Williams 	return acpi_nfit_register_region(acpi_desc, nfit_spa);
2805bc6ba808SDan Williams }
2806bc6ba808SDan Williams 
ars_complete_all(struct acpi_nfit_desc * acpi_desc)2807bc6ba808SDan Williams static void ars_complete_all(struct acpi_nfit_desc *acpi_desc)
2808bc6ba808SDan Williams {
2809bc6ba808SDan Williams 	struct nfit_spa *nfit_spa;
2810bc6ba808SDan Williams 
2811bc6ba808SDan Williams 	list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
2812bc6ba808SDan Williams 		if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
2813bc6ba808SDan Williams 			continue;
2814bc6ba808SDan Williams 		ars_complete(acpi_desc, nfit_spa);
2815bc6ba808SDan Williams 	}
2816bc6ba808SDan Williams }
2817bc6ba808SDan Williams 
__acpi_nfit_scrub(struct acpi_nfit_desc * acpi_desc,int query_rc)2818bc6ba808SDan Williams static unsigned int __acpi_nfit_scrub(struct acpi_nfit_desc *acpi_desc,
2819bc6ba808SDan Williams 		int query_rc)
2820bc6ba808SDan Williams {
2821bc6ba808SDan Williams 	unsigned int tmo = acpi_desc->scrub_tmo;
2822bc6ba808SDan Williams 	struct device *dev = acpi_desc->dev;
2823bc6ba808SDan Williams 	struct nfit_spa *nfit_spa;
2824bdf97013SDan Williams 
2825d3abaf43SDan Williams 	lockdep_assert_held(&acpi_desc->init_mutex);
2826d3abaf43SDan Williams 
2827e34b8252SDan Williams 	if (test_bit(ARS_CANCEL, &acpi_desc->scrub_flags))
2828bc6ba808SDan Williams 		return 0;
2829bc6ba808SDan Williams 
2830bc6ba808SDan Williams 	if (query_rc == -EBUSY) {
2831bc6ba808SDan Williams 		dev_dbg(dev, "ARS: ARS busy\n");
2832bc6ba808SDan Williams 		return min(30U * 60U, tmo * 2);
2833bc6ba808SDan Williams 	}
2834bc6ba808SDan Williams 	if (query_rc == -ENOSPC) {
2835bc6ba808SDan Williams 		dev_dbg(dev, "ARS: ARS continue\n");
2836bc6ba808SDan Williams 		ars_continue(acpi_desc);
2837bc6ba808SDan Williams 		return 1;
2838bc6ba808SDan Williams 	}
2839bc6ba808SDan Williams 	if (query_rc && query_rc != -EAGAIN) {
2840bc6ba808SDan Williams 		unsigned long long addr, end;
2841bc6ba808SDan Williams 
2842bc6ba808SDan Williams 		addr = acpi_desc->ars_status->address;
2843bc6ba808SDan Williams 		end = addr + acpi_desc->ars_status->length;
2844bc6ba808SDan Williams 		dev_dbg(dev, "ARS: %llx-%llx failed (%d)\n", addr, end,
2845bc6ba808SDan Williams 				query_rc);
2846bdf97013SDan Williams 	}
2847bdf97013SDan Williams 
2848bc6ba808SDan Williams 	ars_complete_all(acpi_desc);
2849bc6ba808SDan Williams 	list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
2850d3abaf43SDan Williams 		enum nfit_ars_state req_type;
2851d3abaf43SDan Williams 		int rc;
2852d3abaf43SDan Williams 
2853bc6ba808SDan Williams 		if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
2854bdf97013SDan Williams 			continue;
2855bdf97013SDan Williams 
2856d3abaf43SDan Williams 		/* prefer short ARS requests first */
2857d3abaf43SDan Williams 		if (test_bit(ARS_REQ_SHORT, &nfit_spa->ars_state))
2858d3abaf43SDan Williams 			req_type = ARS_REQ_SHORT;
2859d3abaf43SDan Williams 		else if (test_bit(ARS_REQ_LONG, &nfit_spa->ars_state))
2860d3abaf43SDan Williams 			req_type = ARS_REQ_LONG;
2861d3abaf43SDan Williams 		else
2862d3abaf43SDan Williams 			continue;
2863d3abaf43SDan Williams 		rc = ars_start(acpi_desc, nfit_spa, req_type);
2864d3abaf43SDan Williams 
2865bc6ba808SDan Williams 		dev = nd_region_dev(nfit_spa->nd_region);
2866d3abaf43SDan Williams 		dev_dbg(dev, "ARS: range %d ARS start %s (%d)\n",
2867d3abaf43SDan Williams 				nfit_spa->spa->range_index,
2868d3abaf43SDan Williams 				req_type == ARS_REQ_SHORT ? "short" : "long",
2869d3abaf43SDan Williams 				rc);
2870d3abaf43SDan Williams 		/*
2871d3abaf43SDan Williams 		 * Hmm, we raced someone else starting ARS? Try again in
2872d3abaf43SDan Williams 		 * a bit.
2873d3abaf43SDan Williams 		 */
2874d3abaf43SDan Williams 		if (rc == -EBUSY)
2875bc6ba808SDan Williams 			return 1;
2876d3abaf43SDan Williams 		if (rc == 0) {
2877d3abaf43SDan Williams 			dev_WARN_ONCE(dev, acpi_desc->scrub_spa,
2878d3abaf43SDan Williams 					"scrub start while range %d active\n",
2879d3abaf43SDan Williams 					acpi_desc->scrub_spa->spa->range_index);
2880d3abaf43SDan Williams 			clear_bit(req_type, &nfit_spa->ars_state);
2881d3abaf43SDan Williams 			acpi_desc->scrub_spa = nfit_spa;
2882d3abaf43SDan Williams 			/*
2883d3abaf43SDan Williams 			 * Consider this spa last for future scrub
2884d3abaf43SDan Williams 			 * requests
2885d3abaf43SDan Williams 			 */
2886d3abaf43SDan Williams 			list_move_tail(&nfit_spa->list, &acpi_desc->spas);
2887d3abaf43SDan Williams 			return 1;
2888d3abaf43SDan Williams 		}
2889d3abaf43SDan Williams 
2890bc6ba808SDan Williams 		dev_err(dev, "ARS: range %d ARS failed (%d)\n",
2891bc6ba808SDan Williams 				nfit_spa->spa->range_index, rc);
2892bc6ba808SDan Williams 		set_bit(ARS_FAILED, &nfit_spa->ars_state);
2893bdf97013SDan Williams 	}
2894bc6ba808SDan Williams 	return 0;
2895bdf97013SDan Williams }
2896bdf97013SDan Williams 
__sched_ars(struct acpi_nfit_desc * acpi_desc,unsigned int tmo)289733cc2c96SDan Williams static void __sched_ars(struct acpi_nfit_desc *acpi_desc, unsigned int tmo)
289833cc2c96SDan Williams {
289933cc2c96SDan Williams 	lockdep_assert_held(&acpi_desc->init_mutex);
290033cc2c96SDan Williams 
2901e34b8252SDan Williams 	set_bit(ARS_BUSY, &acpi_desc->scrub_flags);
290233cc2c96SDan Williams 	/* note this should only be set from within the workqueue */
290333cc2c96SDan Williams 	if (tmo)
290433cc2c96SDan Williams 		acpi_desc->scrub_tmo = tmo;
290533cc2c96SDan Williams 	queue_delayed_work(nfit_wq, &acpi_desc->dwork, tmo * HZ);
290633cc2c96SDan Williams }
290733cc2c96SDan Williams 
sched_ars(struct acpi_nfit_desc * acpi_desc)290833cc2c96SDan Williams static void sched_ars(struct acpi_nfit_desc *acpi_desc)
290933cc2c96SDan Williams {
291033cc2c96SDan Williams 	__sched_ars(acpi_desc, 0);
291133cc2c96SDan Williams }
291233cc2c96SDan Williams 
notify_ars_done(struct acpi_nfit_desc * acpi_desc)291333cc2c96SDan Williams static void notify_ars_done(struct acpi_nfit_desc *acpi_desc)
291433cc2c96SDan Williams {
291533cc2c96SDan Williams 	lockdep_assert_held(&acpi_desc->init_mutex);
291633cc2c96SDan Williams 
2917e34b8252SDan Williams 	clear_bit(ARS_BUSY, &acpi_desc->scrub_flags);
291833cc2c96SDan Williams 	acpi_desc->scrub_count++;
291933cc2c96SDan Williams 	if (acpi_desc->scrub_count_state)
292033cc2c96SDan Williams 		sysfs_notify_dirent(acpi_desc->scrub_count_state);
292133cc2c96SDan Williams }
292233cc2c96SDan Williams 
acpi_nfit_scrub(struct work_struct * work)2923bdf97013SDan Williams static void acpi_nfit_scrub(struct work_struct *work)
2924bdf97013SDan Williams {
2925bdf97013SDan Williams 	struct acpi_nfit_desc *acpi_desc;
2926bc6ba808SDan Williams 	unsigned int tmo;
2927bc6ba808SDan Williams 	int query_rc;
2928bdf97013SDan Williams 
2929bc6ba808SDan Williams 	acpi_desc = container_of(work, typeof(*acpi_desc), dwork.work);
2930bdf97013SDan Williams 	mutex_lock(&acpi_desc->init_mutex);
2931bc6ba808SDan Williams 	query_rc = acpi_nfit_query_poison(acpi_desc);
2932bc6ba808SDan Williams 	tmo = __acpi_nfit_scrub(acpi_desc, query_rc);
293333cc2c96SDan Williams 	if (tmo)
293433cc2c96SDan Williams 		__sched_ars(acpi_desc, tmo);
293533cc2c96SDan Williams 	else
293633cc2c96SDan Williams 		notify_ars_done(acpi_desc);
2937bc6ba808SDan Williams 	memset(acpi_desc->ars_status, 0, acpi_desc->max_ars);
29385479b275SDan Williams 	clear_bit(ARS_POLL, &acpi_desc->scrub_flags);
2939bdf97013SDan Williams 	mutex_unlock(&acpi_desc->init_mutex);
2940bdf97013SDan Williams }
2941bdf97013SDan Williams 
acpi_nfit_init_ars(struct acpi_nfit_desc * acpi_desc,struct nfit_spa * nfit_spa)2942459d0ddbSDan Williams static void acpi_nfit_init_ars(struct acpi_nfit_desc *acpi_desc,
2943459d0ddbSDan Williams 		struct nfit_spa *nfit_spa)
2944459d0ddbSDan Williams {
2945459d0ddbSDan Williams 	int type = nfit_spa_type(nfit_spa->spa);
2946459d0ddbSDan Williams 	struct nd_cmd_ars_cap ars_cap;
2947459d0ddbSDan Williams 	int rc;
2948459d0ddbSDan Williams 
2949d3abaf43SDan Williams 	set_bit(ARS_FAILED, &nfit_spa->ars_state);
2950459d0ddbSDan Williams 	memset(&ars_cap, 0, sizeof(ars_cap));
2951459d0ddbSDan Williams 	rc = ars_get_cap(acpi_desc, &ars_cap, nfit_spa);
2952459d0ddbSDan Williams 	if (rc < 0)
2953459d0ddbSDan Williams 		return;
2954459d0ddbSDan Williams 	/* check that the supported scrub types match the spa type */
2955459d0ddbSDan Williams 	if (type == NFIT_SPA_VOLATILE && ((ars_cap.status >> 16)
2956459d0ddbSDan Williams 				& ND_ARS_VOLATILE) == 0)
2957459d0ddbSDan Williams 		return;
2958459d0ddbSDan Williams 	if (type == NFIT_SPA_PM && ((ars_cap.status >> 16)
2959459d0ddbSDan Williams 				& ND_ARS_PERSISTENT) == 0)
2960459d0ddbSDan Williams 		return;
2961459d0ddbSDan Williams 
2962459d0ddbSDan Williams 	nfit_spa->max_ars = ars_cap.max_ars_out;
2963459d0ddbSDan Williams 	nfit_spa->clear_err_unit = ars_cap.clear_err_unit;
2964459d0ddbSDan Williams 	acpi_desc->max_ars = max(nfit_spa->max_ars, acpi_desc->max_ars);
2965bc6ba808SDan Williams 	clear_bit(ARS_FAILED, &nfit_spa->ars_state);
2966459d0ddbSDan Williams }
2967459d0ddbSDan Williams 
acpi_nfit_register_regions(struct acpi_nfit_desc * acpi_desc)2968bdf97013SDan Williams static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc)
2969bdf97013SDan Williams {
2970bdf97013SDan Williams 	struct nfit_spa *nfit_spa;
29719f1048d4SDan Williams 	int rc, do_sched_ars = 0;
2972bdf97013SDan Williams 
297378153dd4SDan Williams 	set_bit(ARS_VALID, &acpi_desc->scrub_flags);
29748d0d8ed3SDan Williams 	list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
2975bc6ba808SDan Williams 		switch (nfit_spa_type(nfit_spa->spa)) {
2976bc6ba808SDan Williams 		case NFIT_SPA_VOLATILE:
2977bc6ba808SDan Williams 		case NFIT_SPA_PM:
2978459d0ddbSDan Williams 			acpi_nfit_init_ars(acpi_desc, nfit_spa);
2979bc6ba808SDan Williams 			break;
2980459d0ddbSDan Williams 		}
2981bc6ba808SDan Williams 	}
2982bc6ba808SDan Williams 
29839f1048d4SDan Williams 	list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
2984bc6ba808SDan Williams 		switch (nfit_spa_type(nfit_spa->spa)) {
2985bc6ba808SDan Williams 		case NFIT_SPA_VOLATILE:
2986bc6ba808SDan Williams 		case NFIT_SPA_PM:
2987bc6ba808SDan Williams 			/* register regions and kick off initial ARS run */
2988d3abaf43SDan Williams 			rc = ars_register(acpi_desc, nfit_spa);
2989bc6ba808SDan Williams 			if (rc)
2990bc6ba808SDan Williams 				return rc;
29919f1048d4SDan Williams 
29929f1048d4SDan Williams 			/*
29939f1048d4SDan Williams 			 * Kick off background ARS if at least one
29949f1048d4SDan Williams 			 * region successfully registered ARS
29959f1048d4SDan Williams 			 */
29969f1048d4SDan Williams 			if (!test_bit(ARS_FAILED, &nfit_spa->ars_state))
29979f1048d4SDan Williams 				do_sched_ars++;
2998bc6ba808SDan Williams 			break;
2999bc6ba808SDan Williams 		case NFIT_SPA_BDW:
3000bc6ba808SDan Williams 			/* nothing to register */
3001bc6ba808SDan Williams 			break;
3002bc6ba808SDan Williams 		case NFIT_SPA_DCR:
3003bc6ba808SDan Williams 		case NFIT_SPA_VDISK:
3004bc6ba808SDan Williams 		case NFIT_SPA_VCD:
3005bc6ba808SDan Williams 		case NFIT_SPA_PDISK:
3006bc6ba808SDan Williams 		case NFIT_SPA_PCD:
3007bc6ba808SDan Williams 			/* register known regions that don't support ARS */
3008bdf97013SDan Williams 			rc = acpi_nfit_register_region(acpi_desc, nfit_spa);
3009bdf97013SDan Williams 			if (rc)
3010bdf97013SDan Williams 				return rc;
3011bc6ba808SDan Williams 			break;
3012bc6ba808SDan Williams 		default:
3013bc6ba808SDan Williams 			/* don't register unknown regions */
3014bc6ba808SDan Williams 			break;
3015bdf97013SDan Williams 		}
30169f1048d4SDan Williams 	}
3017bdf97013SDan Williams 
30189f1048d4SDan Williams 	if (do_sched_ars)
301933cc2c96SDan Williams 		sched_ars(acpi_desc);
3020bdf97013SDan Williams 	return 0;
3021bdf97013SDan Williams }
3022bdf97013SDan Williams 
acpi_nfit_check_deletions(struct acpi_nfit_desc * acpi_desc,struct nfit_table_prev * prev)3023bdf97013SDan Williams static int acpi_nfit_check_deletions(struct acpi_nfit_desc *acpi_desc,
3024bdf97013SDan Williams 		struct nfit_table_prev *prev)
3025bdf97013SDan Williams {
3026bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
3027bdf97013SDan Williams 
3028bdf97013SDan Williams 	if (!list_empty(&prev->spas) ||
3029bdf97013SDan Williams 			!list_empty(&prev->memdevs) ||
3030bdf97013SDan Williams 			!list_empty(&prev->dcrs) ||
3031bdf97013SDan Williams 			!list_empty(&prev->bdws) ||
3032bdf97013SDan Williams 			!list_empty(&prev->idts) ||
3033bdf97013SDan Williams 			!list_empty(&prev->flushes)) {
3034bdf97013SDan Williams 		dev_err(dev, "new nfit deletes entries (unsupported)\n");
3035bdf97013SDan Williams 		return -ENXIO;
3036bdf97013SDan Williams 	}
3037bdf97013SDan Williams 	return 0;
3038bdf97013SDan Williams }
3039bdf97013SDan Williams 
acpi_nfit_desc_init_scrub_attr(struct acpi_nfit_desc * acpi_desc)3040bdf97013SDan Williams static int acpi_nfit_desc_init_scrub_attr(struct acpi_nfit_desc *acpi_desc)
3041bdf97013SDan Williams {
3042bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
3043bdf97013SDan Williams 	struct kernfs_node *nfit;
3044bdf97013SDan Williams 	struct device *bus_dev;
3045bdf97013SDan Williams 
3046bdf97013SDan Williams 	if (!ars_supported(acpi_desc->nvdimm_bus))
3047bdf97013SDan Williams 		return 0;
3048bdf97013SDan Williams 
3049bdf97013SDan Williams 	bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus);
3050bdf97013SDan Williams 	nfit = sysfs_get_dirent(bus_dev->kobj.sd, "nfit");
3051bdf97013SDan Williams 	if (!nfit) {
3052bdf97013SDan Williams 		dev_err(dev, "sysfs_get_dirent 'nfit' failed\n");
3053bdf97013SDan Williams 		return -ENODEV;
3054bdf97013SDan Williams 	}
3055bdf97013SDan Williams 	acpi_desc->scrub_count_state = sysfs_get_dirent(nfit, "scrub");
3056bdf97013SDan Williams 	sysfs_put(nfit);
3057bdf97013SDan Williams 	if (!acpi_desc->scrub_count_state) {
3058bdf97013SDan Williams 		dev_err(dev, "sysfs_get_dirent 'scrub' failed\n");
3059bdf97013SDan Williams 		return -ENODEV;
3060bdf97013SDan Williams 	}
3061bdf97013SDan Williams 
3062bdf97013SDan Williams 	return 0;
3063bdf97013SDan Williams }
3064bdf97013SDan Williams 
acpi_nfit_unregister(void * data)3065fbabd829SDan Williams static void acpi_nfit_unregister(void *data)
3066bdf97013SDan Williams {
3067bdf97013SDan Williams 	struct acpi_nfit_desc *acpi_desc = data;
3068bdf97013SDan Williams 
3069bdf97013SDan Williams 	nvdimm_bus_unregister(acpi_desc->nvdimm_bus);
3070bdf97013SDan Williams }
3071bdf97013SDan Williams 
acpi_nfit_init(struct acpi_nfit_desc * acpi_desc,void * data,acpi_size sz)3072bdf97013SDan Williams int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *data, acpi_size sz)
3073bdf97013SDan Williams {
3074bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
3075bdf97013SDan Williams 	struct nfit_table_prev prev;
3076bdf97013SDan Williams 	const void *end;
3077bdf97013SDan Williams 	int rc;
3078bdf97013SDan Williams 
3079bdf97013SDan Williams 	if (!acpi_desc->nvdimm_bus) {
3080bdf97013SDan Williams 		acpi_nfit_init_dsms(acpi_desc);
3081bdf97013SDan Williams 
3082bdf97013SDan Williams 		acpi_desc->nvdimm_bus = nvdimm_bus_register(dev,
3083bdf97013SDan Williams 				&acpi_desc->nd_desc);
3084bdf97013SDan Williams 		if (!acpi_desc->nvdimm_bus)
3085bdf97013SDan Williams 			return -ENOMEM;
3086bdf97013SDan Williams 
3087fbabd829SDan Williams 		rc = devm_add_action_or_reset(dev, acpi_nfit_unregister,
3088bdf97013SDan Williams 				acpi_desc);
3089bdf97013SDan Williams 		if (rc)
3090bdf97013SDan Williams 			return rc;
3091bdf97013SDan Williams 
3092bdf97013SDan Williams 		rc = acpi_nfit_desc_init_scrub_attr(acpi_desc);
3093bdf97013SDan Williams 		if (rc)
3094bdf97013SDan Williams 			return rc;
30956839a6d9SVishal Verma 
30966839a6d9SVishal Verma 		/* register this acpi_desc for mce notifications */
30976839a6d9SVishal Verma 		mutex_lock(&acpi_desc_lock);
30986839a6d9SVishal Verma 		list_add_tail(&acpi_desc->list, &acpi_descs);
30996839a6d9SVishal Verma 		mutex_unlock(&acpi_desc_lock);
3100bdf97013SDan Williams 	}
3101bdf97013SDan Williams 
3102bdf97013SDan Williams 	mutex_lock(&acpi_desc->init_mutex);
3103bdf97013SDan Williams 
3104bdf97013SDan Williams 	INIT_LIST_HEAD(&prev.spas);
3105bdf97013SDan Williams 	INIT_LIST_HEAD(&prev.memdevs);
3106bdf97013SDan Williams 	INIT_LIST_HEAD(&prev.dcrs);
3107bdf97013SDan Williams 	INIT_LIST_HEAD(&prev.bdws);
3108bdf97013SDan Williams 	INIT_LIST_HEAD(&prev.idts);
3109bdf97013SDan Williams 	INIT_LIST_HEAD(&prev.flushes);
3110bdf97013SDan Williams 
3111bdf97013SDan Williams 	list_cut_position(&prev.spas, &acpi_desc->spas,
3112bdf97013SDan Williams 				acpi_desc->spas.prev);
3113bdf97013SDan Williams 	list_cut_position(&prev.memdevs, &acpi_desc->memdevs,
3114bdf97013SDan Williams 				acpi_desc->memdevs.prev);
3115bdf97013SDan Williams 	list_cut_position(&prev.dcrs, &acpi_desc->dcrs,
3116bdf97013SDan Williams 				acpi_desc->dcrs.prev);
3117bdf97013SDan Williams 	list_cut_position(&prev.bdws, &acpi_desc->bdws,
3118bdf97013SDan Williams 				acpi_desc->bdws.prev);
3119bdf97013SDan Williams 	list_cut_position(&prev.idts, &acpi_desc->idts,
3120bdf97013SDan Williams 				acpi_desc->idts.prev);
3121bdf97013SDan Williams 	list_cut_position(&prev.flushes, &acpi_desc->flushes,
3122bdf97013SDan Williams 				acpi_desc->flushes.prev);
3123bdf97013SDan Williams 
3124bdf97013SDan Williams 	end = data + sz;
3125bdf97013SDan Williams 	while (!IS_ERR_OR_NULL(data))
3126bdf97013SDan Williams 		data = add_table(acpi_desc, &prev, data, end);
3127bdf97013SDan Williams 
3128bdf97013SDan Williams 	if (IS_ERR(data)) {
3129b814735fSJohannes Thumshirn 		dev_dbg(dev, "nfit table parsing error: %ld\n",	PTR_ERR(data));
3130bdf97013SDan Williams 		rc = PTR_ERR(data);
3131bdf97013SDan Williams 		goto out_unlock;
3132bdf97013SDan Williams 	}
3133bdf97013SDan Williams 
3134bdf97013SDan Williams 	rc = acpi_nfit_check_deletions(acpi_desc, &prev);
3135bdf97013SDan Williams 	if (rc)
3136bdf97013SDan Williams 		goto out_unlock;
3137bdf97013SDan Williams 
3138bdf97013SDan Williams 	rc = nfit_mem_init(acpi_desc);
3139bdf97013SDan Williams 	if (rc)
3140bdf97013SDan Williams 		goto out_unlock;
3141bdf97013SDan Williams 
3142bdf97013SDan Williams 	rc = acpi_nfit_register_dimms(acpi_desc);
3143bdf97013SDan Williams 	if (rc)
3144bdf97013SDan Williams 		goto out_unlock;
3145bdf97013SDan Williams 
3146bdf97013SDan Williams 	rc = acpi_nfit_register_regions(acpi_desc);
3147bdf97013SDan Williams 
3148bdf97013SDan Williams  out_unlock:
3149bdf97013SDan Williams 	mutex_unlock(&acpi_desc->init_mutex);
3150bdf97013SDan Williams 	return rc;
3151bdf97013SDan Williams }
3152bdf97013SDan Williams EXPORT_SYMBOL_GPL(acpi_nfit_init);
3153bdf97013SDan Williams 
acpi_nfit_flush_probe(struct nvdimm_bus_descriptor * nd_desc)3154bdf97013SDan Williams static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc)
3155bdf97013SDan Williams {
31568a7f02f6SXiaochun Lee 	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
3157bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
3158bdf97013SDan Williams 
3159bc6ba808SDan Williams 	/* Bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */
31601550a17aSDan Williams 	device_lock(dev);
31611550a17aSDan Williams 	device_unlock(dev);
3162bdf97013SDan Williams 
3163bc6ba808SDan Williams 	/* Bounce the init_mutex to complete initial registration */
31649ccaed4bSDan Williams 	mutex_lock(&acpi_desc->init_mutex);
31659ccaed4bSDan Williams 	mutex_unlock(&acpi_desc->init_mutex);
3166bc6ba808SDan Williams 
31679ccaed4bSDan Williams 	return 0;
3168fbabd829SDan Williams }
31699ccaed4bSDan Williams 
__acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor * nd_desc,struct nvdimm * nvdimm,unsigned int cmd)3170b3ed2ce0SDave Jiang static int __acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
3171bdf97013SDan Williams 		struct nvdimm *nvdimm, unsigned int cmd)
3172bdf97013SDan Williams {
31738a7f02f6SXiaochun Lee 	struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
3174bdf97013SDan Williams 
3175bdf97013SDan Williams 	if (nvdimm)
3176bdf97013SDan Williams 		return 0;
3177bdf97013SDan Williams 	if (cmd != ND_CMD_ARS_START)
3178bdf97013SDan Williams 		return 0;
3179bdf97013SDan Williams 
3180bdf97013SDan Williams 	/*
3181bdf97013SDan Williams 	 * The kernel and userspace may race to initiate a scrub, but
3182bdf97013SDan Williams 	 * the scrub thread is prepared to lose that initial race.  It
318359486121SDan Williams 	 * just needs guarantees that any ARS it initiates are not
318459486121SDan Williams 	 * interrupted by any intervening start requests from userspace.
3185bdf97013SDan Williams 	 */
31862121db09SDan Williams 	if (work_busy(&acpi_desc->dwork.work))
31872121db09SDan Williams 		return -EBUSY;
3188bdf97013SDan Williams 
31892121db09SDan Williams 	return 0;
3190bdf97013SDan Williams }
3191bdf97013SDan Williams 
31926450ddbdSDan Williams /*
31936450ddbdSDan Williams  * Prevent security and firmware activate commands from being issued via
31946450ddbdSDan Williams  * ioctl.
31956450ddbdSDan Williams  */
acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor * nd_desc,struct nvdimm * nvdimm,unsigned int cmd,void * buf)3196b3ed2ce0SDave Jiang static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
3197b3ed2ce0SDave Jiang 		struct nvdimm *nvdimm, unsigned int cmd, void *buf)
3198b3ed2ce0SDave Jiang {
3199b3ed2ce0SDave Jiang 	struct nd_cmd_pkg *call_pkg = buf;
3200b3ed2ce0SDave Jiang 	unsigned int func;
3201b3ed2ce0SDave Jiang 
3202b3ed2ce0SDave Jiang 	if (nvdimm && cmd == ND_CMD_CALL &&
3203b3ed2ce0SDave Jiang 			call_pkg->nd_family == NVDIMM_FAMILY_INTEL) {
3204b3ed2ce0SDave Jiang 		func = call_pkg->nd_command;
320501091c49SDan Carpenter 		if (func > NVDIMM_CMD_MAX ||
32066450ddbdSDan Williams 		    (1 << func) & NVDIMM_INTEL_DENY_CMDMASK)
3207b3ed2ce0SDave Jiang 			return -EOPNOTSUPP;
3208b3ed2ce0SDave Jiang 	}
3209b3ed2ce0SDave Jiang 
32106450ddbdSDan Williams 	/* block all non-nfit bus commands */
32116450ddbdSDan Williams 	if (!nvdimm && cmd == ND_CMD_CALL &&
32126450ddbdSDan Williams 			call_pkg->nd_family != NVDIMM_BUS_FAMILY_NFIT)
32136450ddbdSDan Williams 		return -EOPNOTSUPP;
32146450ddbdSDan Williams 
3215b3ed2ce0SDave Jiang 	return __acpi_nfit_clear_to_send(nd_desc, nvdimm, cmd);
3216b3ed2ce0SDave Jiang }
3217b3ed2ce0SDave Jiang 
acpi_nfit_ars_rescan(struct acpi_nfit_desc * acpi_desc,enum nfit_ars_state req_type)3218d3abaf43SDan Williams int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc,
3219d3abaf43SDan Williams 		enum nfit_ars_state req_type)
3220bdf97013SDan Williams {
3221bdf97013SDan Williams 	struct device *dev = acpi_desc->dev;
3222bc6ba808SDan Williams 	int scheduled = 0, busy = 0;
3223bdf97013SDan Williams 	struct nfit_spa *nfit_spa;
3224bdf97013SDan Williams 
3225bdf97013SDan Williams 	mutex_lock(&acpi_desc->init_mutex);
3226e34b8252SDan Williams 	if (test_bit(ARS_CANCEL, &acpi_desc->scrub_flags)) {
3227fbabd829SDan Williams 		mutex_unlock(&acpi_desc->init_mutex);
3228fbabd829SDan Williams 		return 0;
3229fbabd829SDan Williams 	}
3230fbabd829SDan Williams 
3231bdf97013SDan Williams 	list_for_each_entry(nfit_spa, &acpi_desc->spas, list) {
3232bc6ba808SDan Williams 		int type = nfit_spa_type(nfit_spa->spa);
3233bdf97013SDan Williams 
3234bc6ba808SDan Williams 		if (type != NFIT_SPA_PM && type != NFIT_SPA_VOLATILE)
3235bc6ba808SDan Williams 			continue;
3236bc6ba808SDan Williams 		if (test_bit(ARS_FAILED, &nfit_spa->ars_state))
3237bdf97013SDan Williams 			continue;
3238bdf97013SDan Williams 
3239d3abaf43SDan Williams 		if (test_and_set_bit(req_type, &nfit_spa->ars_state))
3240bc6ba808SDan Williams 			busy++;
3241d3abaf43SDan Williams 		else
3242bc6ba808SDan Williams 			scheduled++;
3243bdf97013SDan Williams 	}
3244bc6ba808SDan Williams 	if (scheduled) {
324533cc2c96SDan Williams 		sched_ars(acpi_desc);
3246b814735fSJohannes Thumshirn 		dev_dbg(dev, "ars_scan triggered\n");
3247bc6ba808SDan Williams 	}
3248bdf97013SDan Williams 	mutex_unlock(&acpi_desc->init_mutex);
3249bdf97013SDan Williams 
3250bc6ba808SDan Williams 	if (scheduled)
3251bdf97013SDan Williams 		return 0;
3252bc6ba808SDan Williams 	if (busy)
3253bc6ba808SDan Williams 		return -EBUSY;
3254bc6ba808SDan Williams 	return -ENOTTY;
3255bdf97013SDan Williams }
3256bdf97013SDan Williams 
acpi_nfit_desc_init(struct acpi_nfit_desc * acpi_desc,struct device * dev)3257bdf97013SDan Williams void acpi_nfit_desc_init(struct acpi_nfit_desc *acpi_desc, struct device *dev)
3258bdf97013SDan Williams {
3259bdf97013SDan Williams 	struct nvdimm_bus_descriptor *nd_desc;
3260bdf97013SDan Williams 
3261bdf97013SDan Williams 	dev_set_drvdata(dev, acpi_desc);
3262bdf97013SDan Williams 	acpi_desc->dev = dev;
3263bdf97013SDan Williams 	nd_desc = &acpi_desc->nd_desc;
3264bdf97013SDan Williams 	nd_desc->provider_name = "ACPI.NFIT";
3265bdf97013SDan Williams 	nd_desc->module = THIS_MODULE;
3266bdf97013SDan Williams 	nd_desc->ndctl = acpi_nfit_ctl;
3267bdf97013SDan Williams 	nd_desc->flush_probe = acpi_nfit_flush_probe;
3268bdf97013SDan Williams 	nd_desc->clear_to_send = acpi_nfit_clear_to_send;
3269bdf97013SDan Williams 	nd_desc->attr_groups = acpi_nfit_attribute_groups;
3270bdf97013SDan Williams 
3271bdf97013SDan Williams 	INIT_LIST_HEAD(&acpi_desc->spas);
3272bdf97013SDan Williams 	INIT_LIST_HEAD(&acpi_desc->dcrs);
3273bdf97013SDan Williams 	INIT_LIST_HEAD(&acpi_desc->bdws);
3274bdf97013SDan Williams 	INIT_LIST_HEAD(&acpi_desc->idts);
3275bdf97013SDan Williams 	INIT_LIST_HEAD(&acpi_desc->flushes);
3276bdf97013SDan Williams 	INIT_LIST_HEAD(&acpi_desc->memdevs);
3277bdf97013SDan Williams 	INIT_LIST_HEAD(&acpi_desc->dimms);
32786839a6d9SVishal Verma 	INIT_LIST_HEAD(&acpi_desc->list);
3279bdf97013SDan Williams 	mutex_init(&acpi_desc->init_mutex);
3280bc6ba808SDan Williams 	acpi_desc->scrub_tmo = 1;
3281bc6ba808SDan Williams 	INIT_DELAYED_WORK(&acpi_desc->dwork, acpi_nfit_scrub);
3282bdf97013SDan Williams }
3283bdf97013SDan Williams EXPORT_SYMBOL_GPL(acpi_nfit_desc_init);
3284bdf97013SDan Williams 
acpi_nfit_put_table(void * table)32853c87f372SDan Williams static void acpi_nfit_put_table(void *table)
32863c87f372SDan Williams {
32873c87f372SDan Williams 	acpi_put_table(table);
32883c87f372SDan Williams }
32893c87f372SDan Williams 
acpi_nfit_notify(acpi_handle handle,u32 event,void * data)3290dcca12abSMichal Wilczynski static void acpi_nfit_notify(acpi_handle handle, u32 event, void *data)
3291dcca12abSMichal Wilczynski {
3292dcca12abSMichal Wilczynski 	struct acpi_device *adev = data;
3293dcca12abSMichal Wilczynski 
3294dcca12abSMichal Wilczynski 	device_lock(&adev->dev);
3295dcca12abSMichal Wilczynski 	__acpi_nfit_notify(&adev->dev, handle, event);
3296dcca12abSMichal Wilczynski 	device_unlock(&adev->dev);
3297dcca12abSMichal Wilczynski }
3298dcca12abSMichal Wilczynski 
acpi_nfit_remove_notify_handler(void * data)3299dcca12abSMichal Wilczynski static void acpi_nfit_remove_notify_handler(void *data)
3300dcca12abSMichal Wilczynski {
3301dcca12abSMichal Wilczynski 	struct acpi_device *adev = data;
3302dcca12abSMichal Wilczynski 
3303dcca12abSMichal Wilczynski 	acpi_dev_remove_notify_handler(adev, ACPI_DEVICE_NOTIFY,
3304dcca12abSMichal Wilczynski 				       acpi_nfit_notify);
3305dcca12abSMichal Wilczynski }
3306dcca12abSMichal Wilczynski 
acpi_nfit_shutdown(void * data)3307fbabd829SDan Williams void acpi_nfit_shutdown(void *data)
3308fbabd829SDan Williams {
3309fbabd829SDan Williams 	struct acpi_nfit_desc *acpi_desc = data;
3310fbabd829SDan Williams 	struct device *bus_dev = to_nvdimm_bus_dev(acpi_desc->nvdimm_bus);
3311fbabd829SDan Williams 
3312fbabd829SDan Williams 	/*
3313fbabd829SDan Williams 	 * Destruct under acpi_desc_lock so that nfit_handle_mce does not
3314fbabd829SDan Williams 	 * race teardown
3315fbabd829SDan Williams 	 */
3316fbabd829SDan Williams 	mutex_lock(&acpi_desc_lock);
3317fbabd829SDan Williams 	list_del(&acpi_desc->list);
3318fbabd829SDan Williams 	mutex_unlock(&acpi_desc_lock);
3319fbabd829SDan Williams 
3320fbabd829SDan Williams 	mutex_lock(&acpi_desc->init_mutex);
3321e34b8252SDan Williams 	set_bit(ARS_CANCEL, &acpi_desc->scrub_flags);
3322fbabd829SDan Williams 	mutex_unlock(&acpi_desc->init_mutex);
3323fb6df436SVishal Verma 	cancel_delayed_work_sync(&acpi_desc->dwork);
3324fbabd829SDan Williams 
3325fbabd829SDan Williams 	/*
3326fbabd829SDan Williams 	 * Bounce the nvdimm bus lock to make sure any in-flight
3327fbabd829SDan Williams 	 * acpi_nfit_ars_rescan() submissions have had a chance to
3328fbabd829SDan Williams 	 * either submit or see ->cancel set.
3329fbabd829SDan Williams 	 */
33301550a17aSDan Williams 	device_lock(bus_dev);
33311550a17aSDan Williams 	device_unlock(bus_dev);
3332fbabd829SDan Williams 
3333fbabd829SDan Williams 	flush_workqueue(nfit_wq);
3334fbabd829SDan Williams }
3335fbabd829SDan Williams EXPORT_SYMBOL_GPL(acpi_nfit_shutdown);
3336fbabd829SDan Williams 
acpi_nfit_add(struct acpi_device * adev)3337bdf97013SDan Williams static int acpi_nfit_add(struct acpi_device *adev)
3338bdf97013SDan Williams {
3339bdf97013SDan Williams 	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
3340bdf97013SDan Williams 	struct acpi_nfit_desc *acpi_desc;
3341bdf97013SDan Williams 	struct device *dev = &adev->dev;
3342bdf97013SDan Williams 	struct acpi_table_header *tbl;
3343bdf97013SDan Williams 	acpi_status status = AE_OK;
3344bdf97013SDan Williams 	acpi_size sz;
3345bdf97013SDan Williams 	int rc = 0;
3346bdf97013SDan Williams 
33479b311b73SXiang Chen 	rc = acpi_dev_install_notify_handler(adev, ACPI_DEVICE_NOTIFY,
33489b311b73SXiang Chen 					     acpi_nfit_notify);
33499b311b73SXiang Chen 	if (rc)
33509b311b73SXiang Chen 		return rc;
33519b311b73SXiang Chen 
33529b311b73SXiang Chen 	rc = devm_add_action_or_reset(dev, acpi_nfit_remove_notify_handler,
33539b311b73SXiang Chen 					adev);
33549b311b73SXiang Chen 	if (rc)
33559b311b73SXiang Chen 		return rc;
33569b311b73SXiang Chen 
33576b11d1d6SLv Zheng 	status = acpi_get_table(ACPI_SIG_NFIT, 0, &tbl);
3358bdf97013SDan Williams 	if (ACPI_FAILURE(status)) {
33599f619d47SOcean He 		/* The NVDIMM root device allows OS to trigger enumeration of
33609f619d47SOcean He 		 * NVDIMMs through NFIT at boot time and re-enumeration at
33619f619d47SOcean He 		 * root level via the _FIT method during runtime.
33629f619d47SOcean He 		 * This is ok to return 0 here, we could have an nvdimm
33639f619d47SOcean He 		 * hotplugged later and evaluate _FIT method which returns
33649f619d47SOcean He 		 * data in the format of a series of NFIT Structures.
33659f619d47SOcean He 		 */
3366bdf97013SDan Williams 		dev_dbg(dev, "failed to find NFIT at startup\n");
3367bdf97013SDan Williams 		return 0;
3368bdf97013SDan Williams 	}
33693c87f372SDan Williams 
33703c87f372SDan Williams 	rc = devm_add_action_or_reset(dev, acpi_nfit_put_table, tbl);
33713c87f372SDan Williams 	if (rc)
33723c87f372SDan Williams 		return rc;
33736b11d1d6SLv Zheng 	sz = tbl->length;
3374bdf97013SDan Williams 
3375bdf97013SDan Williams 	acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL);
3376bdf97013SDan Williams 	if (!acpi_desc)
3377bdf97013SDan Williams 		return -ENOMEM;
3378bdf97013SDan Williams 	acpi_nfit_desc_init(acpi_desc, &adev->dev);
3379bdf97013SDan Williams 
3380bdf97013SDan Williams 	/* Save the acpi header for exporting the revision via sysfs */
3381bdf97013SDan Williams 	acpi_desc->acpi_header = *tbl;
3382bdf97013SDan Williams 
3383bdf97013SDan Williams 	/* Evaluate _FIT and override with that if present */
3384bdf97013SDan Williams 	status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf);
3385bdf97013SDan Williams 	if (ACPI_SUCCESS(status) && buf.length > 0) {
3386bdf97013SDan Williams 		union acpi_object *obj = buf.pointer;
3387bdf97013SDan Williams 
3388bdf97013SDan Williams 		if (obj->type == ACPI_TYPE_BUFFER)
3389bdf97013SDan Williams 			rc = acpi_nfit_init(acpi_desc, obj->buffer.pointer,
3390bdf97013SDan Williams 					obj->buffer.length);
3391bdf97013SDan Williams 		else
3392b814735fSJohannes Thumshirn 			dev_dbg(dev, "invalid type %d, ignoring _FIT\n",
3393b814735fSJohannes Thumshirn 				(int) obj->type);
3394bdf97013SDan Williams 		kfree(buf.pointer);
3395bdf97013SDan Williams 	} else
3396bdf97013SDan Williams 		/* skip over the lead-in header table */
3397bdf97013SDan Williams 		rc = acpi_nfit_init(acpi_desc, (void *) tbl
3398bdf97013SDan Williams 				+ sizeof(struct acpi_table_nfit),
3399bdf97013SDan Williams 				sz - sizeof(struct acpi_table_nfit));
3400fbabd829SDan Williams 
3401fbabd829SDan Williams 	if (rc)
3402bdf97013SDan Williams 		return rc;
3403dcca12abSMichal Wilczynski 
34049b311b73SXiang Chen 	return devm_add_action_or_reset(dev, acpi_nfit_shutdown, acpi_desc);
3405bdf97013SDan Williams }
3406bdf97013SDan Williams 
acpi_nfit_update_notify(struct device * dev,acpi_handle handle)340756b47fe6SToshi Kani static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle)
3408bdf97013SDan Williams {
3409c14a868aSDan Williams 	struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev);
3410bdf97013SDan Williams 	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
3411bdf97013SDan Williams 	union acpi_object *obj;
3412bdf97013SDan Williams 	acpi_status status;
3413bdf97013SDan Williams 	int ret;
3414bdf97013SDan Williams 
3415bdf97013SDan Williams 	if (!dev->driver) {
3416bdf97013SDan Williams 		/* dev->driver may be null if we're being removed */
3417b814735fSJohannes Thumshirn 		dev_dbg(dev, "no driver found for dev\n");
3418c14a868aSDan Williams 		return;
3419bdf97013SDan Williams 	}
3420bdf97013SDan Williams 
3421bdf97013SDan Williams 	if (!acpi_desc) {
3422bdf97013SDan Williams 		acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL);
3423bdf97013SDan Williams 		if (!acpi_desc)
3424c14a868aSDan Williams 			return;
3425c14a868aSDan Williams 		acpi_nfit_desc_init(acpi_desc, dev);
3426bdf97013SDan Williams 	} else {
3427bdf97013SDan Williams 		/*
3428bdf97013SDan Williams 		 * Finish previous registration before considering new
3429bdf97013SDan Williams 		 * regions.
3430bdf97013SDan Williams 		 */
3431bdf97013SDan Williams 		flush_workqueue(nfit_wq);
3432bdf97013SDan Williams 	}
3433bdf97013SDan Williams 
3434bdf97013SDan Williams 	/* Evaluate _FIT */
3435c14a868aSDan Williams 	status = acpi_evaluate_object(handle, "_FIT", NULL, &buf);
3436bdf97013SDan Williams 	if (ACPI_FAILURE(status)) {
3437bdf97013SDan Williams 		dev_err(dev, "failed to evaluate _FIT\n");
3438c14a868aSDan Williams 		return;
3439bdf97013SDan Williams 	}
3440bdf97013SDan Williams 
3441bdf97013SDan Williams 	obj = buf.pointer;
3442bdf97013SDan Williams 	if (obj->type == ACPI_TYPE_BUFFER) {
3443bdf97013SDan Williams 		ret = acpi_nfit_init(acpi_desc, obj->buffer.pointer,
3444bdf97013SDan Williams 				obj->buffer.length);
3445bdf97013SDan Williams 		if (ret)
3446bdf97013SDan Williams 			dev_err(dev, "failed to merge updated NFIT\n");
3447bdf97013SDan Williams 	} else
3448bdf97013SDan Williams 		dev_err(dev, "Invalid _FIT\n");
3449bdf97013SDan Williams 	kfree(buf.pointer);
3450c14a868aSDan Williams }
345156b47fe6SToshi Kani 
acpi_nfit_uc_error_notify(struct device * dev,acpi_handle handle)345256b47fe6SToshi Kani static void acpi_nfit_uc_error_notify(struct device *dev, acpi_handle handle)
345356b47fe6SToshi Kani {
345456b47fe6SToshi Kani 	struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev);
345556b47fe6SToshi Kani 
3456d3abaf43SDan Williams 	if (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON)
3457d3abaf43SDan Williams 		acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_LONG);
3458d3abaf43SDan Williams 	else
3459d3abaf43SDan Williams 		acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_SHORT);
346056b47fe6SToshi Kani }
346156b47fe6SToshi Kani 
__acpi_nfit_notify(struct device * dev,acpi_handle handle,u32 event)346256b47fe6SToshi Kani void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event)
346356b47fe6SToshi Kani {
3464b814735fSJohannes Thumshirn 	dev_dbg(dev, "event: 0x%x\n", event);
346556b47fe6SToshi Kani 
346656b47fe6SToshi Kani 	switch (event) {
346756b47fe6SToshi Kani 	case NFIT_NOTIFY_UPDATE:
346856b47fe6SToshi Kani 		return acpi_nfit_update_notify(dev, handle);
346956b47fe6SToshi Kani 	case NFIT_NOTIFY_UC_MEMORY_ERROR:
347056b47fe6SToshi Kani 		return acpi_nfit_uc_error_notify(dev, handle);
347156b47fe6SToshi Kani 	default:
347256b47fe6SToshi Kani 		return;
347356b47fe6SToshi Kani 	}
347456b47fe6SToshi Kani }
3475c14a868aSDan Williams EXPORT_SYMBOL_GPL(__acpi_nfit_notify);
3476bdf97013SDan Williams 
3477bdf97013SDan Williams static const struct acpi_device_id acpi_nfit_ids[] = {
3478bdf97013SDan Williams 	{ "ACPI0012", 0 },
3479bdf97013SDan Williams 	{ "", 0 },
3480bdf97013SDan Williams };
3481bdf97013SDan Williams MODULE_DEVICE_TABLE(acpi, acpi_nfit_ids);
3482bdf97013SDan Williams 
3483bdf97013SDan Williams static struct acpi_driver acpi_nfit_driver = {
3484bdf97013SDan Williams 	.name = KBUILD_MODNAME,
3485bdf97013SDan Williams 	.ids = acpi_nfit_ids,
3486bdf97013SDan Williams 	.ops = {
3487bdf97013SDan Williams 		.add = acpi_nfit_add,
3488bdf97013SDan Williams 	},
3489bdf97013SDan Williams };
3490bdf97013SDan Williams 
nfit_init(void)3491bdf97013SDan Williams static __init int nfit_init(void)
3492bdf97013SDan Williams {
34937e700d2cSPrarit Bhargava 	int ret;
34947e700d2cSPrarit Bhargava 
3495bdf97013SDan Williams 	BUILD_BUG_ON(sizeof(struct acpi_table_nfit) != 40);
3496cf16b05cSBob Moore 	BUILD_BUG_ON(sizeof(struct acpi_nfit_system_address) != 64);
3497bdf97013SDan Williams 	BUILD_BUG_ON(sizeof(struct acpi_nfit_memory_map) != 48);
34982a5ab998SKees Cook 	BUILD_BUG_ON(sizeof(struct acpi_nfit_interleave) != 16);
349974522feaSKees Cook 	BUILD_BUG_ON(sizeof(struct acpi_nfit_smbios) != 8);
3500bdf97013SDan Williams 	BUILD_BUG_ON(sizeof(struct acpi_nfit_control_region) != 80);
3501bdf97013SDan Williams 	BUILD_BUG_ON(sizeof(struct acpi_nfit_data_region) != 40);
350206e8ccdaSDave Jiang 	BUILD_BUG_ON(sizeof(struct acpi_nfit_capabilities) != 16);
3503bdf97013SDan Williams 
350441c8bdb3SAndy Shevchenko 	guid_parse(UUID_VOLATILE_MEMORY, &nfit_uuid[NFIT_SPA_VOLATILE]);
350541c8bdb3SAndy Shevchenko 	guid_parse(UUID_PERSISTENT_MEMORY, &nfit_uuid[NFIT_SPA_PM]);
350641c8bdb3SAndy Shevchenko 	guid_parse(UUID_CONTROL_REGION, &nfit_uuid[NFIT_SPA_DCR]);
350741c8bdb3SAndy Shevchenko 	guid_parse(UUID_DATA_REGION, &nfit_uuid[NFIT_SPA_BDW]);
350841c8bdb3SAndy Shevchenko 	guid_parse(UUID_VOLATILE_VIRTUAL_DISK, &nfit_uuid[NFIT_SPA_VDISK]);
350941c8bdb3SAndy Shevchenko 	guid_parse(UUID_VOLATILE_VIRTUAL_CD, &nfit_uuid[NFIT_SPA_VCD]);
351041c8bdb3SAndy Shevchenko 	guid_parse(UUID_PERSISTENT_VIRTUAL_DISK, &nfit_uuid[NFIT_SPA_PDISK]);
351141c8bdb3SAndy Shevchenko 	guid_parse(UUID_PERSISTENT_VIRTUAL_CD, &nfit_uuid[NFIT_SPA_PCD]);
351241c8bdb3SAndy Shevchenko 	guid_parse(UUID_NFIT_BUS, &nfit_uuid[NFIT_DEV_BUS]);
351341c8bdb3SAndy Shevchenko 	guid_parse(UUID_NFIT_DIMM, &nfit_uuid[NFIT_DEV_DIMM]);
351441c8bdb3SAndy Shevchenko 	guid_parse(UUID_NFIT_DIMM_N_HPE1, &nfit_uuid[NFIT_DEV_DIMM_N_HPE1]);
351541c8bdb3SAndy Shevchenko 	guid_parse(UUID_NFIT_DIMM_N_HPE2, &nfit_uuid[NFIT_DEV_DIMM_N_HPE2]);
351641c8bdb3SAndy Shevchenko 	guid_parse(UUID_NFIT_DIMM_N_MSFT, &nfit_uuid[NFIT_DEV_DIMM_N_MSFT]);
35171194c413SDexuan Cui 	guid_parse(UUID_NFIT_DIMM_N_HYPERV, &nfit_uuid[NFIT_DEV_DIMM_N_HYPERV]);
35186450ddbdSDan Williams 	guid_parse(UUID_INTEL_BUS, &nfit_uuid[NFIT_BUS_INTEL]);
3519bdf97013SDan Williams 
3520bdf97013SDan Williams 	nfit_wq = create_singlethread_workqueue("nfit");
3521bdf97013SDan Williams 	if (!nfit_wq)
3522bdf97013SDan Williams 		return -ENOMEM;
3523bdf97013SDan Williams 
35246839a6d9SVishal Verma 	nfit_mce_register();
35257e700d2cSPrarit Bhargava 	ret = acpi_bus_register_driver(&acpi_nfit_driver);
35267e700d2cSPrarit Bhargava 	if (ret) {
35277e700d2cSPrarit Bhargava 		nfit_mce_unregister();
35287e700d2cSPrarit Bhargava 		destroy_workqueue(nfit_wq);
35297e700d2cSPrarit Bhargava 	}
35306839a6d9SVishal Verma 
35317e700d2cSPrarit Bhargava 	return ret;
35327e700d2cSPrarit Bhargava 
3533bdf97013SDan Williams }
3534bdf97013SDan Williams 
nfit_exit(void)3535bdf97013SDan Williams static __exit void nfit_exit(void)
3536bdf97013SDan Williams {
35376839a6d9SVishal Verma 	nfit_mce_unregister();
3538bdf97013SDan Williams 	acpi_bus_unregister_driver(&acpi_nfit_driver);
3539bdf97013SDan Williams 	destroy_workqueue(nfit_wq);
35406839a6d9SVishal Verma 	WARN_ON(!list_empty(&acpi_descs));
3541bdf97013SDan Williams }
3542bdf97013SDan Williams 
3543bdf97013SDan Williams module_init(nfit_init);
3544bdf97013SDan Williams module_exit(nfit_exit);
3545bdf97013SDan Williams MODULE_LICENSE("GPL v2");
3546bdf97013SDan Williams MODULE_AUTHOR("Intel Corporation");
3547