User Tools

Site Tools


devm_ioremap_resource

This is an old revision of the document!


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;
}

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;
}
devm_ioremap_resource.1533034489.txt.gz · Last modified: 2018/07/31 10:54 by rpjday