16db7f8e5SKonstantin Belousov /*- 26db7f8e5SKonstantin Belousov * Copyright (c) 2017 The FreeBSD Foundation 36db7f8e5SKonstantin Belousov * All rights reserved. 4fc4a961aSKonstantin Belousov * Copyright (c) 2018, 2019 Intel Corporation 56db7f8e5SKonstantin Belousov * 66db7f8e5SKonstantin Belousov * This software was developed by Konstantin Belousov <kib@FreeBSD.org> 76db7f8e5SKonstantin Belousov * under sponsorship from the FreeBSD Foundation. 86db7f8e5SKonstantin Belousov * 96db7f8e5SKonstantin Belousov * Redistribution and use in source and binary forms, with or without 106db7f8e5SKonstantin Belousov * modification, are permitted provided that the following conditions 116db7f8e5SKonstantin Belousov * are met: 126db7f8e5SKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 136db7f8e5SKonstantin Belousov * notice, this list of conditions and the following disclaimer. 146db7f8e5SKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 156db7f8e5SKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 166db7f8e5SKonstantin Belousov * documentation and/or other materials provided with the distribution. 176db7f8e5SKonstantin Belousov * 186db7f8e5SKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 196db7f8e5SKonstantin Belousov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 206db7f8e5SKonstantin Belousov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 216db7f8e5SKonstantin Belousov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 226db7f8e5SKonstantin Belousov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 236db7f8e5SKonstantin Belousov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 246db7f8e5SKonstantin Belousov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 256db7f8e5SKonstantin Belousov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 266db7f8e5SKonstantin Belousov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 276db7f8e5SKonstantin Belousov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 286db7f8e5SKonstantin Belousov * SUCH DAMAGE. 296db7f8e5SKonstantin Belousov */ 306db7f8e5SKonstantin Belousov 316db7f8e5SKonstantin Belousov #include <sys/cdefs.h> 326db7f8e5SKonstantin Belousov __FBSDID("$FreeBSD$"); 336db7f8e5SKonstantin Belousov 346db7f8e5SKonstantin Belousov #include "opt_acpi.h" 356db7f8e5SKonstantin Belousov #include "opt_ddb.h" 366db7f8e5SKonstantin Belousov 376db7f8e5SKonstantin Belousov #include <sys/param.h> 386db7f8e5SKonstantin Belousov #include <sys/bio.h> 396db7f8e5SKonstantin Belousov #include <sys/bus.h> 406db7f8e5SKonstantin Belousov #include <sys/kernel.h> 416db7f8e5SKonstantin Belousov #include <sys/lock.h> 426db7f8e5SKonstantin Belousov #include <sys/malloc.h> 436db7f8e5SKonstantin Belousov #include <sys/module.h> 446db7f8e5SKonstantin Belousov #include <sys/uuid.h> 456db7f8e5SKonstantin Belousov #include <contrib/dev/acpica/include/acpi.h> 466db7f8e5SKonstantin Belousov #include <contrib/dev/acpica/include/accommon.h> 476db7f8e5SKonstantin Belousov #include <contrib/dev/acpica/include/acuuid.h> 486db7f8e5SKonstantin Belousov #include <dev/acpica/acpivar.h> 496db7f8e5SKonstantin Belousov #include <dev/nvdimm/nvdimm_var.h> 506db7f8e5SKonstantin Belousov 516db7f8e5SKonstantin Belousov #define _COMPONENT ACPI_OEM 526db7f8e5SKonstantin Belousov ACPI_MODULE_NAME("NVDIMM") 536db7f8e5SKonstantin Belousov 546db7f8e5SKonstantin Belousov static devclass_t nvdimm_devclass; 55fc4a961aSKonstantin Belousov static devclass_t nvdimm_root_devclass; 566db7f8e5SKonstantin Belousov MALLOC_DEFINE(M_NVDIMM, "nvdimm", "NVDIMM driver memory"); 576db7f8e5SKonstantin Belousov 586db7f8e5SKonstantin Belousov struct nvdimm_dev * 596db7f8e5SKonstantin Belousov nvdimm_find_by_handle(nfit_handle_t nv_handle) 606db7f8e5SKonstantin Belousov { 61fc4a961aSKonstantin Belousov struct nvdimm_dev *res; 62fc4a961aSKonstantin Belousov device_t *dimms; 63fc4a961aSKonstantin Belousov int i, error, num_dimms; 646db7f8e5SKonstantin Belousov 656db7f8e5SKonstantin Belousov res = NULL; 66fc4a961aSKonstantin Belousov error = devclass_get_devices(nvdimm_devclass, &dimms, &num_dimms); 67fc4a961aSKonstantin Belousov if (error != 0) 68fc4a961aSKonstantin Belousov return (NULL); 69fc4a961aSKonstantin Belousov for (i = 0; i < num_dimms; i++) { 70fc4a961aSKonstantin Belousov if (nvdimm_root_get_device_handle(dimms[i]) == nv_handle) { 71fc4a961aSKonstantin Belousov res = device_get_softc(dimms[i]); 726db7f8e5SKonstantin Belousov break; 736db7f8e5SKonstantin Belousov } 746db7f8e5SKonstantin Belousov } 75fc4a961aSKonstantin Belousov free(dimms, M_TEMP); 766db7f8e5SKonstantin Belousov return (res); 776db7f8e5SKonstantin Belousov } 786db7f8e5SKonstantin Belousov 796db7f8e5SKonstantin Belousov static int 806db7f8e5SKonstantin Belousov nvdimm_probe(device_t dev) 816db7f8e5SKonstantin Belousov { 826db7f8e5SKonstantin Belousov 836db7f8e5SKonstantin Belousov return (BUS_PROBE_NOWILDCARD); 846db7f8e5SKonstantin Belousov } 856db7f8e5SKonstantin Belousov 866db7f8e5SKonstantin Belousov static int 876db7f8e5SKonstantin Belousov nvdimm_attach(device_t dev) 886db7f8e5SKonstantin Belousov { 896db7f8e5SKonstantin Belousov struct nvdimm_dev *nv; 906db7f8e5SKonstantin Belousov ACPI_TABLE_NFIT *nfitbl; 916db7f8e5SKonstantin Belousov ACPI_HANDLE handle; 926db7f8e5SKonstantin Belousov ACPI_STATUS status; 936db7f8e5SKonstantin Belousov 946db7f8e5SKonstantin Belousov nv = device_get_softc(dev); 95fc4a961aSKonstantin Belousov handle = nvdimm_root_get_acpi_handle(dev); 966db7f8e5SKonstantin Belousov if (handle == NULL) 976db7f8e5SKonstantin Belousov return (EINVAL); 986db7f8e5SKonstantin Belousov nv->nv_dev = dev; 99fc4a961aSKonstantin Belousov nv->nv_handle = nvdimm_root_get_device_handle(dev); 1006db7f8e5SKonstantin Belousov 1016db7f8e5SKonstantin Belousov status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); 1026db7f8e5SKonstantin Belousov if (ACPI_FAILURE(status)) { 1036db7f8e5SKonstantin Belousov if (bootverbose) 1046db7f8e5SKonstantin Belousov device_printf(dev, "cannot get NFIT\n"); 1056db7f8e5SKonstantin Belousov return (ENXIO); 1066db7f8e5SKonstantin Belousov } 1077674dce0SKonstantin Belousov acpi_nfit_get_flush_addrs(nfitbl, nv->nv_handle, &nv->nv_flush_addr, 1087674dce0SKonstantin Belousov &nv->nv_flush_addr_cnt); 1096db7f8e5SKonstantin Belousov AcpiPutTable(&nfitbl->Header); 1106db7f8e5SKonstantin Belousov return (0); 1116db7f8e5SKonstantin Belousov } 1126db7f8e5SKonstantin Belousov 1136db7f8e5SKonstantin Belousov static int 1146db7f8e5SKonstantin Belousov nvdimm_detach(device_t dev) 1156db7f8e5SKonstantin Belousov { 1166db7f8e5SKonstantin Belousov struct nvdimm_dev *nv; 1176db7f8e5SKonstantin Belousov 1186db7f8e5SKonstantin Belousov nv = device_get_softc(dev); 1196db7f8e5SKonstantin Belousov free(nv->nv_flush_addr, M_NVDIMM); 1206db7f8e5SKonstantin Belousov return (0); 1216db7f8e5SKonstantin Belousov } 1226db7f8e5SKonstantin Belousov 1236db7f8e5SKonstantin Belousov static int 1246db7f8e5SKonstantin Belousov nvdimm_suspend(device_t dev) 1256db7f8e5SKonstantin Belousov { 1266db7f8e5SKonstantin Belousov 1276db7f8e5SKonstantin Belousov return (0); 1286db7f8e5SKonstantin Belousov } 1296db7f8e5SKonstantin Belousov 1306db7f8e5SKonstantin Belousov static int 1316db7f8e5SKonstantin Belousov nvdimm_resume(device_t dev) 1326db7f8e5SKonstantin Belousov { 1336db7f8e5SKonstantin Belousov 1346db7f8e5SKonstantin Belousov return (0); 1356db7f8e5SKonstantin Belousov } 1366db7f8e5SKonstantin Belousov 137fc4a961aSKonstantin Belousov static ACPI_STATUS 1387674dce0SKonstantin Belousov find_dimm(ACPI_HANDLE handle, UINT32 nesting_level, void *context, 139fc4a961aSKonstantin Belousov void **return_value) 140fc4a961aSKonstantin Belousov { 141fc4a961aSKonstantin Belousov ACPI_DEVICE_INFO *device_info; 1427674dce0SKonstantin Belousov ACPI_STATUS status; 143fc4a961aSKonstantin Belousov 144fc4a961aSKonstantin Belousov status = AcpiGetObjectInfo(handle, &device_info); 1457674dce0SKonstantin Belousov if (ACPI_FAILURE(status)) 146fc4a961aSKonstantin Belousov return_ACPI_STATUS(AE_ERROR); 1477674dce0SKonstantin Belousov if (device_info->Address == (uintptr_t)context) { 1487674dce0SKonstantin Belousov *(ACPI_HANDLE *)return_value = handle; 1497674dce0SKonstantin Belousov return_ACPI_STATUS(AE_CTRL_TERMINATE); 150fc4a961aSKonstantin Belousov } 1517674dce0SKonstantin Belousov return_ACPI_STATUS(AE_OK); 1527674dce0SKonstantin Belousov } 1537674dce0SKonstantin Belousov 1547674dce0SKonstantin Belousov static ACPI_HANDLE 1557674dce0SKonstantin Belousov get_dimm_acpi_handle(ACPI_HANDLE root_handle, nfit_handle_t adr) 1567674dce0SKonstantin Belousov { 1577674dce0SKonstantin Belousov ACPI_HANDLE res; 1587674dce0SKonstantin Belousov ACPI_STATUS status; 1597674dce0SKonstantin Belousov 1607674dce0SKonstantin Belousov res = NULL; 1617674dce0SKonstantin Belousov status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, root_handle, 1, find_dimm, 1627674dce0SKonstantin Belousov NULL, (void *)(uintptr_t)adr, &res); 1637674dce0SKonstantin Belousov if (ACPI_FAILURE(status)) 1647674dce0SKonstantin Belousov res = NULL; 1657674dce0SKonstantin Belousov return (res); 1667674dce0SKonstantin Belousov } 1677674dce0SKonstantin Belousov 1687674dce0SKonstantin Belousov static int 1697674dce0SKonstantin Belousov nvdimm_root_create_devs(device_t dev, ACPI_TABLE_NFIT *nfitbl) 1707674dce0SKonstantin Belousov { 1717674dce0SKonstantin Belousov ACPI_HANDLE root_handle, dimm_handle; 1727674dce0SKonstantin Belousov device_t child; 1737674dce0SKonstantin Belousov nfit_handle_t *dimm_ids, *dimm; 1747674dce0SKonstantin Belousov uintptr_t *ivars; 1757674dce0SKonstantin Belousov int num_dimm_ids; 1767674dce0SKonstantin Belousov 1777674dce0SKonstantin Belousov root_handle = acpi_get_handle(dev); 1787674dce0SKonstantin Belousov acpi_nfit_get_dimm_ids(nfitbl, &dimm_ids, &num_dimm_ids); 1797674dce0SKonstantin Belousov for (dimm = dimm_ids; dimm < dimm_ids + num_dimm_ids; dimm++) { 1807674dce0SKonstantin Belousov dimm_handle = get_dimm_acpi_handle(root_handle, *dimm); 1817674dce0SKonstantin Belousov child = BUS_ADD_CHILD(dev, 100, "nvdimm", -1); 1827674dce0SKonstantin Belousov if (child == NULL) { 1837674dce0SKonstantin Belousov device_printf(dev, "failed to create nvdimm\n"); 1847674dce0SKonstantin Belousov return (ENXIO); 1857674dce0SKonstantin Belousov } 1867674dce0SKonstantin Belousov ivars = mallocarray(NVDIMM_ROOT_IVAR_MAX, sizeof(uintptr_t), 187fc4a961aSKonstantin Belousov M_NVDIMM, M_ZERO | M_WAITOK); 188fc4a961aSKonstantin Belousov device_set_ivars(child, ivars); 1897674dce0SKonstantin Belousov nvdimm_root_set_acpi_handle(child, dimm_handle); 1907674dce0SKonstantin Belousov nvdimm_root_set_device_handle(child, *dimm); 1917674dce0SKonstantin Belousov } 1927674dce0SKonstantin Belousov free(dimm_ids, M_NVDIMM); 1937674dce0SKonstantin Belousov return (0); 1947674dce0SKonstantin Belousov } 1957674dce0SKonstantin Belousov 1967674dce0SKonstantin Belousov static int 1977674dce0SKonstantin Belousov nvdimm_root_create_spas(struct nvdimm_root_dev *dev, ACPI_TABLE_NFIT *nfitbl) 1987674dce0SKonstantin Belousov { 1997674dce0SKonstantin Belousov ACPI_NFIT_SYSTEM_ADDRESS **spas, **spa; 2007674dce0SKonstantin Belousov struct SPA_mapping *spa_mapping; 2017674dce0SKonstantin Belousov enum SPA_mapping_type spa_type; 2027674dce0SKonstantin Belousov int error, num_spas; 2037674dce0SKonstantin Belousov 2047674dce0SKonstantin Belousov error = 0; 2057674dce0SKonstantin Belousov acpi_nfit_get_spa_ranges(nfitbl, &spas, &num_spas); 2067674dce0SKonstantin Belousov for (spa = spas; spa < spas + num_spas; spa++) { 2077674dce0SKonstantin Belousov spa_type = nvdimm_spa_type_from_uuid( 2087674dce0SKonstantin Belousov (struct uuid *)(*spa)->RangeGuid); 2097674dce0SKonstantin Belousov if (spa_type == SPA_TYPE_UNKNOWN) 2107674dce0SKonstantin Belousov continue; 2117674dce0SKonstantin Belousov spa_mapping = malloc(sizeof(struct SPA_mapping), M_NVDIMM, 2127674dce0SKonstantin Belousov M_WAITOK | M_ZERO); 2137674dce0SKonstantin Belousov error = nvdimm_spa_init(spa_mapping, *spa, spa_type); 2147674dce0SKonstantin Belousov if (error != 0) { 2157674dce0SKonstantin Belousov nvdimm_spa_fini(spa_mapping); 2167674dce0SKonstantin Belousov free(spa, M_NVDIMM); 2177674dce0SKonstantin Belousov break; 2187674dce0SKonstantin Belousov } 2197674dce0SKonstantin Belousov SLIST_INSERT_HEAD(&dev->spas, spa_mapping, link); 2207674dce0SKonstantin Belousov } 2217674dce0SKonstantin Belousov free(spas, M_NVDIMM); 2227674dce0SKonstantin Belousov return (error); 223fc4a961aSKonstantin Belousov } 224fc4a961aSKonstantin Belousov 225fc4a961aSKonstantin Belousov static char *nvdimm_root_id[] = {"ACPI0012", NULL}; 226fc4a961aSKonstantin Belousov 227fc4a961aSKonstantin Belousov static int 228fc4a961aSKonstantin Belousov nvdimm_root_probe(device_t dev) 229fc4a961aSKonstantin Belousov { 230fc4a961aSKonstantin Belousov int rv; 231fc4a961aSKonstantin Belousov 232fc4a961aSKonstantin Belousov if (acpi_disabled("nvdimm")) 233fc4a961aSKonstantin Belousov return (ENXIO); 234fc4a961aSKonstantin Belousov rv = ACPI_ID_PROBE(device_get_parent(dev), dev, nvdimm_root_id, NULL); 235fc4a961aSKonstantin Belousov if (rv <= 0) 236fc4a961aSKonstantin Belousov device_set_desc(dev, "ACPI NVDIMM root device"); 237fc4a961aSKonstantin Belousov 238fc4a961aSKonstantin Belousov return (rv); 239fc4a961aSKonstantin Belousov } 240fc4a961aSKonstantin Belousov 241fc4a961aSKonstantin Belousov static int 242fc4a961aSKonstantin Belousov nvdimm_root_attach(device_t dev) 243fc4a961aSKonstantin Belousov { 2447674dce0SKonstantin Belousov struct nvdimm_root_dev *root; 2457dcbca8dSKonstantin Belousov ACPI_TABLE_NFIT *nfitbl; 2467674dce0SKonstantin Belousov ACPI_STATUS status; 247fc4a961aSKonstantin Belousov int error; 248fc4a961aSKonstantin Belousov 2497dcbca8dSKonstantin Belousov status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); 2507dcbca8dSKonstantin Belousov if (ACPI_FAILURE(status)) { 2517dcbca8dSKonstantin Belousov device_printf(dev, "cannot get NFIT\n"); 2527dcbca8dSKonstantin Belousov return (ENXIO); 2537dcbca8dSKonstantin Belousov } 2547674dce0SKonstantin Belousov error = nvdimm_root_create_devs(dev, nfitbl); 2557674dce0SKonstantin Belousov if (error != 0) 2567674dce0SKonstantin Belousov return (error); 2577674dce0SKonstantin Belousov error = bus_generic_attach(dev); 2587674dce0SKonstantin Belousov if (error != 0) 2597674dce0SKonstantin Belousov return (error); 2607674dce0SKonstantin Belousov root = device_get_softc(dev); 2617674dce0SKonstantin Belousov error = nvdimm_root_create_spas(root, nfitbl); 2627dcbca8dSKonstantin Belousov AcpiPutTable(&nfitbl->Header); 263fc4a961aSKonstantin Belousov return (error); 264fc4a961aSKonstantin Belousov } 265fc4a961aSKonstantin Belousov 266fc4a961aSKonstantin Belousov static int 267fc4a961aSKonstantin Belousov nvdimm_root_detach(device_t dev) 268fc4a961aSKonstantin Belousov { 2697dcbca8dSKonstantin Belousov struct nvdimm_root_dev *root; 2707dcbca8dSKonstantin Belousov struct SPA_mapping *spa, *next; 271fc4a961aSKonstantin Belousov device_t *children; 272fc4a961aSKonstantin Belousov int i, error, num_children; 273fc4a961aSKonstantin Belousov 2747dcbca8dSKonstantin Belousov root = device_get_softc(dev); 2757dcbca8dSKonstantin Belousov SLIST_FOREACH_SAFE(spa, &root->spas, link, next) { 2767dcbca8dSKonstantin Belousov nvdimm_spa_fini(spa); 2777dcbca8dSKonstantin Belousov SLIST_REMOVE_HEAD(&root->spas, link); 2787dcbca8dSKonstantin Belousov free(spa, M_NVDIMM); 2797dcbca8dSKonstantin Belousov } 280fc4a961aSKonstantin Belousov error = bus_generic_detach(dev); 281fc4a961aSKonstantin Belousov if (error != 0) 282fc4a961aSKonstantin Belousov return (error); 283fc4a961aSKonstantin Belousov error = device_get_children(dev, &children, &num_children); 284fc4a961aSKonstantin Belousov if (error != 0) 285fc4a961aSKonstantin Belousov return (error); 286fc4a961aSKonstantin Belousov for (i = 0; i < num_children; i++) 287fc4a961aSKonstantin Belousov free(device_get_ivars(children[i]), M_NVDIMM); 288fc4a961aSKonstantin Belousov free(children, M_TEMP); 289fc4a961aSKonstantin Belousov error = device_delete_children(dev); 290fc4a961aSKonstantin Belousov return (error); 291fc4a961aSKonstantin Belousov } 292fc4a961aSKonstantin Belousov 293fc4a961aSKonstantin Belousov static int 294fc4a961aSKonstantin Belousov nvdimm_root_read_ivar(device_t dev, device_t child, int index, 295fc4a961aSKonstantin Belousov uintptr_t *result) 296fc4a961aSKonstantin Belousov { 297fc4a961aSKonstantin Belousov 298fc4a961aSKonstantin Belousov if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX) 299fc4a961aSKonstantin Belousov return (ENOENT); 300fc4a961aSKonstantin Belousov *result = ((uintptr_t *)device_get_ivars(child))[index]; 301fc4a961aSKonstantin Belousov return (0); 302fc4a961aSKonstantin Belousov } 303fc4a961aSKonstantin Belousov 304fc4a961aSKonstantin Belousov static int 305fc4a961aSKonstantin Belousov nvdimm_root_write_ivar(device_t dev, device_t child, int index, 306fc4a961aSKonstantin Belousov uintptr_t value) 307fc4a961aSKonstantin Belousov { 308fc4a961aSKonstantin Belousov 309fc4a961aSKonstantin Belousov if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX) 310fc4a961aSKonstantin Belousov return (ENOENT); 311fc4a961aSKonstantin Belousov ((uintptr_t *)device_get_ivars(child))[index] = value; 312fc4a961aSKonstantin Belousov return (0); 313fc4a961aSKonstantin Belousov } 314fc4a961aSKonstantin Belousov 3156db7f8e5SKonstantin Belousov static device_method_t nvdimm_methods[] = { 3166db7f8e5SKonstantin Belousov DEVMETHOD(device_probe, nvdimm_probe), 3176db7f8e5SKonstantin Belousov DEVMETHOD(device_attach, nvdimm_attach), 3186db7f8e5SKonstantin Belousov DEVMETHOD(device_detach, nvdimm_detach), 3196db7f8e5SKonstantin Belousov DEVMETHOD(device_suspend, nvdimm_suspend), 3206db7f8e5SKonstantin Belousov DEVMETHOD(device_resume, nvdimm_resume), 3216db7f8e5SKonstantin Belousov DEVMETHOD_END 3226db7f8e5SKonstantin Belousov }; 3236db7f8e5SKonstantin Belousov 3246db7f8e5SKonstantin Belousov static driver_t nvdimm_driver = { 3256db7f8e5SKonstantin Belousov "nvdimm", 3266db7f8e5SKonstantin Belousov nvdimm_methods, 3276db7f8e5SKonstantin Belousov sizeof(struct nvdimm_dev), 3286db7f8e5SKonstantin Belousov }; 3296db7f8e5SKonstantin Belousov 330fc4a961aSKonstantin Belousov static device_method_t nvdimm_root_methods[] = { 331fc4a961aSKonstantin Belousov DEVMETHOD(device_probe, nvdimm_root_probe), 332fc4a961aSKonstantin Belousov DEVMETHOD(device_attach, nvdimm_root_attach), 333fc4a961aSKonstantin Belousov DEVMETHOD(device_detach, nvdimm_root_detach), 334fc4a961aSKonstantin Belousov DEVMETHOD(bus_add_child, bus_generic_add_child), 335fc4a961aSKonstantin Belousov DEVMETHOD(bus_read_ivar, nvdimm_root_read_ivar), 336fc4a961aSKonstantin Belousov DEVMETHOD(bus_write_ivar, nvdimm_root_write_ivar), 337fc4a961aSKonstantin Belousov DEVMETHOD_END 338fc4a961aSKonstantin Belousov }; 3396db7f8e5SKonstantin Belousov 340fc4a961aSKonstantin Belousov static driver_t nvdimm_root_driver = { 341fc4a961aSKonstantin Belousov "nvdimm_root", 342fc4a961aSKonstantin Belousov nvdimm_root_methods, 3437dcbca8dSKonstantin Belousov sizeof(struct nvdimm_root_dev), 344fc4a961aSKonstantin Belousov }; 3456db7f8e5SKonstantin Belousov 346fc4a961aSKonstantin Belousov DRIVER_MODULE(nvdimm_root, acpi, nvdimm_root_driver, nvdimm_root_devclass, NULL, 347fc4a961aSKonstantin Belousov NULL); 348fc4a961aSKonstantin Belousov DRIVER_MODULE(nvdimm, nvdimm_root, nvdimm_driver, nvdimm_devclass, NULL, NULL); 3496db7f8e5SKonstantin Belousov MODULE_DEPEND(nvdimm, acpi, 1, 1, 1); 350