There exist platforms, namely at least all Rockchip Cortex-A9 based ones,
that don't use the paradigm of reading-changing-writing the register contents,
but instead only write the changes to the register with a mask that indicates
the changed bits.
This patch adds flags and code to support the case where the lower 16 bit of
hold the information and the upper 16 bit are used as mask to indicate the
written changes.
As hardware-specific flags should not be added to the common clk flags, the
flags are added to gate, mux and divider clocks individually.
Signed-off-by: Heiko Stuebner <heiko@...>
drivers/clk/clk-divider.c | 15 +++++++++++++--
drivers/clk/clk-gate.c | 24 ++++++++++++++++++------
drivers/clk/clk-mux.c | 15 +++++++++++++--
include/linux/clk-provider.h | 16 ++++++++++++++++
4 files changed, 60 insertions(+), 10 deletions(-)
@@ -217,8 +217,12 @@ static int clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
if (divider->lock)
spin_lock_irqsave(divider->lock, flags);
- val = readl(divider->reg);
- val &= ~(div_mask(divider) << divider->shift);
+ if (divider->flags & CLK_DIVIDER_MASK_UPPER_HALF) {
+ val = div_mask(divider) << (divider->shift + 16);
+ } else {
+ val = readl(divider->reg);
+ val &= ~(div_mask(divider) << divider->shift);
+ }
val |= value << divider->shift;
writel(val, divider->reg);
@@ -245,6 +249,13 @@ static struct clk *_register_divider(struct device *dev, const char *name,
struct clk *clk;
struct clk_init_data init;
+ if ((clk_divider_flags & CLK_DIVIDER_MASK_UPPER_HALF) &&
+ (shift + width > 15)) {
+ pr_err("%s: shift %d + width %d invalid\n", __func__,
+ shift, width);
+ return ERR_PTR(-EINVAL);
+ }
+
/* allocate the divider */
div = kzalloc(sizeof(struct clk_divider), GFP_KERNEL);
if (!div) {
@@ -53,12 +53,19 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable)
if (gate->lock)
spin_lock_irqsave(gate->lock, flags);
- reg = readl(gate->reg);
-
- if (set)
- reg |= BIT(gate->bit_idx);
- else
- reg &= ~BIT(gate->bit_idx);
+ if (gate->flags & CLK_GATE_MASK_UPPER_HALF) {
+ reg = BIT(gate->bit_idx + 16);
+
+ if (set)
+ reg |= BIT(gate->bit_idx);
+ } else {
+ reg = readl(gate->reg);
+
+ if (set)
+ reg |= BIT(gate->bit_idx);
+ else
+ reg &= ~BIT(gate->bit_idx);
+ }
writel(reg, gate->reg);
@@ -121,6 +128,11 @@ struct clk *clk_register_gate(struct device *dev, const char *name,
struct clk *clk;
struct clk_init_data init;
+ if ((clk_gate_flags & CLK_GATE_MASK_UPPER_HALF) && bit_idx > 15) {
+ pr_err("%s: bit_idx %d invalid\n", __func__, bit_idx);
+ return ERR_PTR(-EINVAL);
+ }
+
/* allocate the gate */
gate = kzalloc(sizeof(struct clk_gate), GFP_KERNEL);
if (!gate) {
@@ -86,8 +86,12 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
if (mux->lock)
spin_lock_irqsave(mux->lock, flags);
- val = readl(mux->reg);
- val &= ~(mux->mask << mux->shift);
+ if (mux->flags & CLK_MUX_MASK_UPPER_HALF) {
+ val = mux->mask << (mux->shift + 16);
+ } else {
+ val = readl(mux->reg);
+ val &= ~(mux->mask << mux->shift);
+ }
val |= index << mux->shift;
writel(val, mux->reg);
@@ -112,6 +116,13 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name,
struct clk *clk;
struct clk_init_data init;
+ if (!mask || ((clk_mux_flags & CLK_MUX_MASK_UPPER_HALF) &&
+ (__fls(mask << shift) > 15))) {
+ pr_err("%s: shift %d + mask %d invalid\n", __func__,
+ shift, mask);
+ return ERR_PTR(-EINVAL);
+ }
+
/* allocate the mux */
mux = kzalloc(sizeof(struct clk_mux), GFP_KERNEL);
if (!mux) {
@@ -210,6 +210,11 @@ void of_fixed_clk_setup(struct device_node *np);
* CLK_GATE_SET_TO_DISABLE - by default this clock sets the bit at bit_idx to
* enable the clock. Setting this flag does the opposite: setting the bit
* disable the clock and clearing it enables the clock
+ * CLK_GATE_MASK_UPPER_HALF - this clock only uses the lower 16 bit of the
+ * register to hold the gate bits, while using the upper 16 bit to
+ * indicate the bits that get changed during a write. So gating the
+ * clock in bit 2 would write BIT(18) | BIT(2) to the register, while
+ * ungating this clock would write only BIT(18) to the register.
*/
struct clk_gate {
struct clk_hw hw;
@@ -220,6 +225,7 @@ struct clk_gate {
};
#define CLK_GATE_SET_TO_DISABLE BIT(0)
+#define CLK_GATE_MASK_UPPER_HALF BIT(1)
extern const struct clk_ops clk_gate_ops;
struct clk *clk_register_gate(struct device *dev, const char *name,
@@ -257,6 +263,11 @@ struct clk_div_table {
* Some hardware implementations gracefully handle this case and allow a
* zero divisor by not modifying their input clock
* (divide by one / bypass).
+ * CLK_DIVIDER_MASK_UPPER_HALF - this clock only uses the lower 16 bit of the
+ * register to hold the divider bits, while using the upper 16 bit to
+ * indicate the bits that get changed during a write. So for a clock with
+ * shift 0 and width 2, setting the divider to 2 would result in a write
+ * of (3 << 16) | (2 << 0).
*/
struct clk_divider {
struct clk_hw hw;
@@ -271,6 +282,7 @@ struct clk_divider {
#define CLK_DIVIDER_ONE_BASED BIT(0)
#define CLK_DIVIDER_POWER_OF_TWO BIT(1)
#define CLK_DIVIDER_ALLOW_ZERO BIT(2)
+#define CLK_DIVIDER_MASK_UPPER_HALF BIT(3)
extern const struct clk_ops clk_divider_ops;
struct clk *clk_register_divider(struct device *dev, const char *name,
@@ -299,6 +311,9 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,
* Flags:
* CLK_MUX_INDEX_ONE - register index starts at 1, not 0
* CLK_MUX_INDEX_BIT - register index is a single bit (power of two)
+ * CLK_MUX_MASK_UPPER_HALF - this clock only uses the lower 16 bit of the
+ * register to hold the mux bits, while using the upper 16 bit to
+ * indicate the bits that get changed during a write.
*/
struct clk_mux {
struct clk_hw hw;
@@ -312,6 +327,7 @@ struct clk_mux {
#define CLK_MUX_INDEX_ONE BIT(0)
#define CLK_MUX_INDEX_BIT BIT(1)
+#define CLK_MUX_MASK_UPPER_HALF BIT(3)
extern const struct clk_ops clk_mux_ops;