Skip to content

Commit 23fbabe

Browse files
mripardpopcornmix
authored andcommitted
clk: Introduce a clock request API
It's not unusual to find clocks being shared across multiple devices that need to change the rate depending on what the device is doing at a given time. The SoC found on the RaspberryPi4 (BCM2711) is in such a situation between its two HDMI controllers that share a clock that needs to be raised depending on the output resolution of each controller. The current clk_set_rate API doesn't really allow to support that case since there's really no synchronisation between multiple users, it's essentially a fire-and-forget solution. clk_set_min_rate does allow for such a synchronisation, but has another drawback: it doesn't allow to reduce the clock rate once the work is over. In our previous example, this means that if we were to raise the resolution of one HDMI controller to the largest resolution and then changing for a smaller one, we would still have the clock running at the largest resolution rate resulting in a poor power-efficiency. In order to address both issues, let's create an API that allows user to create temporary requests to increase the rate to a minimum, before going back to the initial rate once the request is done. This introduces mainly two side-effects: * There's an interaction between clk_set_rate and requests. This has been addressed by having clk_set_rate increasing the rate if it's greater than what the requests asked for, and in any case changing the rate the clock will return to once all the requests are done. * Similarly, clk_round_rate has been adjusted to take the requests into account and return a rate that will be greater or equal to the requested rates. Signed-off-by: Maxime Ripard <[email protected]>
1 parent 6fe6f9b commit 23fbabe

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

