diff options
Diffstat (limited to 'drivers/dca/dca-core.c')
-rw-r--r-- | drivers/dca/dca-core.c | 131 |
1 files changed, 103 insertions, 28 deletions
diff --git a/drivers/dca/dca-core.c b/drivers/dca/dca-core.c index bf5b92f86df7..ec249d2db633 100644 --- a/drivers/dca/dca-core.c +++ b/drivers/dca/dca-core.c | |||
@@ -28,13 +28,29 @@ | |||
28 | #include <linux/device.h> | 28 | #include <linux/device.h> |
29 | #include <linux/dca.h> | 29 | #include <linux/dca.h> |
30 | 30 | ||
31 | MODULE_LICENSE("GPL"); | 31 | #define DCA_VERSION "1.4" |
32 | 32 | ||
33 | /* For now we're assuming a single, global, DCA provider for the system. */ | 33 | MODULE_VERSION(DCA_VERSION); |
34 | MODULE_LICENSE("GPL"); | ||
35 | MODULE_AUTHOR("Intel Corporation"); | ||
34 | 36 | ||
35 | static DEFINE_SPINLOCK(dca_lock); | 37 | static DEFINE_SPINLOCK(dca_lock); |
36 | 38 | ||
37 | static struct dca_provider *global_dca = NULL; | 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 | } | ||
38 | 54 | ||
39 | /** | 55 | /** |
40 | * dca_add_requester - add a dca client to the list | 56 | * dca_add_requester - add a dca client to the list |
@@ -42,25 +58,39 @@ static struct dca_provider *global_dca = NULL; | |||
42 | */ | 58 | */ |
43 | int dca_add_requester(struct device *dev) | 59 | int dca_add_requester(struct device *dev) |
44 | { | 60 | { |
45 | int err, slot; | 61 | struct dca_provider *dca; |
62 | int err, slot = -ENODEV; | ||
46 | 63 | ||
47 | if (!global_dca) | 64 | if (!dev) |
48 | return -ENODEV; | 65 | return -EFAULT; |
49 | 66 | ||
50 | spin_lock(&dca_lock); | 67 | spin_lock(&dca_lock); |
51 | slot = global_dca->ops->add_requester(global_dca, dev); | 68 | |
52 | spin_unlock(&dca_lock); | 69 | /* check if the requester has not been added already */ |
53 | if (slot < 0) | 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); | ||
54 | return slot; | 83 | return slot; |
84 | } | ||
55 | 85 | ||
56 | err = dca_sysfs_add_req(global_dca, dev, slot); | 86 | err = dca_sysfs_add_req(dca, dev, slot); |
57 | if (err) { | 87 | if (err) { |
58 | spin_lock(&dca_lock); | 88 | dca->ops->remove_requester(dca, dev); |
59 | global_dca->ops->remove_requester(global_dca, dev); | ||
60 | spin_unlock(&dca_lock); | 89 | spin_unlock(&dca_lock); |
61 | return err; | 90 | return err; |
62 | } | 91 | } |
63 | 92 | ||
93 | spin_unlock(&dca_lock); | ||
64 | return 0; | 94 | return 0; |
65 | } | 95 | } |
66 | EXPORT_SYMBOL_GPL(dca_add_requester); | 96 | EXPORT_SYMBOL_GPL(dca_add_requester); |
@@ -71,30 +101,78 @@ EXPORT_SYMBOL_GPL(dca_add_requester); | |||
71 | */ | 101 | */ |
72 | int dca_remove_requester(struct device *dev) | 102 | int dca_remove_requester(struct device *dev) |
73 | { | 103 | { |
104 | struct dca_provider *dca; | ||
74 | int slot; | 105 | int slot; |
75 | if (!global_dca) | 106 | |
76 | return -ENODEV; | 107 | if (!dev) |
108 | return -EFAULT; | ||
77 | 109 | ||
78 | spin_lock(&dca_lock); | 110 | spin_lock(&dca_lock); |
79 | slot = global_dca->ops->remove_requester(global_dca, dev); | 111 | dca = dca_find_provider_by_dev(dev); |
80 | spin_unlock(&dca_lock); | 112 | if (!dca) { |
81 | if (slot < 0) | 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); | ||
82 | return slot; | 119 | return slot; |
120 | } | ||
83 | 121 | ||
84 | dca_sysfs_remove_req(global_dca, slot); | 122 | dca_sysfs_remove_req(dca, slot); |
123 | |||
124 | spin_unlock(&dca_lock); | ||
85 | return 0; | 125 | return 0; |
86 | } | 126 | } |
87 | EXPORT_SYMBOL_GPL(dca_remove_requester); | 127 | EXPORT_SYMBOL_GPL(dca_remove_requester); |
88 | 128 | ||
89 | /** | 129 | /** |
90 | * dca_get_tag - return the dca tag for the given cpu | 130 | * dca_common_get_tag - return the dca tag (serves both new and old api) |
131 | * @dev - the device that wants dca service | ||
91 | * @cpu - the cpuid as returned by get_cpu() | 132 | * @cpu - the cpuid as returned by get_cpu() |
92 | */ | 133 | */ |
93 | u8 dca_get_tag(int cpu) | 134 | u8 dca_common_get_tag(struct device *dev, int cpu) |
94 | { | 135 | { |
95 | if (!global_dca) | 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); | ||
96 | return -ENODEV; | 144 | return -ENODEV; |
97 | return global_dca->ops->get_tag(global_dca, cpu); | 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); | ||
98 | } | 176 | } |
99 | EXPORT_SYMBOL_GPL(dca_get_tag); | 177 | EXPORT_SYMBOL_GPL(dca_get_tag); |
100 | 178 | ||
@@ -140,12 +218,10 @@ int register_dca_provider(struct dca_provider *dca, struct device *dev) | |||
140 | { | 218 | { |
141 | int err; | 219 | int err; |
142 | 220 | ||
143 | if (global_dca) | ||
144 | return -EEXIST; | ||
145 | err = dca_sysfs_add_provider(dca, dev); | 221 | err = dca_sysfs_add_provider(dca, dev); |
146 | if (err) | 222 | if (err) |
147 | return err; | 223 | return err; |
148 | global_dca = dca; | 224 | list_add(&dca->node, &dca_providers); |
149 | blocking_notifier_call_chain(&dca_provider_chain, | 225 | blocking_notifier_call_chain(&dca_provider_chain, |
150 | DCA_PROVIDER_ADD, NULL); | 226 | DCA_PROVIDER_ADD, NULL); |
151 | return 0; | 227 | return 0; |
@@ -158,11 +234,9 @@ EXPORT_SYMBOL_GPL(register_dca_provider); | |||
158 | */ | 234 | */ |
159 | void unregister_dca_provider(struct dca_provider *dca) | 235 | void unregister_dca_provider(struct dca_provider *dca) |
160 | { | 236 | { |
161 | if (!global_dca) | ||
162 | return; | ||
163 | blocking_notifier_call_chain(&dca_provider_chain, | 237 | blocking_notifier_call_chain(&dca_provider_chain, |
164 | DCA_PROVIDER_REMOVE, NULL); | 238 | DCA_PROVIDER_REMOVE, NULL); |
165 | global_dca = NULL; | 239 | list_del(&dca->node); |
166 | dca_sysfs_remove_provider(dca); | 240 | dca_sysfs_remove_provider(dca); |
167 | } | 241 | } |
168 | EXPORT_SYMBOL_GPL(unregister_dca_provider); | 242 | EXPORT_SYMBOL_GPL(unregister_dca_provider); |
@@ -187,6 +261,7 @@ EXPORT_SYMBOL_GPL(dca_unregister_notify); | |||
187 | 261 | ||
188 | static int __init dca_init(void) | 262 | static int __init dca_init(void) |
189 | { | 263 | { |
264 | printk(KERN_ERR "dca service started, version %s\n", DCA_VERSION); | ||
190 | return dca_sysfs_init(); | 265 | return dca_sysfs_init(); |
191 | } | 266 | } |
192 | 267 | ||