Skip to content

Commit 4db9fd3

Browse files
committed
OPP: Add dev_pm_opp_set_config() and friends
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2122311 commit 11b9b66 Author: Viresh Kumar <viresh.kumar@linaro.org> Date: Wed May 25 15:23:16 2022 +0530 The OPP core already have few configuration specific APIs and it is getting complex or messy for both the OPP core and its users. Lets introduce a new set of API which will be used for all kind of different configurations, and shall eventually be used by all the existing ones. The new API, returns a unique token instead of a pointer to the OPP table, which allows the OPP core to drop the resources selectively later on. Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Mark Langsdorf <mlangsdo@redhat.com>
1 parent eed25eb commit 4db9fd3

File tree

3 files changed

+291
-1
lines changed

3 files changed

+291
-1
lines changed

drivers/opp/core.c

Lines changed: 228 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,12 @@
1313
#include <linux/clk.h>
1414
#include <linux/errno.h>
1515
#include <linux/err.h>
16-
#include <linux/slab.h>
1716
#include <linux/device.h>
1817
#include <linux/export.h>
1918
#include <linux/pm_domain.h>
2019
#include <linux/regulator/consumer.h>
20+
#include <linux/slab.h>
21+
#include <linux/xarray.h>
2122

2223
#include "opp.h"
2324

@@ -36,6 +37,9 @@ DEFINE_MUTEX(opp_table_lock);
3637
/* Flag indicating that opp_tables list is being updated at the moment */
3738
static bool opp_tables_busy;
3839

