Commit dc613840 authored by Alex Elder's avatar Alex Elder Committed by Mike Turquette

clk: bcm281xx: add clock hysteresis support

Add support for clock gate hysteresis control.  For now, if it's
defined for a clock, it's enabled.
Signed-off-by: default avatarAlex Elder <elder@linaro.org>
Signed-off-by: default avatarMike Turquette <mturquette@linaro.org>
parent a597facc
...@@ -81,6 +81,7 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk) ...@@ -81,6 +81,7 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
struct peri_clk_data *peri; struct peri_clk_data *peri;
struct bcm_clk_policy *policy; struct bcm_clk_policy *policy;
struct bcm_clk_gate *gate; struct bcm_clk_gate *gate;
struct bcm_clk_hyst *hyst;
struct bcm_clk_div *div; struct bcm_clk_div *div;
struct bcm_clk_sel *sel; struct bcm_clk_sel *sel;
struct bcm_clk_trig *trig; struct bcm_clk_trig *trig;
...@@ -106,12 +107,25 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk) ...@@ -106,12 +107,25 @@ static bool peri_clk_data_offsets_valid(struct kona_clk *bcm_clk)
} }
gate = &peri->gate; gate = &peri->gate;
hyst = &peri->hyst;
if (gate_exists(gate)) { if (gate_exists(gate)) {
if (gate->offset > limit) { if (gate->offset > limit) {
pr_err("%s: bad gate offset for %s (%u > %u)\n", pr_err("%s: bad gate offset for %s (%u > %u)\n",
__func__, name, gate->offset, limit); __func__, name, gate->offset, limit);
return false; return false;
} }
if (hyst_exists(hyst)) {
if (hyst->offset > limit) {
pr_err("%s: bad hysteresis offset for %s "
"(%u > %u)\n", __func__,
name, hyst->offset, limit);
return false;
}
}
} else if (hyst_exists(hyst)) {
pr_err("%s: hysteresis but no gate for %s\n", __func__, name);
return false;
} }
div = &peri->div; div = &peri->div;
...@@ -261,6 +275,17 @@ static bool gate_valid(struct bcm_clk_gate *gate, const char *field_name, ...@@ -261,6 +275,17 @@ static bool gate_valid(struct bcm_clk_gate *gate, const char *field_name,
return true; return true;
} }
static bool hyst_valid(struct bcm_clk_hyst *hyst, const char *clock_name)
{
if (!bit_posn_valid(hyst->en_bit, "hysteresis enable", clock_name))
return false;
if (!bit_posn_valid(hyst->val_bit, "hysteresis value", clock_name))
return false;
return true;
}
/* /*
* A selector bitfield must be valid. Its parent_sel array must * A selector bitfield must be valid. Its parent_sel array must
* also be reasonable for the field. * also be reasonable for the field.
...@@ -379,6 +404,7 @@ peri_clk_data_valid(struct kona_clk *bcm_clk) ...@@ -379,6 +404,7 @@ peri_clk_data_valid(struct kona_clk *bcm_clk)
struct peri_clk_data *peri; struct peri_clk_data *peri;
struct bcm_clk_policy *policy; struct bcm_clk_policy *policy;
struct bcm_clk_gate *gate; struct bcm_clk_gate *gate;
struct bcm_clk_hyst *hyst;
struct bcm_clk_sel *sel; struct bcm_clk_sel *sel;
struct bcm_clk_div *div; struct bcm_clk_div *div;
struct bcm_clk_div *pre_div; struct bcm_clk_div *pre_div;
...@@ -406,6 +432,10 @@ peri_clk_data_valid(struct kona_clk *bcm_clk) ...@@ -406,6 +432,10 @@ peri_clk_data_valid(struct kona_clk *bcm_clk)
if (gate_exists(gate) && !gate_valid(gate, "gate", name)) if (gate_exists(gate) && !gate_valid(gate, "gate", name))
return false; return false;
hyst = &peri->hyst;
if (hyst_exists(hyst) && !hyst_valid(hyst, name))
return false;
sel = &peri->sel; sel = &peri->sel;
if (selector_exists(sel)) { if (selector_exists(sel)) {
if (!sel_valid(sel, "selector", name)) if (!sel_valid(sel, "selector", name))
......
...@@ -527,6 +527,35 @@ static int clk_gate(struct ccu_data *ccu, const char *name, ...@@ -527,6 +527,35 @@ static int clk_gate(struct ccu_data *ccu, const char *name,
return -EIO; return -EIO;
} }
/* Hysteresis operations */
/*
* If a clock gate requires a turn-off delay it will have
* "hysteresis" register bits defined. The first, if set, enables
* the delay; and if enabled, the second bit determines whether the
* delay is "low" or "high" (1 means high). For now, if it's
* defined for a clock, we set it.
*/
static bool hyst_init(struct ccu_data *ccu, struct bcm_clk_hyst *hyst)
{
u32 offset;
u32 reg_val;
u32 mask;
if (!hyst_exists(hyst))
return true;
offset = hyst->offset;
mask = (u32)1 << hyst->en_bit;
mask |= (u32)1 << hyst->val_bit;
reg_val = __ccu_read(ccu, offset);
reg_val |= mask;
__ccu_write(ccu, offset, reg_val);
return true;
}
/* Trigger operations */ /* Trigger operations */
/* /*
...@@ -1131,6 +1160,10 @@ static bool __peri_clk_init(struct kona_clk *bcm_clk) ...@@ -1131,6 +1160,10 @@ static bool __peri_clk_init(struct kona_clk *bcm_clk)
pr_err("%s: error initializing gate for %s\n", __func__, name); pr_err("%s: error initializing gate for %s\n", __func__, name);
return false; return false;
} }
if (!hyst_init(ccu, &peri->hyst)) {
pr_err("%s: error initializing hyst for %s\n", __func__, name);
return false;
}
if (!div_init(ccu, &peri->gate, &peri->div, &peri->trig)) { if (!div_init(ccu, &peri->gate, &peri->div, &peri->trig)) {
pr_err("%s: error initializing divider for %s\n", __func__, pr_err("%s: error initializing divider for %s\n", __func__,
name); name);
......
...@@ -60,6 +60,8 @@ ...@@ -60,6 +60,8 @@
#define gate_flip_enabled(gate) FLAG_FLIP(gate, GATE, ENABLED) #define gate_flip_enabled(gate) FLAG_FLIP(gate, GATE, ENABLED)
#define hyst_exists(hyst) ((hyst)->offset != 0)
#define divider_exists(div) FLAG_TEST(div, DIV, EXISTS) #define divider_exists(div) FLAG_TEST(div, DIV, EXISTS)
#define divider_is_fixed(div) FLAG_TEST(div, DIV, FIXED) #define divider_is_fixed(div) FLAG_TEST(div, DIV, FIXED)
#define divider_has_fraction(div) (!divider_is_fixed(div) && \ #define divider_has_fraction(div) (!divider_is_fixed(div) && \
...@@ -205,6 +207,22 @@ struct bcm_clk_gate { ...@@ -205,6 +207,22 @@ struct bcm_clk_gate {
.flags = FLAG(GATE, HW)|FLAG(GATE, EXISTS), \ .flags = FLAG(GATE, HW)|FLAG(GATE, EXISTS), \
} }
/* Gate hysteresis for clocks */
struct bcm_clk_hyst {
u32 offset; /* hyst register offset (normally CLKGATE) */
u32 en_bit; /* bit used to enable hysteresis */
u32 val_bit; /* if enabled: 0 = low delay; 1 = high delay */
};
/* Hysteresis initialization macro */
#define HYST(_offset, _en_bit, _val_bit) \
{ \
.offset = (_offset), \
.en_bit = (_en_bit), \
.val_bit = (_val_bit), \
}
/* /*
* Each clock can have zero, one, or two dividers which change the * Each clock can have zero, one, or two dividers which change the
* output rate of the clock. Each divider can be either fixed or * output rate of the clock. Each divider can be either fixed or
...@@ -372,6 +390,7 @@ struct bcm_clk_trig { ...@@ -372,6 +390,7 @@ struct bcm_clk_trig {
struct peri_clk_data { struct peri_clk_data {
struct bcm_clk_policy policy; struct bcm_clk_policy policy;
struct bcm_clk_gate gate; struct bcm_clk_gate gate;
struct bcm_clk_hyst hyst;
struct bcm_clk_trig pre_trig; struct bcm_clk_trig pre_trig;
struct bcm_clk_div pre_div; struct bcm_clk_div pre_div;
struct bcm_clk_trig trig; struct bcm_clk_trig trig;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment