1963c89ffSConrad Meyer /*- 2963c89ffSConrad Meyer * Copyright (c) 2017 The FreeBSD Foundation 3963c89ffSConrad Meyer * Copyright (c) 2018, 2019 Intel Corporation 4963c89ffSConrad Meyer * 5963c89ffSConrad Meyer * This software was developed by Konstantin Belousov <kib@FreeBSD.org> 6963c89ffSConrad Meyer * under sponsorship from the FreeBSD Foundation. 7963c89ffSConrad Meyer * 8963c89ffSConrad Meyer * Redistribution and use in source and binary forms, with or without 9963c89ffSConrad Meyer * modification, are permitted provided that the following conditions 10963c89ffSConrad Meyer * are met: 11963c89ffSConrad Meyer * 1. Redistributions of source code must retain the above copyright 12963c89ffSConrad Meyer * notice, this list of conditions and the following disclaimer. 13963c89ffSConrad Meyer * 2. Redistributions in binary form must reproduce the above copyright 14963c89ffSConrad Meyer * notice, this list of conditions and the following disclaimer in the 15963c89ffSConrad Meyer * documentation and/or other materials provided with the distribution. 16963c89ffSConrad Meyer * 17963c89ffSConrad Meyer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18963c89ffSConrad Meyer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19963c89ffSConrad Meyer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20963c89ffSConrad Meyer * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21963c89ffSConrad Meyer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22963c89ffSConrad Meyer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23963c89ffSConrad Meyer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24963c89ffSConrad Meyer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25963c89ffSConrad Meyer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26963c89ffSConrad Meyer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27963c89ffSConrad Meyer * SUCH DAMAGE. 28963c89ffSConrad Meyer */ 29963c89ffSConrad Meyer 30963c89ffSConrad Meyer #include <sys/cdefs.h> 31963c89ffSConrad Meyer #include "opt_acpi.h" 32963c89ffSConrad Meyer #include "opt_ddb.h" 33963c89ffSConrad Meyer 34963c89ffSConrad Meyer #include <sys/param.h> 35963c89ffSConrad Meyer #include <sys/bio.h> 36963c89ffSConrad Meyer #include <sys/bitstring.h> 37963c89ffSConrad Meyer #include <sys/bus.h> 38963c89ffSConrad Meyer #include <sys/kernel.h> 39963c89ffSConrad Meyer #include <sys/lock.h> 40963c89ffSConrad Meyer #include <sys/malloc.h> 41963c89ffSConrad Meyer #include <sys/module.h> 42ddfc9c4cSWarner Losh #include <sys/sbuf.h> 43963c89ffSConrad Meyer #include <sys/uuid.h> 44963c89ffSConrad Meyer 45963c89ffSConrad Meyer #include <contrib/dev/acpica/include/acpi.h> 46963c89ffSConrad Meyer #include <contrib/dev/acpica/include/accommon.h> 47963c89ffSConrad Meyer #include <contrib/dev/acpica/include/acuuid.h> 48963c89ffSConrad Meyer #include <dev/acpica/acpivar.h> 49963c89ffSConrad Meyer 50963c89ffSConrad Meyer #include <dev/nvdimm/nvdimm_var.h> 51963c89ffSConrad Meyer 52963c89ffSConrad Meyer #define _COMPONENT ACPI_OEM 53963c89ffSConrad Meyer ACPI_MODULE_NAME("NVDIMM_ACPI") 54963c89ffSConrad Meyer 55963c89ffSConrad Meyer struct nvdimm_root_dev { 56963c89ffSConrad Meyer SLIST_HEAD(, SPA_mapping) spas; 57963c89ffSConrad Meyer }; 58963c89ffSConrad Meyer 59963c89ffSConrad Meyer static MALLOC_DEFINE(M_NVDIMM_ACPI, "nvdimm_acpi", "NVDIMM ACPI bus memory"); 60963c89ffSConrad Meyer 61963c89ffSConrad Meyer static ACPI_STATUS 62963c89ffSConrad Meyer find_dimm(ACPI_HANDLE handle, UINT32 nesting_level, void *context, 63963c89ffSConrad Meyer void **return_value) 64963c89ffSConrad Meyer { 65963c89ffSConrad Meyer ACPI_DEVICE_INFO *device_info; 66963c89ffSConrad Meyer ACPI_STATUS status; 67963c89ffSConrad Meyer 683ff49706SRobert Herndon device_info = NULL; 69963c89ffSConrad Meyer status = AcpiGetObjectInfo(handle, &device_info); 70963c89ffSConrad Meyer if (ACPI_FAILURE(status)) 71963c89ffSConrad Meyer return_ACPI_STATUS(AE_ERROR); 72963c89ffSConrad Meyer if (device_info->Address == (uintptr_t)context) { 73963c89ffSConrad Meyer *(ACPI_HANDLE *)return_value = handle; 743ff49706SRobert Herndon status = AE_CTRL_TERMINATE; 753ff49706SRobert Herndon } else 763ff49706SRobert Herndon status = AE_OK; 773ff49706SRobert Herndon 783ff49706SRobert Herndon AcpiOsFree(device_info); 793ff49706SRobert Herndon return_ACPI_STATUS(status); 80963c89ffSConrad Meyer } 81963c89ffSConrad Meyer 82963c89ffSConrad Meyer static ACPI_HANDLE 83963c89ffSConrad Meyer get_dimm_acpi_handle(ACPI_HANDLE root_handle, nfit_handle_t adr) 84963c89ffSConrad Meyer { 85963c89ffSConrad Meyer ACPI_HANDLE res; 86963c89ffSConrad Meyer ACPI_STATUS status; 87963c89ffSConrad Meyer 88963c89ffSConrad Meyer res = NULL; 89963c89ffSConrad Meyer status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, root_handle, 1, find_dimm, 90963c89ffSConrad Meyer NULL, (void *)(uintptr_t)adr, &res); 91963c89ffSConrad Meyer if (ACPI_FAILURE(status)) 92963c89ffSConrad Meyer res = NULL; 93963c89ffSConrad Meyer return (res); 94963c89ffSConrad Meyer } 95963c89ffSConrad Meyer 96963c89ffSConrad Meyer static int 97963c89ffSConrad Meyer nvdimm_root_create_devs(device_t dev, ACPI_TABLE_NFIT *nfitbl) 98963c89ffSConrad Meyer { 99963c89ffSConrad Meyer ACPI_HANDLE root_handle, dimm_handle; 100963c89ffSConrad Meyer device_t child; 101963c89ffSConrad Meyer nfit_handle_t *dimm_ids, *dimm; 102963c89ffSConrad Meyer uintptr_t *ivars; 103963c89ffSConrad Meyer int num_dimm_ids; 104963c89ffSConrad Meyer 105963c89ffSConrad Meyer root_handle = acpi_get_handle(dev); 106963c89ffSConrad Meyer acpi_nfit_get_dimm_ids(nfitbl, &dimm_ids, &num_dimm_ids); 107963c89ffSConrad Meyer for (dimm = dimm_ids; dimm < dimm_ids + num_dimm_ids; dimm++) { 108963c89ffSConrad Meyer dimm_handle = get_dimm_acpi_handle(root_handle, *dimm); 109963c89ffSConrad Meyer if (dimm_handle == NULL) 110963c89ffSConrad Meyer continue; 111963c89ffSConrad Meyer 112a05a6804SWarner Losh child = BUS_ADD_CHILD(dev, 100, "nvdimm", DEVICE_UNIT_ANY); 113963c89ffSConrad Meyer if (child == NULL) { 114963c89ffSConrad Meyer device_printf(dev, "failed to create nvdimm\n"); 115963c89ffSConrad Meyer return (ENXIO); 116963c89ffSConrad Meyer } 117963c89ffSConrad Meyer ivars = mallocarray(NVDIMM_ROOT_IVAR_MAX, sizeof(uintptr_t), 118963c89ffSConrad Meyer M_NVDIMM_ACPI, M_ZERO | M_WAITOK); 119963c89ffSConrad Meyer device_set_ivars(child, ivars); 120963c89ffSConrad Meyer nvdimm_root_set_acpi_handle(child, dimm_handle); 121963c89ffSConrad Meyer nvdimm_root_set_device_handle(child, *dimm); 122963c89ffSConrad Meyer } 123963c89ffSConrad Meyer free(dimm_ids, M_NVDIMM_ACPI); 124963c89ffSConrad Meyer return (0); 125963c89ffSConrad Meyer } 126963c89ffSConrad Meyer 127963c89ffSConrad Meyer static int 128963c89ffSConrad Meyer nvdimm_root_create_spas(struct nvdimm_root_dev *dev, ACPI_TABLE_NFIT *nfitbl) 129963c89ffSConrad Meyer { 130963c89ffSConrad Meyer ACPI_NFIT_SYSTEM_ADDRESS **spas, **spa; 131963c89ffSConrad Meyer struct SPA_mapping *spa_mapping; 132963c89ffSConrad Meyer enum SPA_mapping_type spa_type; 133963c89ffSConrad Meyer int error, num_spas; 134963c89ffSConrad Meyer 135963c89ffSConrad Meyer error = 0; 136963c89ffSConrad Meyer acpi_nfit_get_spa_ranges(nfitbl, &spas, &num_spas); 137963c89ffSConrad Meyer for (spa = spas; spa < spas + num_spas; spa++) { 138963c89ffSConrad Meyer spa_type = nvdimm_spa_type_from_uuid( 139963c89ffSConrad Meyer (struct uuid *)(*spa)->RangeGuid); 140963c89ffSConrad Meyer if (spa_type == SPA_TYPE_UNKNOWN) 141963c89ffSConrad Meyer continue; 142963c89ffSConrad Meyer spa_mapping = malloc(sizeof(struct SPA_mapping), M_NVDIMM_ACPI, 143963c89ffSConrad Meyer M_WAITOK | M_ZERO); 144963c89ffSConrad Meyer error = nvdimm_spa_init(spa_mapping, *spa, spa_type); 145963c89ffSConrad Meyer if (error != 0) { 146963c89ffSConrad Meyer nvdimm_spa_fini(spa_mapping); 14731f1c8fcSConrad Meyer free(spa_mapping, M_NVDIMM_ACPI); 148963c89ffSConrad Meyer break; 149963c89ffSConrad Meyer } 150cf8b104fSD Scott Phillips if (nvdimm_spa_type_user_accessible(spa_type) && 151cf8b104fSD Scott Phillips spa_type != SPA_TYPE_CONTROL_REGION) 152963c89ffSConrad Meyer nvdimm_create_namespaces(spa_mapping, nfitbl); 153963c89ffSConrad Meyer SLIST_INSERT_HEAD(&dev->spas, spa_mapping, link); 154963c89ffSConrad Meyer } 155963c89ffSConrad Meyer free(spas, M_NVDIMM_ACPI); 156963c89ffSConrad Meyer return (error); 157963c89ffSConrad Meyer } 158963c89ffSConrad Meyer 159963c89ffSConrad Meyer static char *nvdimm_root_id[] = {"ACPI0012", NULL}; 160963c89ffSConrad Meyer 161963c89ffSConrad Meyer static int 162963c89ffSConrad Meyer nvdimm_root_probe(device_t dev) 163963c89ffSConrad Meyer { 164963c89ffSConrad Meyer int rv; 165963c89ffSConrad Meyer 166963c89ffSConrad Meyer if (acpi_disabled("nvdimm")) 167963c89ffSConrad Meyer return (ENXIO); 168963c89ffSConrad Meyer rv = ACPI_ID_PROBE(device_get_parent(dev), dev, nvdimm_root_id, NULL); 169963c89ffSConrad Meyer if (rv <= 0) 170963c89ffSConrad Meyer device_set_desc(dev, "ACPI NVDIMM root device"); 171963c89ffSConrad Meyer 172963c89ffSConrad Meyer return (rv); 173963c89ffSConrad Meyer } 174963c89ffSConrad Meyer 175963c89ffSConrad Meyer static int 176963c89ffSConrad Meyer nvdimm_root_attach(device_t dev) 177963c89ffSConrad Meyer { 178963c89ffSConrad Meyer struct nvdimm_root_dev *root; 179963c89ffSConrad Meyer ACPI_TABLE_NFIT *nfitbl; 180963c89ffSConrad Meyer ACPI_STATUS status; 181963c89ffSConrad Meyer int error; 182963c89ffSConrad Meyer 183963c89ffSConrad Meyer status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); 184963c89ffSConrad Meyer if (ACPI_FAILURE(status)) { 185963c89ffSConrad Meyer device_printf(dev, "cannot get NFIT\n"); 186963c89ffSConrad Meyer return (ENXIO); 187963c89ffSConrad Meyer } 188963c89ffSConrad Meyer error = nvdimm_root_create_devs(dev, nfitbl); 189963c89ffSConrad Meyer if (error != 0) 190963c89ffSConrad Meyer return (error); 19118250ec6SJohn Baldwin bus_attach_children(dev); 192963c89ffSConrad Meyer root = device_get_softc(dev); 193963c89ffSConrad Meyer error = nvdimm_root_create_spas(root, nfitbl); 194963c89ffSConrad Meyer AcpiPutTable(&nfitbl->Header); 195963c89ffSConrad Meyer return (error); 196963c89ffSConrad Meyer } 197963c89ffSConrad Meyer 1985c7af849SJohn Baldwin static void 1995c7af849SJohn Baldwin nvdimm_root_child_deleted(device_t dev, device_t child) 2005c7af849SJohn Baldwin { 2015c7af849SJohn Baldwin free(device_get_ivars(child), M_NVDIMM_ACPI); 2025c7af849SJohn Baldwin } 2035c7af849SJohn Baldwin 204963c89ffSConrad Meyer static int 205963c89ffSConrad Meyer nvdimm_root_detach(device_t dev) 206963c89ffSConrad Meyer { 207963c89ffSConrad Meyer struct nvdimm_root_dev *root; 208963c89ffSConrad Meyer struct SPA_mapping *spa, *next; 209963c89ffSConrad Meyer 210963c89ffSConrad Meyer root = device_get_softc(dev); 211963c89ffSConrad Meyer SLIST_FOREACH_SAFE(spa, &root->spas, link, next) { 212963c89ffSConrad Meyer nvdimm_destroy_namespaces(spa); 213963c89ffSConrad Meyer nvdimm_spa_fini(spa); 214963c89ffSConrad Meyer SLIST_REMOVE_HEAD(&root->spas, link); 215963c89ffSConrad Meyer free(spa, M_NVDIMM_ACPI); 216963c89ffSConrad Meyer } 217*160179eaSJohn Baldwin return (bus_generic_detach(dev)); 218963c89ffSConrad Meyer } 219963c89ffSConrad Meyer 220963c89ffSConrad Meyer static int 221963c89ffSConrad Meyer nvdimm_root_read_ivar(device_t dev, device_t child, int index, 222963c89ffSConrad Meyer uintptr_t *result) 223963c89ffSConrad Meyer { 224963c89ffSConrad Meyer 225963c89ffSConrad Meyer if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX) 226963c89ffSConrad Meyer return (ENOENT); 227963c89ffSConrad Meyer *result = ((uintptr_t *)device_get_ivars(child))[index]; 228963c89ffSConrad Meyer return (0); 229963c89ffSConrad Meyer } 230963c89ffSConrad Meyer 231963c89ffSConrad Meyer static int 232963c89ffSConrad Meyer nvdimm_root_write_ivar(device_t dev, device_t child, int index, 233963c89ffSConrad Meyer uintptr_t value) 234963c89ffSConrad Meyer { 235963c89ffSConrad Meyer 236963c89ffSConrad Meyer if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX) 237963c89ffSConrad Meyer return (ENOENT); 238963c89ffSConrad Meyer ((uintptr_t *)device_get_ivars(child))[index] = value; 239963c89ffSConrad Meyer return (0); 240963c89ffSConrad Meyer } 241963c89ffSConrad Meyer 242963c89ffSConrad Meyer static int 243ddfc9c4cSWarner Losh nvdimm_root_child_location(device_t dev, device_t child, struct sbuf *sb) 244963c89ffSConrad Meyer { 245963c89ffSConrad Meyer ACPI_HANDLE handle; 246963c89ffSConrad Meyer 247963c89ffSConrad Meyer handle = nvdimm_root_get_acpi_handle(child); 248963c89ffSConrad Meyer if (handle != NULL) 249ddfc9c4cSWarner Losh sbuf_printf(sb, "handle=%s", acpi_name(handle)); 250963c89ffSConrad Meyer 251963c89ffSConrad Meyer return (0); 252963c89ffSConrad Meyer } 253963c89ffSConrad Meyer 254963c89ffSConrad Meyer static device_method_t nvdimm_acpi_methods[] = { 255963c89ffSConrad Meyer DEVMETHOD(device_probe, nvdimm_root_probe), 256963c89ffSConrad Meyer DEVMETHOD(device_attach, nvdimm_root_attach), 257963c89ffSConrad Meyer DEVMETHOD(device_detach, nvdimm_root_detach), 258963c89ffSConrad Meyer DEVMETHOD(bus_add_child, bus_generic_add_child), 2595c7af849SJohn Baldwin DEVMETHOD(bus_child_deleted, nvdimm_root_child_deleted), 260963c89ffSConrad Meyer DEVMETHOD(bus_read_ivar, nvdimm_root_read_ivar), 261963c89ffSConrad Meyer DEVMETHOD(bus_write_ivar, nvdimm_root_write_ivar), 262ddfc9c4cSWarner Losh DEVMETHOD(bus_child_location, nvdimm_root_child_location), 263cae7d9ecSWarner Losh DEVMETHOD(bus_get_device_path, acpi_get_acpi_device_path), 264963c89ffSConrad Meyer DEVMETHOD_END 265963c89ffSConrad Meyer }; 266963c89ffSConrad Meyer 267963c89ffSConrad Meyer static driver_t nvdimm_acpi_driver = { 268963c89ffSConrad Meyer "nvdimm_acpi_root", 269963c89ffSConrad Meyer nvdimm_acpi_methods, 270963c89ffSConrad Meyer sizeof(struct nvdimm_root_dev), 271963c89ffSConrad Meyer }; 272963c89ffSConrad Meyer 273534c3629SJohn Baldwin DRIVER_MODULE(nvdimm_acpi_root, acpi, nvdimm_acpi_driver, NULL, NULL); 274963c89ffSConrad Meyer MODULE_DEPEND(nvdimm_acpi_root, acpi, 1, 1, 1); 275