aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLorenzo Pieralisi <lorenzo.pieralisi@arm.com>2017-08-07 06:29:48 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2017-08-07 08:28:51 -0400
commitc04ac679c6b86e4e36fbb675c6c061b4091f5810 (patch)
tree52a0da44e4652e433d94f198a6f4e96547cf88d6
parent4f0450af530e62b0217522cab4803b5a65dccc46 (diff)
ACPI: Introduce DMA ranges parsing
Some devices have limited addressing capabilities and cannot reference the whole memory address space while carrying out DMA operations (eg some devices with bus address bits range smaller than system bus - which prevents them from using bus addresses that are otherwise valid for the system). The ACPI _DMA object allows bus devices to define the DMA window that is actually addressable by devices that sit upstream the bus, therefore providing a means to parse and initialize the devices DMA masks and addressable DMA range size. By relying on the generic ACPI kernel layer to retrieve and parse resources, introduce ACPI core code to parse the _DMA object. Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Tested-by: Nate Watterson <nwatters@codeaurora.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/acpi/resource.c35
-rw-r--r--drivers/acpi/scan.c79
-rw-r--r--include/acpi/acpi_bus.h2
-rw-r--r--include/linux/acpi.h8
4 files changed, 124 insertions, 0 deletions
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index 93f1b5ce89b9..d85e010ee2cc 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -635,6 +635,41 @@ int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list,
635} 635}
636EXPORT_SYMBOL_GPL(acpi_dev_get_resources); 636EXPORT_SYMBOL_GPL(acpi_dev_get_resources);
637 637
638static int is_memory(struct acpi_resource *ares, void *not_used)
639{
640 struct resource_win win;
641 struct resource *res = &win.res;
642
643 memset(&win, 0, sizeof(win));
644
645 return !(acpi_dev_resource_memory(ares, res)
646 || acpi_dev_resource_address_space(ares, &win)
647 || acpi_dev_resource_ext_address_space(ares, &win));
648}
649
650/**
651 * acpi_dev_get_dma_resources - Get current DMA resources of a device.
652 * @adev: ACPI device node to get the resources for.
653 * @list: Head of the resultant list of resources (must be empty).
654 *
655 * Evaluate the _DMA method for the given device node and process its
656 * output.
657 *
658 * The resultant struct resource objects are put on the list pointed to
659 * by @list, that must be empty initially, as members of struct
660 * resource_entry objects. Callers of this routine should use
661 * %acpi_dev_free_resource_list() to free that list.
662 *
663 * The number of resources in the output list is returned on success,
664 * an error code reflecting the error condition is returned otherwise.
665 */
666int acpi_dev_get_dma_resources(struct acpi_device *adev, struct list_head *list)
667{
668 return __acpi_dev_get_resources(adev, list, is_memory, NULL,
669 METHOD_NAME__DMA);
670}
671EXPORT_SYMBOL_GPL(acpi_dev_get_dma_resources);
672
638/** 673/**
639 * acpi_dev_filter_resource_type - Filter ACPI resource according to resource 674 * acpi_dev_filter_resource_type - Filter ACPI resource according to resource
640 * types 675 * types
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index 33897298f03e..94500d99f2d6 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1360,6 +1360,85 @@ enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
1360} 1360}
1361 1361
1362/** 1362/**
1363 * acpi_dma_get_range() - Get device DMA parameters.
1364 *
1365 * @dev: device to configure
1366 * @dma_addr: pointer device DMA address result
1367 * @offset: pointer to the DMA offset result
1368 * @size: pointer to DMA range size result
1369 *
1370 * Evaluate DMA regions and return respectively DMA region start, offset
1371 * and size in dma_addr, offset and size on parsing success; it does not
1372 * update the passed in values on failure.
1373 *
1374 * Return 0 on success, < 0 on failure.
1375 */
1376int acpi_dma_get_range(struct device *dev, u64 *dma_addr, u64 *offset,
1377 u64 *size)
1378{
1379 struct acpi_device *adev;
1380 LIST_HEAD(list);
1381 struct resource_entry *rentry;
1382 int ret;
1383 struct device *dma_dev = dev;
1384 u64 len, dma_start = U64_MAX, dma_end = 0, dma_offset = 0;
1385
1386 /*
1387 * Walk the device tree chasing an ACPI companion with a _DMA
1388 * object while we go. Stop if we find a device with an ACPI
1389 * companion containing a _DMA method.
1390 */
1391 do {
1392 adev = ACPI_COMPANION(dma_dev);
1393 if (adev && acpi_has_method(adev->handle, METHOD_NAME__DMA))
1394 break;
1395
1396 dma_dev = dma_dev->parent;
1397 } while (dma_dev);
1398
1399 if (!dma_dev)
1400 return -ENODEV;
1401
1402 if (!acpi_has_method(adev->handle, METHOD_NAME__CRS)) {
1403 acpi_handle_warn(adev->handle, "_DMA is valid only if _CRS is present\n");
1404 return -EINVAL;
1405 }
1406
1407 ret = acpi_dev_get_dma_resources(adev, &list);
1408 if (ret > 0) {
1409 list_for_each_entry(rentry, &list, node) {
1410 if (dma_offset && rentry->offset != dma_offset) {
1411 ret = -EINVAL;
1412 dev_warn(dma_dev, "Can't handle multiple windows with different offsets\n");
1413 goto out;
1414 }
1415 dma_offset = rentry->offset;
1416
1417 /* Take lower and upper limits */
1418 if (rentry->res->start < dma_start)
1419 dma_start = rentry->res->start;
1420 if (rentry->res->end > dma_end)
1421 dma_end = rentry->res->end;
1422 }
1423
1424 if (dma_start >= dma_end) {
1425 ret = -EINVAL;
1426 dev_dbg(dma_dev, "Invalid DMA regions configuration\n");
1427 goto out;
1428 }
1429
1430 *dma_addr = dma_start - dma_offset;
1431 len = dma_end - dma_start;
1432 *size = max(len, len + 1);
1433 *offset = dma_offset;
1434 }
1435 out:
1436 acpi_dev_free_resource_list(&list);
1437
1438 return ret >= 0 ? 0 : ret;
1439}
1440
1441/**
1363 * acpi_dma_configure - Set-up DMA configuration for the device. 1442 * acpi_dma_configure - Set-up DMA configuration for the device.
1364 * @dev: The pointer to the device 1443 * @dev: The pointer to the device
1365 * @attr: device dma attributes 1444 * @attr: device dma attributes
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 68bc6be447fd..07eb963b5026 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -578,6 +578,8 @@ struct acpi_pci_root {
578 578
579bool acpi_dma_supported(struct acpi_device *adev); 579bool acpi_dma_supported(struct acpi_device *adev);
580enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev); 580enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev);
581int acpi_dma_get_range(struct device *dev, u64 *dma_addr, u64 *offset,
582 u64 *size);
581int acpi_dma_configure(struct device *dev, enum dev_dma_attr attr); 583int acpi_dma_configure(struct device *dev, enum dev_dma_attr attr);
582void acpi_dma_deconfigure(struct device *dev); 584void acpi_dma_deconfigure(struct device *dev);
583 585
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index c749eef1daa1..a5eaff9f2c6d 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -427,6 +427,8 @@ void acpi_dev_free_resource_list(struct list_head *list);
427int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list, 427int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list,
428 int (*preproc)(struct acpi_resource *, void *), 428 int (*preproc)(struct acpi_resource *, void *),
429 void *preproc_data); 429 void *preproc_data);
430int acpi_dev_get_dma_resources(struct acpi_device *adev,
431 struct list_head *list);
430int acpi_dev_filter_resource_type(struct acpi_resource *ares, 432int acpi_dev_filter_resource_type(struct acpi_resource *ares,
431 unsigned long types); 433 unsigned long types);
432 434
@@ -774,6 +776,12 @@ static inline enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
774 return DEV_DMA_NOT_SUPPORTED; 776 return DEV_DMA_NOT_SUPPORTED;
775} 777}
776 778
779static inline int acpi_dma_get_range(struct device *dev, u64 *dma_addr,
780 u64 *offset, u64 *size)
781{
782 return -ENODEV;
783}
784
777static inline int acpi_dma_configure(struct device *dev, 785static inline int acpi_dma_configure(struct device *dev,
778 enum dev_dma_attr attr) 786 enum dev_dma_attr attr)
779{ 787{