1 /* 2 * devfreq-event: a framework to provide raw data and events of devfreq devices 3 * 4 * Copyright (C) 2015 Samsung Electronics 5 * Author: Chanwoo Choi <cw00.choi@samsung.com> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 * This driver is based on drivers/devfreq/devfreq.c. 12 */ 13 14 #include <linux/devfreq-event.h> 15 #include <linux/kernel.h> 16 #include <linux/err.h> 17 #include <linux/init.h> 18 #include <linux/module.h> 19 #include <linux/slab.h> 20 #include <linux/list.h> 21 #include <linux/of.h> 22 23 static struct class *devfreq_event_class; 24 25 /* The list of all devfreq event list */ 26 static LIST_HEAD(devfreq_event_list); 27 static DEFINE_MUTEX(devfreq_event_list_lock); 28 29 #define to_devfreq_event(DEV) container_of(DEV, struct devfreq_event_dev, dev) 30 31 /** 32 * devfreq_event_enable_edev() - Enable the devfreq-event dev and increase 33 * the enable_count of devfreq-event dev. 34 * @edev : the devfreq-event device 35 * 36 * Note that this function increase the enable_count and enable the 37 * devfreq-event device. The devfreq-event device should be enabled before 38 * using it by devfreq device. 39 */ 40 int devfreq_event_enable_edev(struct devfreq_event_dev *edev) 41 { 42 int ret = 0; 43 44 if (!edev || !edev->desc) 45 return -EINVAL; 46 47 mutex_lock(&edev->lock); 48 if (edev->desc->ops && edev->desc->ops->enable 49 && edev->enable_count == 0) { 50 ret = edev->desc->ops->enable(edev); 51 if (ret < 0) 52 goto err; 53 } 54 edev->enable_count++; 55 err: 56 mutex_unlock(&edev->lock); 57 58 return ret; 59 } 60 EXPORT_SYMBOL_GPL(devfreq_event_enable_edev); 61 62 /** 63 * devfreq_event_disable_edev() - Disable the devfreq-event dev and decrease 64 * the enable_count of the devfreq-event dev. 65 * @edev : the devfreq-event device 66 * 67 * Note that this function decrease the enable_count and disable the 68 * devfreq-event device. After the devfreq-event device is disabled, 69 * devfreq device can't use the devfreq-event device for get/set/reset 70 * operations. 71 */ 72 int devfreq_event_disable_edev(struct devfreq_event_dev *edev) 73 { 74 int ret = 0; 75 76 if (!edev || !edev->desc) 77 return -EINVAL; 78 79 mutex_lock(&edev->lock); 80 if (edev->enable_count <= 0) { 81 dev_warn(&edev->dev, "unbalanced enable_count\n"); 82 ret = -EIO; 83 goto err; 84 } 85 86 if (edev->desc->ops && edev->desc->ops->disable 87 && edev->enable_count == 1) { 88 ret = edev->desc->ops->disable(edev); 89 if (ret < 0) 90 goto err; 91 } 92 edev->enable_count--; 93 err: 94 mutex_unlock(&edev->lock); 95 96 return ret; 97 } 98 EXPORT_SYMBOL_GPL(devfreq_event_disable_edev); 99 100 /** 101 * devfreq_event_is_enabled() - Check whether devfreq-event dev is enabled or 102 * not. 103 * @edev : the devfreq-event device 104 * 105 * Note that this function check whether devfreq-event dev is enabled or not. 106 * If return true, the devfreq-event dev is enabeld. If return false, the 107 * devfreq-event dev is disabled. 108 */ 109 bool devfreq_event_is_enabled(struct devfreq_event_dev *edev) 110 { 111 bool enabled = false; 112 113 if (!edev || !edev->desc) 114 return enabled; 115 116 mutex_lock(&edev->lock); 117 118 if (edev->enable_count > 0) 119 enabled = true; 120 121 mutex_unlock(&edev->lock); 122 123 return enabled; 124 } 125 EXPORT_SYMBOL_GPL(devfreq_event_is_enabled); 126 127 /** 128 * devfreq_event_set_event() - Set event to devfreq-event dev to start. 129 * @edev : the devfreq-event device 130 * 131 * Note that this function set the event to the devfreq-event device to start 132 * for getting the event data which could be various event type. 133 */ 134 int devfreq_event_set_event(struct devfreq_event_dev *edev) 135 { 136 int ret; 137 138 if (!edev || !edev->desc) 139 return -EINVAL; 140 141 if (!edev->desc->ops || !edev->desc->ops->set_event) 142 return -EINVAL; 143 144 if (!devfreq_event_is_enabled(edev)) 145 return -EPERM; 146 147 mutex_lock(&edev->lock); 148 ret = edev->desc->ops->set_event(edev); 149 mutex_unlock(&edev->lock); 150 151 return ret; 152 } 153 EXPORT_SYMBOL_GPL(devfreq_event_set_event); 154 155 /** 156 * devfreq_event_get_event() - Get {load|total}_count from devfreq-event dev. 157 * @edev : the devfreq-event device 158 * @edata : the calculated data of devfreq-event device 159 * 160 * Note that this function get the calculated event data from devfreq-event dev 161 * after stoping the progress of whole sequence of devfreq-event dev. 162 */ 163 int devfreq_event_get_event(struct devfreq_event_dev *edev, 164 struct devfreq_event_data *edata) 165 { 166 int ret; 167 168 if (!edev || !edev->desc) 169 return -EINVAL; 170 171 if (!edev->desc->ops || !edev->desc->ops->get_event) 172 return -EINVAL; 173 174 if (!devfreq_event_is_enabled(edev)) 175 return -EINVAL; 176 177 edata->total_count = edata->load_count = 0; 178 179 mutex_lock(&edev->lock); 180 ret = edev->desc->ops->get_event(edev, edata); 181 if (ret < 0) 182 edata->total_count = edata->load_count = 0; 183 mutex_unlock(&edev->lock); 184 185 return ret; 186 } 187 EXPORT_SYMBOL_GPL(devfreq_event_get_event); 188 189 /** 190 * devfreq_event_reset_event() - Reset all opeations of devfreq-event dev. 191 * @edev : the devfreq-event device 192 * 193 * Note that this function stop all operations of devfreq-event dev and reset 194 * the current event data to make the devfreq-event device into initial state. 195 */ 196 int devfreq_event_reset_event(struct devfreq_event_dev *edev) 197 { 198 int ret = 0; 199 200 if (!edev || !edev->desc) 201 return -EINVAL; 202 203 if (!devfreq_event_is_enabled(edev)) 204 return -EPERM; 205 206 mutex_lock(&edev->lock); 207 if (edev->desc->ops && edev->desc->ops->reset) 208 ret = edev->desc->ops->reset(edev); 209 mutex_unlock(&edev->lock); 210 211 return ret; 212 } 213 EXPORT_SYMBOL_GPL(devfreq_event_reset_event); 214 215 /** 216 * devfreq_event_get_edev_by_phandle() - Get the devfreq-event dev from 217 * devicetree. 218 * @dev : the pointer to the given device 219 * @index : the index into list of devfreq-event device 220 * 221 * Note that this function return the pointer of devfreq-event device. 222 */ 223 struct devfreq_event_dev *devfreq_event_get_edev_by_phandle(struct device *dev, 224 int index) 225 { 226 struct device_node *node; 227 struct devfreq_event_dev *edev; 228 229 if (!dev->of_node) 230 return ERR_PTR(-EINVAL); 231 232 node = of_parse_phandle(dev->of_node, "devfreq-events", index); 233 if (!node) 234 return ERR_PTR(-ENODEV); 235 236 mutex_lock(&devfreq_event_list_lock); 237 list_for_each_entry(edev, &devfreq_event_list, node) { 238 if (edev->dev.parent && edev->dev.parent->of_node == node) 239 goto out; 240 } 241 242 list_for_each_entry(edev, &devfreq_event_list, node) { 243 if (!strcmp(edev->desc->name, node->name)) 244 goto out; 245 } 246 edev = NULL; 247 out: 248 mutex_unlock(&devfreq_event_list_lock); 249 250 if (!edev) { 251 of_node_put(node); 252 return ERR_PTR(-ENODEV); 253 } 254 255 of_node_put(node); 256 257 return edev; 258 } 259 EXPORT_SYMBOL_GPL(devfreq_event_get_edev_by_phandle); 260 261 /** 262 * devfreq_event_get_edev_count() - Get the count of devfreq-event dev 263 * @dev : the pointer to the given device 264 * 265 * Note that this function return the count of devfreq-event devices. 266 */ 267 int devfreq_event_get_edev_count(struct device *dev) 268 { 269 int count; 270 271 if (!dev->of_node) { 272 dev_err(dev, "device does not have a device node entry\n"); 273 return -EINVAL; 274 } 275 276 count = of_property_count_elems_of_size(dev->of_node, "devfreq-events", 277 sizeof(u32)); 278 if (count < 0) { 279 dev_err(dev, 280 "failed to get the count of devfreq-event in %s node\n", 281 dev->of_node->full_name); 282 return count; 283 } 284 285 return count; 286 } 287 EXPORT_SYMBOL_GPL(devfreq_event_get_edev_count); 288 289 static void devfreq_event_release_edev(struct device *dev) 290 { 291 struct devfreq_event_dev *edev = to_devfreq_event(dev); 292 293 kfree(edev); 294 } 295 296 /** 297 * devfreq_event_add_edev() - Add new devfreq-event device. 298 * @dev : the device owning the devfreq-event device being created 299 * @desc : the devfreq-event device's decriptor which include essential 300 * data for devfreq-event device. 301 * 302 * Note that this function add new devfreq-event device to devfreq-event class 303 * list and register the device of the devfreq-event device. 304 */ 305 struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev, 306 struct devfreq_event_desc *desc) 307 { 308 struct devfreq_event_dev *edev; 309 static atomic_t event_no = ATOMIC_INIT(0); 310 int ret; 311 312 if (!dev || !desc) 313 return ERR_PTR(-EINVAL); 314 315 if (!desc->name || !desc->ops) 316 return ERR_PTR(-EINVAL); 317 318 if (!desc->ops->set_event || !desc->ops->get_event) 319 return ERR_PTR(-EINVAL); 320 321 edev = kzalloc(sizeof(struct devfreq_event_dev), GFP_KERNEL); 322 if (!edev) 323 return ERR_PTR(-ENOMEM); 324 325 mutex_init(&edev->lock); 326 edev->desc = desc; 327 edev->enable_count = 0; 328 edev->dev.parent = dev; 329 edev->dev.class = devfreq_event_class; 330 edev->dev.release = devfreq_event_release_edev; 331 332 dev_set_name(&edev->dev, "event.%d", atomic_inc_return(&event_no) - 1); 333 ret = device_register(&edev->dev); 334 if (ret < 0) { 335 put_device(&edev->dev); 336 return ERR_PTR(ret); 337 } 338 dev_set_drvdata(&edev->dev, edev); 339 340 INIT_LIST_HEAD(&edev->node); 341 342 mutex_lock(&devfreq_event_list_lock); 343 list_add(&edev->node, &devfreq_event_list); 344 mutex_unlock(&devfreq_event_list_lock); 345 346 return edev; 347 } 348 EXPORT_SYMBOL_GPL(devfreq_event_add_edev); 349 350 /** 351 * devfreq_event_remove_edev() - Remove the devfreq-event device registered. 352 * @dev : the devfreq-event device 353 * 354 * Note that this function remove the registered devfreq-event device. 355 */ 356 int devfreq_event_remove_edev(struct devfreq_event_dev *edev) 357 { 358 if (!edev) 359 return -EINVAL; 360 361 WARN_ON(edev->enable_count); 362 363 mutex_lock(&devfreq_event_list_lock); 364 list_del(&edev->node); 365 mutex_unlock(&devfreq_event_list_lock); 366 367 device_unregister(&edev->dev); 368 369 return 0; 370 } 371 EXPORT_SYMBOL_GPL(devfreq_event_remove_edev); 372 373 static int devm_devfreq_event_match(struct device *dev, void *res, void *data) 374 { 375 struct devfreq_event_dev **r = res; 376 377 if (WARN_ON(!r || !*r)) 378 return 0; 379 380 return *r == data; 381 } 382 383 static void devm_devfreq_event_release(struct device *dev, void *res) 384 { 385 devfreq_event_remove_edev(*(struct devfreq_event_dev **)res); 386 } 387 388 /** 389 * devm_devfreq_event_add_edev() - Resource-managed devfreq_event_add_edev() 390 * @dev : the device owning the devfreq-event device being created 391 * @desc : the devfreq-event device's decriptor which include essential 392 * data for devfreq-event device. 393 * 394 * Note that this function manages automatically the memory of devfreq-event 395 * device using device resource management and simplify the free operation 396 * for memory of devfreq-event device. 397 */ 398 struct devfreq_event_dev *devm_devfreq_event_add_edev(struct device *dev, 399 struct devfreq_event_desc *desc) 400 { 401 struct devfreq_event_dev **ptr, *edev; 402 403 ptr = devres_alloc(devm_devfreq_event_release, sizeof(*ptr), 404 GFP_KERNEL); 405 if (!ptr) 406 return ERR_PTR(-ENOMEM); 407 408 edev = devfreq_event_add_edev(dev, desc); 409 if (IS_ERR(edev)) { 410 devres_free(ptr); 411 return ERR_PTR(-ENOMEM); 412 } 413 414 *ptr = edev; 415 devres_add(dev, ptr); 416 417 return edev; 418 } 419 EXPORT_SYMBOL_GPL(devm_devfreq_event_add_edev); 420 421 /** 422 * devm_devfreq_event_remove_edev()- Resource-managed devfreq_event_remove_edev() 423 * @dev : the device owning the devfreq-event device being created 424 * @edev : the devfreq-event device 425 * 426 * Note that this function manages automatically the memory of devfreq-event 427 * device using device resource management. 428 */ 429 void devm_devfreq_event_remove_edev(struct device *dev, 430 struct devfreq_event_dev *edev) 431 { 432 WARN_ON(devres_release(dev, devm_devfreq_event_release, 433 devm_devfreq_event_match, edev)); 434 } 435 EXPORT_SYMBOL_GPL(devm_devfreq_event_remove_edev); 436 437 /* 438 * Device attributes for devfreq-event class. 439 */ 440 static ssize_t name_show(struct device *dev, struct device_attribute *attr, 441 char *buf) 442 { 443 struct devfreq_event_dev *edev = to_devfreq_event(dev); 444 445 if (!edev || !edev->desc) 446 return -EINVAL; 447 448 return sprintf(buf, "%s\n", edev->desc->name); 449 } 450 static DEVICE_ATTR_RO(name); 451 452 static ssize_t enable_count_show(struct device *dev, 453 struct device_attribute *attr, char *buf) 454 { 455 struct devfreq_event_dev *edev = to_devfreq_event(dev); 456 457 if (!edev || !edev->desc) 458 return -EINVAL; 459 460 return sprintf(buf, "%d\n", edev->enable_count); 461 } 462 static DEVICE_ATTR_RO(enable_count); 463 464 static struct attribute *devfreq_event_attrs[] = { 465 &dev_attr_name.attr, 466 &dev_attr_enable_count.attr, 467 NULL, 468 }; 469 ATTRIBUTE_GROUPS(devfreq_event); 470 471 static int __init devfreq_event_init(void) 472 { 473 devfreq_event_class = class_create(THIS_MODULE, "devfreq-event"); 474 if (IS_ERR(devfreq_event_class)) { 475 pr_err("%s: couldn't create class\n", __FILE__); 476 return PTR_ERR(devfreq_event_class); 477 } 478 479 devfreq_event_class->dev_groups = devfreq_event_groups; 480 481 return 0; 482 } 483 subsys_initcall(devfreq_event_init); 484 485 static void __exit devfreq_event_exit(void) 486 { 487 class_destroy(devfreq_event_class); 488 } 489 module_exit(devfreq_event_exit); 490 491 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); 492 MODULE_DESCRIPTION("DEVFREQ-Event class support"); 493 MODULE_LICENSE("GPL"); 494