162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * devfreq-event: a framework to provide raw data and events of devfreq devices
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2015 Samsung Electronics
662306a36Sopenharmony_ci * Author: Chanwoo Choi <cw00.choi@samsung.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This driver is based on drivers/devfreq/devfreq.c.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/devfreq-event.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/err.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/export.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/list.h>
1862306a36Sopenharmony_ci#include <linux/of.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic struct class *devfreq_event_class;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* The list of all devfreq event list */
2362306a36Sopenharmony_cistatic LIST_HEAD(devfreq_event_list);
2462306a36Sopenharmony_cistatic DEFINE_MUTEX(devfreq_event_list_lock);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define to_devfreq_event(DEV) container_of(DEV, struct devfreq_event_dev, dev)
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/**
2962306a36Sopenharmony_ci * devfreq_event_enable_edev() - Enable the devfreq-event dev and increase
3062306a36Sopenharmony_ci *				 the enable_count of devfreq-event dev.
3162306a36Sopenharmony_ci * @edev	: the devfreq-event device
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * Note that this function increase the enable_count and enable the
3462306a36Sopenharmony_ci * devfreq-event device. The devfreq-event device should be enabled before
3562306a36Sopenharmony_ci * using it by devfreq device.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ciint devfreq_event_enable_edev(struct devfreq_event_dev *edev)
3862306a36Sopenharmony_ci{
3962306a36Sopenharmony_ci	int ret = 0;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	if (!edev || !edev->desc)
4262306a36Sopenharmony_ci		return -EINVAL;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	mutex_lock(&edev->lock);
4562306a36Sopenharmony_ci	if (edev->desc->ops && edev->desc->ops->enable
4662306a36Sopenharmony_ci			&& edev->enable_count == 0) {
4762306a36Sopenharmony_ci		ret = edev->desc->ops->enable(edev);
4862306a36Sopenharmony_ci		if (ret < 0)
4962306a36Sopenharmony_ci			goto err;
5062306a36Sopenharmony_ci	}
5162306a36Sopenharmony_ci	edev->enable_count++;
5262306a36Sopenharmony_cierr:
5362306a36Sopenharmony_ci	mutex_unlock(&edev->lock);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	return ret;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_event_enable_edev);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci/**
6062306a36Sopenharmony_ci * devfreq_event_disable_edev() - Disable the devfreq-event dev and decrease
6162306a36Sopenharmony_ci *				  the enable_count of the devfreq-event dev.
6262306a36Sopenharmony_ci * @edev	: the devfreq-event device
6362306a36Sopenharmony_ci *
6462306a36Sopenharmony_ci * Note that this function decrease the enable_count and disable the
6562306a36Sopenharmony_ci * devfreq-event device. After the devfreq-event device is disabled,
6662306a36Sopenharmony_ci * devfreq device can't use the devfreq-event device for get/set/reset
6762306a36Sopenharmony_ci * operations.
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_ciint devfreq_event_disable_edev(struct devfreq_event_dev *edev)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	int ret = 0;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	if (!edev || !edev->desc)
7462306a36Sopenharmony_ci		return -EINVAL;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	mutex_lock(&edev->lock);
7762306a36Sopenharmony_ci	if (edev->enable_count <= 0) {
7862306a36Sopenharmony_ci		dev_warn(&edev->dev, "unbalanced enable_count\n");
7962306a36Sopenharmony_ci		ret = -EIO;
8062306a36Sopenharmony_ci		goto err;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (edev->desc->ops && edev->desc->ops->disable
8462306a36Sopenharmony_ci			&& edev->enable_count == 1) {
8562306a36Sopenharmony_ci		ret = edev->desc->ops->disable(edev);
8662306a36Sopenharmony_ci		if (ret < 0)
8762306a36Sopenharmony_ci			goto err;
8862306a36Sopenharmony_ci	}
8962306a36Sopenharmony_ci	edev->enable_count--;
9062306a36Sopenharmony_cierr:
9162306a36Sopenharmony_ci	mutex_unlock(&edev->lock);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	return ret;
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_event_disable_edev);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci/**
9862306a36Sopenharmony_ci * devfreq_event_is_enabled() - Check whether devfreq-event dev is enabled or
9962306a36Sopenharmony_ci *				not.
10062306a36Sopenharmony_ci * @edev	: the devfreq-event device
10162306a36Sopenharmony_ci *
10262306a36Sopenharmony_ci * Note that this function check whether devfreq-event dev is enabled or not.
10362306a36Sopenharmony_ci * If return true, the devfreq-event dev is enabeld. If return false, the
10462306a36Sopenharmony_ci * devfreq-event dev is disabled.
10562306a36Sopenharmony_ci */
10662306a36Sopenharmony_cibool devfreq_event_is_enabled(struct devfreq_event_dev *edev)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	bool enabled = false;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	if (!edev || !edev->desc)
11162306a36Sopenharmony_ci		return enabled;
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	mutex_lock(&edev->lock);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (edev->enable_count > 0)
11662306a36Sopenharmony_ci		enabled = true;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	mutex_unlock(&edev->lock);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	return enabled;
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_event_is_enabled);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci/**
12562306a36Sopenharmony_ci * devfreq_event_set_event() - Set event to devfreq-event dev to start.
12662306a36Sopenharmony_ci * @edev	: the devfreq-event device
12762306a36Sopenharmony_ci *
12862306a36Sopenharmony_ci * Note that this function set the event to the devfreq-event device to start
12962306a36Sopenharmony_ci * for getting the event data which could be various event type.
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_ciint devfreq_event_set_event(struct devfreq_event_dev *edev)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	int ret;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (!edev || !edev->desc)
13662306a36Sopenharmony_ci		return -EINVAL;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	if (!edev->desc->ops || !edev->desc->ops->set_event)
13962306a36Sopenharmony_ci		return -EINVAL;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if (!devfreq_event_is_enabled(edev))
14262306a36Sopenharmony_ci		return -EPERM;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	mutex_lock(&edev->lock);
14562306a36Sopenharmony_ci	ret = edev->desc->ops->set_event(edev);
14662306a36Sopenharmony_ci	mutex_unlock(&edev->lock);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return ret;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_event_set_event);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/**
15362306a36Sopenharmony_ci * devfreq_event_get_event() - Get {load|total}_count from devfreq-event dev.
15462306a36Sopenharmony_ci * @edev	: the devfreq-event device
15562306a36Sopenharmony_ci * @edata	: the calculated data of devfreq-event device
15662306a36Sopenharmony_ci *
15762306a36Sopenharmony_ci * Note that this function get the calculated event data from devfreq-event dev
15862306a36Sopenharmony_ci * after stoping the progress of whole sequence of devfreq-event dev.
15962306a36Sopenharmony_ci */
16062306a36Sopenharmony_ciint devfreq_event_get_event(struct devfreq_event_dev *edev,
16162306a36Sopenharmony_ci			    struct devfreq_event_data *edata)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	int ret;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	if (!edev || !edev->desc)
16662306a36Sopenharmony_ci		return -EINVAL;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (!edev->desc->ops || !edev->desc->ops->get_event)
16962306a36Sopenharmony_ci		return -EINVAL;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (!devfreq_event_is_enabled(edev))
17262306a36Sopenharmony_ci		return -EINVAL;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	edata->total_count = edata->load_count = 0;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	mutex_lock(&edev->lock);
17762306a36Sopenharmony_ci	ret = edev->desc->ops->get_event(edev, edata);
17862306a36Sopenharmony_ci	if (ret < 0)
17962306a36Sopenharmony_ci		edata->total_count = edata->load_count = 0;
18062306a36Sopenharmony_ci	mutex_unlock(&edev->lock);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	return ret;
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_event_get_event);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci/**
18762306a36Sopenharmony_ci * devfreq_event_reset_event() - Reset all opeations of devfreq-event dev.
18862306a36Sopenharmony_ci * @edev	: the devfreq-event device
18962306a36Sopenharmony_ci *
19062306a36Sopenharmony_ci * Note that this function stop all operations of devfreq-event dev and reset
19162306a36Sopenharmony_ci * the current event data to make the devfreq-event device into initial state.
19262306a36Sopenharmony_ci */
19362306a36Sopenharmony_ciint devfreq_event_reset_event(struct devfreq_event_dev *edev)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	int ret = 0;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (!edev || !edev->desc)
19862306a36Sopenharmony_ci		return -EINVAL;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (!devfreq_event_is_enabled(edev))
20162306a36Sopenharmony_ci		return -EPERM;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	mutex_lock(&edev->lock);
20462306a36Sopenharmony_ci	if (edev->desc->ops && edev->desc->ops->reset)
20562306a36Sopenharmony_ci		ret = edev->desc->ops->reset(edev);
20662306a36Sopenharmony_ci	mutex_unlock(&edev->lock);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	return ret;
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_event_reset_event);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci/**
21362306a36Sopenharmony_ci * devfreq_event_get_edev_by_phandle() - Get the devfreq-event dev from
21462306a36Sopenharmony_ci *					 devicetree.
21562306a36Sopenharmony_ci * @dev		: the pointer to the given device
21662306a36Sopenharmony_ci * @phandle_name: name of property holding a phandle value
21762306a36Sopenharmony_ci * @index	: the index into list of devfreq-event device
21862306a36Sopenharmony_ci *
21962306a36Sopenharmony_ci * Note that this function return the pointer of devfreq-event device.
22062306a36Sopenharmony_ci */
22162306a36Sopenharmony_cistruct devfreq_event_dev *devfreq_event_get_edev_by_phandle(struct device *dev,
22262306a36Sopenharmony_ci					const char *phandle_name, int index)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	struct device_node *node;
22562306a36Sopenharmony_ci	struct devfreq_event_dev *edev;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (!dev->of_node || !phandle_name)
22862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	node = of_parse_phandle(dev->of_node, phandle_name, index);
23162306a36Sopenharmony_ci	if (!node)
23262306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	mutex_lock(&devfreq_event_list_lock);
23562306a36Sopenharmony_ci	list_for_each_entry(edev, &devfreq_event_list, node) {
23662306a36Sopenharmony_ci		if (edev->dev.parent && device_match_of_node(edev->dev.parent, node))
23762306a36Sopenharmony_ci			goto out;
23862306a36Sopenharmony_ci	}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	list_for_each_entry(edev, &devfreq_event_list, node) {
24162306a36Sopenharmony_ci		if (of_node_name_eq(node, edev->desc->name))
24262306a36Sopenharmony_ci			goto out;
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci	edev = NULL;
24562306a36Sopenharmony_ciout:
24662306a36Sopenharmony_ci	mutex_unlock(&devfreq_event_list_lock);
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (!edev) {
24962306a36Sopenharmony_ci		of_node_put(node);
25062306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	of_node_put(node);
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	return edev;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_event_get_edev_by_phandle);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/**
26062306a36Sopenharmony_ci * devfreq_event_get_edev_count() - Get the count of devfreq-event dev
26162306a36Sopenharmony_ci * @dev		: the pointer to the given device
26262306a36Sopenharmony_ci * @phandle_name: name of property holding a phandle value
26362306a36Sopenharmony_ci *
26462306a36Sopenharmony_ci * Note that this function return the count of devfreq-event devices.
26562306a36Sopenharmony_ci */
26662306a36Sopenharmony_ciint devfreq_event_get_edev_count(struct device *dev, const char *phandle_name)
26762306a36Sopenharmony_ci{
26862306a36Sopenharmony_ci	int count;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	if (!dev->of_node || !phandle_name) {
27162306a36Sopenharmony_ci		dev_err(dev, "device does not have a device node entry\n");
27262306a36Sopenharmony_ci		return -EINVAL;
27362306a36Sopenharmony_ci	}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	count = of_property_count_elems_of_size(dev->of_node, phandle_name,
27662306a36Sopenharmony_ci						sizeof(u32));
27762306a36Sopenharmony_ci	if (count < 0) {
27862306a36Sopenharmony_ci		dev_err(dev,
27962306a36Sopenharmony_ci			"failed to get the count of devfreq-event in %pOF node\n",
28062306a36Sopenharmony_ci			dev->of_node);
28162306a36Sopenharmony_ci		return count;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	return count;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_event_get_edev_count);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic void devfreq_event_release_edev(struct device *dev)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	struct devfreq_event_dev *edev = to_devfreq_event(dev);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	kfree(edev);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/**
29662306a36Sopenharmony_ci * devfreq_event_add_edev() - Add new devfreq-event device.
29762306a36Sopenharmony_ci * @dev		: the device owning the devfreq-event device being created
29862306a36Sopenharmony_ci * @desc	: the devfreq-event device's descriptor which include essential
29962306a36Sopenharmony_ci *		  data for devfreq-event device.
30062306a36Sopenharmony_ci *
30162306a36Sopenharmony_ci * Note that this function add new devfreq-event device to devfreq-event class
30262306a36Sopenharmony_ci * list and register the device of the devfreq-event device.
30362306a36Sopenharmony_ci */
30462306a36Sopenharmony_cistruct devfreq_event_dev *devfreq_event_add_edev(struct device *dev,
30562306a36Sopenharmony_ci						struct devfreq_event_desc *desc)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct devfreq_event_dev *edev;
30862306a36Sopenharmony_ci	static atomic_t event_no = ATOMIC_INIT(-1);
30962306a36Sopenharmony_ci	int ret;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (!dev || !desc)
31262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	if (!desc->name || !desc->ops)
31562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (!desc->ops->set_event || !desc->ops->get_event)
31862306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	edev = kzalloc(sizeof(struct devfreq_event_dev), GFP_KERNEL);
32162306a36Sopenharmony_ci	if (!edev)
32262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	mutex_init(&edev->lock);
32562306a36Sopenharmony_ci	edev->desc = desc;
32662306a36Sopenharmony_ci	edev->enable_count = 0;
32762306a36Sopenharmony_ci	edev->dev.parent = dev;
32862306a36Sopenharmony_ci	edev->dev.class = devfreq_event_class;
32962306a36Sopenharmony_ci	edev->dev.release = devfreq_event_release_edev;
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	dev_set_name(&edev->dev, "event%d", atomic_inc_return(&event_no));
33262306a36Sopenharmony_ci	ret = device_register(&edev->dev);
33362306a36Sopenharmony_ci	if (ret < 0) {
33462306a36Sopenharmony_ci		put_device(&edev->dev);
33562306a36Sopenharmony_ci		return ERR_PTR(ret);
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	dev_set_drvdata(&edev->dev, edev);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	INIT_LIST_HEAD(&edev->node);
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	mutex_lock(&devfreq_event_list_lock);
34262306a36Sopenharmony_ci	list_add(&edev->node, &devfreq_event_list);
34362306a36Sopenharmony_ci	mutex_unlock(&devfreq_event_list_lock);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	return edev;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_event_add_edev);
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci/**
35062306a36Sopenharmony_ci * devfreq_event_remove_edev() - Remove the devfreq-event device registered.
35162306a36Sopenharmony_ci * @edev	: the devfreq-event device
35262306a36Sopenharmony_ci *
35362306a36Sopenharmony_ci * Note that this function removes the registered devfreq-event device.
35462306a36Sopenharmony_ci */
35562306a36Sopenharmony_ciint devfreq_event_remove_edev(struct devfreq_event_dev *edev)
35662306a36Sopenharmony_ci{
35762306a36Sopenharmony_ci	if (!edev)
35862306a36Sopenharmony_ci		return -EINVAL;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	WARN_ON(edev->enable_count);
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	mutex_lock(&devfreq_event_list_lock);
36362306a36Sopenharmony_ci	list_del(&edev->node);
36462306a36Sopenharmony_ci	mutex_unlock(&devfreq_event_list_lock);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	device_unregister(&edev->dev);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	return 0;
36962306a36Sopenharmony_ci}
37062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devfreq_event_remove_edev);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_cistatic int devm_devfreq_event_match(struct device *dev, void *res, void *data)
37362306a36Sopenharmony_ci{
37462306a36Sopenharmony_ci	struct devfreq_event_dev **r = res;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	if (WARN_ON(!r || !*r))
37762306a36Sopenharmony_ci		return 0;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	return *r == data;
38062306a36Sopenharmony_ci}
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_cistatic void devm_devfreq_event_release(struct device *dev, void *res)
38362306a36Sopenharmony_ci{
38462306a36Sopenharmony_ci	devfreq_event_remove_edev(*(struct devfreq_event_dev **)res);
38562306a36Sopenharmony_ci}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci/**
38862306a36Sopenharmony_ci * devm_devfreq_event_add_edev() - Resource-managed devfreq_event_add_edev()
38962306a36Sopenharmony_ci * @dev		: the device owning the devfreq-event device being created
39062306a36Sopenharmony_ci * @desc	: the devfreq-event device's descriptor which include essential
39162306a36Sopenharmony_ci *		  data for devfreq-event device.
39262306a36Sopenharmony_ci *
39362306a36Sopenharmony_ci * Note that this function manages automatically the memory of devfreq-event
39462306a36Sopenharmony_ci * device using device resource management and simplify the free operation
39562306a36Sopenharmony_ci * for memory of devfreq-event device.
39662306a36Sopenharmony_ci */
39762306a36Sopenharmony_cistruct devfreq_event_dev *devm_devfreq_event_add_edev(struct device *dev,
39862306a36Sopenharmony_ci						struct devfreq_event_desc *desc)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	struct devfreq_event_dev **ptr, *edev;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	ptr = devres_alloc(devm_devfreq_event_release, sizeof(*ptr),
40362306a36Sopenharmony_ci				GFP_KERNEL);
40462306a36Sopenharmony_ci	if (!ptr)
40562306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	edev = devfreq_event_add_edev(dev, desc);
40862306a36Sopenharmony_ci	if (IS_ERR(edev)) {
40962306a36Sopenharmony_ci		devres_free(ptr);
41062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	*ptr = edev;
41462306a36Sopenharmony_ci	devres_add(dev, ptr);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	return edev;
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_devfreq_event_add_edev);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci/**
42162306a36Sopenharmony_ci * devm_devfreq_event_remove_edev()- Resource-managed devfreq_event_remove_edev()
42262306a36Sopenharmony_ci * @dev		: the device owning the devfreq-event device being created
42362306a36Sopenharmony_ci * @edev	: the devfreq-event device
42462306a36Sopenharmony_ci *
42562306a36Sopenharmony_ci * Note that this function manages automatically the memory of devfreq-event
42662306a36Sopenharmony_ci * device using device resource management.
42762306a36Sopenharmony_ci */
42862306a36Sopenharmony_civoid devm_devfreq_event_remove_edev(struct device *dev,
42962306a36Sopenharmony_ci				struct devfreq_event_dev *edev)
43062306a36Sopenharmony_ci{
43162306a36Sopenharmony_ci	WARN_ON(devres_release(dev, devm_devfreq_event_release,
43262306a36Sopenharmony_ci			       devm_devfreq_event_match, edev));
43362306a36Sopenharmony_ci}
43462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(devm_devfreq_event_remove_edev);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci/*
43762306a36Sopenharmony_ci * Device attributes for devfreq-event class.
43862306a36Sopenharmony_ci */
43962306a36Sopenharmony_cistatic ssize_t name_show(struct device *dev, struct device_attribute *attr,
44062306a36Sopenharmony_ci			 char *buf)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci	struct devfreq_event_dev *edev = to_devfreq_event(dev);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	if (!edev || !edev->desc)
44562306a36Sopenharmony_ci		return -EINVAL;
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_ci	return sprintf(buf, "%s\n", edev->desc->name);
44862306a36Sopenharmony_ci}
44962306a36Sopenharmony_cistatic DEVICE_ATTR_RO(name);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_cistatic ssize_t enable_count_show(struct device *dev,
45262306a36Sopenharmony_ci				  struct device_attribute *attr, char *buf)
45362306a36Sopenharmony_ci{
45462306a36Sopenharmony_ci	struct devfreq_event_dev *edev = to_devfreq_event(dev);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	if (!edev || !edev->desc)
45762306a36Sopenharmony_ci		return -EINVAL;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	return sprintf(buf, "%d\n", edev->enable_count);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_cistatic DEVICE_ATTR_RO(enable_count);
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_cistatic struct attribute *devfreq_event_attrs[] = {
46462306a36Sopenharmony_ci	&dev_attr_name.attr,
46562306a36Sopenharmony_ci	&dev_attr_enable_count.attr,
46662306a36Sopenharmony_ci	NULL,
46762306a36Sopenharmony_ci};
46862306a36Sopenharmony_ciATTRIBUTE_GROUPS(devfreq_event);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_cistatic int __init devfreq_event_init(void)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	devfreq_event_class = class_create("devfreq-event");
47362306a36Sopenharmony_ci	if (IS_ERR(devfreq_event_class)) {
47462306a36Sopenharmony_ci		pr_err("%s: couldn't create class\n", __FILE__);
47562306a36Sopenharmony_ci		return PTR_ERR(devfreq_event_class);
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	devfreq_event_class->dev_groups = devfreq_event_groups;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	return 0;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_cisubsys_initcall(devfreq_event_init);
483