xref: /openbmc/linux/drivers/dca/dca-core.c (revision 8fa5723aa7e053d498336b48448b292fc2e0458b)
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 module_init(dca_init);
274 module_exit(dca_exit);
275 
276