• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/mfd/
1/*
2 * drivers/mfd/mfd-core.c
3 *
4 * core MFD support
5 * Copyright (c) 2006 Ian Molton
6 * Copyright (c) 2007,2008 Dmitry Baryshkov
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 *
12 */
13
14#include <linux/kernel.h>
15#include <linux/platform_device.h>
16#include <linux/acpi.h>
17#include <linux/mfd/core.h>
18#include <linux/slab.h>
19
20static int mfd_add_device(struct device *parent, int id,
21			  const struct mfd_cell *cell,
22			  struct resource *mem_base,
23			  int irq_base)
24{
25	struct resource *res;
26	struct platform_device *pdev;
27	int ret = -ENOMEM;
28	int r;
29
30	pdev = platform_device_alloc(cell->name, id + cell->id);
31	if (!pdev)
32		goto fail_alloc;
33
34	res = kzalloc(sizeof(*res) * cell->num_resources, GFP_KERNEL);
35	if (!res)
36		goto fail_device;
37
38	pdev->dev.parent = parent;
39	platform_set_drvdata(pdev, cell->driver_data);
40
41	ret = platform_device_add_data(pdev,
42			cell->platform_data, cell->data_size);
43	if (ret)
44		goto fail_res;
45
46	for (r = 0; r < cell->num_resources; r++) {
47		res[r].name = cell->resources[r].name;
48		res[r].flags = cell->resources[r].flags;
49
50		/* Find out base to use */
51		if ((cell->resources[r].flags & IORESOURCE_MEM) && mem_base) {
52			res[r].parent = mem_base;
53			res[r].start = mem_base->start +
54				cell->resources[r].start;
55			res[r].end = mem_base->start +
56				cell->resources[r].end;
57		} else if (cell->resources[r].flags & IORESOURCE_IRQ) {
58			res[r].start = irq_base +
59				cell->resources[r].start;
60			res[r].end   = irq_base +
61				cell->resources[r].end;
62		} else {
63			res[r].parent = cell->resources[r].parent;
64			res[r].start = cell->resources[r].start;
65			res[r].end   = cell->resources[r].end;
66		}
67
68		ret = acpi_check_resource_conflict(res);
69		if (ret)
70			goto fail_res;
71	}
72
73	ret = platform_device_add_resources(pdev, res, cell->num_resources);
74	if (ret)
75		goto fail_res;
76
77	ret = platform_device_add(pdev);
78	if (ret)
79		goto fail_res;
80
81	kfree(res);
82
83	return 0;
84
85/*	platform_device_del(pdev); */
86fail_res:
87	kfree(res);
88fail_device:
89	platform_device_put(pdev);
90fail_alloc:
91	return ret;
92}
93
94int mfd_add_devices(struct device *parent, int id,
95		    const struct mfd_cell *cells, int n_devs,
96		    struct resource *mem_base,
97		    int irq_base)
98{
99	int i;
100	int ret = 0;
101
102	for (i = 0; i < n_devs; i++) {
103		ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base);
104		if (ret)
105			break;
106	}
107
108	if (ret)
109		mfd_remove_devices(parent);
110
111	return ret;
112}
113EXPORT_SYMBOL(mfd_add_devices);
114
115static int mfd_remove_devices_fn(struct device *dev, void *unused)
116{
117	platform_device_unregister(to_platform_device(dev));
118	return 0;
119}
120
121void mfd_remove_devices(struct device *parent)
122{
123	device_for_each_child(parent, NULL, mfd_remove_devices_fn);
124}
125EXPORT_SYMBOL(mfd_remove_devices);
126
127MODULE_LICENSE("GPL");
128MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov");
129