mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 19:00:25 +00:00
![Mark Brown](/assets/img/avatar_default.png)
Provide basic support for MFDs having multiple cells of a given type with different IDs by adding an id to the mfd_cell structure and then adding that to the id passed in to mfd_add_devices(). As it stands this approach requires that MFDs using this feature deal with ensuring that there aren't any ID collisions resulting from multiple MFDs of the same type being instantiated. This needs to happen with the existing code too, but with this approach there is a knock on effect on the IDs for non-duplicated devices. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
120 lines
2.7 KiB
C
120 lines
2.7 KiB
C
/*
|
|
* drivers/mfd/mfd-core.c
|
|
*
|
|
* core MFD support
|
|
* Copyright (c) 2006 Ian Molton
|
|
* Copyright (c) 2007,2008 Dmitry Baryshkov
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/mfd/core.h>
|
|
|
|
static int mfd_add_device(struct device *parent, int id,
|
|
const struct mfd_cell *cell,
|
|
struct resource *mem_base,
|
|
int irq_base)
|
|
{
|
|
struct resource *res;
|
|
struct platform_device *pdev;
|
|
int ret = -ENOMEM;
|
|
int r;
|
|
|
|
pdev = platform_device_alloc(cell->name, id + cell->id);
|
|
if (!pdev)
|
|
goto fail_alloc;
|
|
|
|
res = kzalloc(sizeof(*res) * cell->num_resources, GFP_KERNEL);
|
|
if (!res)
|
|
goto fail_device;
|
|
|
|
pdev->dev.parent = parent;
|
|
platform_set_drvdata(pdev, cell->driver_data);
|
|
|
|
ret = platform_device_add_data(pdev,
|
|
cell->platform_data, cell->data_size);
|
|
if (ret)
|
|
goto fail_res;
|
|
|
|
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) {
|
|
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) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
platform_device_add_resources(pdev, res, cell->num_resources);
|
|
|
|
ret = platform_device_add(pdev);
|
|
if (ret)
|
|
goto fail_res;
|
|
|
|
kfree(res);
|
|
|
|
return 0;
|
|
|
|
/* platform_device_del(pdev); */
|
|
fail_res:
|
|
kfree(res);
|
|
fail_device:
|
|
platform_device_put(pdev);
|
|
fail_alloc:
|
|
return ret;
|
|
}
|
|
|
|
int mfd_add_devices(struct device *parent, int id,
|
|
const struct mfd_cell *cells, int n_devs,
|
|
struct resource *mem_base,
|
|
int irq_base)
|
|
{
|
|
int i;
|
|
int ret = 0;
|
|
|
|
for (i = 0; i < n_devs; i++) {
|
|
ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base);
|
|
if (ret)
|
|
break;
|
|
}
|
|
|
|
if (ret)
|
|
mfd_remove_devices(parent);
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(mfd_add_devices);
|
|
|
|
static int mfd_remove_devices_fn(struct device *dev, void *unused)
|
|
{
|
|
platform_device_unregister(to_platform_device(dev));
|
|
return 0;
|
|
}
|
|
|
|
void mfd_remove_devices(struct device *parent)
|
|
{
|
|
device_for_each_child(parent, NULL, mfd_remove_devices_fn);
|
|
}
|
|
EXPORT_SYMBOL(mfd_remove_devices);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov");
|