Skip to content

Commit 262ca38

Browse files
mripardbebarino
authored andcommitted
clk: Stop forwarding clk_rate_requests to the parent
If the clock cannot modify its rate and has CLK_SET_RATE_PARENT, clk_mux_determine_rate_flags(), clk_core_round_rate_nolock() and a number of drivers will forward the clk_rate_request to the parent clock. clk_core_round_rate_nolock() will pass the pointer directly, which means that we pass a clk_rate_request to the parent that has the rate, min_rate and max_rate of the child, and the best_parent_rate and best_parent_hw fields will be relative to the child as well, so will point to our current clock and its rate. The most common case for CLK_SET_RATE_PARENT is that the child and parent clock rates will be equal, so the rate field isn't a worry, but the other fields are. Similarly, if the parent clock driver ever modifies the best_parent_rate or best_parent_hw, this will be applied to the child once the call to clk_core_round_rate_nolock() is done. best_parent_hw is probably not going to be a valid parent, and best_parent_rate might lead to a parent rate change different to the one that was initially computed. clk_mux_determine_rate_flags() and the affected drivers will copy the request before forwarding it to the parents, so they won't be affected by the latter issue, but the former is still going to be there and will lead to erroneous data and context being passed to the various clock drivers in the same sub-tree. Let's create two new functions, clk_core_forward_rate_req() and clk_hw_forward_rate_request() for the framework and the clock providers that will copy a request from a child clock and update the context to match the parent's. We also update the relevant call sites in the framework and drivers to use that new function. Let's also add a test to make sure we avoid regressions there. Tested-by: Alexander Stein <[email protected]> # imx8mp Tested-by: Marek Szyprowski <[email protected]> # exynos4210, meson g12b Signed-off-by: Maxime Ripard <[email protected]> Link: https://lore.kernel.org/r/[email protected] Tested-by: Linux Kernel Functional Testing <[email protected]> Tested-by: Naresh Kamboju <[email protected]> Signed-off-by: Stephen Boyd <[email protected]>
1 parent 22fb0e2 commit 262ca38

File tree

7 files changed

+279
-16
lines changed

7 files changed

+279
-16
lines changed

