libnvdimm, nfit: initial libnvdimm infrastructure and NFIT support
[firefly-linux-kernel-4.4.55.git] / 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");