blob: 9292e5d2faff1a056a55624876efb7af04b6fa01 [file] [log] [blame]
/* Copyright (c) 2009-2011, 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;
};
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);
/*
* 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);
/**
* 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 */