drivers/clk/at91/clk-generated.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,6 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
136136
{
137137
struct clk_generated *gck = to_clk_generated(hw);
138138
struct clk_hw *parent = NULL;
139-
struct clk_rate_request req_parent = *req;
140139
long best_rate = -EINVAL;
141140
unsigned long min_rate, parent_rate;
142141
int best_diff = -1;
@@ -192,7 +191,9 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
192191
goto end;
193192

194193
for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
195-
req_parent.rate = req->rate * div;
194+
struct clk_rate_request req_parent;
195+
196+
clk_hw_forward_rate_request(hw, req, parent, &req_parent, req->rate * div);
196197
if (__clk_determine_rate(parent, &req_parent))
197198
continue;
198199
clk_generated_best_diff(req, parent, req_parent.rate, div,

drivers/clk/at91/clk-master.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -581,7 +581,6 @@ static int clk_sama7g5_master_determine_rate(struct clk_hw *hw,
581581
struct clk_rate_request *req)
582582
{
583583
struct clk_master *master = to_clk_master(hw);
584-
struct clk_rate_request req_parent = *req;
585584
struct clk_hw *parent;
586585
long best_rate = LONG_MIN, best_diff = LONG_MIN;
587586
unsigned long parent_rate;
@@ -618,11 +617,15 @@ static int clk_sama7g5_master_determine_rate(struct clk_hw *hw,
618617
goto end;
619618

620619
for (div = 0; div < MASTER_PRES_MAX + 1; div++) {
620+
struct clk_rate_request req_parent;
621+
unsigned long req_rate;
622+
621623
if (div == MASTER_PRES_MAX)
622-
req_parent.rate = req->rate * 3;
624+
req_rate = req->rate * 3;
623625
else
624-
req_parent.rate = req->rate << div;
626+
req_rate = req->rate << div;
625627

628+
clk_hw_forward_rate_request(hw, req, parent, &req_parent, req_rate);
626629
if (__clk_determine_rate(parent, &req_parent))
627630
continue;
628631

drivers/clk/at91/clk-peripheral.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,6 @@ static int clk_sam9x5_peripheral_determine_rate(struct clk_hw *hw,
269269
{
270270
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
271271
struct clk_hw *parent = clk_hw_get_parent(hw);
272-
struct clk_rate_request req_parent = *req;
273272
unsigned long parent_rate = clk_hw_get_rate(parent);
274273
unsigned long tmp_rate;
275274
long best_rate = LONG_MIN;
@@ -302,8 +301,9 @@ static int clk_sam9x5_peripheral_determine_rate(struct clk_hw *hw,
302301
goto end;
303302

304303
for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
305-
req_parent.rate = req->rate << shift;
304+
struct clk_rate_request req_parent;
306305

306+
clk_hw_forward_rate_request(hw, req, parent, &req_parent, req->rate << shift);
307307
if (__clk_determine_rate(parent, &req_parent))
308308
continue;
309309

drivers/clk/clk-composite.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,11 @@ static int clk_composite_determine_rate(struct clk_hw *hw,
8585
req->best_parent_hw = NULL;
8686

8787
if (clk_hw_get_flags(hw) & CLK_SET_RATE_NO_REPARENT) {
88-
struct clk_rate_request tmp_req = *req;
88+
struct clk_rate_request tmp_req;
8989

9090
parent = clk_hw_get_parent(mux_hw);
9191

92+
clk_hw_forward_rate_request(hw, req, parent, &tmp_req, req->rate);
9293
ret = clk_composite_determine_rate_for_parent(rate_hw,
9394
&tmp_req,
9495
parent,
@@ -104,12 +105,13 @@ static int clk_composite_determine_rate(struct clk_hw *hw,
104105
}
105106

106107
for (i = 0; i < clk_hw_get_num_parents(mux_hw); i++) {
107-
struct clk_rate_request tmp_req = *req;
108+
struct clk_rate_request tmp_req;
108109

109110
parent = clk_hw_get_parent_by_index(mux_hw, i);
110111
if (!parent)
111112
continue;
112113

114+
clk_hw_forward_rate_request(hw, req, parent, &tmp_req, req->rate);
113115
ret = clk_composite_determine_rate_for_parent(rate_hw,
114116
&tmp_req,
115117
parent,

drivers/clk/clk.c

Lines changed: 77 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,10 @@ static bool mux_is_better_rate(unsigned long rate, unsigned long now,
536536
return now <= rate && now > best;
537537
}
538538

539+
static void clk_core_init_rate_req(struct clk_core * const core,
540+
struct clk_rate_request *req,
541+
unsigned long rate);
542+
539543
static int clk_core_round_rate_nolock(struct clk_core *core,
540544
struct clk_rate_request *req);
541545

@@ -560,24 +564,45 @@ static bool clk_core_has_parent(struct clk_core *core, const struct clk_core *pa
560564
return false;
561565
}
562566

567+
static void
568+
clk_core_forward_rate_req(struct clk_core *core,
569+
const struct clk_rate_request *old_req,
570+
struct clk_core *parent,
571+
struct clk_rate_request *req,
572+
unsigned long parent_rate)
573+
{
574+
if (WARN_ON(!clk_core_has_parent(core, parent)))
575+
return;
576+
577+
clk_core_init_rate_req(parent, req, parent_rate);
578+
579+
if (req->min_rate < old_req->min_rate)
580+
req->min_rate = old_req->min_rate;
581+
582+
if (req->max_rate > old_req->max_rate)
583+
req->max_rate = old_req->max_rate;
584+
}
585+
563586
int clk_mux_determine_rate_flags(struct clk_hw *hw,
564587
struct clk_rate_request *req,
565588
unsigned long flags)
566589
{
567590
struct clk_core *core = hw->core, *parent, *best_parent = NULL;
568591
int i, num_parents, ret;
569592
unsigned long best = 0;
570-
struct clk_rate_request parent_req = *req;
571593

572594
/* if NO_REPARENT flag set, pass through to current parent */
573595
if (core->flags & CLK_SET_RATE_NO_REPARENT) {
574596
parent = core->parent;
575597
if (core->flags & CLK_SET_RATE_PARENT) {
598+
struct clk_rate_request parent_req;
599+
576600
if (!parent) {
577601
req->rate = 0;
578602
return 0;
579603
}
580604

605+
clk_core_forward_rate_req(core, req, parent, &parent_req, req->rate);
581606
ret = clk_core_round_rate_nolock(parent, &parent_req);
582607
if (ret)
583608
return ret;
@@ -595,23 +620,29 @@ int clk_mux_determine_rate_flags(struct clk_hw *hw,
595620
/* find the parent that can provide the fastest rate <= rate */
596621
num_parents = core->num_parents;
597622
for (i = 0; i < num_parents; i++) {
623+
unsigned long parent_rate;
624+
598625
parent = clk_core_get_parent_by_index(core, i);
599626
if (!parent)
600627
continue;
601628

602629
if (core->flags & CLK_SET_RATE_PARENT) {
603-
parent_req = *req;
630+
struct clk_rate_request parent_req;
631+
632+
clk_core_forward_rate_req(core, req, parent, &parent_req, req->rate);
604633
ret = clk_core_round_rate_nolock(parent, &parent_req);
605634
if (ret)
606635
continue;
636+
637+
parent_rate = parent_req.rate;
607638
} else {
608-
parent_req.rate = clk_core_get_rate_nolock(parent);
639+
parent_rate = clk_core_get_rate_nolock(parent);
609640
}
610641

611-
if (mux_is_better_rate(req->rate, parent_req.rate,
642+
if (mux_is_better_rate(req->rate, parent_rate,
612643
best, flags)) {
613644
best_parent = parent;
614-
best = parent_req.rate;
645+
best = parent_rate;
615646
}
616647
}
617648

@@ -1449,6 +1480,31 @@ void clk_hw_init_rate_request(const struct clk_hw *hw,
14491480
}
14501481
EXPORT_SYMBOL_GPL(clk_hw_init_rate_request);
14511482

1483+
/**
1484+
* clk_hw_forward_rate_request - Forwards a clk_rate_request to a clock's parent
1485+
* @hw: the original clock that got the rate request
1486+
* @old_req: the original clk_rate_request structure we want to forward
1487+
* @parent: the clk we want to forward @old_req to
1488+
* @req: the clk_rate_request structure we want to initialise
1489+
* @parent_rate: The rate which is to be requested to @parent
1490+
*
1491+
* Initializes a clk_rate_request structure to submit to a clock parent
1492+
* in __clk_determine_rate() or similar functions.
1493+
*/
1494+
void clk_hw_forward_rate_request(const struct clk_hw *hw,
1495+
const struct clk_rate_request *old_req,
1496+
const struct clk_hw *parent,
1497+
struct clk_rate_request *req,
1498+
unsigned long parent_rate)
1499+
{
1500+
if (WARN_ON(!hw || !old_req || !parent || !req))
1501+
return;
1502+
1503+
clk_core_forward_rate_req(hw->core, old_req,
1504+
parent->core, req,
1505+
parent_rate);
1506+
}
1507+
14521508
static bool clk_core_can_round(struct clk_core * const core)
14531509
{
14541510
return core->ops->determine_rate || core->ops->round_rate;
@@ -1457,6 +1513,8 @@ static bool clk_core_can_round(struct clk_core * const core)
14571513
static int clk_core_round_rate_nolock(struct clk_core *core,
14581514
struct clk_rate_request *req)
14591515
{
1516+
int ret;
1517+
14601518
lockdep_assert_held(&prepare_lock);
14611519

14621520
if (!core) {
@@ -1466,8 +1524,20 @@ static int clk_core_round_rate_nolock(struct clk_core *core,
14661524

14671525
if (clk_core_can_round(core))
14681526
return clk_core_determine_round_nolock(core, req);
1469-
else if (core->flags & CLK_SET_RATE_PARENT)
1470-
return clk_core_round_rate_nolock(core->parent, req);
1527+
1528+
if (core->flags & CLK_SET_RATE_PARENT) {
1529+
struct clk_rate_request parent_req;
1530+
1531+
clk_core_forward_rate_req(core, req, core->parent, &parent_req, req->rate);
1532+
ret = clk_core_round_rate_nolock(core->parent, &parent_req);
1533+
if (ret)
1534+
return ret;
1535+
1536+
req->best_parent_rate = parent_req.rate;
1537+
req->rate = parent_req.rate;
1538+
1539+
return 0;
1540+
}
14711541

14721542
req->rate = core->rate;
14731543
return 0;

0 commit comments

Comments
 (0)