in mfd-core.c [153:310]
static int mfd_add_device(struct device *parent, int id,
const struct mfd_cell *cell,
struct resource *mem_base,
int irq_base, struct irq_domain *domain)
{
struct resource *res;
struct platform_device *pdev;
struct device_node *np = NULL;
struct mfd_of_node_entry *of_entry, *tmp;
int ret = -ENOMEM;
int platform_id;
int r;
if (id == PLATFORM_DEVID_AUTO)
platform_id = id;
else
platform_id = id + cell->id;
pdev = platform_device_alloc(cell->name, platform_id);
if (!pdev)
goto fail_alloc;
pdev->mfd_cell = kmemdup(cell, sizeof(*cell), GFP_KERNEL);
if (!pdev->mfd_cell)
goto fail_device;
res = kcalloc(cell->num_resources, sizeof(*res), GFP_KERNEL);
if (!res)
goto fail_device;
pdev->dev.parent = parent;
pdev->dev.type = &mfd_dev_type;
pdev->dev.dma_mask = parent->dma_mask;
pdev->dev.dma_parms = parent->dma_parms;
pdev->dev.coherent_dma_mask = parent->coherent_dma_mask;
ret = regulator_bulk_register_supply_alias(
&pdev->dev, cell->parent_supplies,
parent, cell->parent_supplies,
cell->num_parent_supplies);
if (ret < 0)
goto fail_res;
if (IS_ENABLED(CONFIG_OF) && parent->of_node && cell->of_compatible) {
for_each_child_of_node(parent->of_node, np) {
if (of_device_is_compatible(np, cell->of_compatible)) {
/* Ignore 'disabled' devices error free */
if (!of_device_is_available(np)) {
of_node_put(np);
ret = 0;
goto fail_alias;
}
ret = mfd_match_of_node_to_dev(pdev, np, cell);
if (ret == -EAGAIN)
continue;
of_node_put(np);
if (ret)
goto fail_alias;
break;
}
}
if (!pdev->dev.of_node)
pr_warn("%s: Failed to locate of_node [id: %d]\n",
cell->name, platform_id);
}
mfd_acpi_add_device(cell, pdev);
if (cell->pdata_size) {
ret = platform_device_add_data(pdev,
cell->platform_data, cell->pdata_size);
if (ret)
goto fail_of_entry;
}
if (cell->swnode) {
ret = device_add_software_node(&pdev->dev, cell->swnode);
if (ret)
goto fail_of_entry;
}
for (r = 0; r < cell->num_resources; r++) {
res[r].name = cell->resources[r].name;
res[r].flags = cell->resources[r].flags;
/* Find out base to use */
if ((cell->resources[r].flags & IORESOURCE_MEM) && mem_base) {
res[r].parent = mem_base;
res[r].start = mem_base->start +
cell->resources[r].start;
res[r].end = mem_base->start +
cell->resources[r].end;
} else if (cell->resources[r].flags & IORESOURCE_IRQ) {
if (domain) {
/* Unable to create mappings for IRQ ranges. */
WARN_ON(cell->resources[r].start !=
cell->resources[r].end);
res[r].start = res[r].end = irq_create_mapping(
domain, cell->resources[r].start);
} else {
res[r].start = irq_base +
cell->resources[r].start;
res[r].end = irq_base +
cell->resources[r].end;
}
} else {
res[r].parent = cell->resources[r].parent;
res[r].start = cell->resources[r].start;
res[r].end = cell->resources[r].end;
}
if (!cell->ignore_resource_conflicts) {
if (has_acpi_companion(&pdev->dev)) {
ret = acpi_check_resource_conflict(&res[r]);
if (ret)
goto fail_res_conflict;
}
}
}
ret = platform_device_add_resources(pdev, res, cell->num_resources);
if (ret)
goto fail_res_conflict;
ret = platform_device_add(pdev);
if (ret)
goto fail_res_conflict;
if (cell->pm_runtime_no_callbacks)
pm_runtime_no_callbacks(&pdev->dev);
kfree(res);
return 0;
fail_res_conflict:
if (cell->swnode)
device_remove_software_node(&pdev->dev);
fail_of_entry:
list_for_each_entry_safe(of_entry, tmp, &mfd_of_node_list, list)
if (of_entry->dev == &pdev->dev) {
list_del(&of_entry->list);
kfree(of_entry);
}
fail_alias:
regulator_bulk_unregister_supply_alias(&pdev->dev,
cell->parent_supplies,
cell->num_parent_supplies);
fail_res:
kfree(res);
fail_device:
platform_device_put(pdev);
fail_alloc:
return ret;
}