Linux ioremap重复map问题

 

 

源码路径:

 

/*
 * devm_of_iomap - Requests a resource and maps the memory mapped IO
 *           for a given device_node managed by a given device
 *
 * 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 of the device.
 *
 * This is to be used when a device requests/maps resources described
 * by other device tree nodes (children or otherwise).
 *
 * @dev:    The device "managing" the resource
 * @node:       The device-tree node where the resource resides
 * @index:    index of the MMIO range in the "reg" property
 * @size:    Returns the size of the resource (pass NULL if not needed)
 *
 * Usage example:
 *
 *    base = devm_of_iomap(&pdev->dev, node, 0, NULL);
 *    if (IS_ERR(base))
 *        return PTR_ERR(base);
 *
 * Please Note: This is not a one-to-one replacement for of_iomap() because the
 * of_iomap() function does not track whether the region is already mapped.  If
 * two drivers try to map the same memory, the of_iomap() function will succeed
 * but the devm_of_iomap() function will return -EBUSY.
 *
 * Return: a pointer to the requested and mapped memory or an ERR_PTR() encoded
 * error code on failure.
 */
void __iomem *devm_of_iomap(struct device *dev, struct device_node *node, int index,
                resource_size_t *size)
{
    struct resource res;

    if (of_address_to_resource(node, index, &res))
        return IOMEM_ERR_PTR(-EINVAL);
    if (size)
        *size = resource_size(&res);
    return devm_ioremap_resource(dev, &res);
}
EXPORT_SYMBOL(devm_of_iomap);

 

 

of_iomap、devm_ioremap 可以映射已经map的区域,这些API直接调用ioremap
/**
 * of_iomap - Maps the memory mapped IO for a given device_node
 * @np:        the device whose io range will be mapped
 * @index:    index of the io range
 *
 * Returns a pointer to the mapped memory
 */
void __iomem *of_iomap(struct device_node *np, int index)
{
    struct resource res;

    if (of_address_to_resource(np, index, &res))
        return NULL;

    return ioremap(res.start, resource_size(&res));
}
EXPORT_SYMBOL(of_iomap);
static void __iomem *__devm_ioremap(struct device *dev, resource_size_t offset,
                    resource_size_t size,
                    enum devm_ioremap_type type)
{
    void __iomem **ptr, *addr = NULL;

    ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL);
    if (!ptr)
        return NULL;

    switch (type) {
    case DEVM_IOREMAP:
        addr = ioremap(offset, size);
        break;
    case DEVM_IOREMAP_UC:
        addr = ioremap_uc(offset, size);
        break;
    case DEVM_IOREMAP_WC:
        addr = ioremap_wc(offset, size);
        break;
    }

    if (addr) {
        *ptr = addr;
        devres_add(dev, ptr);
    } else
        devres_free(ptr);

    return addr;
}

 

 

 

devm_of_iomap、devm_ioremap_resource 不可以映射已经map的区域,
因为这些API需要先request然后再map。如果已经map的区域,再去requset会报错 can't request region for resource
static void __iomem *
__devm_ioremap_resource(struct device *dev, const struct resource *res,
            enum devm_ioremap_type type)
{
    resource_size_t size;
    void __iomem *dest_ptr;
    char *pretty_name;

    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)
        pretty_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
                         dev_name(dev), res->name);
    else
        pretty_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
    if (!pretty_name)
        return IOMEM_ERR_PTR(-ENOMEM);

    if (!devm_request_mem_region(dev, res->start, size, pretty_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, type);
    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;
}

 

 

可以这样使用

    base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!base) {
        dev_err(&pdev->dev, "failed to retrieve base register\n");
        err = -ENODEV;
        goto out;
    }

   base_virt = devm_ioremap(&pdev->dev, base->start, resource_size(base));

 

 

https://elixir.bootlin.com/linux/v5.10.120/source/drivers/net/ethernet/amd/au1000_eth.c

