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