Overview
Explain devm_ioremap_resource(), with examples particularly related to memory regions. Add some links.
struct resource
From include/linux/ioport.h, a device can have a number of resources of different types:
/*
* Resources are tree-like, allowing
* nesting etc..
*/
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
unsigned long desc;
struct resource *parent, *sibling, *child;
}
In particular, one resource type is a memory range (flags of IORESOURCE_MEM):
#define IORESOURCE_BITS 0x000000ff /* Bus-specific bits */ #define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */ #define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */ #define IORESOURCE_MEM 0x00000200 #define IORESOURCE_REG 0x00000300 /* Register offsets */ #define IORESOURCE_IRQ 0x00000400 #define IORESOURCE_DMA 0x00000800 #define IORESOURCE_BUS 0x00001000 ... snip ...
Here's a single example from arch/arm/mach-davinci/dm355.c, defining an array of resources:
static struct resource edma_resources[] = {
{
.name = "edma3_cc",
.start = 0x01c00000,
.end = 0x01c00000 + SZ_64K - 1,
.flags = IORESOURCE_MEM,
},
{
.name = "edma3_tc0",
.start = 0x01c10000,
.end = 0x01c10000 + SZ_1K - 1,
.flags = IORESOURCE_MEM,
},
{
.name = "edma3_tc1",
.start = 0x01c10400,
.end = 0x01c10400 + SZ_1K - 1,
.flags = IORESOURCE_MEM,
},
... snip ...
struct platform_device
From include/linux/platform_device.h, showing how a platform_device can represent any number of resources:
struct platform_device {
const char *name;
int id;
bool id_auto;
struct device dev; <--- wrapper around generic device
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
From the same file:
#define to_platform_device(x) container_of((x), struct platform_device, dev)
platform_get_resource()
From drivers/base/platform.c, given a struct platform_device *dev and a resource type, return that (numbered) resource type or NULL:
/**
* platform_get_resource - get a resource for a device
* @dev: platform device
* @type: resource type
* @num: resource index
*/
struct resource *platform_get_resource(struct platform_device *dev,
unsigned int type, unsigned int num)
{
int i;
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
if (type == resource_type(r) && num-- == 0)
return r;
}
return NULL;
}
You can also get a resource by an optional name:
/**
* platform_get_resource_byname - get a resource for a device by name
* @dev: platform device
* @type: resource type
* @name: resource name
*/
struct resource *platform_get_resource_byname(struct platform_device *dev,
unsigned int type,
const char *name)
{
int i;
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
if (unlikely(!r->name))
continue;
if (type == resource_type(r) && !strcmp(r->name, name))
return r;
}
devm_ioremap_resource()
include/linux/device.h
Specifically map the memory resource of a (platform) device:
void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
lib/devres.c
/**
* devm_ioremap_resource() - check, request region, and ioremap resource
* @dev: generic device to handle the resource for
* @res: resource to be handled
*
* Checks that a resource is a valid memory region, requests the memory
* region and ioremaps it. All operations are managed and will be undone
* on driver detach.
*
* Returns a pointer to the remapped memory or an ERR_PTR() encoded error code
* on failure. Usage example:
*
* res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
* base = devm_ioremap_resource(&pdev->dev, res);
* if (IS_ERR(base))
* return PTR_ERR(base);
*/
void __iomem *devm_ioremap_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);
name = res->name ?: dev_name(dev);
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_ioremap(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;
}
Examples
Broadcom Cygnus
arch/arm/boot/dts/bcm-cygnus.dtsi
eth0: ethernet@18042000 {
compatible = "brcm,amac";
reg = <0x18042000 0x1000>,
<0x18110000 0x1000>;
reg-names = "amac_base", "idm_base";
interrupts = <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
status = "disabled";
};
drivers/net/ethernet/broadcom/bgmac-platform.c
static const struct of_device_id bgmac_of_enet_match[] = {
{.compatible = "brcm,amac",},
{.compatible = "brcm,nsp-amac",},
{.compatible = "brcm,ns2-amac",},
{},
};
MODULE_DEVICE_TABLE(of, bgmac_of_enet_match);
static struct platform_driver bgmac_enet_driver = {
.driver = {
.name = "bgmac-enet",
.of_match_table = bgmac_of_enet_match,
.pm = BGMAC_PM_OPS
},
.probe = bgmac_probe,
.remove = bgmac_remove,
};
module_platform_driver(bgmac_enet_driver);
Also:
regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "amac_base");
if (!regs) {
dev_err(&pdev->dev, "Unable to obtain base resource\n");
return -EINVAL;
}
bgmac->plat.base = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(bgmac->plat.base))
return PTR_ERR(bgmac->plat.base);
regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "idm_base");
if (regs) {
bgmac->plat.idm_base = devm_ioremap_resource(&pdev->dev, regs);
if (IS_ERR(bgmac->plat.idm_base))
return PTR_ERR(bgmac->plat.idm_base);
bgmac->feature_flags &= ~BGMAC_FEAT_IDM_MASK;
}