Merge commit 'v2.6.28-rc5' into x86/cleanups
[linux-2.6] / drivers / dca / dca-core.c
1 /*
2  * Copyright(c) 2007 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
31 #define DCA_VERSION "1.4"
32
33 MODULE_VERSION(DCA_VERSION);
34 MODULE_LICENSE("GPL");
35 MODULE_AUTHOR("Intel Corporation");
36
37 static DEFINE_SPINLOCK(dca_lock);
38
39 static LIST_HEAD(dca_providers);
40
41 static struct dca_provider *dca_find_provider_by_dev(struct device *dev)
42 {
43         struct dca_provider *dca, *ret = NULL;
44
45         list_for_each_entry(dca, &dca_providers, node) {
46                 if ((!dev) || (dca->ops->dev_managed(dca, dev))) {
47                         ret = dca;
48                         break;
49                 }
50         }
51
52         return ret;
53 }
54
55 /**
56  * dca_add_requester - add a dca client to the list
57  * @dev - the device that wants dca service
58  */
59 int dca_add_requester(struct device *dev)
60 {
61         struct dca_provider *dca;
62         int err, slot = -ENODEV;
63
64         if (!dev)
65                 return -EFAULT;
66
67         spin_lock(&dca_lock);
68
69         /* check if the requester has not been added already */
70         dca = dca_find_provider_by_dev(dev);
71         if (dca) {
72                 spin_unlock(&dca_lock);
73                 return -EEXIST;
74         }
75
76         list_for_each_entry(dca, &dca_providers, node) {
77                 slot = dca->ops->add_requester(dca, dev);
78                 if (slot >= 0)
79                         break;
80         }
81         if (slot < 0) {
82                 spin_unlock(&dca_lock);
83                 return slot;
84         }
85
86         err = dca_sysfs_add_req(dca, dev, slot);
87         if (err) {
88                 dca->ops->remove_requester(dca, dev);
89                 spin_unlock(&dca_lock);
90                 return err;
91         }
92
93         spin_unlock(&dca_lock);
94         return 0;
95 }
96 EXPORT_SYMBOL_GPL(dca_add_requester);
97
98 /**
99  * dca_remove_requester - remove a dca client from the list
100  * @dev - the device that wants dca service
101  */
102 int dca_remove_requester(struct device *dev)
103 {
104         struct dca_provider *dca;
105         int slot;
106
107         if (!dev)
108                 return -EFAULT;
109
110         spin_lock(&dca_lock);
111         dca = dca_find_provider_by_dev(dev);
112         if (!dca) {
113                 spin_unlock(&dca_lock);
114                 return -ENODEV;
115         }
116         slot = dca->ops->remove_requester(dca, dev);
117         if (slot < 0) {
118                 spin_unlock(&dca_lock);
119                 return slot;
120         }
121
122         dca_sysfs_remove_req(dca, slot);
123
124         spin_unlock(&dca_lock);
125         return 0;
126 }
127 EXPORT_SYMBOL_GPL(dca_remove_requester);
128
129 /**
130  * dca_common_get_tag - return the dca tag (serves both new and old api)
131  * @dev - the device that wants dca service
132  * @cpu - the cpuid as returned by get_cpu()
133  */
134 u8 dca_common_get_tag(struct device *dev, int cpu)
135 {
136         struct dca_provider *dca;
137         u8 tag;
138
139         spin_lock(&dca_lock);
140
141         dca = dca_find_provider_by_dev(dev);
142         if (!dca) {
143                 spin_unlock(&dca_lock);
144                 return -ENODEV;
145         }
146         tag = dca->ops->get_tag(dca, dev, cpu);
147
148         spin_unlock(&dca_lock);
149         return tag;
150 }
151
152 /**
153  * dca3_get_tag - return the dca tag to the requester device
154  *                for the given cpu (new api)
155  * @dev - the device that wants dca service
156  * @cpu - the cpuid as returned by get_cpu()
157  */
158 u8 dca3_get_tag(struct device *dev, int cpu)
159 {
160         if (!dev)
161                 return -EFAULT;
162
163         return dca_common_get_tag(dev, cpu);
164 }
165 EXPORT_SYMBOL_GPL(dca3_get_tag);
166
167 /**
168  * dca_get_tag - return the dca tag for the given cpu (old api)
169  * @cpu - the cpuid as returned by get_cpu()
170  */
171 u8 dca_get_tag(int cpu)
172 {
173         struct device *dev = NULL;
174
175         return dca_common_get_tag(dev, cpu);
176 }
177 EXPORT_SYMBOL_GPL(dca_get_tag);
178
179 /**
180  * alloc_dca_provider - get data struct for describing a dca provider
181  * @ops - pointer to struct of dca operation function pointers
182  * @priv_size - size of extra mem to be added for provider's needs
183  */
184 struct dca_provider *alloc_dca_provider(struct dca_ops *ops, int priv_size)
185 {
186         struct dca_provider *dca;
187         int alloc_size;
188
189         alloc_size = (sizeof(*dca) + priv_size);
190         dca = kzalloc(alloc_size, GFP_KERNEL);
191         if (!dca)
192                 return NULL;
193         dca->ops = ops;
194
195         return dca;
196 }
197 EXPORT_SYMBOL_GPL(alloc_dca_provider);
198
199 /**
200  * free_dca_provider - release the dca provider data struct
201  * @ops - pointer to struct of dca operation function pointers
202  * @priv_size - size of extra mem to be added for provider's needs
203  */
204 void free_dca_provider(struct dca_provider *dca)
205 {
206         kfree(dca);
207 }
208 EXPORT_SYMBOL_GPL(free_dca_provider);
209
210 static BLOCKING_NOTIFIER_HEAD(dca_provider_chain);
211
212 /**
213  * register_dca_provider - register a dca provider
214  * @dca - struct created by alloc_dca_provider()
215  * @dev - device providing dca services
216  */
217 int register_dca_provider(struct dca_provider *dca, struct device *dev)
218 {
219         int err;
220
221         err = dca_sysfs_add_provider(dca, dev);
222         if (err)
223                 return err;
224         list_add(&dca->node, &dca_providers);
225         blocking_notifier_call_chain(&dca_provider_chain,
226                                      DCA_PROVIDER_ADD, NULL);
227         return 0;
228 }
229 EXPORT_SYMBOL_GPL(register_dca_provider);
230
231 /**
232  * unregister_dca_provider - remove a dca provider
233  * @dca - struct created by alloc_dca_provider()
234  */
235 void unregister_dca_provider(struct dca_provider *dca)
236 {
237         blocking_notifier_call_chain(&dca_provider_chain,
238                                      DCA_PROVIDER_REMOVE, NULL);
239         list_del(&dca->node);
240         dca_sysfs_remove_provider(dca);
241 }
242 EXPORT_SYMBOL_GPL(unregister_dca_provider);
243
244 /**
245  * dca_register_notify - register a client's notifier callback
246  */
247 void dca_register_notify(struct notifier_block *nb)
248 {
249         blocking_notifier_chain_register(&dca_provider_chain, nb);
250 }
251 EXPORT_SYMBOL_GPL(dca_register_notify);
252
253 /**
254  * dca_unregister_notify - remove a client's notifier callback
255  */
256 void dca_unregister_notify(struct notifier_block *nb)
257 {
258         blocking_notifier_chain_unregister(&dca_provider_chain, nb);
259 }
260 EXPORT_SYMBOL_GPL(dca_unregister_notify);
261
262 static int __init dca_init(void)
263 {
264         printk(KERN_ERR "dca service started, version %s\n", DCA_VERSION);
265         return dca_sysfs_init();
266 }
267
268 static void __exit dca_exit(void)
269 {
270         dca_sysfs_exit();
271 }
272
273 subsys_initcall(dca_init);
274 module_exit(dca_exit);
275