summaryrefslogtreecommitdiff
path: root/drivers/acpi/nfit/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi/nfit/core.c')
-rw-r--r--drivers/acpi/nfit/core.c167
1 files changed, 142 insertions, 25 deletions
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c
index 097eff0b963d..b75b734ee73a 100644
--- a/drivers/acpi/nfit/core.c
+++ b/drivers/acpi/nfit/core.c
@@ -20,7 +20,6 @@
#include <linux/list.h>
#include <linux/acpi.h>
#include <linux/sort.h>
-#include <linux/pmem.h>
#include <linux/io.h>
#include <linux/nd.h>
#include <asm/cacheflush.h>
@@ -253,6 +252,8 @@ int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm,
cmd_name = nvdimm_bus_cmd_name(cmd);
cmd_mask = nd_desc->cmd_mask;
dsm_mask = cmd_mask;
+ if (cmd == ND_CMD_CALL)
+ dsm_mask = nd_desc->bus_dsm_mask;
desc = nd_cmd_bus_desc(cmd);
guid = to_nfit_uuid(NFIT_DEV_BUS);
handle = adev->handle;
@@ -927,6 +928,17 @@ static int nfit_mem_init(struct acpi_nfit_desc *acpi_desc)
return 0;
}
+static ssize_t bus_dsm_mask_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
+ struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
+
+ return sprintf(buf, "%#lx\n", nd_desc->bus_dsm_mask);
+}
+static struct device_attribute dev_attr_bus_dsm_mask =
+ __ATTR(dsm_mask, 0444, bus_dsm_mask_show, NULL);
+
static ssize_t revision_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1031,7 +1043,7 @@ static ssize_t scrub_store(struct device *dev,
if (nd_desc) {
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
- rc = acpi_nfit_ars_rescan(acpi_desc);
+ rc = acpi_nfit_ars_rescan(acpi_desc, 0);
}
device_unlock(dev);
if (rc)
@@ -1063,10 +1075,11 @@ static struct attribute *acpi_nfit_attributes[] = {
&dev_attr_revision.attr,
&dev_attr_scrub.attr,
&dev_attr_hw_error_scrub.attr,
+ &dev_attr_bus_dsm_mask.attr,
NULL,
};
-static struct attribute_group acpi_nfit_attribute_group = {
+static const struct attribute_group acpi_nfit_attribute_group = {
.name = "nfit",
.attrs = acpi_nfit_attributes,
.is_visible = nfit_visible,
@@ -1346,7 +1359,7 @@ static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj,
return a->mode;
}
-static struct attribute_group acpi_nfit_dimm_attribute_group = {
+static const struct attribute_group acpi_nfit_dimm_attribute_group = {
.name = "nfit",
.attrs = acpi_nfit_dimm_attributes,
.is_visible = acpi_nfit_dimm_attr_visible,
@@ -1608,11 +1621,23 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
acpi_desc);
}
+/*
+ * These constants are private because there are no kernel consumers of
+ * these commands.
+ */
+enum nfit_aux_cmds {
+ NFIT_CMD_TRANSLATE_SPA = 5,
+ NFIT_CMD_ARS_INJECT_SET = 7,
+ NFIT_CMD_ARS_INJECT_CLEAR = 8,
+ NFIT_CMD_ARS_INJECT_GET = 9,
+};
+
static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
{
struct nvdimm_bus_descriptor *nd_desc = &acpi_desc->nd_desc;
const guid_t *guid = to_nfit_uuid(NFIT_DEV_BUS);
struct acpi_device *adev;
+ unsigned long dsm_mask;
int i;
nd_desc->cmd_mask = acpi_desc->bus_cmd_force_en;
@@ -1623,6 +1648,20 @@ static void acpi_nfit_init_dsms(struct acpi_nfit_desc *acpi_desc)
for (i = ND_CMD_ARS_CAP; i <= ND_CMD_CLEAR_ERROR; i++)
if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i))
set_bit(i, &nd_desc->cmd_mask);
+ set_bit(ND_CMD_CALL, &nd_desc->cmd_mask);
+
+ dsm_mask =
+ (1 << ND_CMD_ARS_CAP) |
+ (1 << ND_CMD_ARS_START) |
+ (1 << ND_CMD_ARS_STATUS) |
+ (1 << ND_CMD_CLEAR_ERROR) |
+ (1 << NFIT_CMD_TRANSLATE_SPA) |
+ (1 << NFIT_CMD_ARS_INJECT_SET) |
+ (1 << NFIT_CMD_ARS_INJECT_CLEAR) |
+ (1 << NFIT_CMD_ARS_INJECT_GET);
+ for_each_set_bit(i, &dsm_mask, BITS_PER_LONG)
+ if (acpi_check_dsm(adev->handle, guid, 1, 1ULL << i))
+ set_bit(i, &nd_desc->bus_dsm_mask);
}
static ssize_t range_index_show(struct device *dev,
@@ -1640,7 +1679,7 @@ static struct attribute *acpi_nfit_region_attributes[] = {
NULL,
};
-static struct attribute_group acpi_nfit_region_attribute_group = {
+static const struct attribute_group acpi_nfit_region_attribute_group = {
.name = "nfit",
.attrs = acpi_nfit_region_attributes,
};
@@ -1663,12 +1702,29 @@ struct nfit_set_info {
} mapping[0];
};
+struct nfit_set_info2 {
+ struct nfit_set_info_map2 {
+ u64 region_offset;
+ u32 serial_number;
+ u16 vendor_id;
+ u16 manufacturing_date;
+ u8 manufacturing_location;
+ u8 reserved[31];
+ } mapping[0];
+};
+
static size_t sizeof_nfit_set_info(int num_mappings)
{
return sizeof(struct nfit_set_info)
+ num_mappings * sizeof(struct nfit_set_info_map);
}
+static size_t sizeof_nfit_set_info2(int num_mappings)
+{
+ return sizeof(struct nfit_set_info2)
+ + num_mappings * sizeof(struct nfit_set_info_map2);
+}
+
static int cmp_map_compat(const void *m0, const void *m1)
{
const struct nfit_set_info_map *map0 = m0;
@@ -1690,6 +1746,18 @@ static int cmp_map(const void *m0, const void *m1)
return 0;
}
+static int cmp_map2(const void *m0, const void *m1)
+{
+ const struct nfit_set_info_map2 *map0 = m0;
+ const struct nfit_set_info_map2 *map1 = m1;
+
+ if (map0->region_offset < map1->region_offset)
+ return -1;
+ else if (map0->region_offset > map1->region_offset)
+ return 1;
+ return 0;
+}
+
/* Retrieve the nth entry referencing this spa */
static struct acpi_nfit_memory_map *memdev_from_spa(
struct acpi_nfit_desc *acpi_desc, u16 range_index, int n)
@@ -1707,27 +1775,31 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
struct nd_region_desc *ndr_desc,
struct acpi_nfit_system_address *spa)
{
- int i, spa_type = nfit_spa_type(spa);
struct device *dev = acpi_desc->dev;
struct nd_interleave_set *nd_set;
u16 nr = ndr_desc->num_mappings;
+ struct nfit_set_info2 *info2;
struct nfit_set_info *info;
-
- if (spa_type == NFIT_SPA_PM || spa_type == NFIT_SPA_VOLATILE)
- /* pass */;
- else
- return 0;
+ int i;
nd_set = devm_kzalloc(dev, sizeof(*nd_set), GFP_KERNEL);
if (!nd_set)
return -ENOMEM;
+ ndr_desc->nd_set = nd_set;
+ guid_copy(&nd_set->type_guid, (guid_t *) spa->range_guid);
info = devm_kzalloc(dev, sizeof_nfit_set_info(nr), GFP_KERNEL);
if (!info)
return -ENOMEM;
+
+ info2 = devm_kzalloc(dev, sizeof_nfit_set_info2(nr), GFP_KERNEL);
+ if (!info2)
+ return -ENOMEM;
+
for (i = 0; i < nr; i++) {
struct nd_mapping_desc *mapping = &ndr_desc->mapping[i];
struct nfit_set_info_map *map = &info->mapping[i];
+ struct nfit_set_info_map2 *map2 = &info2->mapping[i];
struct nvdimm *nvdimm = mapping->nvdimm;
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
struct acpi_nfit_memory_map *memdev = memdev_from_spa(acpi_desc,
@@ -1740,19 +1812,32 @@ static int acpi_nfit_init_interleave_set(struct acpi_nfit_desc *acpi_desc,
map->region_offset = memdev->region_offset;
map->serial_number = nfit_mem->dcr->serial_number;
+
+ map2->region_offset = memdev->region_offset;
+ map2->serial_number = nfit_mem->dcr->serial_number;
+ map2->vendor_id = nfit_mem->dcr->vendor_id;
+ map2->manufacturing_date = nfit_mem->dcr->manufacturing_date;
+ map2->manufacturing_location = nfit_mem->dcr->manufacturing_location;
}
+ /* v1.1 namespaces */
sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
cmp_map, NULL);
- nd_set->cookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
+ nd_set->cookie1 = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
+
+ /* v1.2 namespaces */
+ sort(&info2->mapping[0], nr, sizeof(struct nfit_set_info_map2),
+ cmp_map2, NULL);
+ nd_set->cookie2 = nd_fletcher64(info2, sizeof_nfit_set_info2(nr), 0);
- /* support namespaces created with the wrong sort order */
+ /* support v1.1 namespaces created with the wrong sort order */
sort(&info->mapping[0], nr, sizeof(struct nfit_set_info_map),
cmp_map_compat, NULL);
nd_set->altcookie = nd_fletcher64(info, sizeof_nfit_set_info(nr), 0);
ndr_desc->nd_set = nd_set;
devm_kfree(dev, info);
+ devm_kfree(dev, info2);
return 0;
}
@@ -1842,8 +1927,7 @@ static int acpi_nfit_blk_single_io(struct nfit_blk *nfit_blk,
}
if (rw)
- memcpy_to_pmem(mmio->addr.aperture + offset,
- iobuf + copied, c);
+ memcpy_flushcache(mmio->addr.aperture + offset, iobuf + copied, c);
else {
if (nfit_blk->dimm_flags & NFIT_BLK_READ_FLUSH)
mmio_flush_range((void __force *)
@@ -1957,7 +2041,7 @@ static int acpi_nfit_blk_region_enable(struct nvdimm_bus *nvdimm_bus,
nfit_blk->bdw_offset = nfit_mem->bdw->offset;
mmio = &nfit_blk->mmio[BDW];
mmio->addr.base = devm_nvdimm_memremap(dev, nfit_mem->spa_bdw->address,
- nfit_mem->spa_bdw->length, ARCH_MEMREMAP_PMEM);
+ nfit_mem->spa_bdw->length, nd_blk_memremap_flags(ndbr));
if (!mmio->addr.base) {
dev_dbg(dev, "%s: %s failed to map bdw\n", __func__,
nvdimm_name(nvdimm));
@@ -2051,6 +2135,7 @@ static int ars_start(struct acpi_nfit_desc *acpi_desc, struct nfit_spa *nfit_spa
memset(&ars_start, 0, sizeof(ars_start));
ars_start.address = spa->address;
ars_start.length = spa->length;
+ ars_start.flags = acpi_desc->ars_start_flags;
if (nfit_spa_type(spa) == NFIT_SPA_PM)
ars_start.type = ND_ARS_PERSISTENT;
else if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE)
@@ -2077,6 +2162,7 @@ static int ars_continue(struct acpi_nfit_desc *acpi_desc)
ars_start.address = ars_status->restart_address;
ars_start.length = ars_status->restart_length;
ars_start.type = ars_status->type;
+ ars_start.flags = acpi_desc->ars_start_flags;
rc = nd_desc->ndctl(nd_desc, NULL, ND_CMD_ARS_START, &ars_start,
sizeof(ars_start), &cmd_rc);
if (rc < 0)
@@ -2179,7 +2265,7 @@ static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc,
struct acpi_nfit_system_address *spa = nfit_spa->spa;
struct nd_blk_region_desc *ndbr_desc;
struct nfit_mem *nfit_mem;
- int blk_valid = 0;
+ int blk_valid = 0, rc;
if (!nvdimm) {
dev_err(acpi_desc->dev, "spa%d dimm: %#x not found\n",
@@ -2211,6 +2297,9 @@ static int acpi_nfit_init_mapping(struct acpi_nfit_desc *acpi_desc,
ndbr_desc = to_blk_region_desc(ndr_desc);
ndbr_desc->enable = acpi_nfit_blk_region_enable;
ndbr_desc->do_io = acpi_desc->blk_do_io;
+ rc = acpi_nfit_init_interleave_set(acpi_desc, ndr_desc, spa);
+ if (rc)
+ return rc;
nfit_spa->nd_region = nvdimm_blk_region_create(acpi_desc->nvdimm_bus,
ndr_desc);
if (!nfit_spa->nd_region)
@@ -2229,6 +2318,13 @@ static bool nfit_spa_is_virtual(struct acpi_nfit_system_address *spa)
nfit_spa_type(spa) == NFIT_SPA_PCD);
}
+static bool nfit_spa_is_volatile(struct acpi_nfit_system_address *spa)
+{
+ return (nfit_spa_type(spa) == NFIT_SPA_VDISK ||
+ nfit_spa_type(spa) == NFIT_SPA_VCD ||
+ nfit_spa_type(spa) == NFIT_SPA_VOLATILE);
+}
+
static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc,
struct nfit_spa *nfit_spa)
{
@@ -2303,7 +2399,7 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc,
ndr_desc);
if (!nfit_spa->nd_region)
rc = -ENOMEM;
- } else if (nfit_spa_type(spa) == NFIT_SPA_VOLATILE) {
+ } else if (nfit_spa_is_volatile(spa)) {
nfit_spa->nd_region = nvdimm_volatile_region_create(nvdimm_bus,
ndr_desc);
if (!nfit_spa->nd_region)
@@ -2595,6 +2691,7 @@ static void acpi_nfit_scrub(struct work_struct *work)
list_for_each_entry(nfit_spa, &acpi_desc->spas, list)
acpi_nfit_async_scrub(acpi_desc, nfit_spa);
acpi_desc->scrub_count++;
+ acpi_desc->ars_start_flags = 0;
if (acpi_desc->scrub_count_state)
sysfs_notify_dirent(acpi_desc->scrub_count_state);
mutex_unlock(&acpi_desc->init_mutex);
@@ -2613,6 +2710,7 @@ static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc)
return rc;
}
+ acpi_desc->ars_start_flags = 0;
if (!acpi_desc->cancel)
queue_work(nfit_wq, &acpi_desc->work);
return 0;
@@ -2817,7 +2915,7 @@ static int acpi_nfit_clear_to_send(struct nvdimm_bus_descriptor *nd_desc,
return 0;
}
-int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc)
+int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc, u8 flags)
{
struct device *dev = acpi_desc->dev;
struct nfit_spa *nfit_spa;
@@ -2839,6 +2937,7 @@ int acpi_nfit_ars_rescan(struct acpi_nfit_desc *acpi_desc)
nfit_spa->ars_required = 1;
}
+ acpi_desc->ars_start_flags = flags;
queue_work(nfit_wq, &acpi_desc->work);
dev_dbg(dev, "%s: ars_scan triggered\n", __func__);
mutex_unlock(&acpi_desc->init_mutex);
@@ -2967,7 +3066,7 @@ static int acpi_nfit_remove(struct acpi_device *adev)
return 0;
}
-void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event)
+static void acpi_nfit_update_notify(struct device *dev, acpi_handle handle)
{
struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev);
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
@@ -2975,11 +3074,6 @@ void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event)
acpi_status status;
int ret;
- dev_dbg(dev, "%s: event: %d\n", __func__, event);
-
- if (event != NFIT_NOTIFY_UPDATE)
- return;
-
if (!dev->driver) {
/* dev->driver may be null if we're being removed */
dev_dbg(dev, "%s: no driver found for dev\n", __func__);
@@ -3016,6 +3110,29 @@ void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event)
dev_err(dev, "Invalid _FIT\n");
kfree(buf.pointer);
}
+
+static void acpi_nfit_uc_error_notify(struct device *dev, acpi_handle handle)
+{
+ struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(dev);
+ u8 flags = (acpi_desc->scrub_mode == HW_ERROR_SCRUB_ON) ?
+ 0 : ND_ARS_RETURN_PREV_DATA;
+
+ acpi_nfit_ars_rescan(acpi_desc, flags);
+}
+
+void __acpi_nfit_notify(struct device *dev, acpi_handle handle, u32 event)
+{
+ dev_dbg(dev, "%s: event: 0x%x\n", __func__, event);
+
+ switch (event) {
+ case NFIT_NOTIFY_UPDATE:
+ return acpi_nfit_update_notify(dev, handle);
+ case NFIT_NOTIFY_UC_MEMORY_ERROR:
+ return acpi_nfit_uc_error_notify(dev, handle);
+ default:
+ return;
+ }
+}
EXPORT_SYMBOL_GPL(__acpi_nfit_notify);
static void acpi_nfit_notify(struct acpi_device *adev, u32 event)