40+
/* OPP ID allocator */
41+
static DEFINE_XARRAY_ALLOC1(opp_configs);
42+
3943
static bool _find_opp_dev(const struct device *dev, struct opp_table *opp_table)
4044
{
4145
struct opp_device *opp_dev;
@@ -2497,6 +2501,229 @@ int devm_pm_opp_attach_genpd(struct device *dev, const char * const *names,
24972501
}
24982502
EXPORT_SYMBOL_GPL(devm_pm_opp_attach_genpd);
24992503

2504+
static void _opp_clear_config(struct opp_config_data *data)
2505+
{
2506+
if (data->flags & OPP_CONFIG_GENPD)
2507+
dev_pm_opp_detach_genpd(data->opp_table);
2508+
if (data->flags & OPP_CONFIG_REGULATOR)
2509+
dev_pm_opp_put_regulators(data->opp_table);
2510+
if (data->flags & OPP_CONFIG_SUPPORTED_HW)
2511+
dev_pm_opp_put_supported_hw(data->opp_table);
2512+
if (data->flags & OPP_CONFIG_REGULATOR_HELPER)
2513+
dev_pm_opp_unregister_set_opp_helper(data->opp_table);
2514+
if (data->flags & OPP_CONFIG_PROP_NAME)
2515+
dev_pm_opp_put_prop_name(data->opp_table);
2516+
if (data->flags & OPP_CONFIG_CLK)
2517+
dev_pm_opp_put_clkname(data->opp_table);
2518+
2519+
dev_pm_opp_put_opp_table(data->opp_table);
2520+
kfree(data);
2521+
}
2522+
2523+
/**
2524+
* dev_pm_opp_set_config() - Set OPP configuration for the device.
2525+
* @dev: Device for which configuration is being set.
2526+
* @config: OPP configuration.
2527+
*
2528+
* This allows all device OPP configurations to be performed at once.
2529+
*
2530+
* This must be called before any OPPs are initialized for the device. This may
2531+
* be called multiple times for the same OPP table, for example once for each
2532+
* CPU that share the same table. This must be balanced by the same number of
2533+
* calls to dev_pm_opp_clear_config() in order to free the OPP table properly.
2534+
*
2535+
* This returns a token to the caller, which must be passed to
2536+
* dev_pm_opp_clear_config() to free the resources later. The value of the
2537+
* returned token will be >= 1 for success and negative for errors. The minimum
2538+
* value of 1 is chosen here to make it easy for callers to manage the resource.
2539+
*/
2540+
int dev_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config)
2541+
{
2542+
struct opp_table *opp_table, *err;
2543+
struct opp_config_data *data;
2544+
unsigned int id;
2545+
int ret;
2546+
2547+
data = kmalloc(sizeof(*data), GFP_KERNEL);
2548+
if (!data)
2549+
return -ENOMEM;
2550+
2551+
opp_table = _add_opp_table(dev, false);
2552+
if (IS_ERR(opp_table)) {
2553+
kfree(data);
2554+
return PTR_ERR(opp_table);
2555+
}
2556+
2557+
data->opp_table = opp_table;
2558+
data->flags = 0;
2559+
2560+
/* This should be called before OPPs are initialized */
2561+
if (WARN_ON(!list_empty(&opp_table->opp_list))) {
2562+
ret = -EBUSY;
2563+
goto err;
2564+
}
2565+
2566+
/* Configure clocks */
2567+
if (config->clk_names) {
2568+
const char * const *temp = config->clk_names;
2569+
int count = 0;
2570+
2571+
/* Count number of clks */
2572+
while (*temp++)
2573+
count++;
2574+
2575+
/*
2576+
* This is a special case where we have a single clock, whose
2577+
* connection id name is NULL, i.e. first two entries are NULL
2578+
* in the array.
2579+
*/
2580+
if (!count && !config->clk_names[1])
2581+
count = 1;
2582+
2583+
/* We support only one clock name for now */
2584+
if (count != 1) {
2585+
ret = -EINVAL;
2586+
goto err;
2587+
}
2588+
2589+
err = dev_pm_opp_set_clkname(dev, config->clk_names[0]);
2590+
if (IS_ERR(err)) {
2591+
ret = PTR_ERR(err);
2592+
goto err;
2593+
}
2594+
2595+
data->flags |= OPP_CONFIG_CLK;
2596+
}
2597+
2598+
/* Configure property names */
2599+
if (config->prop_name) {
2600+
err = dev_pm_opp_set_prop_name(dev, config->prop_name);
2601+
if (IS_ERR(err)) {
2602+
ret = PTR_ERR(err);
2603+
goto err;
2604+
}
2605+
2606+
data->flags |= OPP_CONFIG_PROP_NAME;
2607+
}
2608+
2609+
/* Configure opp helper */
2610+
if (config->set_opp) {
2611+
err = dev_pm_opp_register_set_opp_helper(dev, config->set_opp);
2612+
if (IS_ERR(err)) {
2613+
ret = PTR_ERR(err);
2614+
goto err;
2615+
}
2616+
2617+
data->flags |= OPP_CONFIG_REGULATOR_HELPER;
2618+
}
2619+
2620+
/* Configure supported hardware */
2621+
if (config->supported_hw) {
2622+
err = dev_pm_opp_set_supported_hw(dev, config->supported_hw,
2623+
config->supported_hw_count);
2624+
if (IS_ERR(err)) {
2625+
ret = PTR_ERR(err);
2626+
goto err;
2627+
}
2628+
2629+
data->flags |= OPP_CONFIG_SUPPORTED_HW;
2630+
}
2631+
2632+
/* Configure supplies */
2633+
if (config->regulator_names) {
2634+
err = dev_pm_opp_set_regulators(dev, config->regulator_names);
2635+
if (IS_ERR(err)) {
2636+
ret = PTR_ERR(err);
2637+
goto err;
2638+
}
2639+
2640+
data->flags |= OPP_CONFIG_REGULATOR;
2641+
}
2642+
2643+
/* Attach genpds */
2644+
if (config->genpd_names) {
2645+
err = dev_pm_opp_attach_genpd(dev, config->genpd_names,
2646+
config->virt_devs);
2647+
if (IS_ERR(err)) {
2648+
ret = PTR_ERR(err);
2649+
goto err;
2650+
}
2651+
2652+
data->flags |= OPP_CONFIG_GENPD;
2653+
}
2654+
2655+
ret = xa_alloc(&opp_configs, &id, data, XA_LIMIT(1, INT_MAX),
2656+
GFP_KERNEL);
2657+
if (ret)
2658+
goto err;
2659+
2660+
return id;
2661+
2662+
err:
2663+
_opp_clear_config(data);
2664+
return ret;
2665+
}
2666+
EXPORT_SYMBOL_GPL(dev_pm_opp_set_config);
2667+
2668+
/**
2669+
* dev_pm_opp_clear_config() - Releases resources blocked for OPP configuration.
2670+
* @opp_table: OPP table returned from dev_pm_opp_set_config().
2671+
*
2672+
* This allows all device OPP configurations to be cleared at once. This must be
2673+
* called once for each call made to dev_pm_opp_set_config(), in order to free
2674+
* the OPPs properly.
2675+
*
2676+
* Currently the first call itself ends up freeing all the OPP configurations,
2677+
* while the later ones only drop the OPP table reference. This works well for
2678+
* now as we would never want to use an half initialized OPP table and want to
2679+
* remove the configurations together.
2680+
*/
2681+
void dev_pm_opp_clear_config(int token)
2682+
{
2683+
struct opp_config_data *data;
2684+
2685+
/*
2686+
* This lets the callers call this unconditionally and keep their code
2687+
* simple.
2688+
*/
2689+
if (unlikely(token <= 0))
2690+
return;
2691+
2692+
data = xa_erase(&opp_configs, token);
2693+
if (WARN_ON(!data))
2694+
return;
2695+
2696+
_opp_clear_config(data);
2697+
}
2698+
EXPORT_SYMBOL_GPL(dev_pm_opp_clear_config);
2699+
2700+
static void devm_pm_opp_config_release(void *token)
2701+
{
2702+
dev_pm_opp_clear_config((unsigned long)token);
2703+
}
2704+
2705+
/**
2706+
* devm_pm_opp_set_config() - Set OPP configuration for the device.
2707+
* @dev: Device for which configuration is being set.
2708+
* @config: OPP configuration.
2709+
*
2710+
* This allows all device OPP configurations to be performed at once.
2711+
* This is a resource-managed variant of dev_pm_opp_set_config().
2712+
*
2713+
* Return: 0 on success and errorno otherwise.
2714+
*/
2715+
int devm_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config)
2716+
{
2717+
int token = dev_pm_opp_set_config(dev, config);
2718+
2719+
if (token < 0)
2720+
return token;
2721+
2722+
return devm_add_action_or_reset(dev, devm_pm_opp_config_release,
2723+
(void *) ((unsigned long) token));
2724+
}
2725+
EXPORT_SYMBOL_GPL(devm_pm_opp_set_config);
2726+
25002727
/**
25012728
* dev_pm_opp_xlate_required_opp() - Find required OPP for @src_table OPP.
25022729
* @src_table: OPP table which has @dst_table as one of its required OPP table.

drivers/opp/opp.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,27 @@ extern struct mutex opp_table_lock;
2828

2929
extern struct list_head opp_tables, lazy_opp_tables;
3030

31+
/* OPP Config flags */
32+
#define OPP_CONFIG_CLK BIT(0)
33+
#define OPP_CONFIG_REGULATOR BIT(1)
34+
#define OPP_CONFIG_REGULATOR_HELPER BIT(2)
35+
#define OPP_CONFIG_PROP_NAME BIT(3)
36+
#define OPP_CONFIG_SUPPORTED_HW BIT(4)
37+
#define OPP_CONFIG_GENPD BIT(5)
38+
39+
/**
40+
* struct opp_config_data - data for set config operations
41+
* @opp_table: OPP table
42+
* @flags: OPP config flags
43+
*
44+
* This structure stores the OPP config information for each OPP table
45+
* configuration by the callers.
46+
*/
47+
struct opp_config_data {
48+
struct opp_table *opp_table;
49+
unsigned int flags;
50+
};
51+
3152
/*
3253
* Internal data structure organization with the OPP layer library is as
3354
* follows:

include/linux/pm_opp.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,32 @@ struct dev_pm_set_opp_data {
9090
struct device *dev;
9191
};
9292

93+
/**
94+
* struct dev_pm_opp_config - Device OPP configuration values
95+
* @clk_names: Clk names, NULL terminated array, max 1 clock for now.
96+
* @prop_name: Name to postfix to properties.
97+
* @set_opp: Custom set OPP helper.
98+
* @supported_hw: Array of hierarchy of versions to match.
99+
* @supported_hw_count: Number of elements in the array.
100+
* @regulator_names: Array of pointers to the names of the regulator, NULL terminated.
101+
* @genpd_names: Null terminated array of pointers containing names of genpd to
102+
* attach.
103+
* @virt_devs: Pointer to return the array of virtual devices.
104+
*
105+
* This structure contains platform specific OPP configurations for the device.
106+
*/
107+
struct dev_pm_opp_config {
108+
/* NULL terminated */
109+
const char * const *clk_names;
110+
const char *prop_name;
111+
int (*set_opp)(struct dev_pm_set_opp_data *data);
112+
const unsigned int *supported_hw;
113+
unsigned int supported_hw_count;
114+
const char * const *regulator_names;
115+
const char * const *genpd_names;
116+
struct device ***virt_devs;
117+
};
118+
93119
#if defined(CONFIG_PM_OPP)
94120

