aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/tegra/clk-bpmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/tegra/clk-bpmp.c')
-rw-r--r--drivers/clk/tegra/clk-bpmp.c89
1 files changed, 67 insertions, 22 deletions
diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c
index 6ecf18f71c32..d82a71f10c2c 100644
--- a/drivers/clk/tegra/clk-bpmp.c
+++ b/drivers/clk/tegra/clk-bpmp.c
@@ -164,15 +164,18 @@ static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw,
return response.rate;
}
-static long tegra_bpmp_clk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int tegra_bpmp_clk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *rate_req)
{
struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
struct cmd_clk_round_rate_response response;
struct cmd_clk_round_rate_request request;
struct tegra_bpmp_clk_message msg;
+ unsigned long rate;
int err;
+ rate = min(max(rate_req->rate, rate_req->min_rate), rate_req->max_rate);
+
memset(&request, 0, sizeof(request));
request.rate = min_t(u64, rate, S64_MAX);
@@ -188,7 +191,9 @@ static long tegra_bpmp_clk_round_rate(struct clk_hw *hw, unsigned long rate,
if (err < 0)
return err;
- return response.rate;
+ rate_req->rate = (unsigned long)response.rate;
+
+ return 0;
}
static int tegra_bpmp_clk_set_parent(struct clk_hw *hw, u8 index)
@@ -290,7 +295,7 @@ static const struct clk_ops tegra_bpmp_clk_rate_ops = {
.unprepare = tegra_bpmp_clk_unprepare,
.is_prepared = tegra_bpmp_clk_is_prepared,
.recalc_rate = tegra_bpmp_clk_recalc_rate,
- .round_rate = tegra_bpmp_clk_round_rate,
+ .determine_rate = tegra_bpmp_clk_determine_rate,
.set_rate = tegra_bpmp_clk_set_rate,
};
@@ -299,7 +304,7 @@ static const struct clk_ops tegra_bpmp_clk_mux_rate_ops = {
.unprepare = tegra_bpmp_clk_unprepare,
.is_prepared = tegra_bpmp_clk_is_prepared,
.recalc_rate = tegra_bpmp_clk_recalc_rate,
- .round_rate = tegra_bpmp_clk_round_rate,
+ .determine_rate = tegra_bpmp_clk_determine_rate,
.set_parent = tegra_bpmp_clk_set_parent,
.get_parent = tegra_bpmp_clk_get_parent,
.set_rate = tegra_bpmp_clk_set_rate,
@@ -344,7 +349,7 @@ static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id,
if (err < 0)
return err;
- strlcpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN);
+ strscpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN);
info->num_parents = response.num_parents;
for (i = 0; i < info->num_parents; i++)
@@ -448,15 +453,29 @@ static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp,
return count;
}
+static unsigned int
+tegra_bpmp_clk_id_to_index(const struct tegra_bpmp_clk_info *clocks,
+ unsigned int num_clocks, unsigned int id)
+{
+ unsigned int i;
+
+ for (i = 0; i < num_clocks; i++)
+ if (clocks[i].id == id)
+ return i;
+
+ return UINT_MAX;
+}
+
static const struct tegra_bpmp_clk_info *
tegra_bpmp_clk_find(const struct tegra_bpmp_clk_info *clocks,
unsigned int num_clocks, unsigned int id)
{
unsigned int i;
- for (i = 0; i < num_clocks; i++)
- if (clocks[i].id == id)
- return &clocks[i];
+ i = tegra_bpmp_clk_id_to_index(clocks, num_clocks, id);
+
+ if (i < num_clocks)
+ return &clocks[i];
return NULL;
}
@@ -539,31 +558,57 @@ tegra_bpmp_clk_register(struct tegra_bpmp *bpmp,
return clk;
}
+static void tegra_bpmp_register_clocks_one(struct tegra_bpmp *bpmp,
+ struct tegra_bpmp_clk_info *infos,
+ unsigned int i,
+ unsigned int count)
+{
+ unsigned int j;
+ struct tegra_bpmp_clk_info *info;
+ struct tegra_bpmp_clk *clk;
+
+ if (bpmp->clocks[i]) {
+ /* already registered */
+ return;
+ }
+
+ info = &infos[i];
+ for (j = 0; j < info->num_parents; ++j) {
+ unsigned int p_id = info->parents[j];
+ unsigned int p_i = tegra_bpmp_clk_id_to_index(infos, count,
+ p_id);
+ if (p_i < count)
+ tegra_bpmp_register_clocks_one(bpmp, infos, p_i, count);
+ }
+
+ clk = tegra_bpmp_clk_register(bpmp, info, infos, count);
+ if (IS_ERR(clk)) {
+ dev_err(bpmp->dev,
+ "failed to register clock %u (%s): %ld\n",
+ info->id, info->name, PTR_ERR(clk));
+ /* intentionally store the error pointer to
+ * bpmp->clocks[i] to avoid re-attempting the
+ * registration later
+ */
+ }
+
+ bpmp->clocks[i] = clk;
+}
+
static int tegra_bpmp_register_clocks(struct tegra_bpmp *bpmp,
struct tegra_bpmp_clk_info *infos,
unsigned int count)
{
- struct tegra_bpmp_clk *clk;
unsigned int i;
bpmp->num_clocks = count;
- bpmp->clocks = devm_kcalloc(bpmp->dev, count, sizeof(clk), GFP_KERNEL);
+ bpmp->clocks = devm_kcalloc(bpmp->dev, count, sizeof(struct tegra_bpmp_clk), GFP_KERNEL);
if (!bpmp->clocks)
return -ENOMEM;
for (i = 0; i < count; i++) {
- struct tegra_bpmp_clk_info *info = &infos[i];
-
- clk = tegra_bpmp_clk_register(bpmp, info, infos, count);
- if (IS_ERR(clk)) {
- dev_err(bpmp->dev,
- "failed to register clock %u (%s): %ld\n",
- info->id, info->name, PTR_ERR(clk));
- continue;
- }
-
- bpmp->clocks[i] = clk;
+ tegra_bpmp_register_clocks_one(bpmp, infos, i, count);
}
return 0;