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 dev_err(dev, "device does not have a device node entry\n"); 231 return ERR_PTR(-EINVAL); 232 } 233 234 node = of_parse_phandle(dev->of_node, "devfreq-events", index); 235 if (!node) { 236 dev_err(dev, "failed to get phandle in %s node\n", 237 dev->of_node->full_name); 238 return ERR_PTR(-ENODEV); 239 } 240 241 mutex_lock(&devfreq_event_list_lock); 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 dev_err(dev, "unable to get devfreq-event device : %s\n", 252 node->name); 253 of_node_put(node); 254 return ERR_PTR(-ENODEV); 255 } 256 257 of_node_put(node); 258 259 return edev; 260 } 261 EXPORT_SYMBOL_GPL(devfreq_event_get_edev_by_phandle); 262 263 /** 264 * devfreq_event_get_edev_count() - Get the count of devfreq-event dev 265 * @dev : the pointer to the given device 266 * 267 * Note that this function return the count of devfreq-event devices. 268 */ 269 int devfreq_event_get_edev_count(struct device *dev) 270 { 271 int count; 272 273 if (!dev->of_node) { 274 dev_err(dev, "device does not have a device node entry\n"); 275 return -EINVAL; 276 } 277 278 count = of_property_count_elems_of_size(dev->of_node, "devfreq-events", 279 sizeof(u32)); 280 if (count < 0 ) { 281 dev_err(dev, 282 "failed to get the count of devfreq-event in %s node\n", 283 dev->of_node->full_name); 284 return count; 285 } 286 287 return count; 288 } 289 EXPORT_SYMBOL_GPL(devfreq_event_get_edev_count); 290 291 static void devfreq_event_release_edev(struct device *dev) 292 { 293 struct devfreq_event_dev *edev = to_devfreq_event(dev); 294 295 kfree(edev); 296 } 297 298 /** 299 * devfreq_event_add_edev() - Add new devfreq-event device. 300 * @dev : the device owning the devfreq-event device being created 301 * @desc : the devfreq-event device's decriptor which include essential 302 * data for devfreq-event device. 303 * 304 * Note that this function add new devfreq-event device to devfreq-event class 305 * list and register the device of the devfreq-event device. 306 */ 307 struct devfreq_event_dev *devfreq_event_add_edev(struct device *dev, 308 struct devfreq_event_desc *desc) 309 { 310 struct devfreq_event_dev *edev; 311 static atomic_t event_no = ATOMIC_INIT(0); 312 int ret; 313 314 if (!dev || !desc) 315 return ERR_PTR(-EINVAL); 316 317 if (!desc->name || !desc->ops) 318 return ERR_PTR(-EINVAL); 319 320 if (!desc->ops->set_event || !desc->ops->get_event) 321 return ERR_PTR(-EINVAL); 322 323 edev = kzalloc(sizeof(struct devfreq_event_dev), GFP_KERNEL); 324 if (!edev) 325 return ERR_PTR(-ENOMEM); 326 327 mutex_init(&edev->lock); 328 edev->desc = desc; 329 edev->enable_count = 0; 330 edev->dev.parent = dev; 331 edev->dev.class = devfreq_event_class; 332 edev->dev.release = devfreq_event_release_edev; 333 334 dev_set_name(&edev->dev, "event.%d", atomic_inc_return(&event_no) - 1); 335 ret = device_register(&edev->dev); 336 if (ret < 0) { 337 put_device(&edev->dev); 338 return ERR_PTR(ret); 339 } 340 dev_set_drvdata(&edev->dev, edev); 341 342 INIT_LIST_HEAD(&edev->node); 343 344 mutex_lock(&devfreq_event_list_lock); 345 list_add(&edev->node, &devfreq_event_list); 346 mutex_unlock(&devfreq_event_list_lock); 347 348 return edev; 349 } 350 EXPORT_SYMBOL_GPL(devfreq_event_add_edev); 351 352 /** 353 * devfreq_event_remove_edev() - Remove the devfreq-event device registered. 354 * @dev : the devfreq-event device 355 * 356 * Note that this function remove the registered devfreq-event device. 357 */ 358 int devfreq_event_remove_edev(struct devfreq_event_dev *edev) 359 { 360 if (!edev) 361 return -EINVAL; 362 363 WARN_ON(edev->enable_count); 364 365 mutex_lock(&devfreq_event_list_lock); 366 list_del(&edev->node); 367 mutex_unlock(&devfreq_event_list_lock); 368 369 device_unregister(&edev->dev); 370 371 return 0; 372 } 373 EXPORT_SYMBOL_GPL(devfreq_event_remove_edev); 374 375 static int devm_devfreq_event_match(struct device *dev, void *res, void *data) 376 { 377 struct devfreq_event_dev **r = res; 378 379 if (WARN_ON(!r || !*r)) 380 return 0; 381 382 return *r == data; 383 } 384 385 static void devm_devfreq_event_release(struct device *dev, void *res) 386 { 387 devfreq_event_remove_edev(*(struct devfreq_event_dev **)res); 388 } 389 390 /** 391 * devm_devfreq_event_add_edev() - Resource-managed devfreq_event_add_edev() 392 * @dev : the device owning the devfreq-event device being created 393 * @desc : the devfreq-event device's decriptor which include essential 394 * data for devfreq-event device. 395 * 396 * Note that this function manages automatically the memory of devfreq-event 397 * device using device resource management and simplify the free operation 398 * for memory of devfreq-event device. 399 */ 400 struct devfreq_event_dev *devm_devfreq_event_add_edev(struct device *dev, 401 struct devfreq_event_desc *desc) 402 { 403 struct devfreq_event_dev **ptr, *edev; 404 405 ptr = devres_alloc(devm_devfreq_event_release, sizeof(*ptr), GFP_KERNEL); 406 if (!ptr) 407 return ERR_PTR(-ENOMEM); 408 409 edev = devfreq_event_add_edev(dev, desc); 410 if (IS_ERR(edev)) { 411 devres_free(ptr); 412 return ERR_PTR(-ENOMEM); 413 } 414 415 *ptr = edev; 416 devres_add(dev, ptr); 417 418 return edev; 419 } 420 EXPORT_SYMBOL_GPL(devm_devfreq_event_add_edev); 421 422 /** 423 * devm_devfreq_event_remove_edev()- Resource-managed devfreq_event_remove_edev() 424 * @dev : the device owning the devfreq-event device being created 425 * @edev : the devfreq-event device 426 * 427 * Note that this function manages automatically the memory of devfreq-event 428 * device using device resource management. 429 */ 430 void devm_devfreq_event_remove_edev(struct device *dev, 431 struct devfreq_event_dev *edev) 432 { 433 WARN_ON(devres_release(dev, devm_devfreq_event_release, 434 devm_devfreq_event_match, edev)); 435 } 436 EXPORT_SYMBOL_GPL(devm_devfreq_event_remove_edev); 437 438 /* 439 * Device attributes for devfreq-event class. 440 */ 441 static ssize_t name_show(struct device *dev, struct device_attribute *attr, 442 char *buf) 443 { 444 struct devfreq_event_dev *edev = to_devfreq_event(dev); 445 446 if (!edev || !edev->desc) 447 return -EINVAL; 448 449 return sprintf(buf, "%s\n", edev->desc->name); 450 } 451 static DEVICE_ATTR_RO(name); 452 453 static ssize_t enable_count_show(struct device *dev, 454 struct device_attribute *attr, char *buf) 455 { 456 struct devfreq_event_dev *edev = to_devfreq_event(dev); 457 458 if (!edev || !edev->desc) 459 return -EINVAL; 460 461 return sprintf(buf, "%d\n", edev->enable_count); 462 } 463 static DEVICE_ATTR_RO(enable_count); 464 465 static struct attribute *devfreq_event_attrs[] = { 466 &dev_attr_name.attr, 467 &dev_attr_enable_count.attr, 468 NULL, 469 }; 470 ATTRIBUTE_GROUPS(devfreq_event); 471 472 static int __init devfreq_event_init(void) 473 { 474 devfreq_event_class = class_create(THIS_MODULE, "devfreq-event"); 475 if (IS_ERR(devfreq_event_class)) { 476 pr_err("%s: couldn't create class\n", __FILE__); 477 return PTR_ERR(devfreq_event_class); 478 } 479 480 devfreq_event_class->dev_groups = devfreq_event_groups; 481 482 return 0; 483 } 484 subsys_initcall(devfreq_event_init); 485 486 static void __exit devfreq_event_exit(void) 487 { 488 class_destroy(devfreq_event_class); 489 } 490 module_exit(devfreq_event_exit); 491 492 MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>"); 493 MODULE_DESCRIPTION("DEVFREQ-Event class support"); 494 MODULE_LICENSE("GPL"); 495