95121
struct opp_table *dev_pm_opp_get_opp_table(struct device *dev);
@@ -147,6 +173,10 @@ int dev_pm_opp_disable(struct device *dev, unsigned long freq);
147173
int dev_pm_opp_register_notifier(struct device *dev, struct notifier_block *nb);
148174
int dev_pm_opp_unregister_notifier(struct device *dev, struct notifier_block *nb);
149175

176+
int dev_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config);
177+
int devm_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config);
178+
void dev_pm_opp_clear_config(int token);
179+
150180
struct opp_table *dev_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, unsigned int count);
151181
void dev_pm_opp_put_supported_hw(struct opp_table *opp_table);
152182
int devm_pm_opp_set_supported_hw(struct device *dev, const u32 *versions, unsigned int count);
@@ -399,6 +429,18 @@ static inline int devm_pm_opp_attach_genpd(struct device *dev,
399429
return -EOPNOTSUPP;
400430
}
401431

432+
static inline int dev_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config)
433+
{
434+
return -EOPNOTSUPP;
435+
}
436+
437+
static inline int devm_pm_opp_set_config(struct device *dev, struct dev_pm_opp_config *config)
438+
{
439+
return -EOPNOTSUPP;
440+
}
441+
442+
static inline void dev_pm_opp_clear_config(int token) {}
443+
402444
static inline struct dev_pm_opp *dev_pm_opp_xlate_required_opp(struct opp_table *src_table,
403445
struct opp_table *dst_table, struct dev_pm_opp *src_opp)
404446
{

0 commit comments

Comments
 (0)