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