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