Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

libnvdimm, nfit: initial libnvdimm infrastructure and NFIT support

A struct nvdimm_bus is the anchor device for registering nvdimm
resources and interfaces, for example, a character control device,
nvdimm devices, and I/O region devices. The ACPI NFIT (NVDIMM Firmware
Interface Table) is one possible platform description for such
non-volatile memory resources in a system. The nfit.ko driver attaches
to the "ACPI0012" device that indicates the presence of the NFIT and
parses the table to register a struct nvdimm_bus instance.

Cc: <linux-acpi@vger.kernel.org>
Cc: Lv Zheng <lv.zheng@intel.com>
Cc: Robert Moore <robert.moore@intel.com>
Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Jeff Moyer <jmoyer@redhat.com>
Acked-by: Christoph Hellwig <hch@lst.de>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Toshi Kani <toshi.kani@hp.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>

+733
+2
drivers/Kconfig
··· 182 182 183 183 source "drivers/android/Kconfig" 184 184 185 + source "drivers/nvdimm/Kconfig" 186 + 185 187 endmenu
+1
drivers/Makefile
··· 64 64 65 65 obj-$(CONFIG_PARPORT) += parport/ 66 66 obj-y += base/ block/ misc/ mfd/ nfc/ 67 + obj-$(CONFIG_LIBNVDIMM) += nvdimm/ 67 68 obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/ 68 69 obj-$(CONFIG_NUBUS) += nubus/ 69 70 obj-y += macintosh/
+14
drivers/acpi/Kconfig
··· 383 383 384 384 If you are unsure what to do, do not enable this option. 385 385 386 + config ACPI_NFIT 387 + tristate "ACPI NVDIMM Firmware Interface Table (NFIT)" 388 + depends on PHYS_ADDR_T_64BIT 389 + depends on BLK_DEV 390 + select LIBNVDIMM 391 + help 392 + Infrastructure to probe ACPI 6 compliant platforms for 393 + NVDIMMs (NFIT) and register a libnvdimm device tree. In 394 + addition to storage devices this also enables libnvdimm to pass 395 + ACPI._DSM messages for platform/dimm configuration. 396 + 397 + To compile this driver as a module, choose M here: 398 + the module will be called nfit. 399 + 386 400 source "drivers/acpi/apei/Kconfig" 387 401 388 402 config ACPI_EXTLOG
+1
drivers/acpi/Makefile
··· 71 71 obj-$(CONFIG_ACPI_PROCESSOR) += processor.o 72 72 obj-y += container.o 73 73 obj-$(CONFIG_ACPI_THERMAL) += thermal.o 74 + obj-$(CONFIG_ACPI_NFIT) += nfit.o 74 75 obj-y += acpi_memhotplug.o 75 76 obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o 76 77 obj-$(CONFIG_ACPI_BATTERY) += battery.o
+481
drivers/acpi/nfit.c
··· 1 + /* 2 + * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of version 2 of the GNU General Public License as 6 + * published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope that it will be useful, but 9 + * WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 + * General Public License for more details. 12 + */ 13 + #include <linux/list_sort.h> 14 + #include <linux/libnvdimm.h> 15 + #include <linux/module.h> 16 + #include <linux/list.h> 17 + #include <linux/acpi.h> 18 + #include "nfit.h" 19 + 20 + static u8 nfit_uuid[NFIT_UUID_MAX][16]; 21 + 22 + static const u8 *to_nfit_uuid(enum nfit_uuids id) 23 + { 24 + return nfit_uuid[id]; 25 + } 26 + 27 + static int acpi_nfit_ctl(struct nvdimm_bus_descriptor *nd_desc, 28 + struct nvdimm *nvdimm, unsigned int cmd, void *buf, 29 + unsigned int buf_len) 30 + { 31 + return -ENOTTY; 32 + } 33 + 34 + static const char *spa_type_name(u16 type) 35 + { 36 + static const char *to_name[] = { 37 + [NFIT_SPA_VOLATILE] = "volatile", 38 + [NFIT_SPA_PM] = "pmem", 39 + [NFIT_SPA_DCR] = "dimm-control-region", 40 + [NFIT_SPA_BDW] = "block-data-window", 41 + [NFIT_SPA_VDISK] = "volatile-disk", 42 + [NFIT_SPA_VCD] = "volatile-cd", 43 + [NFIT_SPA_PDISK] = "persistent-disk", 44 + [NFIT_SPA_PCD] = "persistent-cd", 45 + 46 + }; 47 + 48 + if (type > NFIT_SPA_PCD) 49 + return "unknown"; 50 + 51 + return to_name[type]; 52 + } 53 + 54 + static int nfit_spa_type(struct acpi_nfit_system_address *spa) 55 + { 56 + int i; 57 + 58 + for (i = 0; i < NFIT_UUID_MAX; i++) 59 + if (memcmp(to_nfit_uuid(i), spa->range_guid, 16) == 0) 60 + return i; 61 + return -1; 62 + } 63 + 64 + static bool add_spa(struct acpi_nfit_desc *acpi_desc, 65 + struct acpi_nfit_system_address *spa) 66 + { 67 + struct device *dev = acpi_desc->dev; 68 + struct nfit_spa *nfit_spa = devm_kzalloc(dev, sizeof(*nfit_spa), 69 + GFP_KERNEL); 70 + 71 + if (!nfit_spa) 72 + return false; 73 + INIT_LIST_HEAD(&nfit_spa->list); 74 + nfit_spa->spa = spa; 75 + list_add_tail(&nfit_spa->list, &acpi_desc->spas); 76 + dev_dbg(dev, "%s: spa index: %d type: %s\n", __func__, 77 + spa->range_index, 78 + spa_type_name(nfit_spa_type(spa))); 79 + return true; 80 + } 81 + 82 + static bool add_memdev(struct acpi_nfit_desc *acpi_desc, 83 + struct acpi_nfit_memory_map *memdev) 84 + { 85 + struct device *dev = acpi_desc->dev; 86 + struct nfit_memdev *nfit_memdev = devm_kzalloc(dev, 87 + sizeof(*nfit_memdev), GFP_KERNEL); 88 + 89 + if (!nfit_memdev) 90 + return false; 91 + INIT_LIST_HEAD(&nfit_memdev->list); 92 + nfit_memdev->memdev = memdev; 93 + list_add_tail(&nfit_memdev->list, &acpi_desc->memdevs); 94 + dev_dbg(dev, "%s: memdev handle: %#x spa: %d dcr: %d\n", 95 + __func__, memdev->device_handle, memdev->range_index, 96 + memdev->region_index); 97 + return true; 98 + } 99 + 100 + static bool add_dcr(struct acpi_nfit_desc *acpi_desc, 101 + struct acpi_nfit_control_region *dcr) 102 + { 103 + struct device *dev = acpi_desc->dev; 104 + struct nfit_dcr *nfit_dcr = devm_kzalloc(dev, sizeof(*nfit_dcr), 105 + GFP_KERNEL); 106 + 107 + if (!nfit_dcr) 108 + return false; 109 + INIT_LIST_HEAD(&nfit_dcr->list); 110 + nfit_dcr->dcr = dcr; 111 + list_add_tail(&nfit_dcr->list, &acpi_desc->dcrs); 112 + dev_dbg(dev, "%s: dcr index: %d windows: %d\n", __func__, 113 + dcr->region_index, dcr->windows); 114 + return true; 115 + } 116 + 117 + static bool add_bdw(struct acpi_nfit_desc *acpi_desc, 118 + struct acpi_nfit_data_region *bdw) 119 + { 120 + struct device *dev = acpi_desc->dev; 121 + struct nfit_bdw *nfit_bdw = devm_kzalloc(dev, sizeof(*nfit_bdw), 122 + GFP_KERNEL); 123 + 124 + if (!nfit_bdw) 125 + return false; 126 + INIT_LIST_HEAD(&nfit_bdw->list); 127 + nfit_bdw->bdw = bdw; 128 + list_add_tail(&nfit_bdw->list, &acpi_desc->bdws); 129 + dev_dbg(dev, "%s: bdw dcr: %d windows: %d\n", __func__, 130 + bdw->region_index, bdw->windows); 131 + return true; 132 + } 133 + 134 + static void *add_table(struct acpi_nfit_desc *acpi_desc, void *table, 135 + const void *end) 136 + { 137 + struct device *dev = acpi_desc->dev; 138 + struct acpi_nfit_header *hdr; 139 + void *err = ERR_PTR(-ENOMEM); 140 + 141 + if (table >= end) 142 + return NULL; 143 + 144 + hdr = table; 145 + switch (hdr->type) { 146 + case ACPI_NFIT_TYPE_SYSTEM_ADDRESS: 147 + if (!add_spa(acpi_desc, table)) 148 + return err; 149 + break; 150 + case ACPI_NFIT_TYPE_MEMORY_MAP: 151 + if (!add_memdev(acpi_desc, table)) 152 + return err; 153 + break; 154 + case ACPI_NFIT_TYPE_CONTROL_REGION: 155 + if (!add_dcr(acpi_desc, table)) 156 + return err; 157 + break; 158 + case ACPI_NFIT_TYPE_DATA_REGION: 159 + if (!add_bdw(acpi_desc, table)) 160 + return err; 161 + break; 162 + /* TODO */ 163 + case ACPI_NFIT_TYPE_INTERLEAVE: 164 + dev_dbg(dev, "%s: idt\n", __func__); 165 + break; 166 + case ACPI_NFIT_TYPE_FLUSH_ADDRESS: 167 + dev_dbg(dev, "%s: flush\n", __func__); 168 + break; 169 + case ACPI_NFIT_TYPE_SMBIOS: 170 + dev_dbg(dev, "%s: smbios\n", __func__); 171 + break; 172 + default: 173 + dev_err(dev, "unknown table '%d' parsing nfit\n", hdr->type); 174 + break; 175 + } 176 + 177 + return table + hdr->length; 178 + } 179 + 180 + static void nfit_mem_find_spa_bdw(struct acpi_nfit_desc *acpi_desc, 181 + struct nfit_mem *nfit_mem) 182 + { 183 + u32 device_handle = __to_nfit_memdev(nfit_mem)->device_handle; 184 + u16 dcr = nfit_mem->dcr->region_index; 185 + struct nfit_spa *nfit_spa; 186 + 187 + list_for_each_entry(nfit_spa, &acpi_desc->spas, list) { 188 + u16 range_index = nfit_spa->spa->range_index; 189 + int type = nfit_spa_type(nfit_spa->spa); 190 + struct nfit_memdev *nfit_memdev; 191 + 192 + if (type != NFIT_SPA_BDW) 193 + continue; 194 + 195 + list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { 196 + if (nfit_memdev->memdev->range_index != range_index) 197 + continue; 198 + if (nfit_memdev->memdev->device_handle != device_handle) 199 + continue; 200 + if (nfit_memdev->memdev->region_index != dcr) 201 + continue; 202 + 203 + nfit_mem->spa_bdw = nfit_spa->spa; 204 + return; 205 + } 206 + } 207 + 208 + dev_dbg(acpi_desc->dev, "SPA-BDW not found for SPA-DCR %d\n", 209 + nfit_mem->spa_dcr->range_index); 210 + nfit_mem->bdw = NULL; 211 + } 212 + 213 + static int nfit_mem_add(struct acpi_nfit_desc *acpi_desc, 214 + struct nfit_mem *nfit_mem, struct acpi_nfit_system_address *spa) 215 + { 216 + u16 dcr = __to_nfit_memdev(nfit_mem)->region_index; 217 + struct nfit_dcr *nfit_dcr; 218 + struct nfit_bdw *nfit_bdw; 219 + 220 + list_for_each_entry(nfit_dcr, &acpi_desc->dcrs, list) { 221 + if (nfit_dcr->dcr->region_index != dcr) 222 + continue; 223 + nfit_mem->dcr = nfit_dcr->dcr; 224 + break; 225 + } 226 + 227 + if (!nfit_mem->dcr) { 228 + dev_dbg(acpi_desc->dev, "SPA %d missing:%s%s\n", 229 + spa->range_index, __to_nfit_memdev(nfit_mem) 230 + ? "" : " MEMDEV", nfit_mem->dcr ? "" : " DCR"); 231 + return -ENODEV; 232 + } 233 + 234 + /* 235 + * We've found enough to create an nvdimm, optionally 236 + * find an associated BDW 237 + */ 238 + list_add(&nfit_mem->list, &acpi_desc->dimms); 239 + 240 + list_for_each_entry(nfit_bdw, &acpi_desc->bdws, list) { 241 + if (nfit_bdw->bdw->region_index != dcr) 242 + continue; 243 + nfit_mem->bdw = nfit_bdw->bdw; 244 + break; 245 + } 246 + 247 + if (!nfit_mem->bdw) 248 + return 0; 249 + 250 + nfit_mem_find_spa_bdw(acpi_desc, nfit_mem); 251 + return 0; 252 + } 253 + 254 + static int nfit_mem_dcr_init(struct acpi_nfit_desc *acpi_desc, 255 + struct acpi_nfit_system_address *spa) 256 + { 257 + struct nfit_mem *nfit_mem, *found; 258 + struct nfit_memdev *nfit_memdev; 259 + int type = nfit_spa_type(spa); 260 + u16 dcr; 261 + 262 + switch (type) { 263 + case NFIT_SPA_DCR: 264 + case NFIT_SPA_PM: 265 + break; 266 + default: 267 + return 0; 268 + } 269 + 270 + list_for_each_entry(nfit_memdev, &acpi_desc->memdevs, list) { 271 + int rc; 272 + 273 + if (nfit_memdev->memdev->range_index != spa->range_index) 274 + continue; 275 + found = NULL; 276 + dcr = nfit_memdev->memdev->region_index; 277 + list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) 278 + if (__to_nfit_memdev(nfit_mem)->region_index == dcr) { 279 + found = nfit_mem; 280 + break; 281 + } 282 + 283 + if (found) 284 + nfit_mem = found; 285 + else { 286 + nfit_mem = devm_kzalloc(acpi_desc->dev, 287 + sizeof(*nfit_mem), GFP_KERNEL); 288 + if (!nfit_mem) 289 + return -ENOMEM; 290 + INIT_LIST_HEAD(&nfit_mem->list); 291 + } 292 + 293 + if (type == NFIT_SPA_DCR) { 294 + /* multiple dimms may share a SPA when interleaved */ 295 + nfit_mem->spa_dcr = spa; 296 + nfit_mem->memdev_dcr = nfit_memdev->memdev; 297 + } else { 298 + /* 299 + * A single dimm may belong to multiple SPA-PM 300 + * ranges, record at least one in addition to 301 + * any SPA-DCR range. 302 + */ 303 + nfit_mem->memdev_pmem = nfit_memdev->memdev; 304 + } 305 + 306 + if (found) 307 + continue; 308 + 309 + rc = nfit_mem_add(acpi_desc, nfit_mem, spa); 310 + if (rc) 311 + return rc; 312 + } 313 + 314 + return 0; 315 + } 316 + 317 + static int nfit_mem_cmp(void *priv, struct list_head *_a, struct list_head *_b) 318 + { 319 + struct nfit_mem *a = container_of(_a, typeof(*a), list); 320 + struct nfit_mem *b = container_of(_b, typeof(*b), list); 321 + u32 handleA, handleB; 322 + 323 + handleA = __to_nfit_memdev(a)->device_handle; 324 + handleB = __to_nfit_memdev(b)->device_handle; 325 + if (handleA < handleB) 326 + return -1; 327 + else if (handleA > handleB) 328 + return 1; 329 + return 0; 330 + } 331 + 332 + static int nfit_mem_init(struct acpi_nfit_desc *acpi_desc) 333 + { 334 + struct nfit_spa *nfit_spa; 335 + 336 + /* 337 + * For each SPA-DCR or SPA-PMEM address range find its 338 + * corresponding MEMDEV(s). From each MEMDEV find the 339 + * corresponding DCR. Then, if we're operating on a SPA-DCR, 340 + * try to find a SPA-BDW and a corresponding BDW that references 341 + * the DCR. Throw it all into an nfit_mem object. Note, that 342 + * BDWs are optional. 343 + */ 344 + list_for_each_entry(nfit_spa, &acpi_desc->spas, list) { 345 + int rc; 346 + 347 + rc = nfit_mem_dcr_init(acpi_desc, nfit_spa->spa); 348 + if (rc) 349 + return rc; 350 + } 351 + 352 + list_sort(NULL, &acpi_desc->dimms, nfit_mem_cmp); 353 + 354 + return 0; 355 + } 356 + 357 + static int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz) 358 + { 359 + struct device *dev = acpi_desc->dev; 360 + const void *end; 361 + u8 *data; 362 + 363 + INIT_LIST_HEAD(&acpi_desc->spas); 364 + INIT_LIST_HEAD(&acpi_desc->dcrs); 365 + INIT_LIST_HEAD(&acpi_desc->bdws); 366 + INIT_LIST_HEAD(&acpi_desc->memdevs); 367 + INIT_LIST_HEAD(&acpi_desc->dimms); 368 + 369 + data = (u8 *) acpi_desc->nfit; 370 + end = data + sz; 371 + data += sizeof(struct acpi_table_nfit); 372 + while (!IS_ERR_OR_NULL(data)) 373 + data = add_table(acpi_desc, data, end); 374 + 375 + if (IS_ERR(data)) { 376 + dev_dbg(dev, "%s: nfit table parsing error: %ld\n", __func__, 377 + PTR_ERR(data)); 378 + return PTR_ERR(data); 379 + } 380 + 381 + if (nfit_mem_init(acpi_desc) != 0) 382 + return -ENOMEM; 383 + 384 + return 0; 385 + } 386 + 387 + static int acpi_nfit_add(struct acpi_device *adev) 388 + { 389 + struct nvdimm_bus_descriptor *nd_desc; 390 + struct acpi_nfit_desc *acpi_desc; 391 + struct device *dev = &adev->dev; 392 + struct acpi_table_header *tbl; 393 + acpi_status status = AE_OK; 394 + acpi_size sz; 395 + int rc; 396 + 397 + status = acpi_get_table_with_size("NFIT", 0, &tbl, &sz); 398 + if (ACPI_FAILURE(status)) { 399 + dev_err(dev, "failed to find NFIT\n"); 400 + return -ENXIO; 401 + } 402 + 403 + acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL); 404 + if (!acpi_desc) 405 + return -ENOMEM; 406 + 407 + dev_set_drvdata(dev, acpi_desc); 408 + acpi_desc->dev = dev; 409 + acpi_desc->nfit = (struct acpi_table_nfit *) tbl; 410 + nd_desc = &acpi_desc->nd_desc; 411 + nd_desc->provider_name = "ACPI.NFIT"; 412 + nd_desc->ndctl = acpi_nfit_ctl; 413 + 414 + acpi_desc->nvdimm_bus = nvdimm_bus_register(dev, nd_desc); 415 + if (!acpi_desc->nvdimm_bus) 416 + return -ENXIO; 417 + 418 + rc = acpi_nfit_init(acpi_desc, sz); 419 + if (rc) { 420 + nvdimm_bus_unregister(acpi_desc->nvdimm_bus); 421 + return rc; 422 + } 423 + return 0; 424 + } 425 + 426 + static int acpi_nfit_remove(struct acpi_device *adev) 427 + { 428 + struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(&adev->dev); 429 + 430 + nvdimm_bus_unregister(acpi_desc->nvdimm_bus); 431 + return 0; 432 + } 433 + 434 + static const struct acpi_device_id acpi_nfit_ids[] = { 435 + { "ACPI0012", 0 }, 436 + { "", 0 }, 437 + }; 438 + MODULE_DEVICE_TABLE(acpi, acpi_nfit_ids); 439 + 440 + static struct acpi_driver acpi_nfit_driver = { 441 + .name = KBUILD_MODNAME, 442 + .ids = acpi_nfit_ids, 443 + .ops = { 444 + .add = acpi_nfit_add, 445 + .remove = acpi_nfit_remove, 446 + }, 447 + }; 448 + 449 + static __init int nfit_init(void) 450 + { 451 + BUILD_BUG_ON(sizeof(struct acpi_table_nfit) != 40); 452 + BUILD_BUG_ON(sizeof(struct acpi_nfit_system_address) != 56); 453 + BUILD_BUG_ON(sizeof(struct acpi_nfit_memory_map) != 48); 454 + BUILD_BUG_ON(sizeof(struct acpi_nfit_interleave) != 20); 455 + BUILD_BUG_ON(sizeof(struct acpi_nfit_smbios) != 9); 456 + BUILD_BUG_ON(sizeof(struct acpi_nfit_control_region) != 80); 457 + BUILD_BUG_ON(sizeof(struct acpi_nfit_data_region) != 40); 458 + 459 + acpi_str_to_uuid(UUID_VOLATILE_MEMORY, nfit_uuid[NFIT_SPA_VOLATILE]); 460 + acpi_str_to_uuid(UUID_PERSISTENT_MEMORY, nfit_uuid[NFIT_SPA_PM]); 461 + acpi_str_to_uuid(UUID_CONTROL_REGION, nfit_uuid[NFIT_SPA_DCR]); 462 + acpi_str_to_uuid(UUID_DATA_REGION, nfit_uuid[NFIT_SPA_BDW]); 463 + acpi_str_to_uuid(UUID_VOLATILE_VIRTUAL_DISK, nfit_uuid[NFIT_SPA_VDISK]); 464 + acpi_str_to_uuid(UUID_VOLATILE_VIRTUAL_CD, nfit_uuid[NFIT_SPA_VCD]); 465 + acpi_str_to_uuid(UUID_PERSISTENT_VIRTUAL_DISK, nfit_uuid[NFIT_SPA_PDISK]); 466 + acpi_str_to_uuid(UUID_PERSISTENT_VIRTUAL_CD, nfit_uuid[NFIT_SPA_PCD]); 467 + acpi_str_to_uuid(UUID_NFIT_BUS, nfit_uuid[NFIT_DEV_BUS]); 468 + acpi_str_to_uuid(UUID_NFIT_DIMM, nfit_uuid[NFIT_DEV_DIMM]); 469 + 470 + return acpi_bus_register_driver(&acpi_nfit_driver); 471 + } 472 + 473 + static __exit void nfit_exit(void) 474 + { 475 + acpi_bus_unregister_driver(&acpi_nfit_driver); 476 + } 477 + 478 + module_init(nfit_init); 479 + module_exit(nfit_exit); 480 + MODULE_LICENSE("GPL v2"); 481 + MODULE_AUTHOR("Intel Corporation");
+90
drivers/acpi/nfit.h
··· 1 + /* 2 + * NVDIMM Firmware Interface Table - NFIT 3 + * 4 + * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of version 2 of the GNU General Public License as 8 + * published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope that it will be useful, but 11 + * WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 + * General Public License for more details. 14 + */ 15 + #ifndef __NFIT_H__ 16 + #define __NFIT_H__ 17 + #include <linux/libnvdimm.h> 18 + #include <linux/types.h> 19 + #include <linux/uuid.h> 20 + #include <linux/acpi.h> 21 + #include <acpi/acuuid.h> 22 + 23 + #define UUID_NFIT_BUS "2f10e7a4-9e91-11e4-89d3-123b93f75cba" 24 + #define UUID_NFIT_DIMM "4309ac30-0d11-11e4-9191-0800200c9a66" 25 + 26 + enum nfit_uuids { 27 + NFIT_SPA_VOLATILE, 28 + NFIT_SPA_PM, 29 + NFIT_SPA_DCR, 30 + NFIT_SPA_BDW, 31 + NFIT_SPA_VDISK, 32 + NFIT_SPA_VCD, 33 + NFIT_SPA_PDISK, 34 + NFIT_SPA_PCD, 35 + NFIT_DEV_BUS, 36 + NFIT_DEV_DIMM, 37 + NFIT_UUID_MAX, 38 + }; 39 + 40 + struct nfit_spa { 41 + struct acpi_nfit_system_address *spa; 42 + struct list_head list; 43 + }; 44 + 45 + struct nfit_dcr { 46 + struct acpi_nfit_control_region *dcr; 47 + struct list_head list; 48 + }; 49 + 50 + struct nfit_bdw { 51 + struct acpi_nfit_data_region *bdw; 52 + struct list_head list; 53 + }; 54 + 55 + struct nfit_memdev { 56 + struct acpi_nfit_memory_map *memdev; 57 + struct list_head list; 58 + }; 59 + 60 + /* assembled tables for a given dimm/memory-device */ 61 + struct nfit_mem { 62 + struct acpi_nfit_memory_map *memdev_dcr; 63 + struct acpi_nfit_memory_map *memdev_pmem; 64 + struct acpi_nfit_control_region *dcr; 65 + struct acpi_nfit_data_region *bdw; 66 + struct acpi_nfit_system_address *spa_dcr; 67 + struct acpi_nfit_system_address *spa_bdw; 68 + struct list_head list; 69 + }; 70 + 71 + struct acpi_nfit_desc { 72 + struct nvdimm_bus_descriptor nd_desc; 73 + struct acpi_table_nfit *nfit; 74 + struct list_head memdevs; 75 + struct list_head dimms; 76 + struct list_head spas; 77 + struct list_head dcrs; 78 + struct list_head bdws; 79 + struct nvdimm_bus *nvdimm_bus; 80 + struct device *dev; 81 + }; 82 + 83 + static inline struct acpi_nfit_memory_map *__to_nfit_memdev( 84 + struct nfit_mem *nfit_mem) 85 + { 86 + if (nfit_mem->memdev_dcr) 87 + return nfit_mem->memdev_dcr; 88 + return nfit_mem->memdev_pmem; 89 + } 90 + #endif /* __NFIT_H__ */
+15
drivers/nvdimm/Kconfig
··· 1 + config LIBNVDIMM 2 + tristate "NVDIMM (Non-Volatile Memory Device) Support" 3 + depends on PHYS_ADDR_T_64BIT 4 + depends on BLK_DEV 5 + help 6 + Generic support for non-volatile memory devices including 7 + ACPI-6-NFIT defined resources. On platforms that define an 8 + NFIT, or otherwise can discover NVDIMM resources, a libnvdimm 9 + bus is registered to advertise PMEM (persistent memory) 10 + namespaces (/dev/pmemX) and BLK (sliding mmio window(s)) 11 + namespaces (/dev/ndX). A PMEM namespace refers to a memory 12 + resource that may span multiple DIMMs and support DAX (see 13 + CONFIG_DAX). A BLK namespace refers to an NVDIMM control 14 + region which exposes an mmio register set for windowed 15 + access mode to non-volatile memory.
+3
drivers/nvdimm/Makefile
··· 1 + obj-$(CONFIG_LIBNVDIMM) += libnvdimm.o 2 + 3 + libnvdimm-y := core.o
+69
drivers/nvdimm/core.c
··· 1 + /* 2 + * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of version 2 of the GNU General Public License as 6 + * published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope that it will be useful, but 9 + * WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 + * General Public License for more details. 12 + */ 13 + #include <linux/libnvdimm.h> 14 + #include <linux/export.h> 15 + #include <linux/module.h> 16 + #include <linux/device.h> 17 + #include <linux/slab.h> 18 + #include "nd-core.h" 19 + 20 + static DEFINE_IDA(nd_ida); 21 + 22 + static void nvdimm_bus_release(struct device *dev) 23 + { 24 + struct nvdimm_bus *nvdimm_bus; 25 + 26 + nvdimm_bus = container_of(dev, struct nvdimm_bus, dev); 27 + ida_simple_remove(&nd_ida, nvdimm_bus->id); 28 + kfree(nvdimm_bus); 29 + } 30 + 31 + struct nvdimm_bus *nvdimm_bus_register(struct device *parent, 32 + struct nvdimm_bus_descriptor *nd_desc) 33 + { 34 + struct nvdimm_bus *nvdimm_bus; 35 + int rc; 36 + 37 + nvdimm_bus = kzalloc(sizeof(*nvdimm_bus), GFP_KERNEL); 38 + if (!nvdimm_bus) 39 + return NULL; 40 + nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL); 41 + if (nvdimm_bus->id < 0) { 42 + kfree(nvdimm_bus); 43 + return NULL; 44 + } 45 + nvdimm_bus->nd_desc = nd_desc; 46 + nvdimm_bus->dev.parent = parent; 47 + nvdimm_bus->dev.release = nvdimm_bus_release; 48 + dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id); 49 + rc = device_register(&nvdimm_bus->dev); 50 + if (rc) { 51 + dev_dbg(&nvdimm_bus->dev, "registration failed: %d\n", rc); 52 + put_device(&nvdimm_bus->dev); 53 + return NULL; 54 + } 55 + 56 + return nvdimm_bus; 57 + } 58 + EXPORT_SYMBOL_GPL(nvdimm_bus_register); 59 + 60 + void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus) 61 + { 62 + if (!nvdimm_bus) 63 + return; 64 + device_unregister(&nvdimm_bus->dev); 65 + } 66 + EXPORT_SYMBOL_GPL(nvdimm_bus_unregister); 67 + 68 + MODULE_LICENSE("GPL v2"); 69 + MODULE_AUTHOR("Intel Corporation");
+23
drivers/nvdimm/nd-core.h
··· 1 + /* 2 + * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of version 2 of the GNU General Public License as 6 + * published by the Free Software Foundation. 7 + * 8 + * This program is distributed in the hope that it will be useful, but 9 + * WITHOUT ANY WARRANTY; without even the implied warranty of 10 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 11 + * General Public License for more details. 12 + */ 13 + #ifndef __ND_CORE_H__ 14 + #define __ND_CORE_H__ 15 + #include <linux/libnvdimm.h> 16 + #include <linux/device.h> 17 + 18 + struct nvdimm_bus { 19 + struct nvdimm_bus_descriptor *nd_desc; 20 + struct device dev; 21 + int id; 22 + }; 23 + #endif /* __ND_CORE_H__ */
+34
include/linux/libnvdimm.h
··· 1 + /* 2 + * libnvdimm - Non-volatile-memory Devices Subsystem 3 + * 4 + * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of version 2 of the GNU General Public License as 8 + * published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope that it will be useful, but 11 + * WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 + * General Public License for more details. 14 + */ 15 + #ifndef __LIBNVDIMM_H__ 16 + #define __LIBNVDIMM_H__ 17 + struct nvdimm; 18 + struct nvdimm_bus_descriptor; 19 + typedef int (*ndctl_fn)(struct nvdimm_bus_descriptor *nd_desc, 20 + struct nvdimm *nvdimm, unsigned int cmd, void *buf, 21 + unsigned int buf_len); 22 + 23 + struct nvdimm_bus_descriptor { 24 + unsigned long dsm_mask; 25 + char *provider_name; 26 + ndctl_fn ndctl; 27 + }; 28 + 29 + struct device; 30 + struct nvdimm_bus; 31 + struct nvdimm_bus *nvdimm_bus_register(struct device *parent, 32 + struct nvdimm_bus_descriptor *nfit_desc); 33 + void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus); 34 + #endif /* __LIBNVDIMM_H__ */