| /* Copyright (c) 2009-2012, Code Aurora Forum. All rights reserved. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 and |
| * only version 2 as published by the Free Software Foundation. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| */ |
| |
| #ifndef __ARCH_ARM_MACH_MSM_CLOCK_LOCAL_H |
| #define __ARCH_ARM_MACH_MSM_CLOCK_LOCAL_H |
| |
| #include <linux/spinlock.h> |
| #include "clock.h" |
| |
| /* |
| * Bit manipulation macros |
| */ |
| #define BM(msb, lsb) (((((uint32_t)-1) << (31-msb)) >> (31-msb+lsb)) << lsb) |
| #define BVAL(msb, lsb, val) (((val) << lsb) & BM(msb, lsb)) |
| |
| #define MN_MODE_DUAL_EDGE 0x2 |
| |
| /* MD Registers */ |
| #define MD4(m_lsb, m, n_lsb, n) \ |
| (BVAL((m_lsb+3), m_lsb, m) | BVAL((n_lsb+3), n_lsb, ~(n))) |
| #define MD8(m_lsb, m, n_lsb, n) \ |
| (BVAL((m_lsb+7), m_lsb, m) | BVAL((n_lsb+7), n_lsb, ~(n))) |
| #define MD16(m, n) (BVAL(31, 16, m) | BVAL(15, 0, ~(n))) |
| |
| /* NS Registers */ |
| #define NS(n_msb, n_lsb, n, m, mde_lsb, d_msb, d_lsb, d, s_msb, s_lsb, s) \ |
| (BVAL(n_msb, n_lsb, ~(n-m) * !!(n)) \ |
| | (BVAL((mde_lsb+1), mde_lsb, MN_MODE_DUAL_EDGE) * !!(n)) \ |
| | BVAL(d_msb, d_lsb, (d-1)) | BVAL(s_msb, s_lsb, s)) |
| |
| #define NS_MM(n_msb, n_lsb, n, m, d_msb, d_lsb, d, s_msb, s_lsb, s) \ |
| (BVAL(n_msb, n_lsb, ~(n-m) * !!(n))|BVAL(d_msb, d_lsb, (d-1)) \ |
| | BVAL(s_msb, s_lsb, s)) |
| |
| #define NS_DIVSRC(d_msb, d_lsb, d, s_msb, s_lsb, s) \ |
| (BVAL(d_msb, d_lsb, (d-1)) | BVAL(s_msb, s_lsb, s)) |
| |
| #define NS_DIV(d_msb, d_lsb, d) \ |
| BVAL(d_msb, d_lsb, (d-1)) |
| |
| #define NS_SRC_SEL(s_msb, s_lsb, s) \ |
| BVAL(s_msb, s_lsb, s) |
| |
| #define NS_MND_BANKED4(n0_lsb, n1_lsb, n, m, s0_lsb, s1_lsb, s) \ |
| (BVAL((n0_lsb+3), n0_lsb, ~(n-m) * !!(n)) \ |
| | BVAL((n1_lsb+3), n1_lsb, ~(n-m) * !!(n)) \ |
| | BVAL((s0_lsb+2), s0_lsb, s) \ |
| | BVAL((s1_lsb+2), s1_lsb, s)) |
| |
| #define NS_MND_BANKED8(n0_lsb, n1_lsb, n, m, s0_lsb, s1_lsb, s) \ |
| (BVAL((n0_lsb+7), n0_lsb, ~(n-m) * !!(n)) \ |
| | BVAL((n1_lsb+7), n1_lsb, ~(n-m) * !!(n)) \ |
| | BVAL((s0_lsb+2), s0_lsb, s) \ |
| | BVAL((s1_lsb+2), s1_lsb, s)) |
| |
| #define NS_DIVSRC_BANKED(d0_msb, d0_lsb, d1_msb, d1_lsb, d, \ |
| s0_msb, s0_lsb, s1_msb, s1_lsb, s) \ |
| (BVAL(d0_msb, d0_lsb, (d-1)) | BVAL(d1_msb, d1_lsb, (d-1)) \ |
| | BVAL(s0_msb, s0_lsb, s) \ |
| | BVAL(s1_msb, s1_lsb, s)) |
| |
| /* CC Registers */ |
| #define CC(mde_lsb, n) (BVAL((mde_lsb+1), mde_lsb, MN_MODE_DUAL_EDGE) * !!(n)) |
| #define CC_BANKED(mde0_lsb, mde1_lsb, n) \ |
| ((BVAL((mde0_lsb+1), mde0_lsb, MN_MODE_DUAL_EDGE) \ |
| | BVAL((mde1_lsb+1), mde1_lsb, MN_MODE_DUAL_EDGE)) \ |
| * !!(n)) |
| |
| /* |
| * Halt/Status Checking Mode Macros |
| */ |
| #define HALT 0 /* Bit pol: 1 = halted */ |
| #define NOCHECK 1 /* No bit to check, do nothing */ |
| #define HALT_VOTED 2 /* Bit pol: 1 = halted; delay on disable */ |
| #define ENABLE 3 /* Bit pol: 1 = running */ |
| #define ENABLE_VOTED 4 /* Bit pol: 1 = running; delay on disable */ |
| #define DELAY 5 /* No bit to check, just delay */ |
| |
| /* |
| * Clock Definition Macros |
| */ |
| #define DEFINE_CLK_MEASURE(name) \ |
| struct clk name = { \ |
| .ops = &clk_ops_measure, \ |
| .dbg_name = #name, \ |
| CLK_INIT(name), \ |
| }; \ |
| |
| /* |
| * Generic frequency-definition structs and macros |
| */ |
| struct clk_freq_tbl { |
| const uint32_t freq_hz; |
| struct clk *src_clk; |
| const uint32_t md_val; |
| const uint32_t ns_val; |
| const uint32_t ctl_val; |
| uint32_t mnd_en_mask; |
| const unsigned sys_vdd; |
| void *const extra_freq_data; |
| }; |
| |
| /* Some clocks have two banks to avoid glitches when switching frequencies. |
| * The unused bank is programmed while running on the other bank, and |
| * switched to afterwards. The following two structs describe the banks. */ |
| struct bank_mask_info { |
| void *const md_reg; |
| const uint32_t ns_mask; |
| const uint32_t rst_mask; |
| const uint32_t mnd_en_mask; |
| const uint32_t mode_mask; |
| }; |
| |
| struct bank_masks { |
| const uint32_t bank_sel_mask; |
| const struct bank_mask_info bank0_mask; |
| const struct bank_mask_info bank1_mask; |
| }; |
| |
| #define F_RAW(f, sc, m_v, n_v, c_v, m_m, e) { \ |
| .freq_hz = f, \ |
| .src_clk = sc, \ |
| .md_val = m_v, \ |
| .ns_val = n_v, \ |
| .ctl_val = c_v, \ |
| .mnd_en_mask = m_m, \ |
| .extra_freq_data = e, \ |
| } |
| #define FREQ_END (UINT_MAX-1) |
| #define F_END { .freq_hz = FREQ_END } |
| |
| /** |
| * struct branch - branch on/off |
| * @ctl_reg: clock control register |
| * @en_mask: ORed with @ctl_reg to enable the clock |
| * @hwcg_reg: hardware clock gating register |
| * @hwcg_mask: ORed with @hwcg_reg to enable hardware clock gating |
| * @halt_reg: halt register |
| * @halt_check: type of halt check to perform |
| * @halt_bit: ANDed with @halt_reg to test for clock halted |
| * @reset_reg: reset register |
| * @reset_mask: ORed with @reset_reg to reset the clock domain |
| */ |
| struct branch { |
| void __iomem *const ctl_reg; |
| const u32 en_mask; |
| |
| void __iomem *hwcg_reg; |
| u32 hwcg_mask; |
| |
| void __iomem *const halt_reg; |
| const u16 halt_check; |
| const u16 halt_bit; |
| |
| void __iomem *const reset_reg; |
| const u32 reset_mask; |
| |
| void __iomem *const retain_reg; |
| const u32 retain_mask; |
| }; |
| |
| int branch_reset(struct branch *b, enum clk_reset_action action); |
| void __branch_clk_enable_reg(const struct branch *clk, const char *name); |
| u32 __branch_clk_disable_reg(const struct branch *clk, const char *name); |
| int branch_clk_handoff(struct clk *c); |
| int branch_clk_set_flags(struct clk *clk, unsigned flags); |
| |
| /* |
| * Generic clock-definition struct and macros |
| */ |
| struct rcg_clk { |
| bool enabled; |
| void *const ns_reg; |
| void *const md_reg; |
| |
| const uint32_t root_en_mask; |
| uint32_t ns_mask; |
| const uint32_t ctl_mask; |
| |
| void *bank_info; |
| void (*set_rate)(struct rcg_clk *, struct clk_freq_tbl *); |
| |
| struct clk_freq_tbl *freq_tbl; |
| struct clk_freq_tbl *current_freq; |
| |
| struct branch b; |
| struct clk c; |
| }; |
| |
| static inline struct rcg_clk *to_rcg_clk(struct clk *clk) |
| { |
| return container_of(clk, struct rcg_clk, c); |
| } |
| |
| extern struct clk_freq_tbl rcg_dummy_freq; |
| |
| int rcg_clk_enable(struct clk *clk); |
| void rcg_clk_disable(struct clk *clk); |
| int rcg_clk_set_rate(struct clk *clk, unsigned long rate); |
| unsigned long rcg_clk_get_rate(struct clk *clk); |
| int rcg_clk_list_rate(struct clk *clk, unsigned n); |
| int rcg_clk_is_enabled(struct clk *clk); |
| long rcg_clk_round_rate(struct clk *clk, unsigned long rate); |
| struct clk *rcg_clk_get_parent(struct clk *c); |
| int rcg_clk_handoff(struct clk *c); |
| int rcg_clk_reset(struct clk *clk, enum clk_reset_action action); |
| void rcg_clk_enable_hwcg(struct clk *clk); |
| void rcg_clk_disable_hwcg(struct clk *clk); |
| int rcg_clk_in_hwcg_mode(struct clk *c); |
| int rcg_clk_set_flags(struct clk *clk, unsigned flags); |
| |
| /** |
| * struct cdiv_clk - integer divider clock with external source selection |
| * @ns_reg: source select and divider settings register |
| * @ext_mask: bit to set to select an external source |
| * @cur_div: current divider setting (or 0 for external source) |
| * @max_div: maximum divider value supported (must be power of 2) |
| * @div_offset: number of bits to shift divider left by in @ns_reg |
| * @b: branch |
| * @c: clock |
| */ |
| struct cdiv_clk { |
| void __iomem *const ns_reg; |
| u32 ext_mask; |
| |
| unsigned long cur_div; |
| u8 div_offset; |
| u32 max_div; |
| |
| struct branch b; |
| struct clk c; |
| }; |
| |
| static inline struct cdiv_clk *to_cdiv_clk(struct clk *clk) |
| { |
| return container_of(clk, struct cdiv_clk, c); |
| } |
| |
| extern struct clk_ops clk_ops_cdiv; |
| |
| /** |
| * struct fixed_clk - fixed rate clock (used for crystal oscillators) |
| * @rate: output rate |
| * @c: clk |
| */ |
| struct fixed_clk { |
| unsigned long rate; |
| struct clk c; |
| }; |
| |
| static inline struct fixed_clk *to_fixed_clk(struct clk *clk) |
| { |
| return container_of(clk, struct fixed_clk, c); |
| } |
| |
| static inline unsigned long fixed_clk_get_rate(struct clk *clk) |
| { |
| struct fixed_clk *f = to_fixed_clk(clk); |
| return f->rate; |
| } |
| |
| |
| /** |
| * struct pll_vote_clk - phase locked loop (HW voteable) |
| * @rate: output rate |
| * @soft_vote: soft voting variable for multiple PLL software instances |
| * @soft_vote_mask: soft voting mask for multiple PLL software instances |
| * @en_reg: enable register |
| * @en_mask: ORed with @en_reg to enable the clock |
| * @status_reg: status register |
| * @parent: clock source |
| * @c: clk |
| */ |
| struct pll_vote_clk { |
| unsigned long rate; |
| |
| u32 *soft_vote; |
| const u32 soft_vote_mask; |
| void __iomem *const en_reg; |
| const u32 en_mask; |
| |
| void __iomem *const status_reg; |
| |
| struct clk *parent; |
| struct clk c; |
| }; |
| |
| extern struct clk_ops clk_ops_pll_vote; |
| |
| static inline struct pll_vote_clk *to_pll_vote_clk(struct clk *clk) |
| { |
| return container_of(clk, struct pll_vote_clk, c); |
| } |
| |
| /** |
| * struct pll_clk - phase locked loop |
| * @rate: output rate |
| * @mode_reg: enable register |
| * @parent: clock source |
| * @c: clk |
| */ |
| struct pll_clk { |
| unsigned long rate; |
| |
| void __iomem *const mode_reg; |
| |
| struct clk *parent; |
| struct clk c; |
| }; |
| |
| extern struct clk_ops clk_ops_pll; |
| |
| static inline struct pll_clk *to_pll_clk(struct clk *clk) |
| { |
| return container_of(clk, struct pll_clk, c); |
| } |
| |
| int sr_pll_clk_enable(struct clk *clk); |
| |
| /** |
| * struct branch_clk - branch |
| * @enabled: true if clock is on, false otherwise |
| * @b: branch |
| * @parent: clock source |
| * @c: clk |
| * |
| * An on/off switch with a rate derived from the parent. |
| */ |
| struct branch_clk { |
| bool enabled; |
| struct branch b; |
| struct clk *parent; |
| struct clk c; |
| }; |
| |
| static inline struct branch_clk *to_branch_clk(struct clk *clk) |
| { |
| return container_of(clk, struct branch_clk, c); |
| } |
| |
| int branch_clk_enable(struct clk *clk); |
| void branch_clk_disable(struct clk *clk); |
| struct clk *branch_clk_get_parent(struct clk *clk); |
| int branch_clk_set_parent(struct clk *clk, struct clk *parent); |
| int branch_clk_is_enabled(struct clk *clk); |
| int branch_clk_reset(struct clk *c, enum clk_reset_action action); |
| void branch_clk_enable_hwcg(struct clk *clk); |
| void branch_clk_disable_hwcg(struct clk *clk); |
| int branch_clk_in_hwcg_mode(struct clk *c); |
| |
| /** |
| * struct measure_clk - for rate measurement debug use |
| * @sample_ticks: sample period in reference clock ticks |
| * @multiplier: measurement scale-up factor |
| * @divider: measurement scale-down factor |
| * @c: clk |
| */ |
| struct measure_clk { |
| u64 sample_ticks; |
| u32 multiplier; |
| u32 divider; |
| struct clk c; |
| }; |
| |
| extern struct clk_ops clk_ops_measure; |
| |
| static inline struct measure_clk *to_measure_clk(struct clk *clk) |
| { |
| return container_of(clk, struct measure_clk, c); |
| } |
| |
| /* |
| * Variables from clock-local driver |
| */ |
| extern spinlock_t local_clock_reg_lock; |
| extern struct fixed_clk gnd_clk; |
| |
| /* |
| * Local-clock APIs |
| */ |
| bool local_clk_is_local(struct clk *clk); |
| |
| /* |
| * PLL vote clock APIs |
| */ |
| int pll_vote_clk_enable(struct clk *clk); |
| void pll_vote_clk_disable(struct clk *clk); |
| unsigned long pll_vote_clk_get_rate(struct clk *clk); |
| struct clk *pll_vote_clk_get_parent(struct clk *clk); |
| int pll_vote_clk_is_enabled(struct clk *clk); |
| |
| /* |
| * Generic set-rate implementations |
| */ |
| void set_rate_mnd(struct rcg_clk *clk, struct clk_freq_tbl *nf); |
| void set_rate_nop(struct rcg_clk *clk, struct clk_freq_tbl *nf); |
| void set_rate_mnd_8(struct rcg_clk *clk, struct clk_freq_tbl *nf); |
| void set_rate_mnd_banked(struct rcg_clk *clk, struct clk_freq_tbl *nf); |
| void set_rate_div_banked(struct rcg_clk *clk, struct clk_freq_tbl *nf); |
| |
| #endif /* __ARCH_ARM_MACH_MSM_CLOCK_LOCAL_H */ |
| |