static int au1000_probe(struct platform_device *pdev)
{
    struct au1000_private *aup = NULL;
    struct au1000_eth_platform_data *pd;
    struct net_device *dev = NULL;
    struct db_dest *pDB, *pDBfree;
    int irq, i, err = 0;
    struct resource *base, *macen, *macdma;

    base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!base) {
        dev_err(&pdev->dev, "failed to retrieve base register\n");
        err = -ENODEV;
        goto out;
    }

    macen = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    if (!macen) {
        dev_err(&pdev->dev, "failed to retrieve MAC Enable register\n");
        err = -ENODEV;
        goto out;
    }

    irq = platform_get_irq(pdev, 0);
    if (irq < 0) {
        err = -ENODEV;
        goto out;
    }

    macdma = platform_get_resource(pdev, IORESOURCE_MEM, 2);
    if (!macdma) {
        dev_err(&pdev->dev, "failed to retrieve MACDMA registers\n");
        err = -ENODEV;
        goto out;
    }

    if (!request_mem_region(base->start, resource_size(base),
                            pdev->name)) {
        dev_err(&pdev->dev, "failed to request memory region for base registers\n");
        err = -ENXIO;
        goto out;
    }

    if (!request_mem_region(macen->start, resource_size(macen),
                            pdev->name)) {
        dev_err(&pdev->dev, "failed to request memory region for MAC enable register\n");
        err = -ENXIO;
        goto err_request;
    }

    if (!request_mem_region(macdma->start, resource_size(macdma),
                            pdev->name)) {
        dev_err(&pdev->dev, "failed to request MACDMA memory region\n");
        err = -ENXIO;
        goto err_macdma;
    }

    dev = alloc_etherdev(sizeof(struct au1000_private));
    if (!dev) {
        err = -ENOMEM;
        goto err_alloc;
    }

    SET_NETDEV_DEV(dev, &pdev->dev);
    platform_set_drvdata(pdev, dev);
    aup = netdev_priv(dev);

    spin_lock_init(&aup->lock);
    aup->msg_enable = (au1000_debug < 4 ?
                AU1000_DEF_MSG_ENABLE : au1000_debug);

    /* Allocate the data buffers
     * Snooping works fine with eth on all au1xxx
     */
    aup->vaddr = (u32)dma_alloc_coherent(&pdev->dev, MAX_BUF_SIZE *
                      (NUM_TX_BUFFS + NUM_RX_BUFFS),
                      &aup->dma_addr, 0);
    if (!aup->vaddr) {
        dev_err(&pdev->dev, "failed to allocate data buffers\n");
        err = -ENOMEM;
        goto err_vaddr;
    }

    /* aup->mac is the base address of the MAC's registers */
    aup->mac = (struct mac_reg *)
            ioremap(base->start, resource_size(base));
    if (!aup->mac) {
        dev_err(&pdev->dev, "failed to ioremap MAC registers\n");
        err = -ENXIO;
        goto err_remap1;
    }

    /* Setup some variables for quick register address access */
    aup->enable = (u32 *)ioremap(macen->start,
                        resource_size(macen));
    if (!aup->enable) {
        dev_err(&pdev->dev, "failed to ioremap MAC enable register\n");
        err = -ENXIO;
        goto err_remap2;
    }
    aup->mac_id = pdev->id;

    aup->macdma = ioremap(macdma->start, resource_size(macdma));
    if (!aup->macdma) {
        dev_err(&pdev->dev, "failed to ioremap MACDMA registers\n");
        err = -ENXIO;
        goto err_remap3;
    }

    au1000_setup_hw_rings(aup, aup->macdma);

    writel(0, aup->enable);
    aup->mac_enabled = 0;

    pd = dev_get_platdata(&pdev->dev);
    if (!pd) {
        dev_info(&pdev->dev, "no platform_data passed,"
                    " PHY search on MAC0\n");
        aup->phy1_search_mac0 = 1;
    } else {
        if (is_valid_ether_addr(pd->mac)) {
            memcpy(dev->dev_addr, pd->mac, ETH_ALEN);
        } else {
            /* Set a random MAC since no valid provided by platform_data. */
            eth_hw_addr_random(dev);
        }

        aup->phy_static_config = pd->phy_static_config;
        aup->phy_search_highest_addr = pd->phy_search_highest_addr;
        aup->phy1_search_mac0 = pd->phy1_search_mac0;
        aup->phy_addr = pd->phy_addr;
        aup->phy_busid = pd->phy_busid;
        aup->phy_irq = pd->phy_irq;
    }

    if (aup->phy_busid > 0) {
        dev_err(&pdev->dev, "MAC0-associated PHY attached 2nd MACs MII bus not supported yet\n");
        err = -ENODEV;
        goto err_mdiobus_alloc;
    }

    aup->mii_bus = mdiobus_alloc();
    if (aup->mii_bus == NULL) {
        dev_err(&pdev->dev, "failed to allocate mdiobus structure\n");
        err = -ENOMEM;
        goto err_mdiobus_alloc;
    }

    aup->mii_bus->priv = dev;
    aup->mii_bus->read = au1000_mdiobus_read;
    aup->mii_bus->write = au1000_mdiobus_write;
    aup->mii_bus->reset = au1000_mdiobus_reset;
    aup->mii_bus->name = "au1000_eth_mii";
    snprintf(aup->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x",
        pdev->name, aup->mac_id);

    /* if known, set corresponding PHY IRQs */
    if (aup->phy_static_config)
        if (aup->phy_irq && aup->phy_busid == aup->mac_id)
            aup->mii_bus->irq[aup->phy_addr] = aup->phy_irq;

    err = mdiobus_register(aup->mii_bus);
    if (err) {
        dev_err(&pdev->dev, "failed to register MDIO bus\n");
        goto err_mdiobus_reg;
    }

    err = au1000_mii_probe(dev);
    if (err != 0)
        goto err_out;

    pDBfree = NULL;
    /* setup the data buffer descriptors and attach a buffer to each one */
    pDB = aup->db;
    for (i = 0; i < (NUM_TX_BUFFS+NUM_RX_BUFFS); i++) {
        pDB->pnext = pDBfree;
        pDBfree = pDB;
        pDB->vaddr = (u32 *)((unsigned)aup->vaddr + MAX_BUF_SIZE*i);
        pDB->dma_addr = (dma_addr_t)virt_to_bus(pDB->vaddr);
        pDB++;
    }
    aup->pDBfree = pDBfree;

    err = -ENODEV;
    for (i = 0; i < NUM_RX_DMA; i++) {
        pDB = au1000_GetFreeDB(aup);
        if (!pDB)
            goto err_out;

        aup->rx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
        aup->rx_db_inuse[i] = pDB;
    }

    for (i = 0; i < NUM_TX_DMA; i++) {
        pDB = au1000_GetFreeDB(aup);
        if (!pDB)
            goto err_out;

        aup->tx_dma_ring[i]->buff_stat = (unsigned)pDB->dma_addr;
        aup->tx_dma_ring[i]->len = 0;
        aup->tx_db_inuse[i] = pDB;
    }

    dev->base_addr = base->start;
    dev->irq = irq;
    dev->netdev_ops = &au1000_netdev_ops;
    dev->ethtool_ops = &au1000_ethtool_ops;
    dev->watchdog_timeo = ETH_TX_TIMEOUT;

    /*
     * The boot code uses the ethernet controller, so reset it to start
     * fresh.  au1000_init() expects that the device is in reset state.
     */
    au1000_reset_mac(dev);

    err = register_netdev(dev);
    if (err) {
        netdev_err(dev, "Cannot register net device, aborting.\n");
        goto err_out;
    }

    netdev_info(dev, "Au1xx0 Ethernet found at 0x%lx, irq %d\n",
            (unsigned long)base->start, irq);

    return 0;

