Skip to content

Commit 8ffea6e

Browse files
author
Tero Kristo
committed
clk: ti: divider: convert to use min,max,mask instead of width
The existing width field used to check divider validity does not provide enough protection against bad values. For example, if max divider value is 4, the smallest all-1 bitmask that can hold this value is 7, which allows values higher than 4 to be used. This typically causes unpredictable results with hardware. So far this issue hasn't been noticed as most of the dividers actually have maximum values which fit the whole bitfield, but there are certain clocks for which this is a problem, like dpll4_m4 divider on omap3 devices. Thus, convert the whole validity logic to use min,max and mask values for determining if a specific divider is valid or not. This prevents the odd cases where bad value would otherwise be written to a divider config register. Signed-off-by: Tero Kristo <[email protected]> Tested-by: Adam Ford <[email protected]>
1 parent a229965 commit 8ffea6e

File tree

2 files changed

+74
-91
lines changed

2 files changed

+74
-91
lines changed

drivers/clk/ti/clock.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ struct clk_omap_divider {
2020
struct clk_hw hw;
2121
struct clk_omap_reg reg;
2222
u8 shift;
23-
u8 width;
2423
u8 flags;
2524
s8 latch;
25+
u16 min;
26+
u16 max;
27+
u16 mask;
2628
const struct clk_div_table *table;
2729
u32 context;
2830
};

drivers/clk/ti/divider.c

Lines changed: 71 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -26,30 +26,6 @@
2626
#undef pr_fmt
2727
#define pr_fmt(fmt) "%s: " fmt, __func__
2828

