Skip to content

Commit c04ac67

Browse files
Lorenzo Pieralisirafaeljw
Lorenzo Pieralisi
authored andcommitted
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>
1 parent 4f0450a commit c04ac67

File tree

4 files changed

+124
-0
lines changed

4 files changed

+124
-0
lines changed

drivers/acpi/resource.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,41 @@ int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list,
635635
}
636636
EXPORT_SYMBOL_GPL(acpi_dev_get_resources);
637637

638+
static 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+
*/
666+
int 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+
}
671+
EXPORT_SYMBOL_GPL(acpi_dev_get_dma_resources);
672+
638673
/**
639674
* acpi_dev_filter_resource_type - Filter ACPI resource according to resource
640675
* types

drivers/acpi/scan.c

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1359,6 +1359,85 @@ enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
13591359
return DEV_DMA_NON_COHERENT;
13601360
}
13611361

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+
*/
1376+
int 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+
13621441
/**
13631442
* acpi_dma_configure - Set-up DMA configuration for the device.
13641443
* @dev: The pointer to the device

include/acpi/acpi_bus.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,8 @@ struct acpi_pci_root {
578578

579579
bool acpi_dma_supported(struct acpi_device *adev);
580580
enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev);
581+
int acpi_dma_get_range(struct device *dev, u64 *dma_addr, u64 *offset,
582+
u64 *size);
581583
int acpi_dma_configure(struct device *dev, enum dev_dma_attr attr);
582584
void acpi_dma_deconfigure(struct device *dev);
583585

include/linux/acpi.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,8 @@ void acpi_dev_free_resource_list(struct list_head *list);
427427
int acpi_dev_get_resources(struct acpi_device *adev, struct list_head *list,
428428
int (*preproc)(struct acpi_resource *, void *),
429429
void *preproc_data);
430+
int acpi_dev_get_dma_resources(struct acpi_device *adev,
431+
struct list_head *list);
430432
int acpi_dev_filter_resource_type(struct acpi_resource *ares,
431433
unsigned long types);
432434

@@ -774,6 +776,12 @@ static inline enum dev_dma_attr acpi_get_dma_attr(struct acpi_device *adev)
774776
return DEV_DMA_NOT_SUPPORTED;
775777
}
776778

779+
static inline int acpi_dma_get_range(struct device *dev, u64 *dma_addr,
780+
u64 *offset, u64 *size)
781+
{
782+
return -ENODEV;
783+
}
784+
777785
static inline int acpi_dma_configure(struct device *dev,
778786
enum dev_dma_attr attr)
779787
{

0 commit comments

Comments
 (0)