Linux ioremap重复map问题
源码路径:
https://elixir.bootlin.com/linux/v5.10.120/source/lib/devres.c
/* * 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; }