linux/drivers/devfreq/devfreq-event.c
<<
>>
Prefs
   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/export.h>
  19#include <linux/slab.h>
  20#include <linux/list.h>
  21#include <linux/of.h>
  22
  23static struct class *devfreq_event_class;
  24
  25/* The list of all devfreq event list */
  26static LIST_HEAD(devfreq_event_list);
  27static 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 */
  40int 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++;
  55err:
  56        mutex_unlock(&edev->lock);
  57
  58        return ret;
  59}
  60EXPORT_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 */
  72int 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--;
  93err:
  94        mutex_unlock(&edev->lock);
  95
  96        return ret;
  97}
  98EXPORT_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 */
 109bool 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}
 125EXPORT_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 */
 134int 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}
 153EXPORT_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 */
 163int 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}
 187EXPORT_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 */
 196int 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}
 213EXPORT_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 */
 223struct 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;
 247out:
 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}
 259EXPORT_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 */
 267int 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}
 287EXPORT_SYMBOL_GPL(devfreq_event_get_edev_count);
 288
 289static 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 */
 305struct 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(-1);
 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));
 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}
 348EXPORT_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 */
 356int 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}
 371EXPORT_SYMBOL_GPL(devfreq_event_remove_edev);
 372
 373static 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
 383static 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 */
 398struct 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}
 419EXPORT_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 */
 429void 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}
 435EXPORT_SYMBOL_GPL(devm_devfreq_event_remove_edev);
 436
 437/*
 438 * Device attributes for devfreq-event class.
 439 */
 440static 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}
 450static DEVICE_ATTR_RO(name);
 451
 452static 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}
 462static DEVICE_ATTR_RO(enable_count);
 463
 464static struct attribute *devfreq_event_attrs[] = {
 465        &dev_attr_name.attr,
 466        &dev_attr_enable_count.attr,
 467        NULL,
 468};
 469ATTRIBUTE_GROUPS(devfreq_event);
 470
 471static 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}
 483subsys_initcall(devfreq_event_init);
 484