Skip to content

Commit b4c115c

Browse files
claudiubezneabebarino
authored andcommitted
clk: at91: clk-peripheral: add support for changeable parent rate
Some peripheral clocks on SAMA7G5 supports requesting parent to change its rate (image related clocks: csi, csi2dc, isc). Add support so that if registered with this option the clock rate to be requested from parent. Signed-off-by: Claudiu Beznea <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Stephen Boyd <[email protected]>
1 parent 75c8814 commit b4c115c

File tree

9 files changed

+119
-16
lines changed

9 files changed

+119
-16
lines changed

drivers/clk/at91/at91sam9n12.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ static void __init at91sam9n12_pmc_setup(struct device_node *np)
222222
at91sam9n12_periphck[i].n,
223223
"masterck",
224224
at91sam9n12_periphck[i].id,
225-
&range);
225+
&range, INT_MIN);
226226
if (IS_ERR(hw))
227227
goto err_free;
228228

drivers/clk/at91/at91sam9x5.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
257257
at91sam9x5_periphck[i].n,
258258
"masterck",
259259
at91sam9x5_periphck[i].id,
260-
&range);
260+
&range, INT_MIN);
261261
if (IS_ERR(hw))
262262
goto err_free;
263263

@@ -270,7 +270,7 @@ static void __init at91sam9x5_pmc_setup(struct device_node *np,
270270
extra_pcks[i].n,
271271
"masterck",
272272
extra_pcks[i].id,
273-
&range);
273+
&range, INT_MIN);
274274
if (IS_ERR(hw))
275275
goto err_free;
276276

drivers/clk/at91/clk-peripheral.c

Lines changed: 104 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ struct clk_sam9x5_peripheral {
3838
u32 div;
3939
const struct clk_pcr_layout *layout;
4040
bool auto_div;
41+
int chg_pid;
4142
};
4243

4344
#define to_clk_sam9x5_peripheral(hw) \
@@ -238,6 +239,87 @@ clk_sam9x5_peripheral_recalc_rate(struct clk_hw *hw,
238239
return parent_rate >> periph->div;
239240
}
240241

242+
static void clk_sam9x5_peripheral_best_diff(struct clk_rate_request *req,
243+
struct clk_hw *parent,
244+
unsigned long parent_rate,
245+
u32 shift, long *best_diff,
246+
long *best_rate)
247+
{
248+
unsigned long tmp_rate = parent_rate >> shift;
249+
unsigned long tmp_diff = abs(req->rate - tmp_rate);
250+
251+
if (*best_diff < 0 || *best_diff >= tmp_diff) {
252+
*best_rate = tmp_rate;
253+
*best_diff = tmp_diff;
254+
req->best_parent_rate = parent_rate;
255+
req->best_parent_hw = parent;
256+
}
257+
}
258+
259+
static int clk_sam9x5_peripheral_determine_rate(struct clk_hw *hw,
260+
struct clk_rate_request *req)
261+
{
262+
struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(hw);
263+
struct clk_hw *parent = clk_hw_get_parent(hw);
264+
struct clk_rate_request req_parent = *req;
265+
unsigned long parent_rate = clk_hw_get_rate(parent);
266+
unsigned long tmp_rate;
267+
long best_rate = LONG_MIN;
268+
long best_diff = LONG_MIN;
269+
u32 shift;
270+
271+
if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
272+
return parent_rate;
273+
274+
/* Fist step: check the available dividers. */
275+
for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
276+
tmp_rate = parent_rate >> shift;
277+
278+
if (periph->range.max && tmp_rate > periph->range.max)
279+
continue;
280+
281+
clk_sam9x5_peripheral_best_diff(req, parent, parent_rate,
282+
shift, &best_diff, &best_rate);
283+
284+
if (!best_diff || best_rate <= req->rate)
285+
break;
286+
}
287+
288+
if (periph->chg_pid < 0)
289+
goto end;
290+
291+
/* Step two: try to request rate from parent. */
292+
parent = clk_hw_get_parent_by_index(hw, periph->chg_pid);
293+
if (!parent)
294+
goto end;
295+
296+
for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
297+
req_parent.rate = req->rate << shift;
298+
299+
if (__clk_determine_rate(parent, &req_parent))
300+
continue;
301+
302+
clk_sam9x5_peripheral_best_diff(req, parent, req_parent.rate,
303+
shift, &best_diff, &best_rate);
304+
305+
if (!best_diff)
306+
break;
307+
}
308+
end:
309+
if (best_rate < 0 ||
310+
(periph->range.max && best_rate > periph->range.max))
311+
return -EINVAL;
312+
313+
pr_debug("PCK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
314+
__func__, best_rate,
315+
__clk_get_name((req->best_parent_hw)->clk),
316+
req->best_parent_rate);
317+
318+
req->rate = best_rate;
319+
320+
return 0;
321+
}
322+
241323
static long clk_sam9x5_peripheral_round_rate(struct clk_hw *hw,
242324
unsigned long rate,
243325
unsigned long *parent_rate)
@@ -320,11 +402,21 @@ static const struct clk_ops sam9x5_peripheral_ops = {
320402
.set_rate = clk_sam9x5_peripheral_set_rate,
321403
};
322404

