Merge branch 'core-debugobjects-for-linus' of git://git.kernel.org/pub/scm/linux...
[pandora-kernel.git] / drivers / dca / dca-core.c
1 /*
2  * Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 2 of the License, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program; if not, write to the Free Software Foundation, Inc., 59
16  * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  * The full GNU General Public License is included in this distribution in the
19  * file called COPYING.
20  */
21
22 /*
23  * This driver supports an interface for DCA clients and providers to meet.
24  */
25
26 #include <linux/kernel.h>
27 #include <linux/notifier.h>
28 #include <linux/device.h>
29 #include <linux/dca.h>
30 #include <linux/slab.h>
31
32 #define DCA_VERSION "1.12.1"
33
34 MODULE_VERSION(DCA_VERSION);
35 MODULE_LICENSE("GPL");
36 MODULE_AUTHOR("Intel Corporation");
37
38 static DEFINE_SPINLOCK(dca_lock);
39
40 static LIST_HEAD(dca_domains);
41
42 static struct pci_bus *dca_pci_rc_from_dev(struct device *dev)
43 {
44         struct pci_dev *pdev = to_pci_dev(dev);
45         struct pci_bus *bus = pdev->bus;
46
47         while (bus->parent)
48                 bus = bus->parent;
49
50         return bus;
51 }
52
53 static struct dca_domain *dca_allocate_domain(struct pci_bus *rc)
54 {
55         struct dca_domain *domain;
56
57         domain = kzalloc(sizeof(*domain), GFP_NOWAIT);
58         if (!domain)
59                 return NULL;
60
61         INIT_LIST_HEAD(&domain->dca_providers);
62         domain->pci_rc = rc;
63
64         return domain;
65 }
66
67 static void dca_free_domain(struct dca_domain *domain)
68 {
69         list_del(&domain->node);
70         kfree(domain);
71 }
72
73 static struct dca_domain *dca_find_domain(struct pci_bus *rc)
74 {
75         struct dca_domain *domain;
76
77         list_for_each_entry(domain, &dca_domains, node)
78                 if (domain->pci_rc == rc)
79                         return domain;
80
81         return NULL;
82 }
83
84 static struct dca_domain *dca_get_domain(struct device *dev)
85 {
86         struct pci_bus *rc;
87         struct dca_domain *domain;
88
89         rc = dca_pci_rc_from_dev(dev);
90         domain = dca_find_domain(rc);
91
92         if (!domain) {
93                 domain = dca_allocate_domain(rc);
94                 if (domain)
95                         list_add(&domain->node, &dca_domains);
96         }
97
98         return domain;
99 }
100
101 static struct dca_provider *dca_find_provider_by_dev(struct device *dev)
102 {
103         struct dca_provider *dca;
104         struct pci_bus *rc;
105         struct dca_domain *domain;
106
107         if (dev) {
108                 rc = dca_pci_rc_from_dev(dev);
109                 domain = dca_find_domain(rc);
110                 if (!domain)
111                         return NULL;
112         } else {
113                 if (!list_empty(&dca_domains))
114                         domain = list_first_entry(&dca_domains,
115                                                   struct dca_domain,
116                                                   node);
117                 else
118                         return NULL;
119         }
120
121         list_for_each_entry(dca, &domain->dca_providers, node)
122                 if ((!dev) || (dca->ops->dev_managed(dca, dev)))
123                         return dca;
124
125         return NULL;
126 }
127
128 /**
129  * dca_add_requester - add a dca client to the list
130  * @dev - the device that wants dca service
131  */
132 int dca_add_requester(struct device *dev)
133 {
134         struct dca_provider *dca;
135         int err, slot = -ENODEV;
136         unsigned long flags;
137         struct pci_bus *pci_rc;
138         struct dca_domain *domain;
139
140         if (!dev)
141                 return -EFAULT;
142
143         spin_lock_irqsave(&dca_lock, flags);
144
145         /* check if the requester has not been added already */
146         dca = dca_find_provider_by_dev(dev);
147         if (dca) {
148                 spin_unlock_irqrestore(&dca_lock, flags);
149                 return -EEXIST;
150         }
151
152         pci_rc = dca_pci_rc_from_dev(dev);
153         domain = dca_find_domain(pci_rc);
154         if (!domain) {
155                 spin_unlock_irqrestore(&dca_lock, flags);
156                 return -ENODEV;
157         }
158
159         list_for_each_entry(dca, &domain->dca_providers, node) {
160                 slot = dca->ops->add_requester(dca, dev);
161                 if (slot >= 0)
162                         break;
163         }
164
165         spin_unlock_irqrestore(&dca_lock, flags);
166
167         if (slot < 0)
168                 return slot;
169
170         err = dca_sysfs_add_req(dca, dev, slot);
171         if (err) {
172                 spin_lock_irqsave(&dca_lock, flags);
173                 if (dca == dca_find_provider_by_dev(dev))
174                         dca->ops->remove_requester(dca, dev);
175                 spin_unlock_irqrestore(&dca_lock, flags);
176                 return err;
177         }
178
179         return 0;
180 }
181 EXPORT_SYMBOL_GPL(dca_add_requester);
182
183 /**
184  * dca_remove_requester - remove a dca client from the list
185  * @dev - the device that wants dca service
186  */
187 int dca_remove_requester(struct device *dev)
188 {
189         struct dca_provider *dca;
190         int slot;
191         unsigned long flags;
192
193         if (!dev)
194                 return -EFAULT;
195
196         spin_lock_irqsave(&dca_lock, flags);
197         dca = dca_find_provider_by_dev(dev);
198         if (!dca) {
199                 spin_unlock_irqrestore(&dca_lock, flags);
200                 return -ENODEV;
201         }
202         slot = dca->ops->remove_requester(dca, dev);
203         spin_unlock_irqrestore(&dca_lock, flags);
204
205         if (slot < 0)
206                 return slot;
207
208         dca_sysfs_remove_req(dca, slot);
209
210         return 0;
211 }
212 EXPORT_SYMBOL_GPL(dca_remove_requester);
213
214 /**
215  * dca_common_get_tag - return the dca tag (serves both new and old api)
216  * @dev - the device that wants dca service
217  * @cpu - the cpuid as returned by get_cpu()
218  */
219 u8 dca_common_get_tag(struct device *dev, int cpu)
220 {
221         struct dca_provider *dca;
222         u8 tag;
223         unsigned long flags;
224
225         spin_lock_irqsave(&dca_lock, flags);
226
227         dca = dca_find_provider_by_dev(dev);
228         if (!dca) {
229                 spin_unlock_irqrestore(&dca_lock, flags);
230                 return -ENODEV;
231         }
232         tag = dca->ops->get_tag(dca, dev, cpu);
233
234         spin_unlock_irqrestore(&dca_lock, flags);
235         return tag;
236 }
237
238 /**
239  * dca3_get_tag - return the dca tag to the requester device
240  *                for the given cpu (new api)
241  * @dev - the device that wants dca service
242  * @cpu - the cpuid as returned by get_cpu()
243  */
244 u8 dca3_get_tag(struct device *dev, int cpu)
245 {
246         if (!dev)
247                 return -EFAULT;
248
249         return dca_common_get_tag(dev, cpu);
250 }
251 EXPORT_SYMBOL_GPL(dca3_get_tag);
252
253 /**
254  * dca_get_tag - return the dca tag for the given cpu (old api)
255  * @cpu - the cpuid as returned by get_cpu()
256  */
257 u8 dca_get_tag(int cpu)
258 {
259         struct device *dev = NULL;
260
261         return dca_common_get_tag(dev, cpu);
262 }
263 EXPORT_SYMBOL_GPL(dca_get_tag);
264
265 /**
266  * alloc_dca_provider - get data struct for describing a dca provider
267  * @ops - pointer to struct of dca operation function pointers
268  * @priv_size - size of extra mem to be added for provider's needs
269  */
270 struct dca_provider *alloc_dca_provider(struct dca_ops *ops, int priv_size)
271 {
272         struct dca_provider *dca;
273         int alloc_size;
274
275         alloc_size = (sizeof(*dca) + priv_size);
276         dca = kzalloc(alloc_size, GFP_KERNEL);
277         if (!dca)
278                 return NULL;
279         dca->ops = ops;
280
281         return dca;
282 }
283 EXPORT_SYMBOL_GPL(alloc_dca_provider);
284
285 /**
286  * free_dca_provider - release the dca provider data struct
287  * @ops - pointer to struct of dca operation function pointers
288  * @priv_size - size of extra mem to be added for provider's needs
289  */
290 void free_dca_provider(struct dca_provider *dca)
291 {
292         kfree(dca);
293 }
294 EXPORT_SYMBOL_GPL(free_dca_provider);
295
296 static BLOCKING_NOTIFIER_HEAD(dca_provider_chain);
297
298 /**
299  * register_dca_provider - register a dca provider
300  * @dca - struct created by alloc_dca_provider()
301  * @dev - device providing dca services
302  */
303 int register_dca_provider(struct dca_provider *dca, struct device *dev)
304 {
305         int err;
306         unsigned long flags;
307         struct dca_domain *domain;
308
309         err = dca_sysfs_add_provider(dca, dev);
310         if (err)
311                 return err;
312
313         spin_lock_irqsave(&dca_lock, flags);
314         domain = dca_get_domain(dev);
315         if (!domain) {
316                 spin_unlock_irqrestore(&dca_lock, flags);
317                 return -ENODEV;
318         }
319         list_add(&dca->node, &domain->dca_providers);
320         spin_unlock_irqrestore(&dca_lock, flags);
321
322         blocking_notifier_call_chain(&dca_provider_chain,
323                                      DCA_PROVIDER_ADD, NULL);
324         return 0;
325 }
326 EXPORT_SYMBOL_GPL(register_dca_provider);
327
328 /**
329  * unregister_dca_provider - remove a dca provider
330  * @dca - struct created by alloc_dca_provider()
331  */
332 void unregister_dca_provider(struct dca_provider *dca, struct device *dev)
333 {
334         unsigned long flags;
335         struct pci_bus *pci_rc;
336         struct dca_domain *domain;
337
338         blocking_notifier_call_chain(&dca_provider_chain,
339                                      DCA_PROVIDER_REMOVE, NULL);
340
341         spin_lock_irqsave(&dca_lock, flags);
342
343         list_del(&dca->node);
344
345         pci_rc = dca_pci_rc_from_dev(dev);
346         domain = dca_find_domain(pci_rc);
347         if (list_empty(&domain->dca_providers))
348                 dca_free_domain(domain);
349
350         spin_unlock_irqrestore(&dca_lock, flags);
351
352         dca_sysfs_remove_provider(dca);
353 }
354 EXPORT_SYMBOL_GPL(unregister_dca_provider);
355
356 /**
357  * dca_register_notify - register a client's notifier callback
358  */
359 void dca_register_notify(struct notifier_block *nb)
360 {
361         blocking_notifier_chain_register(&dca_provider_chain, nb);
362 }
363 EXPORT_SYMBOL_GPL(dca_register_notify);
364
365 /**
366  * dca_unregister_notify - remove a client's notifier callback
367  */
368 void dca_unregister_notify(struct notifier_block *nb)
369 {
370         blocking_notifier_chain_unregister(&dca_provider_chain, nb);
371 }
372 EXPORT_SYMBOL_GPL(dca_unregister_notify);
373
374 static int __init dca_init(void)
375 {
376         pr_info("dca service started, version %s\n", DCA_VERSION);
377         return dca_sysfs_init();
378 }
379
380 static void __exit dca_exit(void)
381 {
382         dca_sysfs_exit();
383 }
384
385 arch_initcall(dca_init);
386 module_exit(dca_exit);
387