// SPDX-License-Identifier: GPL-2.0
#include <linux/device.h>
#include <linux/pci.h>
#include "pci.h"
/*
* On the state of PCI ' s devres implementation :
*
* The older PCI devres API has one significant problem :
*
* It is very strongly tied to the statically allocated mapping table in struct
* pcim_iomap_devres below . This is mostly solved in the sense of the pcim_
* functions in this file providing things like ranged mapping by bypassing
* this table , whereas the functions that were present in the old API still
* enter the mapping addresses into the table for users of the old API .
*
* TODO :
* Remove the legacy table entirely once all calls to pcim_iomap_table ( ) in
* the kernel have been removed .
*/
/*
* Legacy struct storing addresses to whole mapped BARs .
*/
struct pcim_iomap_devres {
void __iomem *table[PCI_NUM_RESOURCES];
};
/* Used to restore the old INTx state on driver detach. */
struct pcim_intx_devres {
int orig_intx;
};
enum pcim_addr_devres_type {
/* Default initializer. */
PCIM_ADDR_DEVRES_TYPE_INVALID,
/* A requested region spanning an entire BAR. */
PCIM_ADDR_DEVRES_TYPE_REGION,
/*
* A requested region spanning an entire BAR , and a mapping for
* the entire BAR .
*/
PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING,
/*
* A mapping within a BAR , either spanning the whole BAR or just a
* range . Without a requested region .
*/
PCIM_ADDR_DEVRES_TYPE_MAPPING,
};
/*
* This struct envelops IO or MEM addresses , i . e . , mappings and region
* requests , because those are very frequently requested and released
* together .
*/
struct pcim_addr_devres {
enum pcim_addr_devres_type type;
void __iomem *baseaddr;
unsigned long offset;
unsigned long len;
int bar;
};
static inline void pcim_addr_devres_clear(struct pcim_addr_devres *res)
{
memset(res, 0 , sizeof (*res));
res->bar = -1 ;
}
static void pcim_addr_resource_release(struct device *dev, void *resource_raw)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct pcim_addr_devres *res = resource_raw;
switch (res->type) {
case PCIM_ADDR_DEVRES_TYPE_REGION:
pci_release_region(pdev, res->bar);
break ;
case PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING:
pci_iounmap(pdev, res->baseaddr);
pci_release_region(pdev, res->bar);
break ;
case PCIM_ADDR_DEVRES_TYPE_MAPPING:
pci_iounmap(pdev, res->baseaddr);
break ;
default :
break ;
}
}
static struct pcim_addr_devres *pcim_addr_devres_alloc(struct pci_dev *pdev)
{
struct pcim_addr_devres *res;
res = devres_alloc_node(pcim_addr_resource_release, sizeof (*res),
GFP_KERNEL, dev_to_node(&pdev->dev));
if (res)
pcim_addr_devres_clear(res);
return res;
}
/* Just for consistency and readability. */
static inline void pcim_addr_devres_free(struct pcim_addr_devres *res)
{
devres_free(res);
}
/*
* Used by devres to identify a pcim_addr_devres .
*/
static int pcim_addr_resources_match(struct device *dev,
void *a_raw, void *b_raw)
{
struct pcim_addr_devres *a, *b;
a = a_raw;
b = b_raw;
if (a->type != b->type)
return 0 ;
switch (a->type) {
case PCIM_ADDR_DEVRES_TYPE_REGION:
case PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING:
return a->bar == b->bar;
case PCIM_ADDR_DEVRES_TYPE_MAPPING:
return a->baseaddr == b->baseaddr;
default :
return 0 ;
}
}
static void devm_pci_unmap_iospace(struct device *dev, void *ptr)
{
struct resource **res = ptr;
pci_unmap_iospace(*res);
}
/**
* devm_pci_remap_iospace - Managed pci_remap_iospace ( )
* @ dev : Generic device to remap IO address for
* @ res : Resource describing the I / O space
* @ phys_addr : physical address of range to be mapped
*
* Managed pci_remap_iospace ( ) . Map is automatically unmapped on driver
* detach .
*/
int devm_pci_remap_iospace(struct device *dev, const struct resource *res,
phys_addr_t phys_addr)
{
const struct resource **ptr;
int error;
ptr = devres_alloc(devm_pci_unmap_iospace, sizeof (*ptr), GFP_KERNEL);
if (!ptr)
return -ENOMEM;
error = pci_remap_iospace(res, phys_addr);
if (error) {
devres_free(ptr);
} else {
*ptr = res;
devres_add(dev, ptr);
}
return error;
}
EXPORT_SYMBOL(devm_pci_remap_iospace);
/**
* devm_pci_remap_cfgspace - Managed pci_remap_cfgspace ( )
* @ dev : Generic device to remap IO address for
* @ offset : Resource address to map
* @ size : Size of map
*
* Managed pci_remap_cfgspace ( ) . Map is automatically unmapped on driver
* detach .
*/
void __iomem *devm_pci_remap_cfgspace(struct device *dev,
resource_size_t offset,
resource_size_t size)
{
void __iomem **ptr, *addr;
ptr = devres_alloc(devm_ioremap_release, sizeof (*ptr), GFP_KERNEL);
if (!ptr)
return NULL;
addr = pci_remap_cfgspace(offset, size);
if (addr) {
*ptr = addr;
devres_add(dev, ptr);
} else
devres_free(ptr);
return addr;
}
EXPORT_SYMBOL(devm_pci_remap_cfgspace);
/**
* devm_pci_remap_cfg_resource - check , request region and ioremap cfg resource
* @ dev : generic device to handle the resource for
* @ res : configuration space resource to be handled
*
* Checks that a resource is a valid memory region , requests the memory
* region and ioremaps with pci_remap_cfgspace ( ) API that ensures the
* proper PCI configuration space memory attributes are guaranteed .
*
* All operations are managed and will be undone on driver detach .
*
* Returns a pointer to the remapped memory or an IOMEM_ERR_PTR ( ) encoded error
* code on failure . Usage example : :
*
* res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
* base = devm_pci_remap_cfg_resource ( & pdev - > dev , res ) ;
* if ( IS_ERR ( base ) )
* return PTR_ERR ( base ) ;
*/
void __iomem *devm_pci_remap_cfg_resource(struct device *dev,
struct resource *res)
{
resource_size_t size;
const char *name;
void __iomem *dest_ptr;
BUG_ON(!dev);
if (!res || resource_type(res) != IORESOURCE_MEM) {
dev_err(dev, "invalid resource\n" );
return IOMEM_ERR_PTR(-EINVAL);
}
size = resource_size(res);
if (res->name)
name = devm_kasprintf(dev, GFP_KERNEL, "%s %s" , dev_name(dev),
res->name);
else
name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
if (!name)
return IOMEM_ERR_PTR(-ENOMEM);
if (!devm_request_mem_region(dev, res->start, size, name)) {
dev_err(dev, "can't request region for resource %pR\n" , res);
return IOMEM_ERR_PTR(-EBUSY);
}
dest_ptr = devm_pci_remap_cfgspace(dev, res->start, size);
if (!dest_ptr) {
dev_err(dev, "ioremap failed for resource %pR\n" , res);
devm_release_mem_region(dev, res->start, size);
dest_ptr = IOMEM_ERR_PTR(-ENOMEM);
}
return dest_ptr;
}
EXPORT_SYMBOL(devm_pci_remap_cfg_resource);
static void __pcim_clear_mwi(void *pdev_raw)
{
struct pci_dev *pdev = pdev_raw;
pci_clear_mwi(pdev);
}
/**
* pcim_set_mwi - a device - managed pci_set_mwi ( )
* @ pdev : the PCI device for which MWI is enabled
*
* Managed pci_set_mwi ( ) .
*
* RETURNS : An appropriate - ERRNO error value on error , or zero for success .
*/
int pcim_set_mwi(struct pci_dev *pdev)
{
int ret;
ret = devm_add_action(&pdev->dev, __pcim_clear_mwi, pdev);
if (ret != 0 )
return ret;
ret = pci_set_mwi(pdev);
if (ret != 0 )
devm_remove_action(&pdev->dev, __pcim_clear_mwi, pdev);
return ret;
}
EXPORT_SYMBOL(pcim_set_mwi);
static inline bool mask_contains_bar(int mask, int bar)
{
return mask & BIT(bar);
}
static void pcim_intx_restore(struct device *dev, void *data)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct pcim_intx_devres *res = data;
pci_intx(pdev, res->orig_intx);
}
static void save_orig_intx(struct pci_dev *pdev, struct pcim_intx_devres *res)
{
u16 pci_command;
pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
res->orig_intx = !(pci_command & PCI_COMMAND_INTX_DISABLE);
}
/**
* pcim_intx - managed pci_intx ( )
* @ pdev : the PCI device to operate on
* @ enable : boolean : whether to enable or disable PCI INTx
*
* Returns : 0 on success , - ENOMEM on error .
*
* Enable / disable PCI INTx for device @ pdev .
* Restore the original state on driver detach .
*/
int pcim_intx(struct pci_dev *pdev, int enable)
{
struct pcim_intx_devres *res;
struct device *dev = &pdev->dev;
/*
* pcim_intx ( ) must only restore the INTx value that existed before the
* driver was loaded , i . e . , before it called pcim_intx ( ) for the
* first time .
*/
res = devres_find(dev, pcim_intx_restore, NULL, NULL);
if (!res) {
res = devres_alloc(pcim_intx_restore, sizeof (*res), GFP_KERNEL);
if (!res)
return -ENOMEM;
save_orig_intx(pdev, res);
devres_add(dev, res);
}
pci_intx(pdev, enable);
return 0 ;
}
EXPORT_SYMBOL_GPL(pcim_intx);
static void pcim_disable_device(void *pdev_raw)
{
struct pci_dev *pdev = pdev_raw;
if (!pdev->pinned)
pci_disable_device(pdev);
pdev->is_managed = false ;
}
/**
* pcim_enable_device - Managed pci_enable_device ( )
* @ pdev : PCI device to be initialized
*
* Returns : 0 on success , negative error code on failure .
*
* Managed pci_enable_device ( ) . Device will automatically be disabled on
* driver detach .
*/
int pcim_enable_device(struct pci_dev *pdev)
{
int ret;
ret = devm_add_action(&pdev->dev, pcim_disable_device, pdev);
if (ret != 0 )
return ret;
/*
* We prefer removing the action in case of an error over
* devm_add_action_or_reset ( ) because the latter could theoretically be
* disturbed by users having pinned the device too soon .
*/
ret = pci_enable_device(pdev);
if (ret != 0 ) {
devm_remove_action(&pdev->dev, pcim_disable_device, pdev);
return ret;
}
pdev->is_managed = true ;
return ret;
}
EXPORT_SYMBOL(pcim_enable_device);
/**
* pcim_pin_device - Pin managed PCI device
* @ pdev : PCI device to pin
*
* Pin managed PCI device @ pdev . Pinned device won ' t be disabled on driver
* detach . @ pdev must have been enabled with pcim_enable_device ( ) .
*/
void pcim_pin_device(struct pci_dev *pdev)
{
pdev->pinned = true ;
}
EXPORT_SYMBOL(pcim_pin_device);
static void pcim_iomap_release(struct device *gendev, void *res)
{
/*
* Do nothing . This is legacy code .
*
* Cleanup of the mappings is now done directly through the callbacks
* registered when creating them .
*/
}
/**
* pcim_iomap_table - access iomap allocation table ( DEPRECATED )
* @ pdev : PCI device to access iomap table for
*
* Returns :
* Const pointer to array of _ _ iomem pointers on success , NULL on failure .
*
* Access iomap allocation table for @ dev . If iomap table doesn ' t
* exist and @ pdev is managed , it will be allocated . All iomaps
* recorded in the iomap table are automatically unmapped on driver
* detach .
*
* This function might sleep when the table is first allocated but can
* be safely called without context and guaranteed to succeed once
* allocated .
*
* This function is DEPRECATED . Do not use it in new code . Instead , obtain a
* mapping ' s address directly from one of the pcim_ * mapping functions . For
* example :
* void _ _ iomem \ * mappy = pcim_iomap ( pdev , bar , length ) ;
*/
void __iomem * const *pcim_iomap_table(struct pci_dev *pdev)
{
struct pcim_iomap_devres *dr, *new_dr;
dr = devres_find(&pdev->dev, pcim_iomap_release, NULL, NULL);
if (dr)
return dr->table;
new_dr = devres_alloc_node(pcim_iomap_release, sizeof (*new_dr), GFP_KERNEL,
dev_to_node(&pdev->dev));
if (!new_dr)
return NULL;
dr = devres_get(&pdev->dev, new_dr, NULL, NULL);
return dr->table;
}
EXPORT_SYMBOL(pcim_iomap_table);
/*
* Fill the legacy mapping - table , so that drivers using the old API can
* still get a BAR ' s mapping address through pcim_iomap_table ( ) .
*/
static int pcim_add_mapping_to_legacy_table(struct pci_dev *pdev,
void __iomem *mapping, int bar)
{
void __iomem **legacy_iomap_table;
if (!pci_bar_index_is_valid(bar))
return -EINVAL;
legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev);
if (!legacy_iomap_table)
return -ENOMEM;
/* The legacy mechanism doesn't allow for duplicate mappings. */
WARN_ON(legacy_iomap_table[bar]);
legacy_iomap_table[bar] = mapping;
return 0 ;
}
/*
* Remove a mapping . The table only contains whole - BAR mappings , so this will
* never interfere with ranged mappings .
*/
static void pcim_remove_mapping_from_legacy_table(struct pci_dev *pdev,
void __iomem *addr)
{
int bar;
void __iomem **legacy_iomap_table;
legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev);
if (!legacy_iomap_table)
return ;
for (bar = 0 ; bar < PCI_STD_NUM_BARS; bar++) {
if (legacy_iomap_table[bar] == addr) {
legacy_iomap_table[bar] = NULL;
return ;
}
}
}
/*
* The same as pcim_remove_mapping_from_legacy_table ( ) , but identifies the
* mapping by its BAR index .
*/
static void pcim_remove_bar_from_legacy_table(struct pci_dev *pdev, int bar)
{
void __iomem **legacy_iomap_table;
if (!pci_bar_index_is_valid(bar))
return ;
legacy_iomap_table = (void __iomem **)pcim_iomap_table(pdev);
if (!legacy_iomap_table)
return ;
legacy_iomap_table[bar] = NULL;
}
/**
* pcim_iomap - Managed pcim_iomap ( )
* @ pdev : PCI device to iomap for
* @ bar : BAR to iomap
* @ maxlen : Maximum length of iomap
*
* Returns : _ _ iomem pointer on success , NULL on failure .
*
* Managed pci_iomap ( ) . Map is automatically unmapped on driver detach . If
* desired , unmap manually only with pcim_iounmap ( ) .
*
* This SHOULD only be used once per BAR .
*
* NOTE :
* Contrary to the other pcim_ * functions , this function does not return an
* IOMEM_ERR_PTR ( ) on failure , but a simple NULL . This is done for backwards
* compatibility .
*/
void __iomem *pcim_iomap(struct pci_dev *pdev, int bar, unsigned long maxlen)
{
void __iomem *mapping;
struct pcim_addr_devres *res;
if (!pci_bar_index_is_valid(bar))
return NULL;
res = pcim_addr_devres_alloc(pdev);
if (!res)
return NULL;
res->type = PCIM_ADDR_DEVRES_TYPE_MAPPING;
mapping = pci_iomap(pdev, bar, maxlen);
if (!mapping)
goto err_iomap;
res->baseaddr = mapping;
if (pcim_add_mapping_to_legacy_table(pdev, mapping, bar) != 0 )
goto err_table;
devres_add(&pdev->dev, res);
return mapping;
err_table:
pci_iounmap(pdev, mapping);
err_iomap:
pcim_addr_devres_free(res);
return NULL;
}
EXPORT_SYMBOL(pcim_iomap);
/**
* pcim_iounmap - Managed pci_iounmap ( )
* @ pdev : PCI device to iounmap for
* @ addr : Address to unmap
*
* Managed pci_iounmap ( ) . @ addr must have been mapped using a pcim_ * mapping
* function .
*/
void pcim_iounmap(struct pci_dev *pdev, void __iomem *addr)
{
struct pcim_addr_devres res_searched;
pcim_addr_devres_clear(&res_searched);
res_searched.type = PCIM_ADDR_DEVRES_TYPE_MAPPING;
res_searched.baseaddr = addr;
if (devres_release(&pdev->dev, pcim_addr_resource_release,
pcim_addr_resources_match, &res_searched) != 0 ) {
/* Doesn't exist. User passed nonsense. */
return ;
}
pcim_remove_mapping_from_legacy_table(pdev, addr);
}
EXPORT_SYMBOL(pcim_iounmap);
/**
* pcim_iomap_region - Request and iomap a PCI BAR
* @ pdev : PCI device to map IO resources for
* @ bar : Index of a BAR to map
* @ name : Name of the driver requesting the resource
*
* Returns : _ _ iomem pointer on success , an IOMEM_ERR_PTR on failure .
*
* Mapping and region will get automatically released on driver detach . If
* desired , release manually only with pcim_iounmap_region ( ) .
*/
void __iomem *pcim_iomap_region(struct pci_dev *pdev, int bar,
const char *name)
{
int ret;
struct pcim_addr_devres *res;
if (!pci_bar_index_is_valid(bar))
return IOMEM_ERR_PTR(-EINVAL);
res = pcim_addr_devres_alloc(pdev);
if (!res)
return IOMEM_ERR_PTR(-ENOMEM);
res->type = PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING;
res->bar = bar;
ret = pci_request_region(pdev, bar, name);
if (ret != 0 )
goto err_region;
res->baseaddr = pci_iomap(pdev, bar, 0 );
if (!res->baseaddr) {
ret = -EINVAL;
goto err_iomap;
}
devres_add(&pdev->dev, res);
return res->baseaddr;
err_iomap:
pci_release_region(pdev, bar);
err_region:
pcim_addr_devres_free(res);
return IOMEM_ERR_PTR(ret);
}
EXPORT_SYMBOL(pcim_iomap_region);
/**
* pcim_iounmap_region - Unmap and release a PCI BAR
* @ pdev : PCI device to operate on
* @ bar : Index of BAR to unmap and release
*
* Unmap a BAR and release its region manually . Only pass BARs that were
* previously mapped by pcim_iomap_region ( ) .
*/
void pcim_iounmap_region(struct pci_dev *pdev, int bar)
{
struct pcim_addr_devres res_searched;
pcim_addr_devres_clear(&res_searched);
res_searched.type = PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING;
res_searched.bar = bar;
devres_release(&pdev->dev, pcim_addr_resource_release,
pcim_addr_resources_match, &res_searched);
}
EXPORT_SYMBOL(pcim_iounmap_region);
/**
* pcim_iomap_regions - Request and iomap PCI BARs ( DEPRECATED )
* @ pdev : PCI device to map IO resources for
* @ mask : Mask of BARs to request and iomap
* @ name : Name of the driver requesting the resources
*
* Returns : 0 on success , negative error code on failure .
*
* Request and iomap regions specified by @ mask .
*
* This function is DEPRECATED . Do not use it in new code .
* Use pcim_iomap_region ( ) instead .
*/
int pcim_iomap_regions(struct pci_dev *pdev, int mask, const char *name)
{
int ret;
int bar;
void __iomem *mapping;
for (bar = 0 ; bar < DEVICE_COUNT_RESOURCE; bar++) {
if (!mask_contains_bar(mask, bar))
continue ;
mapping = pcim_iomap_region(pdev, bar, name);
if (IS_ERR(mapping)) {
ret = PTR_ERR(mapping);
goto err;
}
ret = pcim_add_mapping_to_legacy_table(pdev, mapping, bar);
if (ret != 0 )
goto err;
}
return 0 ;
err:
while (--bar >= 0 ) {
pcim_iounmap_region(pdev, bar);
pcim_remove_bar_from_legacy_table(pdev, bar);
}
return ret;
}
EXPORT_SYMBOL(pcim_iomap_regions);
/**
* pcim_request_region - Request a PCI BAR
* @ pdev : PCI device to request region for
* @ bar : Index of BAR to request
* @ name : Name of the driver requesting the resource
*
* Returns : 0 on success , a negative error code on failure .
*
* Request region specified by @ bar .
*
* The region will automatically be released on driver detach . If desired ,
* release manually only with pcim_release_region ( ) .
*/
int pcim_request_region(struct pci_dev *pdev, int bar, const char *name)
{
int ret;
struct pcim_addr_devres *res;
if (!pci_bar_index_is_valid(bar))
return -EINVAL;
res = pcim_addr_devres_alloc(pdev);
if (!res)
return -ENOMEM;
res->type = PCIM_ADDR_DEVRES_TYPE_REGION;
res->bar = bar;
ret = pci_request_region(pdev, bar, name);
if (ret != 0 ) {
pcim_addr_devres_free(res);
return ret;
}
devres_add(&pdev->dev, res);
return 0 ;
}
EXPORT_SYMBOL(pcim_request_region);
/**
* pcim_release_region - Release a PCI BAR
* @ pdev : PCI device to operate on
* @ bar : Index of BAR to release
*
* Release a region manually that was previously requested by
* pcim_request_region ( ) .
*/
static void pcim_release_region(struct pci_dev *pdev, int bar)
{
struct pcim_addr_devres res_searched;
pcim_addr_devres_clear(&res_searched);
res_searched.type = PCIM_ADDR_DEVRES_TYPE_REGION;
res_searched.bar = bar;
devres_release(&pdev->dev, pcim_addr_resource_release,
pcim_addr_resources_match, &res_searched);
}
/**
* pcim_release_all_regions - Release all regions of a PCI - device
* @ pdev : the PCI device
*
* Release all regions previously requested through pcim_request_region ( )
* or pcim_request_all_regions ( ) .
*
* Can be called from any context , i . e . , not necessarily as a counterpart to
* pcim_request_all_regions ( ) .
*/
static void pcim_release_all_regions(struct pci_dev *pdev)
{
int bar;
for (bar = 0 ; bar < PCI_STD_NUM_BARS; bar++)
pcim_release_region(pdev, bar);
}
/**
* pcim_request_all_regions - Request all regions
* @ pdev : PCI device to map IO resources for
* @ name : name of the driver requesting the resources
*
* Returns : 0 on success , negative error code on failure .
*
* Requested regions will automatically be released at driver detach . If
* desired , release individual regions with pcim_release_region ( ) or all of
* them at once with pcim_release_all_regions ( ) .
*/
int pcim_request_all_regions(struct pci_dev *pdev, const char *name)
{
int ret;
int bar;
for (bar = 0 ; bar < PCI_STD_NUM_BARS; bar++) {
ret = pcim_request_region(pdev, bar, name);
if (ret != 0 )
goto err;
}
return 0 ;
err:
pcim_release_all_regions(pdev);
return ret;
}
EXPORT_SYMBOL(pcim_request_all_regions);
/**
* pcim_iomap_range - Create a ranged _ _ iomap mapping within a PCI BAR
* @ pdev : PCI device to map IO resources for
* @ bar : Index of the BAR
* @ offset : Offset from the begin of the BAR
* @ len : Length in bytes for the mapping
*
* Returns : _ _ iomem pointer on success , an IOMEM_ERR_PTR on failure .
*
* Creates a new IO - Mapping within the specified @ bar , ranging from @ offset to
* @ offset + @ len .
*
* The mapping will automatically get unmapped on driver detach . If desired ,
* release manually only with pcim_iounmap ( ) .
*/
void __iomem *pcim_iomap_range(struct pci_dev *pdev, int bar,
unsigned long offset, unsigned long len)
{
void __iomem *mapping;
struct pcim_addr_devres *res;
if (!pci_bar_index_is_valid(bar))
return IOMEM_ERR_PTR(-EINVAL);
res = pcim_addr_devres_alloc(pdev);
if (!res)
return IOMEM_ERR_PTR(-ENOMEM);
mapping = pci_iomap_range(pdev, bar, offset, len);
if (!mapping) {
pcim_addr_devres_free(res);
return IOMEM_ERR_PTR(-EINVAL);
}
res->type = PCIM_ADDR_DEVRES_TYPE_MAPPING;
res->baseaddr = mapping;
/*
* Ranged mappings don ' t get added to the legacy - table , since the table
* only ever keeps track of whole BARs .
*/
devres_add(&pdev->dev, res);
return mapping;
}
EXPORT_SYMBOL(pcim_iomap_range);
Messung V0.5 in Prozent C=96 H=93 G=94
¤ Dauer der Verarbeitung: 0.23 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland