This patch registers an acpi driver and adds code to find the fit
pointer in acpi memory. It then hands the fit pointer off to the
fit parsing code and creates the pmem misc device in /dev.
Signed-off-by: Jeff Moyer <jmoyer(a)redhat.com>
---
drivers/block/nvdimm_acpi.c | 126 ++++++++++++++++++++++++++++++++++++++++++++
drivers/block/nvdimm_core.c | 24 +++++----
include/linux/nvdimm_acpi.h | 2 +
include/linux/nvdimm_core.h | 3 ++
4 files changed, 146 insertions(+), 9 deletions(-)
diff --git a/drivers/block/nvdimm_acpi.c b/drivers/block/nvdimm_acpi.c
index 25f021f..b92a5ab 100644
--- a/drivers/block/nvdimm_acpi.c
+++ b/drivers/block/nvdimm_acpi.c
@@ -14,10 +14,35 @@
#include <linux/sort.h>
#include <linux/nvdimm_core.h>
#include <linux/nvdimm_acpi.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
/* TODO: Change all sizeof(struct type) to sizeof(*variable)*/
/* TODO: Change all casting such as (__u64 *) to typeof(variable_name)*/
+#define ACPI_MEMORY_DEVICE_HID "ACPI0010"
+#undef PREFIX
+#define PREFIX "ACPI:nvdimm:"
+ACPI_MODULE_NAME("nvdimm-acpi");
+
+static const struct acpi_device_id nvdimm_device_ids[] = {
+ {ACPI_MEMORY_DEVICE_HID, 0},
+ {"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, nvdimm_device_ids);
+
+static int nvdimm_acpi_device_add(struct acpi_device *device);
+static int nvdimm_acpi_device_remove(struct acpi_device *device);
+
+static struct acpi_driver nvdimm_acpi_driver = {
+ .name = "NVDIMM ACPI Driver",
+ .class = "persistent memory",
+ .ids = nvdimm_device_ids,
+ .ops = {
+ .add = nvdimm_acpi_device_add,
+ .remove = nvdimm_acpi_device_remove,
+ },
+};
/**
* spa_to_rdpa() - Convert System Physical Address to Device Region Physical
@@ -774,3 +799,104 @@ void free_fit_table(struct fit_header *fit_head)
kfree(fit_head->fit);
kfree(fit_head);
}
+
+static acpi_status
+nvdimm_acpi_get_resource(struct acpi_resource *resource, void *context)
+{
+ struct acpi_resource_address64 addr64;
+ acpi_physical_address fitpp_phys, fitp_phys;
+ void *fitpp_virt, *fitp_virt;
+ acpi_status status;
+ unsigned long long sta;
+ struct pmem_dev *pmdev = context;
+
+ if (!pmdev)
+ return AE_OK;
+
+ status = acpi_resource_to_address64(resource, &addr64);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ status = acpi_evaluate_integer(pmdev->acpi_device->handle,
+ "_STA", NULL, &sta);
+ if (ACPI_FAILURE(status))
+ return AE_OK;
+
+ /* XXX
+ * We should evaluate the bits in the result from _STA to
+ * determine the state of the dimm.
+ */
+
+ fitpp_phys = addr64.minimum;
+ fitpp_virt = acpi_os_map_memory(fitpp_phys, PAGE_SIZE);
+ if (!fitpp_virt)
+ return AE_BAD_ADDRESS;
+ fitp_phys = *(u64 *)fitpp_virt;
+ fitp_virt = acpi_os_map_memory(fitp_phys, PAGE_SIZE);
+ if (!fitp_virt) {
+ acpi_os_unmap_memory(fitpp_virt, PAGE_SIZE);
+ return AE_BAD_ADDRESS;
+ }
+
+ pmdev->fit_head = create_fit_table(fitp_virt);
+
+ acpi_os_unmap_memory(fitp_virt, PAGE_SIZE);
+ acpi_os_unmap_memory(fitpp_virt, PAGE_SIZE);
+
+ return AE_OK;
+}
+
+static int nvdimm_acpi_device_add(struct acpi_device *device)
+{
+ acpi_status status;
+ struct pmem_dev *pmdev;
+
+ if (!device)
+ return -EINVAL;
+
+ pmdev = pmem_dev_init();
+ if (IS_ERR(pmdev))
+ return PTR_ERR(pmdev);
+
+ device->driver_data = pmdev;
+ pmdev->acpi_device = device;
+ status = acpi_walk_resources(device->handle, METHOD_NAME__CRS,
+ nvdimm_acpi_get_resource, pmdev);
+ if (ACPI_FAILURE(status)) {
+ kref_put(&pmdev->kref, pmem_free_dev);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nvdimm_acpi_device_remove(struct acpi_device *device)
+{
+ struct pmem_dev *pmdev;
+
+ if (!device)
+ return -EINVAL;
+
+ pmdev = device->driver_data;
+ kref_put(&pmdev->kref, pmem_free_dev);
+ return 0;
+}
+
+int __init nvdimm_acpi_init(void)
+{
+ int result;
+
+ result = acpi_bus_register_driver(&nvdimm_acpi_driver);
+ if (result < 0) {
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Error registering driver\n"));
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+void __exit nvdimm_acpi_exit(void)
+{
+ acpi_bus_unregister_driver(&nvdimm_acpi_driver);
+}
diff --git a/drivers/block/nvdimm_core.c b/drivers/block/nvdimm_core.c
index 4d2eabd..500baa4 100644
--- a/drivers/block/nvdimm_core.c
+++ b/drivers/block/nvdimm_core.c
@@ -616,7 +616,7 @@ static void pmem_free_volumes(struct pmem_dev *dev)
}
-static void pmem_free_dev(struct kref *kref)
+void pmem_free_dev(struct kref *kref)
{
struct pmem_dev *dev = container_of(kref, struct pmem_dev, kref);
@@ -641,14 +641,14 @@ static const struct file_operations pmem_dev_fops = {
.compat_ioctl = pmem_dev_ioctl,
};
-static int pmem_dev_init(void)
+struct pmem_dev *pmem_dev_init(void)
{
int ret;
+ struct pmem_dev *dev;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
-
if (!dev)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&dev->volumes);
INIT_LIST_HEAD(&dev->nvdimms);
@@ -663,16 +663,14 @@ static int pmem_dev_init(void)
dev->miscdev.fops = &pmem_dev_fops;
ret = misc_register(&dev->miscdev);
-
if (ret)
goto out;
kref_init(&dev->kref);
- return 0;
+ return dev;
out:
- kfree(dev);
- return ret;
+ return ERR_PTR(ret);
}
/**
@@ -728,11 +726,17 @@ static int __init nvdimm_init(void)
pmem_major = ret;
if (debug) {
- ret = pmem_dev_init();
+ dev = pmem_dev_init();
if (ret) {
NVDIMM_ERR("Failed to create pmem_dev");
goto unregister_blkdev;
}
+ } else {
+ ret = nvdimm_acpi_init();
+ if (ret) {
+ NVDIMM_ERR("Failed to initialize acpi subsystem");
+ goto unregister_blkdev;
+ }
}
return ret;
@@ -748,6 +752,8 @@ static void __exit nvdimm_exit(void)
{
if (debug)
kref_put(&dev->kref, pmem_free_dev);
+ else
+ nvdimm_acpi_exit();
unregister_blkdev(pmem_major, "pmem");
}
diff --git a/include/linux/nvdimm_acpi.h b/include/linux/nvdimm_acpi.h
index 91d2fc4..9bf0783 100644
--- a/include/linux/nvdimm_acpi.h
+++ b/include/linux/nvdimm_acpi.h
@@ -154,6 +154,8 @@ struct fit_header {
* ACPI Related Functions
*****************************************************************************/
+extern void __exit nvdimm_acpi_exit(void);
+extern int __init nvdimm_acpi_init(void);
struct fit_header *create_fit_table(void *nvdimm_fit_ptr);
void free_fit_table(struct fit_header *fit_head);
struct memdev_spa_rng_tbl *__get_memdev_spa_tbl(struct fit_header *fit_head,
diff --git a/include/linux/nvdimm_core.h b/include/linux/nvdimm_core.h
index 714466d..5b3c473 100644
--- a/include/linux/nvdimm_core.h
+++ b/include/linux/nvdimm_core.h
@@ -256,6 +256,7 @@ struct pmem_dev {
struct fit_header *fit_head;
struct miscdevice miscdev;
+ struct acpi_device *acpi_device;
struct kref kref;
};
@@ -295,6 +296,8 @@ struct nvdimm_driver {
const struct nvdimm_ioctl_ops *ioctl_ops;
};
+struct pmem_dev *pmem_dev_init(void);
+void pmem_free_dev(struct kref *kref);
int get_dmi_memdev(struct nvdimm *dimm);
int gen_nvdimm_init(struct pmem_dev *dev);
void gen_nvdimm_exit(struct pmem_dev *dev);
--
1.8.3.1