|
@@ -342,6 +342,84 @@ static int nfit_test_cmd_clear_error(struct nd_cmd_clear_error *clear_err,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+struct region_search_spa {
|
|
|
+ u64 addr;
|
|
|
+ struct nd_region *region;
|
|
|
+};
|
|
|
+
|
|
|
+static int is_region_device(struct device *dev)
|
|
|
+{
|
|
|
+ return !strncmp(dev->kobj.name, "region", 6);
|
|
|
+}
|
|
|
+
|
|
|
+static int nfit_test_search_region_spa(struct device *dev, void *data)
|
|
|
+{
|
|
|
+ struct region_search_spa *ctx = data;
|
|
|
+ struct nd_region *nd_region;
|
|
|
+ resource_size_t ndr_end;
|
|
|
+
|
|
|
+ if (!is_region_device(dev))
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ nd_region = to_nd_region(dev);
|
|
|
+ ndr_end = nd_region->ndr_start + nd_region->ndr_size;
|
|
|
+
|
|
|
+ if (ctx->addr >= nd_region->ndr_start && ctx->addr < ndr_end) {
|
|
|
+ ctx->region = nd_region;
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int nfit_test_search_spa(struct nvdimm_bus *bus,
|
|
|
+ struct nd_cmd_translate_spa *spa)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ struct nd_region *nd_region = NULL;
|
|
|
+ struct nvdimm *nvdimm = NULL;
|
|
|
+ struct nd_mapping *nd_mapping = NULL;
|
|
|
+ struct region_search_spa ctx = {
|
|
|
+ .addr = spa->spa,
|
|
|
+ .region = NULL,
|
|
|
+ };
|
|
|
+ u64 dpa;
|
|
|
+
|
|
|
+ ret = device_for_each_child(&bus->dev, &ctx,
|
|
|
+ nfit_test_search_region_spa);
|
|
|
+
|
|
|
+ if (!ret)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ nd_region = ctx.region;
|
|
|
+
|
|
|
+ dpa = ctx.addr - nd_region->ndr_start;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * last dimm is selected for test
|
|
|
+ */
|
|
|
+ nd_mapping = &nd_region->mapping[nd_region->ndr_mappings - 1];
|
|
|
+ nvdimm = nd_mapping->nvdimm;
|
|
|
+
|
|
|
+ spa->devices[0].nfit_device_handle = handle[nvdimm->id];
|
|
|
+ spa->num_nvdimms = 1;
|
|
|
+ spa->devices[0].dpa = dpa;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int nfit_test_cmd_translate_spa(struct nvdimm_bus *bus,
|
|
|
+ struct nd_cmd_translate_spa *spa, unsigned int buf_len)
|
|
|
+{
|
|
|
+ if (buf_len < spa->translate_length)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (nfit_test_search_spa(bus, spa) < 0 || !spa->num_nvdimms)
|
|
|
+ spa->status = 2;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int nfit_test_cmd_smart(struct nd_cmd_smart *smart, unsigned int buf_len)
|
|
|
{
|
|
|
static const struct nd_smart_payload smart_data = {
|
|
@@ -449,6 +527,26 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc,
|
|
|
}
|
|
|
} else {
|
|
|
struct ars_state *ars_state = &t->ars_state;
|
|
|
+ struct nd_cmd_pkg *call_pkg = buf;
|
|
|
+
|
|
|
+ if (!nd_desc)
|
|
|
+ return -ENOTTY;
|
|
|
+
|
|
|
+ if (cmd == ND_CMD_CALL) {
|
|
|
+ func = call_pkg->nd_command;
|
|
|
+
|
|
|
+ buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out;
|
|
|
+ buf = (void *) call_pkg->nd_payload;
|
|
|
+
|
|
|
+ switch (func) {
|
|
|
+ case NFIT_CMD_TRANSLATE_SPA:
|
|
|
+ rc = nfit_test_cmd_translate_spa(
|
|
|
+ acpi_desc->nvdimm_bus, buf, buf_len);
|
|
|
+ return rc;
|
|
|
+ default:
|
|
|
+ return -ENOTTY;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
if (!nd_desc || !test_bit(cmd, &nd_desc->cmd_mask))
|
|
|
return -ENOTTY;
|
|
@@ -1430,7 +1528,9 @@ static void nfit_test0_setup(struct nfit_test *t)
|
|
|
set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en);
|
|
|
set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en);
|
|
|
set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en);
|
|
|
+ set_bit(ND_CMD_CALL, &acpi_desc->bus_cmd_force_en);
|
|
|
set_bit(ND_CMD_SMART_THRESHOLD, &acpi_desc->dimm_cmd_force_en);
|
|
|
+ set_bit(NFIT_CMD_TRANSLATE_SPA, &acpi_desc->bus_nfit_cmd_force_en);
|
|
|
}
|
|
|
|
|
|
static void nfit_test1_setup(struct nfit_test *t)
|
|
@@ -1613,10 +1713,12 @@ static int nfit_ctl_test(struct device *dev)
|
|
|
.cmd_mask = 1UL << ND_CMD_ARS_CAP
|
|
|
| 1UL << ND_CMD_ARS_START
|
|
|
| 1UL << ND_CMD_ARS_STATUS
|
|
|
- | 1UL << ND_CMD_CLEAR_ERROR,
|
|
|
+ | 1UL << ND_CMD_CLEAR_ERROR
|
|
|
+ | 1UL << ND_CMD_CALL,
|
|
|
.module = THIS_MODULE,
|
|
|
.provider_name = "ACPI.NFIT",
|
|
|
.ndctl = acpi_nfit_ctl,
|
|
|
+ .bus_dsm_mask = 1UL << NFIT_CMD_TRANSLATE_SPA,
|
|
|
},
|
|
|
.dev = &adev->dev,
|
|
|
};
|