Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+30 −0
Original line number Original line Diff line number Diff line
@@ -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)
	}
	}


	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,
	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)
	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)
	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))
+33 −0
Original line number Original line Diff line number Diff line
@@ -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)
		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);
+19 −0
Original line number Original line Diff line number Diff line
@@ -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 {
		.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 {
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;