diff options
Diffstat (limited to 'drivers/acpi/nfit.c')
-rw-r--r-- | drivers/acpi/nfit.c | 298 |
1 files changed, 242 insertions, 56 deletions
diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 6e26761a27da..f7dab53b352a 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c | |||
@@ -33,6 +33,15 @@ static bool force_enable_dimms; | |||
33 | module_param(force_enable_dimms, bool, S_IRUGO|S_IWUSR); | 33 | module_param(force_enable_dimms, bool, S_IRUGO|S_IWUSR); |
34 | MODULE_PARM_DESC(force_enable_dimms, "Ignore _STA (ACPI DIMM device) status"); | 34 | MODULE_PARM_DESC(force_enable_dimms, "Ignore _STA (ACPI DIMM device) status"); |
35 | 35 | ||
36 | struct nfit_table_prev { | ||
37 | struct list_head spas; | ||
38 | struct list_head memdevs; | ||
39 | struct list_head dcrs; | ||
40 | struct list_head bdws; | ||
41 | struct list_head idts; | ||
42 | struct list_head flushes; | ||
43 | }; | ||
44 | |||
36 | static u8 nfit_uuid[NFIT_UUID_MAX][16]; | 45 | static u8 nfit_uuid[NFIT_UUID_MAX][16]; |
37 | 46 | ||
38 | const u8 *to_nfit_uuid(enum nfit_uuids id) | 47 | const u8 *to_nfit_uuid(enum nfit_uuids id) |
@@ -221,12 +230,20 @@ static int nfit_spa_type(struct acpi_nfit_system_address *spa) | |||
221 | } | 230 | } |
222 | 231 | ||
223 | static bool add_spa(struct acpi_nfit_desc *acpi_desc, | 232 | static bool add_spa(struct acpi_nfit_desc *acpi_desc, |
233 | struct nfit_table_prev *prev, | ||
224 | struct acpi_nfit_system_address *spa) | 234 | struct acpi_nfit_system_address *spa) |
225 | { | 235 | { |
226 | struct device *dev = acpi_desc->dev; | 236 | struct device *dev = acpi_desc->dev; |
227 | struct nfit_spa *nfit_spa = devm_kzalloc(dev, sizeof(*nfit_spa), | 237 | struct nfit_spa *nfit_spa; |
228 | GFP_KERNEL); | 238 | |
239 | list_for_each_entry(nfit_spa, &prev->spas, list) { | ||
240 | if (memcmp(nfit_spa->spa, spa, sizeof(*spa)) == 0) { | ||
241 | list_move_tail(&nfit_spa->list, &acpi_desc->spas); | ||
242 | return true; | ||
243 | } | ||
244 | } | ||
229 | 245 | ||
246 | nfit_spa = devm_kzalloc(dev, sizeof(*nfit_spa), GFP_KERNEL); | ||
230 | if (!nfit_spa) | 247 | if (!nfit_spa) |
231 | return false; | 248 | return false; |
232 | INIT_LIST_HEAD(&nfit_spa->list); | 249 | INIT_LIST_HEAD(&nfit_spa->list); |
@@ -239,12 +256,19 @@ static bool add_spa(struct acpi_nfit_desc *acpi_desc, | |||
239 | } | 256 | } |
240 | 257 | ||
241 | static bool add_memdev(struct acpi_nfit_desc *acpi_desc, | 258 | static bool add_memdev(struct acpi_nfit_desc *acpi_desc, |
259 | struct nfit_table_prev *prev, | ||
242 | struct acpi_nfit_memory_map *memdev) | 260 | struct acpi_nfit_memory_map *memdev) |
243 | { | 261 | { |
244 | struct device *dev = acpi_desc->dev; | 262 | struct device *dev = acpi_desc->dev; |
245 | struct nfit_memdev *nfit_memdev = devm_kzalloc(dev, | 263 | struct nfit_memdev *nfit_memdev; |
246 | sizeof(*nfit_memdev), GFP_KERNEL); | ||
247 | 264 | ||
265 | list_for_each_entry(nfit_memdev, &prev->memdevs, list) | ||
266 | if (memcmp(nfit_memdev->memdev, memdev, sizeof(*memdev)) == 0) { | ||
267 | list_move_tail(&nfit_memdev->list, &acpi_desc->memdevs); | ||
268 | return true; | ||
269 | } | ||
270 | |||
271 | nfit_memdev = devm_kzalloc(dev, sizeof(*nfit_memdev), GFP_KERNEL); | ||
248 | if (!nfit_memdev) | 272 | if (!nfit_memdev) |
249 | return false; | 273 | return false; |
250 | INIT_LIST_HEAD(&nfit_memdev->list); | 274 | INIT_LIST_HEAD(&nfit_memdev->list); |
@@ -257,12 +281,19 @@ static bool add_memdev(struct acpi_nfit_desc *acpi_desc, | |||
257 | } | 281 | } |
258 | 282 | ||
259 | static bool add_dcr(struct acpi_nfit_desc *acpi_desc, | 283 | static bool add_dcr(struct acpi_nfit_desc *acpi_desc, |
284 | struct nfit_table_prev *prev, | ||
260 | struct acpi_nfit_control_region *dcr) | 285 | struct acpi_nfit_control_region *dcr) |
261 | { | 286 | { |
262 | struct device *dev = acpi_desc->dev; | 287 | struct device *dev = acpi_desc->dev; |
263 | struct nfit_dcr *nfit_dcr = devm_kzalloc(dev, sizeof(*nfit_dcr), | 288 | struct nfit_dcr *nfit_dcr; |
264 | GFP_KERNEL); | 289 | |
290 | list_for_each_entry(nfit_dcr, &prev->dcrs, list) | ||
291 | if (memcmp(nfit_dcr->dcr, dcr, sizeof(*dcr)) == 0) { | ||
292 | list_move_tail(&nfit_dcr->list, &acpi_desc->dcrs); | ||
293 | return true; | ||
294 | } | ||
265 | 295 | ||
296 | nfit_dcr = devm_kzalloc(dev, sizeof(*nfit_dcr), GFP_KERNEL); | ||
266 | if (!nfit_dcr) | 297 | if (!nfit_dcr) |
267 | return false; | 298 | return false; |
268 | INIT_LIST_HEAD(&nfit_dcr->list); | 299 | INIT_LIST_HEAD(&nfit_dcr->list); |
@@ -274,12 +305,19 @@ static bool add_dcr(struct acpi_nfit_desc *acpi_desc, | |||
274 | } | 305 | } |
275 | 306 | ||
276 | static bool add_bdw(struct acpi_nfit_desc *acpi_desc, | 307 | static bool add_bdw(struct acpi_nfit_desc *acpi_desc, |
308 | struct nfit_table_prev *prev, | ||
277 | struct acpi_nfit_data_region *bdw) | 309 | struct acpi_nfit_data_region *bdw) |
278 | { | 310 | { |
279 | struct device *dev = acpi_desc->dev; | 311 | struct device *dev = acpi_desc->dev; |
280 | struct nfit_bdw *nfit_bdw = devm_kzalloc(dev, sizeof(*nfit_bdw), | 312 | struct nfit_bdw *nfit_bdw; |
281 | GFP_KERNEL); | 313 | |
314 | list_for_each_entry(nfit_bdw, &prev->bdws, list) | ||
315 | if (memcmp(nfit_bdw->bdw, bdw, sizeof(*bdw)) == 0) { | ||
316 | list_move_tail(&nfit_bdw->list, &acpi_desc->bdws); | ||
317 | return true; | ||
318 | } | ||
282 | 319 | ||
320 | nfit_bdw = devm_kzalloc(dev, sizeof(*nfit_bdw), GFP_KERNEL); | ||
283 | if (!nfit_bdw) | 321 | if (!nfit_bdw) |
284 | return false; | 322 | return false; |
285 | INIT_LIST_HEAD(&nfit_bdw->list); | 323 | INIT_LIST_HEAD(&nfit_bdw->list); |
@@ -291,12 +329,19 @@ static bool add_bdw(struct acpi_nfit_desc *acpi_desc, | |||
291 | } | 329 | } |
292 | 330 | ||
293 | static bool add_idt(struct acpi_nfit_desc *acpi_desc, | 331 | static bool add_idt(struct acpi_nfit_desc *acpi_desc, |
332 | struct nfit_table_prev *prev, | ||
294 | struct acpi_nfit_interleave *idt) | 333 | struct acpi_nfit_interleave *idt) |
295 | { | 334 | { |
296 | struct device *dev = acpi_desc->dev; | 335 | struct device *dev = acpi_desc->dev; |
297 | struct nfit_idt *nfit_idt = devm_kzalloc(dev, sizeof(*nfit_idt), | 336 | struct nfit_idt *nfit_idt; |
298 | GFP_KERNEL); | 337 | |
338 | list_for_each_entry(nfit_idt, &prev->idts, list) | ||
339 | if (memcmp(nfit_idt->idt, idt, sizeof(*idt)) == 0) { | ||
340 | list_move_tail(&nfit_idt->list, &acpi_desc->idts); | ||
341 | return true; | ||
342 | } | ||
299 | 343 | ||
344 | nfit_idt = devm_kzalloc(dev, sizeof(*nfit_idt), GFP_KERNEL); | ||
300 | if (!nfit_idt) | 345 | if (!nfit_idt) |
301 | return false; | 346 | return false; |
302 | INIT_LIST_HEAD(&nfit_idt->list); | 347 | INIT_LIST_HEAD(&nfit_idt->list); |
@@ -308,12 +353,19 @@ static bool add_idt(struct acpi_nfit_desc *acpi_desc, | |||
308 | } | 353 | } |
309 | 354 | ||
310 | static bool add_flush(struct acpi_nfit_desc *acpi_desc, | 355 | static bool add_flush(struct acpi_nfit_desc *acpi_desc, |
356 | struct nfit_table_prev *prev, | ||
311 | struct acpi_nfit_flush_address *flush) | 357 | struct acpi_nfit_flush_address *flush) |
312 | { | 358 | { |
313 | struct device *dev = acpi_desc->dev; | 359 | struct device *dev = acpi_desc->dev; |
314 | struct nfit_flush *nfit_flush = devm_kzalloc(dev, sizeof(*nfit_flush), | 360 | struct nfit_flush *nfit_flush; |
315 | GFP_KERNEL); | ||
316 | 361 | ||
362 | list_for_each_entry(nfit_flush, &prev->flushes, list) | ||
363 | if (memcmp(nfit_flush->flush, flush, sizeof(*flush)) == 0) { | ||
364 | list_move_tail(&nfit_flush->list, &acpi_desc->flushes); | ||
365 | return true; | ||
366 | } | ||
367 | |||
368 | nfit_flush = devm_kzalloc(dev, sizeof(*nfit_flush), GFP_KERNEL); | ||
317 | if (!nfit_flush) | 369 | if (!nfit_flush) |
318 | return false; | 370 | return false; |
319 | INIT_LIST_HEAD(&nfit_flush->list); | 371 | INIT_LIST_HEAD(&nfit_flush->list); |
@@ -324,8 +376,8 @@ static bool add_flush(struct acpi_nfit_desc *acpi_desc, | |||
324 | return true; | 376 | return true; |
325 | } | 377 | } |
326 | 378 | ||
327 | static void *add_table(struct acpi_nfit_desc *acpi_desc, void *table, | 379 | static void *add_table(struct acpi_nfit_desc *acpi_desc, |
328 | const void *end) | 380 | struct nfit_table_prev *prev, void *table, const void *end) |
329 | { | 381 | { |
330 | struct device *dev = acpi_desc->dev; | 382 | struct device *dev = acpi_desc->dev; |
331 | struct acpi_nfit_header *hdr; | 383 | struct acpi_nfit_header *hdr; |
@@ -335,29 +387,35 @@ static void *add_table(struct acpi_nfit_desc *acpi_desc, void *table, | |||
335 | return NULL; | 387 | return NULL; |
336 | 388 | ||
337 | hdr = table; | 389 | hdr = table; |
390 | if (!hdr->length) { | ||
391 | dev_warn(dev, "found a zero length table '%d' parsing nfit\n", | ||
392 | hdr->type); | ||
393 | return NULL; | ||
394 | } | ||
395 | |||
338 | switch (hdr->type) { | 396 | switch (hdr->type) { |
339 | case ACPI_NFIT_TYPE_SYSTEM_ADDRESS: | 397 | case ACPI_NFIT_TYPE_SYSTEM_ADDRESS: |
340 | if (!add_spa(acpi_desc, table)) | 398 | if (!add_spa(acpi_desc, prev, table)) |
341 | return err; | 399 | return err; |
342 | break; | 400 | break; |
343 | case ACPI_NFIT_TYPE_MEMORY_MAP: | 401 | case ACPI_NFIT_TYPE_MEMORY_MAP: |
344 | if (!add_memdev(acpi_desc, table)) | 402 | if (!add_memdev(acpi_desc, prev, table)) |
345 | return err; | 403 | return err; |
346 | break; | 404 | break; |
347 | case ACPI_NFIT_TYPE_CONTROL_REGION: | 405 | case ACPI_NFIT_TYPE_CONTROL_REGION: |
348 | if (!add_dcr(acpi_desc, table)) | 406 | if (!add_dcr(acpi_desc, prev, table)) |
349 | return err; | 407 | return err; |
350 | break; | 408 | break; |
351 | case ACPI_NFIT_TYPE_DATA_REGION: | 409 | case ACPI_NFIT_TYPE_DATA_REGION: |
352 | if (!add_bdw(acpi_desc, table)) | 410 | if (!add_bdw(acpi_desc, prev, table)) |
353 | return err; | 411 | return err; |
354 | break; | 412 | break; |
355 | case ACPI_NFIT_TYPE_INTERLEAVE: | 413 | case ACPI_NFIT_TYPE_INTERLEAVE: |
356 | if (!add_idt(acpi_desc, table)) | 414 | if (!add_idt(acpi_desc, prev, table)) |
357 | return err; | 415 | return err; |
358 | break; | 416 | break; |
359 | case ACPI_NFIT_TYPE_FLUSH_ADDRESS: | 417 | case ACPI_NFIT_TYPE_FLUSH_ADDRESS: |
360 | if (!add_flush(acpi_desc, table)) | 418 | if (!add_flush(acpi_desc, prev, table)) |
361 | return err; | 419 | return err; |
362 | break; | 420 | break; |
363 | case ACPI_NFIT_TYPE_SMBIOS: | 421 | case ACPI_NFIT_TYPE_SMBIOS: |
@@ -802,12 +860,7 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) | |||
802 | device_handle = __to_nfit_memdev(nfit_mem)->device_handle; | 860 | device_handle = __to_nfit_memdev(nfit_mem)->device_handle; |
803 | nvdimm = acpi_nfit_dimm_by_handle(acpi_desc, device_handle); | 861 | nvdimm = acpi_nfit_dimm_by_handle(acpi_desc, device_handle); |
804 | if (nvdimm) { | 862 | if (nvdimm) { |
805 | /* | 863 | dimm_count++; |
806 | * If for some reason we find multiple DCRs the | ||
807 | * first one wins | ||
808 | */ | ||
809 | dev_err(acpi_desc->dev, "duplicate DCR detected: %s\n", | ||
810 | nvdimm_name(nvdimm)); | ||
811 | continue; | 864 | continue; |
812 | } | 865 | } |
813 | 866 | ||
@@ -1476,6 +1529,9 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, | |||
1476 | struct resource res; | 1529 | struct resource res; |
1477 | int count = 0, rc; | 1530 | int count = 0, rc; |
1478 | 1531 | ||
1532 | if (nfit_spa->is_registered) | ||
1533 | return 0; | ||
1534 | |||
1479 | if (spa->range_index == 0) { | 1535 | if (spa->range_index == 0) { |
1480 | dev_dbg(acpi_desc->dev, "%s: detected invalid spa index\n", | 1536 | dev_dbg(acpi_desc->dev, "%s: detected invalid spa index\n", |
1481 | __func__); | 1537 | __func__); |
@@ -1529,6 +1585,8 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, | |||
1529 | if (!nvdimm_volatile_region_create(nvdimm_bus, ndr_desc)) | 1585 | if (!nvdimm_volatile_region_create(nvdimm_bus, ndr_desc)) |
1530 | return -ENOMEM; | 1586 | return -ENOMEM; |
1531 | } | 1587 | } |
1588 | |||
1589 | nfit_spa->is_registered = 1; | ||
1532 | return 0; | 1590 | return 0; |
1533 | } | 1591 | } |
1534 | 1592 | ||
@@ -1545,71 +1603,101 @@ static int acpi_nfit_register_regions(struct acpi_nfit_desc *acpi_desc) | |||
1545 | return 0; | 1603 | return 0; |
1546 | } | 1604 | } |
1547 | 1605 | ||
1606 | static int acpi_nfit_check_deletions(struct acpi_nfit_desc *acpi_desc, | ||
1607 | struct nfit_table_prev *prev) | ||
1608 | { | ||
1609 | struct device *dev = acpi_desc->dev; | ||
1610 | |||
1611 | if (!list_empty(&prev->spas) || | ||
1612 | !list_empty(&prev->memdevs) || | ||
1613 | !list_empty(&prev->dcrs) || | ||
1614 | !list_empty(&prev->bdws) || | ||
1615 | !list_empty(&prev->idts) || | ||
1616 | !list_empty(&prev->flushes)) { | ||
1617 | dev_err(dev, "new nfit deletes entries (unsupported)\n"); | ||
1618 | return -ENXIO; | ||
1619 | } | ||
1620 | return 0; | ||
1621 | } | ||
1622 | |||
1548 | int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz) | 1623 | int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz) |
1549 | { | 1624 | { |
1550 | struct device *dev = acpi_desc->dev; | 1625 | struct device *dev = acpi_desc->dev; |
1626 | struct nfit_table_prev prev; | ||
1551 | const void *end; | 1627 | const void *end; |
1552 | u8 *data; | 1628 | u8 *data; |
1553 | int rc; | 1629 | int rc; |
1554 | 1630 | ||
1555 | INIT_LIST_HEAD(&acpi_desc->spa_maps); | 1631 | mutex_lock(&acpi_desc->init_mutex); |
1556 | INIT_LIST_HEAD(&acpi_desc->spas); | 1632 | |
1557 | INIT_LIST_HEAD(&acpi_desc->dcrs); | 1633 | INIT_LIST_HEAD(&prev.spas); |
1558 | INIT_LIST_HEAD(&acpi_desc->bdws); | 1634 | INIT_LIST_HEAD(&prev.memdevs); |
1559 | INIT_LIST_HEAD(&acpi_desc->idts); | 1635 | INIT_LIST_HEAD(&prev.dcrs); |
1560 | INIT_LIST_HEAD(&acpi_desc->flushes); | 1636 | INIT_LIST_HEAD(&prev.bdws); |
1561 | INIT_LIST_HEAD(&acpi_desc->memdevs); | 1637 | INIT_LIST_HEAD(&prev.idts); |
1562 | INIT_LIST_HEAD(&acpi_desc->dimms); | 1638 | INIT_LIST_HEAD(&prev.flushes); |
1563 | mutex_init(&acpi_desc->spa_map_mutex); | 1639 | |
1640 | list_cut_position(&prev.spas, &acpi_desc->spas, | ||
1641 | acpi_desc->spas.prev); | ||
1642 | list_cut_position(&prev.memdevs, &acpi_desc->memdevs, | ||
1643 | acpi_desc->memdevs.prev); | ||
1644 | list_cut_position(&prev.dcrs, &acpi_desc->dcrs, | ||
1645 | acpi_desc->dcrs.prev); | ||
1646 | list_cut_position(&prev.bdws, &acpi_desc->bdws, | ||
1647 | acpi_desc->bdws.prev); | ||
1648 | list_cut_position(&prev.idts, &acpi_desc->idts, | ||
1649 | acpi_desc->idts.prev); | ||
1650 | list_cut_position(&prev.flushes, &acpi_desc->flushes, | ||
1651 | acpi_desc->flushes.prev); | ||
1564 | 1652 | ||
1565 | data = (u8 *) acpi_desc->nfit; | 1653 | data = (u8 *) acpi_desc->nfit; |
1566 | end = data + sz; | 1654 | end = data + sz; |
1567 | data += sizeof(struct acpi_table_nfit); | 1655 | data += sizeof(struct acpi_table_nfit); |
1568 | while (!IS_ERR_OR_NULL(data)) | 1656 | while (!IS_ERR_OR_NULL(data)) |
1569 | data = add_table(acpi_desc, data, end); | 1657 | data = add_table(acpi_desc, &prev, data, end); |
1570 | 1658 | ||
1571 | if (IS_ERR(data)) { | 1659 | if (IS_ERR(data)) { |
1572 | dev_dbg(dev, "%s: nfit table parsing error: %ld\n", __func__, | 1660 | dev_dbg(dev, "%s: nfit table parsing error: %ld\n", __func__, |
1573 | PTR_ERR(data)); | 1661 | PTR_ERR(data)); |
1574 | return PTR_ERR(data); | 1662 | rc = PTR_ERR(data); |
1663 | goto out_unlock; | ||
1575 | } | 1664 | } |
1576 | 1665 | ||
1577 | if (nfit_mem_init(acpi_desc) != 0) | 1666 | rc = acpi_nfit_check_deletions(acpi_desc, &prev); |
1578 | return -ENOMEM; | 1667 | if (rc) |
1668 | goto out_unlock; | ||
1669 | |||
1670 | if (nfit_mem_init(acpi_desc) != 0) { | ||
1671 | rc = -ENOMEM; | ||
1672 | goto out_unlock; | ||
1673 | } | ||
1579 | 1674 | ||
1580 | acpi_nfit_init_dsms(acpi_desc); | 1675 | acpi_nfit_init_dsms(acpi_desc); |
1581 | 1676 | ||
1582 | rc = acpi_nfit_register_dimms(acpi_desc); | 1677 | rc = acpi_nfit_register_dimms(acpi_desc); |
1583 | if (rc) | 1678 | if (rc) |
1584 | return rc; | 1679 | goto out_unlock; |
1680 | |||
1681 | rc = acpi_nfit_register_regions(acpi_desc); | ||
1585 | 1682 | ||
1586 | return acpi_nfit_register_regions(acpi_desc); | 1683 | out_unlock: |
1684 | mutex_unlock(&acpi_desc->init_mutex); | ||
1685 | return rc; | ||
1587 | } | 1686 | } |
1588 | EXPORT_SYMBOL_GPL(acpi_nfit_init); | 1687 | EXPORT_SYMBOL_GPL(acpi_nfit_init); |
1589 | 1688 | ||
1590 | static int acpi_nfit_add(struct acpi_device *adev) | 1689 | static struct acpi_nfit_desc *acpi_nfit_desc_init(struct acpi_device *adev) |
1591 | { | 1690 | { |
1592 | struct nvdimm_bus_descriptor *nd_desc; | 1691 | struct nvdimm_bus_descriptor *nd_desc; |
1593 | struct acpi_nfit_desc *acpi_desc; | 1692 | struct acpi_nfit_desc *acpi_desc; |
1594 | struct device *dev = &adev->dev; | 1693 | struct device *dev = &adev->dev; |
1595 | struct acpi_table_header *tbl; | ||
1596 | acpi_status status = AE_OK; | ||
1597 | acpi_size sz; | ||
1598 | int rc; | ||
1599 | |||
1600 | status = acpi_get_table_with_size("NFIT", 0, &tbl, &sz); | ||
1601 | if (ACPI_FAILURE(status)) { | ||
1602 | dev_err(dev, "failed to find NFIT\n"); | ||
1603 | return -ENXIO; | ||
1604 | } | ||
1605 | 1694 | ||
1606 | acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL); | 1695 | acpi_desc = devm_kzalloc(dev, sizeof(*acpi_desc), GFP_KERNEL); |
1607 | if (!acpi_desc) | 1696 | if (!acpi_desc) |
1608 | return -ENOMEM; | 1697 | return ERR_PTR(-ENOMEM); |
1609 | 1698 | ||
1610 | dev_set_drvdata(dev, acpi_desc); | 1699 | dev_set_drvdata(dev, acpi_desc); |
1611 | acpi_desc->dev = dev; | 1700 | acpi_desc->dev = dev; |
1612 | acpi_desc->nfit = (struct acpi_table_nfit *) tbl; | ||
1613 | acpi_desc->blk_do_io = acpi_nfit_blk_region_do_io; | 1701 | acpi_desc->blk_do_io = acpi_nfit_blk_region_do_io; |
1614 | nd_desc = &acpi_desc->nd_desc; | 1702 | nd_desc = &acpi_desc->nd_desc; |
1615 | nd_desc->provider_name = "ACPI.NFIT"; | 1703 | nd_desc->provider_name = "ACPI.NFIT"; |
@@ -1617,8 +1705,57 @@ static int acpi_nfit_add(struct acpi_device *adev) | |||
1617 | nd_desc->attr_groups = acpi_nfit_attribute_groups; | 1705 | nd_desc->attr_groups = acpi_nfit_attribute_groups; |
1618 | 1706 | ||
1619 | acpi_desc->nvdimm_bus = nvdimm_bus_register(dev, nd_desc); | 1707 | acpi_desc->nvdimm_bus = nvdimm_bus_register(dev, nd_desc); |
1620 | if (!acpi_desc->nvdimm_bus) | 1708 | if (!acpi_desc->nvdimm_bus) { |
1621 | return -ENXIO; | 1709 | devm_kfree(dev, acpi_desc); |
1710 | return ERR_PTR(-ENXIO); | ||
1711 | } | ||
1712 | |||
1713 | INIT_LIST_HEAD(&acpi_desc->spa_maps); | ||
1714 | INIT_LIST_HEAD(&acpi_desc->spas); | ||
1715 | INIT_LIST_HEAD(&acpi_desc->dcrs); | ||
1716 | INIT_LIST_HEAD(&acpi_desc->bdws); | ||
1717 | INIT_LIST_HEAD(&acpi_desc->idts); | ||
1718 | INIT_LIST_HEAD(&acpi_desc->flushes); | ||
1719 | INIT_LIST_HEAD(&acpi_desc->memdevs); | ||
1720 | INIT_LIST_HEAD(&acpi_desc->dimms); | ||
1721 | mutex_init(&acpi_desc->spa_map_mutex); | ||
1722 | mutex_init(&acpi_desc->init_mutex); | ||
1723 | |||
1724 | return acpi_desc; | ||
1725 | } | ||
1726 | |||
1727 | static int acpi_nfit_add(struct acpi_device *adev) | ||
1728 | { | ||
1729 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1730 | struct acpi_nfit_desc *acpi_desc; | ||
1731 | struct device *dev = &adev->dev; | ||
1732 | struct acpi_table_header *tbl; | ||
1733 | acpi_status status = AE_OK; | ||
1734 | acpi_size sz; | ||
1735 | int rc; | ||
1736 | |||
1737 | status = acpi_get_table_with_size("NFIT", 0, &tbl, &sz); | ||
1738 | if (ACPI_FAILURE(status)) { | ||
1739 | /* This is ok, we could have an nvdimm hotplugged later */ | ||
1740 | dev_dbg(dev, "failed to find NFIT at startup\n"); | ||
1741 | return 0; | ||
1742 | } | ||
1743 | |||
1744 | acpi_desc = acpi_nfit_desc_init(adev); | ||
1745 | if (IS_ERR(acpi_desc)) { | ||
1746 | dev_err(dev, "%s: error initializing acpi_desc: %ld\n", | ||
1747 | __func__, PTR_ERR(acpi_desc)); | ||
1748 | return PTR_ERR(acpi_desc); | ||
1749 | } | ||
1750 | |||
1751 | acpi_desc->nfit = (struct acpi_table_nfit *) tbl; | ||
1752 | |||
1753 | /* Evaluate _FIT and override with that if present */ | ||
1754 | status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf); | ||
1755 | if (ACPI_SUCCESS(status) && buf.length > 0) { | ||
1756 | acpi_desc->nfit = (struct acpi_table_nfit *)buf.pointer; | ||
1757 | sz = buf.length; | ||
1758 | } | ||
1622 | 1759 | ||
1623 | rc = acpi_nfit_init(acpi_desc, sz); | 1760 | rc = acpi_nfit_init(acpi_desc, sz); |
1624 | if (rc) { | 1761 | if (rc) { |
@@ -1636,6 +1773,54 @@ static int acpi_nfit_remove(struct acpi_device *adev) | |||
1636 | return 0; | 1773 | return 0; |
1637 | } | 1774 | } |
1638 | 1775 | ||
1776 | static void acpi_nfit_notify(struct acpi_device *adev, u32 event) | ||
1777 | { | ||
1778 | struct acpi_nfit_desc *acpi_desc = dev_get_drvdata(&adev->dev); | ||
1779 | struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
1780 | struct acpi_table_nfit *nfit_saved; | ||
1781 | struct device *dev = &adev->dev; | ||
1782 | acpi_status status; | ||
1783 | int ret; | ||
1784 | |||
1785 | dev_dbg(dev, "%s: event: %d\n", __func__, event); | ||
1786 | |||
1787 | device_lock(dev); | ||
1788 | if (!dev->driver) { | ||
1789 | /* dev->driver may be null if we're being removed */ | ||
1790 | dev_dbg(dev, "%s: no driver found for dev\n", __func__); | ||
1791 | return; | ||
1792 | } | ||
1793 | |||
1794 | if (!acpi_desc) { | ||
1795 | acpi_desc = acpi_nfit_desc_init(adev); | ||
1796 | if (IS_ERR(acpi_desc)) { | ||
1797 | dev_err(dev, "%s: error initializing acpi_desc: %ld\n", | ||
1798 | __func__, PTR_ERR(acpi_desc)); | ||
1799 | goto out_unlock; | ||
1800 | } | ||
1801 | } | ||
1802 | |||
1803 | /* Evaluate _FIT */ | ||
1804 | status = acpi_evaluate_object(adev->handle, "_FIT", NULL, &buf); | ||
1805 | if (ACPI_FAILURE(status)) { | ||
1806 | dev_err(dev, "failed to evaluate _FIT\n"); | ||
1807 | goto out_unlock; | ||
1808 | } | ||
1809 | |||
1810 | nfit_saved = acpi_desc->nfit; | ||
1811 | acpi_desc->nfit = (struct acpi_table_nfit *)buf.pointer; | ||
1812 | ret = acpi_nfit_init(acpi_desc, buf.length); | ||
1813 | if (!ret) { | ||
1814 | /* Merge failed, restore old nfit, and exit */ | ||
1815 | acpi_desc->nfit = nfit_saved; | ||
1816 | dev_err(dev, "failed to merge updated NFIT\n"); | ||
1817 | } | ||
1818 | kfree(buf.pointer); | ||
1819 | |||
1820 | out_unlock: | ||
1821 | device_unlock(dev); | ||
1822 | } | ||
1823 | |||
1639 | static const struct acpi_device_id acpi_nfit_ids[] = { | 1824 | static const struct acpi_device_id acpi_nfit_ids[] = { |
1640 | { "ACPI0012", 0 }, | 1825 | { "ACPI0012", 0 }, |
1641 | { "", 0 }, | 1826 | { "", 0 }, |
@@ -1648,6 +1833,7 @@ static struct acpi_driver acpi_nfit_driver = { | |||
1648 | .ops = { | 1833 | .ops = { |
1649 | .add = acpi_nfit_add, | 1834 | .add = acpi_nfit_add, |
1650 | .remove = acpi_nfit_remove, | 1835 | .remove = acpi_nfit_remove, |
1836 | .notify = acpi_nfit_notify, | ||
1651 | }, | 1837 | }, |
1652 | }; | 1838 | }; |
1653 | 1839 | ||