// SPDX-License-Identifier: GPL-2.0-only1/*2* ACPI helpers for DMA request / controller3*4* Based on of-dma.c5*6* Copyright (C) 2013, Intel Corporation7* Authors: Andy Shevchenko <[email protected]>8* Mika Westerberg <[email protected]>9*/1011#include <linux/acpi.h>12#include <linux/acpi_dma.h>13#include <linux/device.h>14#include <linux/dma-mapping.h>15#include <linux/err.h>16#include <linux/errno.h>17#include <linux/export.h>18#include <linux/ioport.h>19#include <linux/kernel.h>20#include <linux/list.h>21#include <linux/mutex.h>22#include <linux/property.h>23#include <linux/slab.h>24#include <linux/string.h>25#include <linux/types.h>2627static LIST_HEAD(acpi_dma_list);28static DEFINE_MUTEX(acpi_dma_lock);2930/**31* acpi_dma_parse_resource_group - match device and parse resource group32* @grp: CSRT resource group33* @adev: ACPI device to match with34* @adma: struct acpi_dma of the given DMA controller35*36* In order to match a device from DSDT table to the corresponding CSRT device37* we use MMIO address and IRQ.38*39* Return:40* 1 on success, 0 when no information is available, or appropriate errno value41* on error.42*/43static int acpi_dma_parse_resource_group(const struct acpi_csrt_group *grp,44struct acpi_device *adev, struct acpi_dma *adma)45{46const struct acpi_csrt_shared_info *si;47struct list_head resource_list;48struct resource_entry *rentry;49resource_size_t mem = 0, irq = 0;50int ret;5152if (grp->shared_info_length != sizeof(struct acpi_csrt_shared_info))53return -ENODEV;5455INIT_LIST_HEAD(&resource_list);56ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL);57if (ret <= 0)58return 0;5960list_for_each_entry(rentry, &resource_list, node) {61if (resource_type(rentry->res) == IORESOURCE_MEM)62mem = rentry->res->start;63else if (resource_type(rentry->res) == IORESOURCE_IRQ)64irq = rentry->res->start;65}6667acpi_dev_free_resource_list(&resource_list);6869/* Consider initial zero values as resource not found */70if (mem == 0 && irq == 0)71return 0;7273si = (const struct acpi_csrt_shared_info *)&grp[1];7475/* Match device by MMIO */76if (si->mmio_base_low != lower_32_bits(mem) ||77si->mmio_base_high != upper_32_bits(mem))78return 0;7980/*81* acpi_gsi_to_irq() can't be used because some platforms do not save82* registered IRQs in the MP table. Instead we just try to register83* the GSI, which is the core part of the above mentioned function.84*/85ret = acpi_register_gsi(NULL, si->gsi_interrupt, si->interrupt_mode, si->interrupt_polarity);86if (ret < 0)87return 0;8889/* Match device by Linux vIRQ */90if (ret != irq)91return 0;9293dev_dbg(&adev->dev, "matches with %.4s%04X (rev %u)\n",94(char *)&grp->vendor_id, grp->device_id, grp->revision);9596/* Check if the request line range is available */97if (si->base_request_line == 0 && si->num_handshake_signals == 0)98return 0;99100/* Set up DMA mask based on value from CSRT */101ret = dma_coerce_mask_and_coherent(&adev->dev,102DMA_BIT_MASK(si->dma_address_width));103if (ret)104return 0;105106adma->base_request_line = si->base_request_line;107adma->end_request_line = si->base_request_line +108si->num_handshake_signals - 1;109110dev_dbg(&adev->dev, "request line base: 0x%04x end: 0x%04x\n",111adma->base_request_line, adma->end_request_line);112113return 1;114}115116/**117* acpi_dma_parse_csrt - parse CSRT to extract additional DMA resources118* @adev: ACPI device to match with119* @adma: struct acpi_dma of the given DMA controller120*121* CSRT or Core System Resources Table is a proprietary ACPI table122* introduced by Microsoft. This table can contain devices that are not in123* the system DSDT table. In particular DMA controllers might be described124* here.125*126* We are using this table to get the request line range of the specific DMA127* controller to be used later.128*/129static void acpi_dma_parse_csrt(struct acpi_device *adev, struct acpi_dma *adma)130{131struct acpi_csrt_group *grp, *end;132struct acpi_table_csrt *csrt;133acpi_status status;134int ret;135136status = acpi_get_table(ACPI_SIG_CSRT, 0,137(struct acpi_table_header **)&csrt);138if (ACPI_FAILURE(status)) {139if (status != AE_NOT_FOUND)140dev_warn(&adev->dev, "failed to get the CSRT table\n");141return;142}143144grp = (struct acpi_csrt_group *)(csrt + 1);145end = (struct acpi_csrt_group *)((void *)csrt + csrt->header.length);146147while (grp < end) {148ret = acpi_dma_parse_resource_group(grp, adev, adma);149if (ret < 0) {150dev_warn(&adev->dev,151"error in parsing resource group\n");152break;153}154155grp = (struct acpi_csrt_group *)((void *)grp + grp->length);156}157158acpi_put_table((struct acpi_table_header *)csrt);159}160161/**162* acpi_dma_controller_register - Register a DMA controller to ACPI DMA helpers163* @dev: struct device of DMA controller164* @acpi_dma_xlate: translation function which converts a dma specifier165* into a dma_chan structure166* @data: pointer to controller specific data to be used by167* translation function168*169* Allocated memory should be freed with appropriate acpi_dma_controller_free()170* call.171*172* Return:173* 0 on success or appropriate errno value on error.174*/175int acpi_dma_controller_register(struct device *dev,176struct dma_chan *(*acpi_dma_xlate)177(struct acpi_dma_spec *, struct acpi_dma *),178void *data)179{180struct acpi_device *adev;181struct acpi_dma *adma;182183if (!dev || !acpi_dma_xlate)184return -EINVAL;185186/* Check if the device was enumerated by ACPI */187adev = ACPI_COMPANION(dev);188if (!adev)189return -EINVAL;190191adma = kzalloc(sizeof(*adma), GFP_KERNEL);192if (!adma)193return -ENOMEM;194195adma->dev = dev;196adma->acpi_dma_xlate = acpi_dma_xlate;197adma->data = data;198199acpi_dma_parse_csrt(adev, adma);200201/* Now queue acpi_dma controller structure in list */202mutex_lock(&acpi_dma_lock);203list_add_tail(&adma->dma_controllers, &acpi_dma_list);204mutex_unlock(&acpi_dma_lock);205206return 0;207}208EXPORT_SYMBOL_GPL(acpi_dma_controller_register);209210/**211* acpi_dma_controller_free - Remove a DMA controller from ACPI DMA helpers list212* @dev: struct device of DMA controller213*214* Memory allocated by acpi_dma_controller_register() is freed here.215*216* Return:217* 0 on success or appropriate errno value on error.218*/219int acpi_dma_controller_free(struct device *dev)220{221struct acpi_dma *adma;222223if (!dev)224return -EINVAL;225226mutex_lock(&acpi_dma_lock);227228list_for_each_entry(adma, &acpi_dma_list, dma_controllers)229if (adma->dev == dev) {230list_del(&adma->dma_controllers);231mutex_unlock(&acpi_dma_lock);232kfree(adma);233return 0;234}235236mutex_unlock(&acpi_dma_lock);237return -ENODEV;238}239EXPORT_SYMBOL_GPL(acpi_dma_controller_free);240241static void devm_acpi_dma_free(void *dev)242{243acpi_dma_controller_free(dev);244}245246/**247* devm_acpi_dma_controller_register - resource managed acpi_dma_controller_register()248* @dev: device that is registering this DMA controller249* @acpi_dma_xlate: translation function250* @data: pointer to controller specific data251*252* Managed acpi_dma_controller_register(). DMA controller registered by this253* function are automatically freed on driver detach. See254* acpi_dma_controller_register() for more information.255*256* Return:257* 0 on success or appropriate errno value on error.258*/259int devm_acpi_dma_controller_register(struct device *dev,260struct dma_chan *(*acpi_dma_xlate)261(struct acpi_dma_spec *, struct acpi_dma *),262void *data)263{264int ret;265266ret = acpi_dma_controller_register(dev, acpi_dma_xlate, data);267if (ret)268return ret;269270return devm_add_action_or_reset(dev, devm_acpi_dma_free, dev);271}272EXPORT_SYMBOL_GPL(devm_acpi_dma_controller_register);273274/**275* acpi_dma_update_dma_spec - prepare dma specifier to pass to translation function276* @adma: struct acpi_dma of DMA controller277* @dma_spec: dma specifier to update278*279* Accordingly to ACPI 5.0 Specification Table 6-170 "Fixed DMA Resource280* Descriptor":281* DMA Request Line bits is a platform-relative number uniquely282* identifying the request line assigned. Request line-to-Controller283* mapping is done in a controller-specific OS driver.284* That's why we can safely adjust slave_id when the appropriate controller is285* found.286*287* Return:288* 0, if no information is available, -1 on mismatch, and 1 otherwise.289*/290static int acpi_dma_update_dma_spec(struct acpi_dma *adma,291struct acpi_dma_spec *dma_spec)292{293/* Set link to the DMA controller device */294dma_spec->dev = adma->dev;295296/* Check if the request line range is available */297if (adma->base_request_line == 0 && adma->end_request_line == 0)298return 0;299300/* Check if slave_id falls to the range */301if (dma_spec->slave_id < adma->base_request_line ||302dma_spec->slave_id > adma->end_request_line)303return -1;304305/*306* Here we adjust slave_id. It should be a relative number to the base307* request line.308*/309dma_spec->slave_id -= adma->base_request_line;310311return 1;312}313314struct acpi_dma_parser_data {315struct acpi_dma_spec dma_spec;316size_t index;317size_t n;318};319320/**321* acpi_dma_parse_fixed_dma - Parse FixedDMA ACPI resources to a DMA specifier322* @res: struct acpi_resource to get FixedDMA resources from323* @data: pointer to a helper struct acpi_dma_parser_data324*/325static int acpi_dma_parse_fixed_dma(struct acpi_resource *res, void *data)326{327struct acpi_dma_parser_data *pdata = data;328329if (res->type == ACPI_RESOURCE_TYPE_FIXED_DMA) {330struct acpi_resource_fixed_dma *dma = &res->data.fixed_dma;331332if (pdata->n++ == pdata->index) {333pdata->dma_spec.chan_id = dma->channels;334pdata->dma_spec.slave_id = dma->request_lines;335}336}337338/* Tell the ACPI core to skip this resource */339return 1;340}341342/**343* acpi_dma_request_slave_chan_by_index - Get the DMA slave channel344* @dev: struct device to get DMA request from345* @index: index of FixedDMA descriptor for @dev346*347* Return:348* Pointer to appropriate dma channel on success or an error pointer.349*/350struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev,351size_t index)352{353struct acpi_dma_parser_data pdata;354struct acpi_dma_spec *dma_spec = &pdata.dma_spec;355struct acpi_device *adev = ACPI_COMPANION(dev);356struct list_head resource_list;357struct acpi_dma *adma;358struct dma_chan *chan = NULL;359int found;360int ret;361362memset(&pdata, 0, sizeof(pdata));363pdata.index = index;364365/* Initial values for the request line and channel */366dma_spec->chan_id = -1;367dma_spec->slave_id = -1;368369INIT_LIST_HEAD(&resource_list);370ret = acpi_dev_get_resources(adev, &resource_list,371acpi_dma_parse_fixed_dma, &pdata);372acpi_dev_free_resource_list(&resource_list);373if (ret < 0)374return ERR_PTR(ret);375376if (dma_spec->slave_id < 0 || dma_spec->chan_id < 0)377return ERR_PTR(-ENODEV);378379mutex_lock(&acpi_dma_lock);380381list_for_each_entry(adma, &acpi_dma_list, dma_controllers) {382/*383* We are not going to call translation function if slave_id384* doesn't fall to the request range.385*/386found = acpi_dma_update_dma_spec(adma, dma_spec);387if (found < 0)388continue;389chan = adma->acpi_dma_xlate(dma_spec, adma);390/*391* Try to get a channel only from the DMA controller that392* matches the slave_id. See acpi_dma_update_dma_spec()393* description for the details.394*/395if (found > 0 || chan)396break;397}398399mutex_unlock(&acpi_dma_lock);400return chan ? chan : ERR_PTR(-EPROBE_DEFER);401}402EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_index);403404/**405* acpi_dma_request_slave_chan_by_name - Get the DMA slave channel406* @dev: struct device to get DMA request from407* @name: represents corresponding FixedDMA descriptor for @dev408*409* In order to support both Device Tree and ACPI in a single driver we410* translate the names "tx" and "rx" here based on the most common case where411* the first FixedDMA descriptor is TX and second is RX.412*413* If the device has "dma-names" property the FixedDMA descriptor indices414* are retrieved based on those. Otherwise the function falls back using415* hardcoded indices.416*417* Return:418* Pointer to appropriate dma channel on success or an error pointer.419*/420struct dma_chan *acpi_dma_request_slave_chan_by_name(struct device *dev,421const char *name)422{423int index;424425index = device_property_match_string(dev, "dma-names", name);426if (index < 0) {427if (!strcmp(name, "tx"))428index = 0;429else if (!strcmp(name, "rx"))430index = 1;431else432return ERR_PTR(-ENODEV);433}434435dev_dbg(dev, "Looking for DMA channel \"%s\" at index %d...\n", name, index);436return acpi_dma_request_slave_chan_by_index(dev, index);437}438EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_name);439440/**441* acpi_dma_simple_xlate - Simple ACPI DMA engine translation helper442* @dma_spec: pointer to ACPI DMA specifier443* @adma: pointer to ACPI DMA controller data444*445* A simple translation function for ACPI based devices. Passes &struct446* dma_spec to the DMA controller driver provided filter function.447*448* Return:449* Pointer to the channel if found or %NULL otherwise.450*/451struct dma_chan *acpi_dma_simple_xlate(struct acpi_dma_spec *dma_spec,452struct acpi_dma *adma)453{454struct acpi_dma_filter_info *info = adma->data;455456if (!info || !info->filter_fn)457return NULL;458459return dma_request_channel(info->dma_cap, info->filter_fn, dma_spec);460}461EXPORT_SYMBOL_GPL(acpi_dma_simple_xlate);462463464