err_out:
    if (aup->mii_bus != NULL)
        mdiobus_unregister(aup->mii_bus);

    /* here we should have a valid dev plus aup-> register addresses
     * so we can reset the mac properly.
     */
    au1000_reset_mac(dev);

    for (i = 0; i < NUM_RX_DMA; i++) {
        if (aup->rx_db_inuse[i])
            au1000_ReleaseDB(aup, aup->rx_db_inuse[i]);
    }
    for (i = 0; i < NUM_TX_DMA; i++) {
        if (aup->tx_db_inuse[i])
            au1000_ReleaseDB(aup, aup->tx_db_inuse[i]);
    }
err_mdiobus_reg:
    mdiobus_free(aup->mii_bus);
err_mdiobus_alloc:
    iounmap(aup->macdma);
err_remap3:
    iounmap(aup->enable);
err_remap2:
    iounmap(aup->mac);
err_remap1:
    dma_free_coherent(&pdev->dev, MAX_BUF_SIZE * (NUM_TX_BUFFS + NUM_RX_BUFFS),
            (void *)aup->vaddr, aup->dma_addr);
err_vaddr:
    free_netdev(dev);
err_alloc:
    release_mem_region(macdma->start, resource_size(macdma));
err_macdma:
    release_mem_region(macen->start, resource_size(macen));
err_request:
    release_mem_region(base->start, resource_size(base));
out:
    return err;
}

 

posted @ 2022-06-08 16:30  sinferwu  阅读(1246)  评论(0编辑  收藏  举报