// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2013 Broadcom Corporation * Copyright 2013 Linaro Limited */ #include "clk-kona.h" #include <linux/delay.h> #include <linux/io.h> #include <linux/kernel.h> #include <linux/clk-provider.h> /* * "Policies" affect the frequencies of bus clocks provided by a * CCU. (I believe these polices are named "Deep Sleep", "Economy", * "Normal", and "Turbo".) A lower policy number has lower power * consumption, and policy 2 is the default. */ #define CCU_POLICY_COUNT … #define CCU_ACCESS_PASSWORD … #define CLK_GATE_DELAY_LOOP … /* Bitfield operations */ /* Produces a mask of set bits covering a range of a 32-bit value */ static inline u32 bitfield_mask(u32 shift, u32 width) { … } /* Extract the value of a bitfield found within a given register value */ static inline u32 bitfield_extract(u32 reg_val, u32 shift, u32 width) { … } /* Replace the value of a bitfield found within a given register value */ static inline u32 bitfield_replace(u32 reg_val, u32 shift, u32 width, u32 val) { … } /* Divider and scaling helpers */ /* Convert a divider into the scaled divisor value it represents. */ static inline u64 scaled_div_value(struct bcm_clk_div *div, u32 reg_div) { … } /* * Build a scaled divider value as close as possible to the * given whole part (div_value) and fractional part (expressed * in billionths). */ u64 scaled_div_build(struct bcm_clk_div *div, u32 div_value, u32 billionths) { … } /* The scaled minimum divisor representable by a divider */ static inline u64 scaled_div_min(struct bcm_clk_div *div) { … } /* The scaled maximum divisor representable by a divider */ u64 scaled_div_max(struct bcm_clk_div *div) { … } /* * Convert a scaled divisor into its divider representation as * stored in a divider register field. */ static inline u32 divider(struct bcm_clk_div *div, u64 scaled_div) { … } /* Return a rate scaled for use when dividing by a scaled divisor. */ static inline u64 scale_rate(struct bcm_clk_div *div, u32 rate) { … } /* CCU access */ /* Read a 32-bit register value from a CCU's address space. */ static inline u32 __ccu_read(struct ccu_data *ccu, u32 reg_offset) { … } /* Write a 32-bit register value into a CCU's address space. */ static inline void __ccu_write(struct ccu_data *ccu, u32 reg_offset, u32 reg_val) { … } static inline unsigned long ccu_lock(struct ccu_data *ccu) { … } static inline void ccu_unlock(struct ccu_data *ccu, unsigned long flags) { … } /* * Enable/disable write access to CCU protected registers. The * WR_ACCESS register for all CCUs is at offset 0. */ static inline void __ccu_write_enable(struct ccu_data *ccu) { … } static inline void __ccu_write_disable(struct ccu_data *ccu) { … } /* * Poll a register in a CCU's address space, returning when the * specified bit in that register's value is set (or clear). Delay * a microsecond after each read of the register. Returns true if * successful, or false if we gave up trying. * * Caller must ensure the CCU lock is held. */ static inline bool __ccu_wait_bit(struct ccu_data *ccu, u32 reg_offset, u32 bit, bool want) { … } /* Policy operations */ static bool __ccu_policy_engine_start(struct ccu_data *ccu, bool sync) { … } static bool __ccu_policy_engine_stop(struct ccu_data *ccu) { … } /* * A CCU has four operating conditions ("policies"), and some clocks * can be disabled or enabled based on which policy is currently in * effect. Such clocks have a bit in a "policy mask" register for * each policy indicating whether the clock is enabled for that * policy or not. The bit position for a clock is the same for all * four registers, and the 32-bit registers are at consecutive * addresses. */ static bool policy_init(struct ccu_data *ccu, struct bcm_clk_policy *policy) { … } /* Gate operations */ /* Determine whether a clock is gated. CCU lock must be held. */ static bool __is_clk_gate_enabled(struct ccu_data *ccu, struct bcm_clk_gate *gate) { … } /* Determine whether a clock is gated. */ static bool is_clk_gate_enabled(struct ccu_data *ccu, struct bcm_clk_gate *gate) { … } /* * Commit our desired gate state to the hardware. * Returns true if successful, false otherwise. */ static bool __gate_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate) { … } /* * Initialize a gate. Our desired state (hardware/software select, * and if software, its enable state) is committed to hardware * without the usual checks to see if it's already set up that way. * Returns true if successful, false otherwise. */ static bool gate_init(struct ccu_data *ccu, struct bcm_clk_gate *gate) { … } /* * Set a gate to enabled or disabled state. Does nothing if the * gate is not currently under software control, or if it is already * in the requested state. Returns true if successful, false * otherwise. CCU lock must be held. */ static bool __clk_gate(struct ccu_data *ccu, struct bcm_clk_gate *gate, bool enable) { … } /* Enable or disable a gate. Returns 0 if successful, -EIO otherwise */ static int clk_gate(struct ccu_data *ccu, const char *name, struct bcm_clk_gate *gate, bool enable) { … } /* 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) { … } /* Trigger operations */ /* * Caller must ensure CCU lock is held and access is enabled. * Returns true if successful, false otherwise. */ static bool __clk_trigger(struct ccu_data *ccu, struct bcm_clk_trig *trig) { … } /* Divider operations */ /* Read a divider value and return the scaled divisor it represents. */ static u64 divider_read_scaled(struct ccu_data *ccu, struct bcm_clk_div *div) { … } /* * Convert a divider's scaled divisor value into its recorded form * and commit it into the hardware divider register. * * Returns 0 on success. Returns -EINVAL for invalid arguments. * Returns -ENXIO if gating failed, and -EIO if a trigger failed. */ static int __div_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate, struct bcm_clk_div *div, struct bcm_clk_trig *trig) { … } /* * Initialize a divider by committing our desired state to hardware * without the usual checks to see if it's already set up that way. * Returns true if successful, false otherwise. */ static bool div_init(struct ccu_data *ccu, struct bcm_clk_gate *gate, struct bcm_clk_div *div, struct bcm_clk_trig *trig) { … } static int divider_write(struct ccu_data *ccu, struct bcm_clk_gate *gate, struct bcm_clk_div *div, struct bcm_clk_trig *trig, u64 scaled_div) { … } /* Common clock rate helpers */ /* * Implement the common clock framework recalc_rate method, taking * into account a divider and an optional pre-divider. The * pre-divider register pointer may be NULL. */ static unsigned long clk_recalc_rate(struct ccu_data *ccu, struct bcm_clk_div *div, struct bcm_clk_div *pre_div, unsigned long parent_rate) { … } /* * Compute the output rate produced when a given parent rate is fed * into two dividers. The pre-divider can be NULL, and even if it's * non-null it may be nonexistent. It's also OK for the divider to * be nonexistent, and in that case the pre-divider is also ignored. * * If scaled_div is non-null, it is used to return the scaled divisor * value used by the (downstream) divider to produce that rate. */ static long round_rate(struct ccu_data *ccu, struct bcm_clk_div *div, struct bcm_clk_div *pre_div, unsigned long rate, unsigned long parent_rate, u64 *scaled_div) { … } /* Common clock parent helpers */ /* * For a given parent selector (register field) value, find the * index into a selector's parent_sel array that contains it. * Returns the index, or BAD_CLK_INDEX if it's not found. */ static u8 parent_index(struct bcm_clk_sel *sel, u8 parent_sel) { … } /* * Fetch the current value of the selector, and translate that into * its corresponding index in the parent array we registered with * the clock framework. * * Returns parent array index that corresponds with the value found, * or BAD_CLK_INDEX if the found value is out of range. */ static u8 selector_read_index(struct ccu_data *ccu, struct bcm_clk_sel *sel) { … } /* * Commit our desired selector value to the hardware. * * Returns 0 on success. Returns -EINVAL for invalid arguments. * Returns -ENXIO if gating failed, and -EIO if a trigger failed. */ static int __sel_commit(struct ccu_data *ccu, struct bcm_clk_gate *gate, struct bcm_clk_sel *sel, struct bcm_clk_trig *trig) { … } /* * Initialize a selector by committing our desired state to hardware * without the usual checks to see if it's already set up that way. * Returns true if successful, false otherwise. */ static bool sel_init(struct ccu_data *ccu, struct bcm_clk_gate *gate, struct bcm_clk_sel *sel, struct bcm_clk_trig *trig) { … } /* * Write a new value into a selector register to switch to a * different parent clock. Returns 0 on success, or an error code * (from __sel_commit()) otherwise. */ static int selector_write(struct ccu_data *ccu, struct bcm_clk_gate *gate, struct bcm_clk_sel *sel, struct bcm_clk_trig *trig, u8 index) { … } /* Clock operations */ static int kona_peri_clk_enable(struct clk_hw *hw) { … } static void kona_peri_clk_disable(struct clk_hw *hw) { … } static int kona_peri_clk_is_enabled(struct clk_hw *hw) { … } static unsigned long kona_peri_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { … } static long kona_peri_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *parent_rate) { … } static int kona_peri_clk_determine_rate(struct clk_hw *hw, struct clk_rate_request *req) { … } static int kona_peri_clk_set_parent(struct clk_hw *hw, u8 index) { … } static u8 kona_peri_clk_get_parent(struct clk_hw *hw) { … } static int kona_peri_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) { … } struct clk_ops kona_peri_clk_ops = …; /* Put a peripheral clock into its initial state */ static bool __peri_clk_init(struct kona_clk *bcm_clk) { … } static bool __kona_clk_init(struct kona_clk *bcm_clk) { … } /* Set a CCU and all its clocks into their desired initial state */ bool __init kona_ccu_init(struct ccu_data *ccu) { … }