405+
static const struct clk_ops sam9x5_peripheral_chg_ops = {
406+
.enable = clk_sam9x5_peripheral_enable,
407+
.disable = clk_sam9x5_peripheral_disable,
408+
.is_enabled = clk_sam9x5_peripheral_is_enabled,
409+
.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
410+
.determine_rate = clk_sam9x5_peripheral_determine_rate,
411+
.set_rate = clk_sam9x5_peripheral_set_rate,
412+
};
413+
323414
struct clk_hw * __init
324415
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
325416
const struct clk_pcr_layout *layout,
326417
const char *name, const char *parent_name,
327-
u32 id, const struct clk_range *range)
418+
u32 id, const struct clk_range *range,
419+
int chg_pid)
328420
{
329421
struct clk_sam9x5_peripheral *periph;
330422
struct clk_init_data init;
@@ -339,10 +431,16 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
339431
return ERR_PTR(-ENOMEM);
340432

341433
init.name = name;
342-
init.ops = &sam9x5_peripheral_ops;
343-
init.parent_names = (parent_name ? &parent_name : NULL);
344-
init.num_parents = (parent_name ? 1 : 0);
345-
init.flags = 0;
434+
init.parent_names = &parent_name;
435+
init.num_parents = 1;
436+
if (chg_pid < 0) {
437+
init.flags = 0;
438+
init.ops = &sam9x5_peripheral_ops;
439+
} else {
440+
init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
441+
CLK_SET_RATE_PARENT;
442+
init.ops = &sam9x5_peripheral_chg_ops;
443+
}
346444

347445
periph->id = id;
348446
periph->hw.init = &init;
@@ -353,6 +451,7 @@ at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
353451
periph->auto_div = true;
354452
periph->layout = layout;
355453
periph->range = *range;
454+
periph->chg_pid = chg_pid;
356455

357456
hw = &periph->hw;
358457
ret = clk_hw_register(NULL, &periph->hw);

drivers/clk/at91/dt-compat.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,8 @@ of_at91_clk_periph_setup(struct device_node *np, u8 type)
463463
&dt_pcr_layout,
464464
name,
465465
parent_name,
466-
id, &range);
466+
id, &range,
467+
INT_MIN);
467468
}
468469

469470
if (IS_ERR(hw))

drivers/clk/at91/pmc.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@ struct clk_hw * __init
168168
at91_clk_register_sam9x5_peripheral(struct regmap *regmap, spinlock_t *lock,
169169
const struct clk_pcr_layout *layout,
170170
const char *name, const char *parent_name,
171-
u32 id, const struct clk_range *range);
171+
u32 id, const struct clk_range *range,
172+
int chg_pid);
172173

173174
struct clk_hw * __init
174175
at91_clk_register_pll(struct regmap *regmap, const char *name,

drivers/clk/at91/sam9x60.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ static void __init sam9x60_pmc_setup(struct device_node *np)
277277
sam9x60_periphck[i].n,
278278
"masterck",
279279
sam9x60_periphck[i].id,
280-
&range);
280+
&range, INT_MIN);
281281
if (IS_ERR(hw))
282282
goto err_free;
283283

drivers/clk/at91/sama5d2.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
291291
sama5d2_periphck[i].n,
292292
"masterck",
293293
sama5d2_periphck[i].id,
294-
&range);
294+
&range, INT_MIN);
295295
if (IS_ERR(hw))
296296
goto err_free;
297297

@@ -304,7 +304,8 @@ static void __init sama5d2_pmc_setup(struct device_node *np)
304304
sama5d2_periph32ck[i].n,
305305
"h32mxck",
306306
sama5d2_periph32ck[i].id,
307-
&sama5d2_periph32ck[i].r);
307+
&sama5d2_periph32ck[i].r,
308+
INT_MIN);
308309
if (IS_ERR(hw))
309310
goto err_free;
310311

drivers/clk/at91/sama5d3.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,8 @@ static void __init sama5d3_pmc_setup(struct device_node *np)
223223
sama5d3_periphck[i].n,
224224
"masterck",
225225
sama5d3_periphck[i].id,
226-
&sama5d3_periphck[i].r);
226+
&sama5d3_periphck[i].r,
227+
INT_MIN);
227228
if (IS_ERR(hw))
228229
goto err_free;
229230

drivers/clk/at91/sama5d4.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
246246
sama5d4_periphck[i].n,
247247
"masterck",
248248
sama5d4_periphck[i].id,
249-
&range);
249+
&range, INT_MIN);
250250
if (IS_ERR(hw))
251251
goto err_free;
252252

@@ -259,7 +259,7 @@ static void __init sama5d4_pmc_setup(struct device_node *np)
259259
sama5d4_periph32ck[i].n,
260260
"h32mxck",
261261
sama5d4_periph32ck[i].id,
262-
&range);
262+
&range, INT_MIN);
263263
if (IS_ERR(hw))
264264
goto err_free;
265265

0 commit comments

Comments
 (0)