diff options
Diffstat (limited to 'target/linux/bcm27xx/patches-5.10/950-0537-clk-Introduce-a-clock-request-API.patch')
-rw-r--r-- | target/linux/bcm27xx/patches-5.10/950-0537-clk-Introduce-a-clock-request-API.patch | 284 |
1 files changed, 0 insertions, 284 deletions
diff --git a/target/linux/bcm27xx/patches-5.10/950-0537-clk-Introduce-a-clock-request-API.patch b/target/linux/bcm27xx/patches-5.10/950-0537-clk-Introduce-a-clock-request-API.patch deleted file mode 100644 index e90134d682..0000000000 --- a/target/linux/bcm27xx/patches-5.10/950-0537-clk-Introduce-a-clock-request-API.patch +++ /dev/null @@ -1,284 +0,0 @@ -From d937a5c25139dd919d857a8e4a6491917b568176 Mon Sep 17 00:00:00 2001 -From: Maxime Ripard <maxime@cerno.tech> -Date: Tue, 13 Apr 2021 11:00:01 +0200 -Subject: [PATCH] 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 <maxime@cerno.tech> ---- - drivers/clk/clk.c | 121 ++++++++++++++++++++++++++++++++++++++++++++ - include/linux/clk.h | 4 ++ - 2 files changed, 125 insertions(+) - ---- a/drivers/clk/clk.c -+++ b/drivers/clk/clk.c -@@ -77,12 +77,14 @@ struct clk_core { - unsigned int protect_count; - unsigned long min_rate; - unsigned long max_rate; -+ unsigned long default_request_rate; - unsigned long accuracy; - int phase; - struct clk_duty duty; - struct hlist_head children; - struct hlist_node child_node; - struct hlist_head clks; -+ struct list_head pending_requests; - unsigned int notifier_count; - #ifdef CONFIG_DEBUG_FS - struct dentry *dentry; -@@ -105,6 +107,12 @@ struct clk { - struct hlist_node clks_node; - }; - -+struct clk_request { -+ struct list_head list; -+ struct clk *clk; -+ unsigned long rate; -+}; -+ - /*** runtime pm ***/ - static int clk_pm_runtime_get(struct clk_core *core) - { -@@ -1431,10 +1439,14 @@ unsigned long clk_hw_round_rate(struct c - { - int ret; - struct clk_rate_request req; -+ struct clk_request *clk_req; - - clk_core_get_boundaries(hw->core, &req.min_rate, &req.max_rate); - req.rate = rate; - -+ list_for_each_entry(clk_req, &hw->core->pending_requests, list) -+ req.min_rate = max(clk_req->rate, req.min_rate); -+ - ret = clk_core_round_rate_nolock(hw->core, &req); - if (ret) - return 0; -@@ -1455,6 +1467,7 @@ EXPORT_SYMBOL_GPL(clk_hw_round_rate); - long clk_round_rate(struct clk *clk, unsigned long rate) - { - struct clk_rate_request req; -+ struct clk_request *clk_req; - int ret; - - if (!clk) -@@ -1468,6 +1481,9 @@ long clk_round_rate(struct clk *clk, uns - clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate); - req.rate = rate; - -+ list_for_each_entry(clk_req, &clk->core->pending_requests, list) -+ req.min_rate = max(clk_req->rate, req.min_rate); -+ - ret = clk_core_round_rate_nolock(clk->core, &req); - - if (clk->exclusive_count) -@@ -1935,6 +1951,7 @@ static struct clk_core *clk_calc_new_rat - unsigned long new_rate; - unsigned long min_rate; - unsigned long max_rate; -+ struct clk_request *req; - int p_index = 0; - long ret; - -@@ -1949,6 +1966,9 @@ static struct clk_core *clk_calc_new_rat - - clk_core_get_boundaries(core, &min_rate, &max_rate); - -+ list_for_each_entry(req, &core->pending_requests, list) -+ min_rate = max(req->rate, min_rate); -+ - /* find the closest rate and parent clk/rate */ - if (clk_core_can_round(core)) { - struct clk_rate_request req; -@@ -2153,6 +2173,7 @@ static unsigned long clk_core_req_round_ - { - int ret, cnt; - struct clk_rate_request req; -+ struct clk_request *clk_req; - - lockdep_assert_held(&prepare_lock); - -@@ -2167,6 +2188,9 @@ static unsigned long clk_core_req_round_ - clk_core_get_boundaries(core, &req.min_rate, &req.max_rate); - req.rate = req_rate; - -+ list_for_each_entry(clk_req, &core->pending_requests, list) -+ req.min_rate = max(clk_req->rate, req.min_rate); -+ - ret = clk_core_round_rate_nolock(core, &req); - - /* restore the protection */ -@@ -2260,6 +2284,9 @@ int clk_set_rate(struct clk *clk, unsign - - ret = clk_core_set_rate_nolock(clk->core, rate); - -+ if (!list_empty(&clk->core->pending_requests)) -+ clk->core->default_request_rate = rate; -+ - if (clk->exclusive_count) - clk_core_rate_protect(clk->core); - -@@ -2426,6 +2453,99 @@ int clk_set_max_rate(struct clk *clk, un - EXPORT_SYMBOL_GPL(clk_set_max_rate); - - /** -+ * clk_request_start - Request a rate to be enforced temporarily -+ * @clk: the clk to act on -+ * @rate: the new rate asked for -+ * -+ * This function will create a request to temporarily increase the rate -+ * of the clock to a given rate to a certain minimum. -+ * -+ * This is meant as a best effort mechanism and while the rate of the -+ * clock will be guaranteed to be equal or higher than the requested -+ * rate, there's none on what the actual rate will be due to other -+ * factors (other requests previously set, clock boundaries, etc.). -+ * -+ * Once the request is marked as done through clk_request_done(), the -+ * rate will be reverted back to what the rate was before the request. -+ * -+ * The reported boundaries of the clock will also be adjusted so that -+ * clk_round_rate() take those requests into account. A call to -+ * clk_set_rate() during a request will affect the rate the clock will -+ * return to after the requests on that clock are done. -+ * -+ * Returns 0 on success, an ERR_PTR otherwise. -+ */ -+struct clk_request *clk_request_start(struct clk *clk, unsigned long rate) -+{ -+ struct clk_request *req; -+ int ret; -+ -+ if (!clk) -+ return ERR_PTR(-EINVAL); -+ -+ req = kzalloc(sizeof(*req), GFP_KERNEL); -+ if (!req) -+ return ERR_PTR(-ENOMEM); -+ -+ clk_prepare_lock(); -+ -+ req->clk = clk; -+ req->rate = rate; -+ -+ if (list_empty(&clk->core->pending_requests)) -+ clk->core->default_request_rate = clk_core_get_rate_recalc(clk->core); -+ -+ ret = clk_core_set_rate_nolock(clk->core, rate); -+ if (ret) { -+ clk_prepare_unlock(); -+ kfree(req); -+ return ERR_PTR(ret); -+ } -+ -+ list_add_tail(&req->list, &clk->core->pending_requests); -+ clk_prepare_unlock(); -+ -+ return req; -+} -+EXPORT_SYMBOL_GPL(clk_request_start); -+ -+/** -+ * clk_request_done - Mark a clk_request as done -+ * @req: the request to mark done -+ * -+ * This function will remove the rate request from the clock and adjust -+ * the clock rate back to either to what it was before the request -+ * started, or if there's any other request on that clock to a proper -+ * rate for them. -+ */ -+void clk_request_done(struct clk_request *req) -+{ -+ struct clk_core *core = req->clk->core; -+ -+ clk_prepare_lock(); -+ -+ list_del(&req->list); -+ -+ if (list_empty(&core->pending_requests)) { -+ clk_core_set_rate_nolock(core, core->default_request_rate); -+ core->default_request_rate = 0; -+ } else { -+ struct clk_request *cur_req; -+ unsigned long new_rate = 0; -+ -+ list_for_each_entry(cur_req, &core->pending_requests, list) -+ new_rate = max(new_rate, cur_req->rate); -+ -+ clk_core_set_rate_nolock(core, new_rate); -+ } -+ -+ clk_prepare_unlock(); -+ -+ kfree(req); -+} -+EXPORT_SYMBOL_GPL(clk_request_done); -+ -+/** - * clk_get_parent - return the parent of a clk - * @clk: the clk whose parent gets returned - * -@@ -3875,6 +3995,7 @@ __clk_register(struct device *dev, struc - goto fail_parents; - - INIT_HLIST_HEAD(&core->clks); -+ INIT_LIST_HEAD(&core->pending_requests); - - /* - * Don't call clk_hw_create_clk() here because that would pin the ---- a/include/linux/clk.h -+++ b/include/linux/clk.h -@@ -15,6 +15,7 @@ - - struct device; - struct clk; -+struct clk_request; - struct device_node; - struct of_phandle_args; - -@@ -743,6 +744,9 @@ int clk_save_context(void); - */ - void clk_restore_context(void); - -+struct clk_request *clk_request_start(struct clk *clk, unsigned long rate); -+void clk_request_done(struct clk_request *req); -+ - #else /* !CONFIG_HAVE_CLK */ - - static inline struct clk *clk_get(struct device *dev, const char *id) |