Explain devm_ioremap_resource()
, with examples particularly related to memory regions. Add some links.
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 ...
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)
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; }
Specifically map the memory resource of a (platform) device:
void __iomem *devm_ioremap_resource(struct device *dev, struct resource *res);
/** * 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; }
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"; };
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; }