29-
#define div_mask(d) ((1 << ((d)->width)) - 1)
30-
31-
static unsigned int _get_table_maxdiv(const struct clk_div_table *table)
32-
{
33-
unsigned int maxdiv = 0;
34-
const struct clk_div_table *clkt;
35-
36-
for (clkt = table; clkt->div; clkt++)
37-
if (clkt->div > maxdiv)
38-
maxdiv = clkt->div;
39-
return maxdiv;
40-
}
41-
42-
static unsigned int _get_maxdiv(struct clk_omap_divider *divider)
43-
{
44-
if (divider->flags & CLK_DIVIDER_ONE_BASED)
45-
return div_mask(divider);
46-
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
47-
return 1 << div_mask(divider);
48-
if (divider->table)
49-
return _get_table_maxdiv(divider->table);
50-
return div_mask(divider) + 1;
51-
}
52-
5329
static unsigned int _get_table_div(const struct clk_div_table *table,
5430
unsigned int val)
5531
{
@@ -61,6 +37,34 @@ static unsigned int _get_table_div(const struct clk_div_table *table,
6137
return 0;
6238
}
6339

40+
static void _setup_mask(struct clk_omap_divider *divider)
41+
{
42+
u16 mask;
43+
u32 max_val;
44+
const struct clk_div_table *clkt;
45+
46+
if (divider->table) {
47+
max_val = 0;
48+
49+
for (clkt = divider->table; clkt->div; clkt++)
50+
if (clkt->val > max_val)
51+
max_val = clkt->val;
52+
} else {
53+
max_val = divider->max;
54+
55+
if (!(divider->flags & CLK_DIVIDER_ONE_BASED) &&
56+
!(divider->flags & CLK_DIVIDER_POWER_OF_TWO))
57+
max_val--;
58+
}
59+
60+
if (divider->flags & CLK_DIVIDER_POWER_OF_TWO)
61+
mask = fls(max_val) - 1;
62+
else
63+
mask = max_val;
64+
65+
divider->mask = (1 << fls(mask)) - 1;
66+
}
67+
6468
static unsigned int _get_div(struct clk_omap_divider *divider, unsigned int val)
6569
{
6670
if (divider->flags & CLK_DIVIDER_ONE_BASED)
@@ -101,7 +105,7 @@ static unsigned long ti_clk_divider_recalc_rate(struct clk_hw *hw,
101105
unsigned int div, val;
102106

103107
val = ti_clk_ll_ops->clk_readl(&divider->reg) >> divider->shift;
104-
val &= div_mask(divider);
108+
val &= divider->mask;
105109

106110
div = _get_div(divider, val);
107111
if (!div) {
@@ -180,7 +184,7 @@ static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
180184
if (!rate)
181185
rate = 1;
182186

183-
maxdiv = _get_maxdiv(divider);
187+
maxdiv = divider->max;
184188

185189
if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) {
186190
parent_rate = *best_parent_rate;
@@ -219,7 +223,7 @@ static int ti_clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,
219223
}
220224

221225
if (!bestdiv) {
222-
bestdiv = _get_maxdiv(divider);
226+
bestdiv = divider->max;
223227
*best_parent_rate =
224228
clk_hw_round_rate(clk_hw_get_parent(hw), 1);
225229
}
@@ -249,17 +253,16 @@ static int ti_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
249253
divider = to_clk_omap_divider(hw);
250254

251255
div = DIV_ROUND_UP(parent_rate, rate);
252-
value = _get_val(divider, div);
253256

254-
if (value > div_mask(divider))
255-
value = div_mask(divider);
257+
if (div > divider->max)
258+
div = divider->max;
259+
if (div < divider->min)
260+
div = divider->min;
256261

257-
if (divider->flags & CLK_DIVIDER_HIWORD_MASK) {
258-
val = div_mask(divider) << (divider->shift + 16);
259-
} else {
260-
val = ti_clk_ll_ops->clk_readl(&divider->reg);
261-
val &= ~(div_mask(divider) << divider->shift);
262-
}
262+
value = _get_val(divider, div);
263+
264+
val = ti_clk_ll_ops->clk_readl(&divider->reg);
265+
val &= ~(divider->mask << divider->shift);
263266
val |= value << divider->shift;
264267
ti_clk_ll_ops->clk_writel(val, &divider->reg);
265268

@@ -280,7 +283,7 @@ static int clk_divider_save_context(struct clk_hw *hw)
280283
u32 val;
281284

282285
val = ti_clk_ll_ops->clk_readl(&divider->reg) >> divider->shift;
283-
divider->context = val & div_mask(divider);
286+
divider->context = val & divider->mask;
284287

285288
return 0;
286289
}
@@ -297,7 +300,7 @@ static void clk_divider_restore_context(struct clk_hw *hw)
297300
u32 val;
298301

299302
val = ti_clk_ll_ops->clk_readl(&divider->reg);
300-
val &= ~(div_mask(divider) << divider->shift);
303+
val &= ~(divider->mask << divider->shift);
301304
val |= divider->context << divider->shift;
302305
ti_clk_ll_ops->clk_writel(val, &divider->reg);
303306
}
@@ -341,29 +344,14 @@ int ti_clk_parse_divider_data(int *div_table, int num_dividers, int max_div,
341344
u8 flags, struct clk_omap_divider *divider)
342345
{
343346
int valid_div = 0;
344-
u32 val;
345-
int div;
346347
int i;
347348
struct clk_div_table *tmp;
349+
u16 min_div = 0;
348350

349351
if (!div_table) {
350-
if (flags & CLKF_INDEX_STARTS_AT_ONE)
351-
val = 1;
352-
else
353-
val = 0;
354-
355-
div = 1;
356-
357-
while (div < max_div) {
358-
if (flags & CLKF_INDEX_POWER_OF_TWO)
359-
div <<= 1;
360-
else
361-
div++;
362-
val++;
363-
}
364-
365-
divider->width = fls(val);
366-
352+
divider->min = 1;
353+
divider->max = max_div;
354+
_setup_mask(divider);
367355
return 0;
368356
}
369357

@@ -384,18 +372,22 @@ int ti_clk_parse_divider_data(int *div_table, int num_dividers, int max_div,
384372
return -ENOMEM;
385373

386374
valid_div = 0;
387-
divider->width = 0;
388375

389376
for (i = 0; i < num_dividers; i++)
390377
if (div_table[i] > 0) {
391378
tmp[valid_div].div = div_table[i];
392379
tmp[valid_div].val = i;
393380
valid_div++;
394-
divider->width = i;
381+
if (div_table[i] > max_div)
382+
max_div = div_table[i];
383+
if (!min_div || div_table[i] < min_div)
384+
min_div = div_table[i];
395385
}
396386

397-
divider->width = fls(divider->width);
387+
divider->min = min_div;
388+
divider->max = max_div;
398389
divider->table = tmp;
390+
_setup_mask(divider);
399391

400392
return 0;
401393
}
@@ -451,16 +443,15 @@ static int __init ti_clk_get_div_table(struct device_node *node,
451443
return 0;
452444
}
453445

454-
static int _get_divider_width(struct device_node *node,
455-
const struct clk_div_table *table,
456-
u8 flags)
446+
static int _populate_divider_min_max(struct device_node *node,
447+
struct clk_omap_divider *divider)
457448
{
458-
u32 min_div;
459-
u32 max_div;
460-
u32 val = 0;
461-
u32 div;
449+
u32 min_div = 0;
450+
u32 max_div = 0;
451+
u32 val;
452+
const struct clk_div_table *clkt;
462453

463-
if (!table) {
454+
if (!divider->table) {
464455
/* Clk divider table not provided, determine min/max divs */
465456
if (of_property_read_u32(node, "ti,min-div", &min_div))
466457
min_div = 1;
@@ -469,30 +460,22 @@ static int _get_divider_width(struct device_node *node,
469460
pr_err("no max-div for %pOFn!\n", node);
470461
return -EINVAL;
471462
}
472-
473-
/* Determine bit width for the field */
474-
if (flags & CLK_DIVIDER_ONE_BASED)
475-
val = 1;
476-
477-
div = min_div;
478-
479-
while (div < max_div) {
480-
if (flags & CLK_DIVIDER_POWER_OF_TWO)
481-
div <<= 1;
482-
else
483-
div++;
484-
val++;
485-
}
486463
} else {
487-
div = 0;
488464

489-
while (table[div].div) {
490-
val = table[div].val;
491-
div++;
465+
for (clkt = divider->table; clkt->div; clkt++) {
466+
val = clkt->div;
467+
if (val > max_div)
468+
max_div = val;
469+
if (!min_div || val < min_div)
470+
min_div = val;
492471
}
493472
}
494473

495-
return fls(val);
474+
divider->min = min_div;
475+
divider->max = max_div;
476+
_setup_mask(divider);
477+
478+
return 0;
496479
}
497480

498481
static int __init ti_clk_divider_populate(struct device_node *node,
@@ -532,9 +515,7 @@ static int __init ti_clk_divider_populate(struct device_node *node,
532515
if (ret)
533516
return ret;
534517

535-
div->width = _get_divider_width(node, div->table, div->flags);
536-
537-
return 0;
518+
return _populate_divider_min_max(node, div);
538519
}
539520

540521
/**

0 commit comments

Comments
 (0)