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 __FBSDID("$FreeBSD$"); 32963c89ffSConrad Meyer 33963c89ffSConrad Meyer #include "opt_acpi.h" 34963c89ffSConrad Meyer #include "opt_ddb.h" 35963c89ffSConrad Meyer 36963c89ffSConrad Meyer #include <sys/param.h> 37963c89ffSConrad Meyer #include <sys/bio.h> 38963c89ffSConrad Meyer #include <sys/bitstring.h> 39963c89ffSConrad Meyer #include <sys/bus.h> 40963c89ffSConrad Meyer #include <sys/kernel.h> 41963c89ffSConrad Meyer #include <sys/lock.h> 42963c89ffSConrad Meyer #include <sys/malloc.h> 43963c89ffSConrad Meyer #include <sys/module.h> 44ddfc9c4cSWarner Losh #include <sys/sbuf.h> 45963c89ffSConrad Meyer #include <sys/uuid.h> 46963c89ffSConrad Meyer 47963c89ffSConrad Meyer #include <contrib/dev/acpica/include/acpi.h> 48963c89ffSConrad Meyer #include <contrib/dev/acpica/include/accommon.h> 49963c89ffSConrad Meyer #include <contrib/dev/acpica/include/acuuid.h> 50963c89ffSConrad Meyer #include <dev/acpica/acpivar.h> 51963c89ffSConrad Meyer 52963c89ffSConrad Meyer #include <dev/nvdimm/nvdimm_var.h> 53963c89ffSConrad Meyer 54963c89ffSConrad Meyer #define _COMPONENT ACPI_OEM 55963c89ffSConrad Meyer ACPI_MODULE_NAME("NVDIMM_ACPI") 56963c89ffSConrad Meyer 57963c89ffSConrad Meyer struct nvdimm_root_dev { 58963c89ffSConrad Meyer SLIST_HEAD(, SPA_mapping) spas; 59963c89ffSConrad Meyer }; 60963c89ffSConrad Meyer 61963c89ffSConrad Meyer static MALLOC_DEFINE(M_NVDIMM_ACPI, "nvdimm_acpi", "NVDIMM ACPI bus memory"); 62963c89ffSConrad Meyer 63963c89ffSConrad Meyer static ACPI_STATUS 64963c89ffSConrad Meyer find_dimm(ACPI_HANDLE handle, UINT32 nesting_level, void *context, 65963c89ffSConrad Meyer void **return_value) 66963c89ffSConrad Meyer { 67963c89ffSConrad Meyer ACPI_DEVICE_INFO *device_info; 68963c89ffSConrad Meyer ACPI_STATUS status; 69963c89ffSConrad Meyer 703ff49706SRobert Herndon device_info = NULL; 71963c89ffSConrad Meyer status = AcpiGetObjectInfo(handle, &device_info); 72963c89ffSConrad Meyer if (ACPI_FAILURE(status)) 73963c89ffSConrad Meyer return_ACPI_STATUS(AE_ERROR); 74963c89ffSConrad Meyer if (device_info->Address == (uintptr_t)context) { 75963c89ffSConrad Meyer *(ACPI_HANDLE *)return_value = handle; 763ff49706SRobert Herndon status = AE_CTRL_TERMINATE; 773ff49706SRobert Herndon } else 783ff49706SRobert Herndon status = AE_OK; 793ff49706SRobert Herndon 803ff49706SRobert Herndon AcpiOsFree(device_info); 813ff49706SRobert Herndon return_ACPI_STATUS(status); 82963c89ffSConrad Meyer } 83963c89ffSConrad Meyer 84963c89ffSConrad Meyer static ACPI_HANDLE 85963c89ffSConrad Meyer get_dimm_acpi_handle(ACPI_HANDLE root_handle, nfit_handle_t adr) 86963c89ffSConrad Meyer { 87963c89ffSConrad Meyer ACPI_HANDLE res; 88963c89ffSConrad Meyer ACPI_STATUS status; 89963c89ffSConrad Meyer 90963c89ffSConrad Meyer res = NULL; 91963c89ffSConrad Meyer status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, root_handle, 1, find_dimm, 92963c89ffSConrad Meyer NULL, (void *)(uintptr_t)adr, &res); 93963c89ffSConrad Meyer if (ACPI_FAILURE(status)) 94963c89ffSConrad Meyer res = NULL; 95963c89ffSConrad Meyer return (res); 96963c89ffSConrad Meyer } 97963c89ffSConrad Meyer 98963c89ffSConrad Meyer static int 99963c89ffSConrad Meyer nvdimm_root_create_devs(device_t dev, ACPI_TABLE_NFIT *nfitbl) 100963c89ffSConrad Meyer { 101963c89ffSConrad Meyer ACPI_HANDLE root_handle, dimm_handle; 102963c89ffSConrad Meyer device_t child; 103963c89ffSConrad Meyer nfit_handle_t *dimm_ids, *dimm; 104963c89ffSConrad Meyer uintptr_t *ivars; 105963c89ffSConrad Meyer int num_dimm_ids; 106963c89ffSConrad Meyer 107963c89ffSConrad Meyer root_handle = acpi_get_handle(dev); 108963c89ffSConrad Meyer acpi_nfit_get_dimm_ids(nfitbl, &dimm_ids, &num_dimm_ids); 109963c89ffSConrad Meyer for (dimm = dimm_ids; dimm < dimm_ids + num_dimm_ids; dimm++) { 110963c89ffSConrad Meyer dimm_handle = get_dimm_acpi_handle(root_handle, *dimm); 111963c89ffSConrad Meyer if (dimm_handle == NULL) 112963c89ffSConrad Meyer continue; 113963c89ffSConrad Meyer 114963c89ffSConrad Meyer child = BUS_ADD_CHILD(dev, 100, "nvdimm", -1); 115963c89ffSConrad Meyer if (child == NULL) { 116963c89ffSConrad Meyer device_printf(dev, "failed to create nvdimm\n"); 117963c89ffSConrad Meyer return (ENXIO); 118963c89ffSConrad Meyer } 119963c89ffSConrad Meyer ivars = mallocarray(NVDIMM_ROOT_IVAR_MAX, sizeof(uintptr_t), 120963c89ffSConrad Meyer M_NVDIMM_ACPI, M_ZERO | M_WAITOK); 121963c89ffSConrad Meyer device_set_ivars(child, ivars); 122963c89ffSConrad Meyer nvdimm_root_set_acpi_handle(child, dimm_handle); 123963c89ffSConrad Meyer nvdimm_root_set_device_handle(child, *dimm); 124963c89ffSConrad Meyer } 125963c89ffSConrad Meyer free(dimm_ids, M_NVDIMM_ACPI); 126963c89ffSConrad Meyer return (0); 127963c89ffSConrad Meyer } 128963c89ffSConrad Meyer 129963c89ffSConrad Meyer static int 130963c89ffSConrad Meyer nvdimm_root_create_spas(struct nvdimm_root_dev *dev, ACPI_TABLE_NFIT *nfitbl) 131963c89ffSConrad Meyer { 132963c89ffSConrad Meyer ACPI_NFIT_SYSTEM_ADDRESS **spas, **spa; 133963c89ffSConrad Meyer struct SPA_mapping *spa_mapping; 134963c89ffSConrad Meyer enum SPA_mapping_type spa_type; 135963c89ffSConrad Meyer int error, num_spas; 136963c89ffSConrad Meyer 137963c89ffSConrad Meyer error = 0; 138963c89ffSConrad Meyer acpi_nfit_get_spa_ranges(nfitbl, &spas, &num_spas); 139963c89ffSConrad Meyer for (spa = spas; spa < spas + num_spas; spa++) { 140963c89ffSConrad Meyer spa_type = nvdimm_spa_type_from_uuid( 141963c89ffSConrad Meyer (struct uuid *)(*spa)->RangeGuid); 142963c89ffSConrad Meyer if (spa_type == SPA_TYPE_UNKNOWN) 143963c89ffSConrad Meyer continue; 144963c89ffSConrad Meyer spa_mapping = malloc(sizeof(struct SPA_mapping), M_NVDIMM_ACPI, 145963c89ffSConrad Meyer M_WAITOK | M_ZERO); 146963c89ffSConrad Meyer error = nvdimm_spa_init(spa_mapping, *spa, spa_type); 147963c89ffSConrad Meyer if (error != 0) { 148963c89ffSConrad Meyer nvdimm_spa_fini(spa_mapping); 14931f1c8fcSConrad Meyer free(spa_mapping, M_NVDIMM_ACPI); 150963c89ffSConrad Meyer break; 151963c89ffSConrad Meyer } 152cf8b104fSD Scott Phillips if (nvdimm_spa_type_user_accessible(spa_type) && 153cf8b104fSD Scott Phillips spa_type != SPA_TYPE_CONTROL_REGION) 154963c89ffSConrad Meyer nvdimm_create_namespaces(spa_mapping, nfitbl); 155963c89ffSConrad Meyer SLIST_INSERT_HEAD(&dev->spas, spa_mapping, link); 156963c89ffSConrad Meyer } 157963c89ffSConrad Meyer free(spas, M_NVDIMM_ACPI); 158963c89ffSConrad Meyer return (error); 159963c89ffSConrad Meyer } 160963c89ffSConrad Meyer 161963c89ffSConrad Meyer static char *nvdimm_root_id[] = {"ACPI0012", NULL}; 162963c89ffSConrad Meyer 163963c89ffSConrad Meyer static int 164963c89ffSConrad Meyer nvdimm_root_probe(device_t dev) 165963c89ffSConrad Meyer { 166963c89ffSConrad Meyer int rv; 167963c89ffSConrad Meyer 168963c89ffSConrad Meyer if (acpi_disabled("nvdimm")) 169963c89ffSConrad Meyer return (ENXIO); 170963c89ffSConrad Meyer rv = ACPI_ID_PROBE(device_get_parent(dev), dev, nvdimm_root_id, NULL); 171963c89ffSConrad Meyer if (rv <= 0) 172963c89ffSConrad Meyer device_set_desc(dev, "ACPI NVDIMM root device"); 173963c89ffSConrad Meyer 174963c89ffSConrad Meyer return (rv); 175963c89ffSConrad Meyer } 176963c89ffSConrad Meyer 177963c89ffSConrad Meyer static int 178963c89ffSConrad Meyer nvdimm_root_attach(device_t dev) 179963c89ffSConrad Meyer { 180963c89ffSConrad Meyer struct nvdimm_root_dev *root; 181963c89ffSConrad Meyer ACPI_TABLE_NFIT *nfitbl; 182963c89ffSConrad Meyer ACPI_STATUS status; 183963c89ffSConrad Meyer int error; 184963c89ffSConrad Meyer 185963c89ffSConrad Meyer status = AcpiGetTable(ACPI_SIG_NFIT, 1, (ACPI_TABLE_HEADER **)&nfitbl); 186963c89ffSConrad Meyer if (ACPI_FAILURE(status)) { 187963c89ffSConrad Meyer device_printf(dev, "cannot get NFIT\n"); 188963c89ffSConrad Meyer return (ENXIO); 189963c89ffSConrad Meyer } 190963c89ffSConrad Meyer error = nvdimm_root_create_devs(dev, nfitbl); 191963c89ffSConrad Meyer if (error != 0) 192963c89ffSConrad Meyer return (error); 193963c89ffSConrad Meyer error = bus_generic_attach(dev); 194963c89ffSConrad Meyer if (error != 0) 195963c89ffSConrad Meyer return (error); 196963c89ffSConrad Meyer root = device_get_softc(dev); 197963c89ffSConrad Meyer error = nvdimm_root_create_spas(root, nfitbl); 198963c89ffSConrad Meyer AcpiPutTable(&nfitbl->Header); 199963c89ffSConrad Meyer return (error); 200963c89ffSConrad Meyer } 201963c89ffSConrad Meyer 202963c89ffSConrad Meyer static int 203963c89ffSConrad Meyer nvdimm_root_detach(device_t dev) 204963c89ffSConrad Meyer { 205963c89ffSConrad Meyer struct nvdimm_root_dev *root; 206963c89ffSConrad Meyer struct SPA_mapping *spa, *next; 207963c89ffSConrad Meyer device_t *children; 208963c89ffSConrad Meyer int i, error, num_children; 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 } 217963c89ffSConrad Meyer error = bus_generic_detach(dev); 218963c89ffSConrad Meyer if (error != 0) 219963c89ffSConrad Meyer return (error); 220963c89ffSConrad Meyer error = device_get_children(dev, &children, &num_children); 221963c89ffSConrad Meyer if (error != 0) 222963c89ffSConrad Meyer return (error); 223963c89ffSConrad Meyer for (i = 0; i < num_children; i++) 224963c89ffSConrad Meyer free(device_get_ivars(children[i]), M_NVDIMM_ACPI); 225963c89ffSConrad Meyer free(children, M_TEMP); 226963c89ffSConrad Meyer error = device_delete_children(dev); 227963c89ffSConrad Meyer return (error); 228963c89ffSConrad Meyer } 229963c89ffSConrad Meyer 230963c89ffSConrad Meyer static int 231963c89ffSConrad Meyer nvdimm_root_read_ivar(device_t dev, device_t child, int index, 232963c89ffSConrad Meyer uintptr_t *result) 233963c89ffSConrad Meyer { 234963c89ffSConrad Meyer 235963c89ffSConrad Meyer if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX) 236963c89ffSConrad Meyer return (ENOENT); 237963c89ffSConrad Meyer *result = ((uintptr_t *)device_get_ivars(child))[index]; 238963c89ffSConrad Meyer return (0); 239963c89ffSConrad Meyer } 240963c89ffSConrad Meyer 241963c89ffSConrad Meyer static int 242963c89ffSConrad Meyer nvdimm_root_write_ivar(device_t dev, device_t child, int index, 243963c89ffSConrad Meyer uintptr_t value) 244963c89ffSConrad Meyer { 245963c89ffSConrad Meyer 246963c89ffSConrad Meyer if (index < 0 || index >= NVDIMM_ROOT_IVAR_MAX) 247963c89ffSConrad Meyer return (ENOENT); 248963c89ffSConrad Meyer ((uintptr_t *)device_get_ivars(child))[index] = value; 249963c89ffSConrad Meyer return (0); 250963c89ffSConrad Meyer } 251963c89ffSConrad Meyer 252963c89ffSConrad Meyer static int 253ddfc9c4cSWarner Losh nvdimm_root_child_location(device_t dev, device_t child, struct sbuf *sb) 254963c89ffSConrad Meyer { 255963c89ffSConrad Meyer ACPI_HANDLE handle; 256963c89ffSConrad Meyer 257963c89ffSConrad Meyer handle = nvdimm_root_get_acpi_handle(child); 258963c89ffSConrad Meyer if (handle != NULL) 259ddfc9c4cSWarner Losh sbuf_printf(sb, "handle=%s", acpi_name(handle)); 260963c89ffSConrad Meyer 261963c89ffSConrad Meyer return (0); 262963c89ffSConrad Meyer } 263963c89ffSConrad Meyer 264963c89ffSConrad Meyer static device_method_t nvdimm_acpi_methods[] = { 265963c89ffSConrad Meyer DEVMETHOD(device_probe, nvdimm_root_probe), 266963c89ffSConrad Meyer DEVMETHOD(device_attach, nvdimm_root_attach), 267963c89ffSConrad Meyer DEVMETHOD(device_detach, nvdimm_root_detach), 268963c89ffSConrad Meyer DEVMETHOD(bus_add_child, bus_generic_add_child), 269963c89ffSConrad Meyer DEVMETHOD(bus_read_ivar, nvdimm_root_read_ivar), 270963c89ffSConrad Meyer DEVMETHOD(bus_write_ivar, nvdimm_root_write_ivar), 271ddfc9c4cSWarner Losh DEVMETHOD(bus_child_location, nvdimm_root_child_location), 272cae7d9ecSWarner Losh DEVMETHOD(bus_get_device_path, acpi_get_acpi_device_path), 273963c89ffSConrad Meyer DEVMETHOD_END 274963c89ffSConrad Meyer }; 275963c89ffSConrad Meyer 276963c89ffSConrad Meyer static driver_t nvdimm_acpi_driver = { 277963c89ffSConrad Meyer "nvdimm_acpi_root", 278963c89ffSConrad Meyer nvdimm_acpi_methods, 279963c89ffSConrad Meyer sizeof(struct nvdimm_root_dev), 280963c89ffSConrad Meyer }; 281963c89ffSConrad Meyer 282534c3629SJohn Baldwin DRIVER_MODULE(nvdimm_acpi_root, acpi, nvdimm_acpi_driver, NULL, NULL); 283963c89ffSConrad Meyer MODULE_DEPEND(nvdimm_acpi_root, acpi, 1, 1, 1); 284