drivers/clk/clk.c

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,14 @@ struct clk_core {
7777
unsigned int protect_count;
7878
unsigned long min_rate;
7979
unsigned long max_rate;
80+
unsigned long default_request_rate;
8081
unsigned long accuracy;
8182
int phase;
8283
struct clk_duty duty;
8384
struct hlist_head children;
8485
struct hlist_node child_node;
8586
struct hlist_head clks;
87+
struct list_head pending_requests;
8688
unsigned int notifier_count;
8789
#ifdef CONFIG_DEBUG_FS
8890
struct dentry *dentry;
@@ -105,6 +107,12 @@ struct clk {
105107
struct hlist_node clks_node;
106108
};
107109

110+
struct clk_request {
111+
struct list_head list;
112+
struct clk *clk;
113+
unsigned long rate;
114+
};
115+
108116
/*** runtime pm ***/
109117
static int clk_pm_runtime_get(struct clk_core *core)
110118
{
@@ -1434,10 +1442,14 @@ unsigned long clk_hw_round_rate(struct clk_hw *hw, unsigned long rate)
14341442
{
14351443
int ret;
14361444
struct clk_rate_request req;
1445+
struct clk_request *clk_req;
14371446

14381447
clk_core_get_boundaries(hw->core, &req.min_rate, &req.max_rate);
14391448
req.rate = rate;
14401449

1450+
list_for_each_entry(clk_req, &hw->core->pending_requests, list)
1451+
req.min_rate = max(clk_req->rate, req.min_rate);
1452+
14411453
ret = clk_core_round_rate_nolock(hw->core, &req);
14421454
if (ret)
14431455
return 0;
@@ -1458,6 +1470,7 @@ EXPORT_SYMBOL_GPL(clk_hw_round_rate);
14581470
long clk_round_rate(struct clk *clk, unsigned long rate)
14591471
{
14601472
struct clk_rate_request req;
1473+
struct clk_request *clk_req;
14611474
int ret;
14621475

14631476
if (!clk)
@@ -1471,6 +1484,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
14711484
clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate);
14721485
req.rate = rate;
14731486

1487+
list_for_each_entry(clk_req, &clk->core->pending_requests, list)
1488+
req.min_rate = max(clk_req->rate, req.min_rate);
1489+
14741490
ret = clk_core_round_rate_nolock(clk->core, &req);
14751491

14761492
if (clk->exclusive_count)
@@ -1938,6 +1954,7 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core,
19381954
unsigned long new_rate;
19391955
unsigned long min_rate;
19401956
unsigned long max_rate;
1957+
struct clk_request *req;
19411958
int p_index = 0;
19421959
long ret;
19431960

@@ -1952,6 +1969,9 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core,
19521969

19531970
clk_core_get_boundaries(core, &min_rate, &max_rate);
19541971

1972+
list_for_each_entry(req, &core->pending_requests, list)
1973+
min_rate = max(req->rate, min_rate);
1974+
19551975
/* find the closest rate and parent clk/rate */
19561976
if (clk_core_can_round(core)) {
19571977
struct clk_rate_request req;
@@ -2148,6 +2168,7 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
21482168
{
21492169
int ret, cnt;
21502170
struct clk_rate_request req;
2171+
struct clk_request *clk_req;
21512172

21522173
lockdep_assert_held(&prepare_lock);
21532174

@@ -2162,6 +2183,9 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
21622183
clk_core_get_boundaries(core, &req.min_rate, &req.max_rate);
21632184
req.rate = req_rate;
21642185

2186+
list_for_each_entry(clk_req, &core->pending_requests, list)
2187+
req.min_rate = max(clk_req->rate, req.min_rate);
2188+
21652189
ret = clk_core_round_rate_nolock(core, &req);
21662190

21672191
/* restore the protection */
@@ -2255,6 +2279,9 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
22552279

22562280
ret = clk_core_set_rate_nolock(clk->core, rate);
22572281

2282+
if (!list_empty(&clk->core->pending_requests))
2283+
clk->core->default_request_rate = rate;
2284+
22582285
if (clk->exclusive_count)
22592286
clk_core_rate_protect(clk->core);
22602287

@@ -2420,6 +2447,99 @@ int clk_set_max_rate(struct clk *clk, unsigned long rate)
24202447
}
24212448
EXPORT_SYMBOL_GPL(clk_set_max_rate);
24222449

2450+
/**
2451+
* clk_request_start - Request a rate to be enforced temporarily
2452+
* @clk: the clk to act on
2453+
* @rate: the new rate asked for
2454+
*
2455+
* This function will create a request to temporarily increase the rate
2456+
* of the clock to a given rate to a certain minimum.
2457+
*
2458+
* This is meant as a best effort mechanism and while the rate of the
2459+
* clock will be guaranteed to be equal or higher than the requested
2460+
* rate, there's none on what the actual rate will be due to other
2461+
* factors (other requests previously set, clock boundaries, etc.).
2462+
*
2463+
* Once the request is marked as done through clk_request_done(), the
2464+
* rate will be reverted back to what the rate was before the request.
2465+
*
2466+
* The reported boundaries of the clock will also be adjusted so that
2467+
* clk_round_rate() take those requests into account. A call to
2468+
* clk_set_rate() during a request will affect the rate the clock will
2469+
* return to after the requests on that clock are done.
2470+
*
2471+
* Returns 0 on success, an ERR_PTR otherwise.
2472+
*/
2473+
struct clk_request *clk_request_start(struct clk *clk, unsigned long rate)
2474+
{
2475+
struct clk_request *req;
2476+
int ret;
2477+
2478+
if (!clk)
2479+
return ERR_PTR(-EINVAL);
2480+
2481+
req = kzalloc(sizeof(*req), GFP_KERNEL);
2482+
if (!req)
2483+
return ERR_PTR(-ENOMEM);
2484+
2485+
clk_prepare_lock();
2486+
2487+
req->clk = clk;
2488+
req->rate = rate;
2489+
2490+
if (list_empty(&clk->core->pending_requests))
2491+
clk->core->default_request_rate = clk_core_get_rate_recalc(clk->core);
2492+
2493+
ret = clk_core_set_rate_nolock(clk->core, rate);
2494+
if (ret) {
2495+
clk_prepare_unlock();
2496+
kfree(req);
2497+
return ERR_PTR(ret);
2498+
}
2499+
2500+
list_add_tail(&req->list, &clk->core->pending_requests);
2501+
clk_prepare_unlock();
2502+
2503+
return req;
2504+
}
2505+
EXPORT_SYMBOL_GPL(clk_request_start);
2506+
2507+
/**
2508+
* clk_request_done - Mark a clk_request as done
2509+
* @req: the request to mark done
2510+
*
2511+
* This function will remove the rate request from the clock and adjust
2512+
* the clock rate back to either to what it was before the request
2513+
* started, or if there's any other request on that clock to a proper
2514+
* rate for them.
2515+
*/
2516+
void clk_request_done(struct clk_request *req)
2517+
{
2518+
struct clk_core *core = req->clk->core;
2519+
2520+
clk_prepare_lock();
2521+
2522+
list_del(&req->list);
2523+
2524+
if (list_empty(&core->pending_requests)) {
2525+
clk_core_set_rate_nolock(core, core->default_request_rate);
2526+
core->default_request_rate = 0;
2527+
} else {
2528+
struct clk_request *cur_req;
2529+
unsigned long new_rate = 0;
2530+
2531+
list_for_each_entry(cur_req, &core->pending_requests, list)
2532+
new_rate = max(new_rate, cur_req->rate);
2533+
2534+
clk_core_set_rate_nolock(core, new_rate);
2535+
}
2536+
2537+
clk_prepare_unlock();
2538+
2539+
kfree(req);
2540+
}
2541+
EXPORT_SYMBOL_GPL(clk_request_done);
2542+
24232543
/**
24242544
* clk_get_parent - return the parent of a clk
24252545
* @clk: the clk whose parent gets returned
@@ -3878,6 +3998,7 @@ __clk_register(struct device *dev, struct device_node *np, struct clk_hw *hw)
38783998
goto fail_parents;
38793999

38804000
INIT_HLIST_HEAD(&core->clks);
4001+
INIT_LIST_HEAD(&core->pending_requests);
38814002

38824003
/*
38834004
* Don't call clk_hw_create_clk() here because that would pin the

include/linux/clk.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
struct device;
1717
struct clk;
18+
struct clk_request;
1819
struct device_node;
1920
struct of_phandle_args;
2021

@@ -783,6 +784,9 @@ int clk_save_context(void);
783784
*/
784785
void clk_restore_context(void);
785786

787+
struct clk_request *clk_request_start(struct clk *clk, unsigned long rate);
788+
void clk_request_done(struct clk_request *req);
789+
786790
#else /* !CONFIG_HAVE_CLK */
787791

788792
static inline struct clk *clk_get(struct device *dev, const char *id)

0 commit comments

Comments
 (0)