diff options
Diffstat (limited to 'drivers/clk/bcm')
-rw-r--r-- | drivers/clk/bcm/clk-bcm2835.c | 97 | ||||
-rw-r--r-- | drivers/clk/bcm/clk-cygnus.c | 59 | ||||
-rw-r--r-- | drivers/clk/bcm/clk-iproc-pll.c | 41 | ||||
-rw-r--r-- | drivers/clk/bcm/clk-iproc.h | 43 |
4 files changed, 200 insertions, 40 deletions
diff --git a/drivers/clk/bcm/clk-bcm2835.c b/drivers/clk/bcm/clk-bcm2835.c index 9f4df8f64..1f79f48d5 100644 --- a/drivers/clk/bcm/clk-bcm2835.c +++ b/drivers/clk/bcm/clk-bcm2835.c @@ -51,6 +51,7 @@ #define CM_GNRICCTL 0x000 #define CM_GNRICDIV 0x004 # define CM_DIV_FRAC_BITS 12 +# define CM_DIV_FRAC_MASK GENMASK(CM_DIV_FRAC_BITS - 1, 0) #define CM_VPUCTL 0x008 #define CM_VPUDIV 0x00c @@ -88,10 +89,23 @@ #define CM_HSMDIV 0x08c #define CM_OTPCTL 0x090 #define CM_OTPDIV 0x094 +#define CM_PCMCTL 0x098 +#define CM_PCMDIV 0x09c #define CM_PWMCTL 0x0a0 #define CM_PWMDIV 0x0a4 +#define CM_SLIMCTL 0x0a8 +#define CM_SLIMDIV 0x0ac #define CM_SMICTL 0x0b0 #define CM_SMIDIV 0x0b4 +/* no definition for 0x0b8 and 0x0bc */ +#define CM_TCNTCTL 0x0c0 +#define CM_TCNTDIV 0x0c4 +#define CM_TECCTL 0x0c8 +#define CM_TECDIV 0x0cc +#define CM_TD0CTL 0x0d0 +#define CM_TD0DIV 0x0d4 +#define CM_TD1CTL 0x0d8 +#define CM_TD1DIV 0x0dc #define CM_TSENSCTL 0x0e0 #define CM_TSENSDIV 0x0e4 #define CM_TIMERCTL 0x0e8 @@ -115,6 +129,7 @@ # define CM_GATE BIT(CM_GATE_BIT) # define CM_BUSY BIT(7) # define CM_BUSYD BIT(8) +# define CM_FRAC BIT(9) # define CM_SRC_SHIFT 0 # define CM_SRC_BITS 4 # define CM_SRC_MASK 0xf @@ -311,21 +326,18 @@ void __init bcm2835_init_clocks(void) struct clk *clk; int ret; - clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, - 126000000); + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, 0, 126000000); if (IS_ERR(clk)) pr_err("apb_pclk not registered\n"); - clk = clk_register_fixed_rate(NULL, "uart0_pclk", NULL, CLK_IS_ROOT, - 3000000); + clk = clk_register_fixed_rate(NULL, "uart0_pclk", NULL, 0, 3000000); if (IS_ERR(clk)) pr_err("uart0_pclk not registered\n"); ret = clk_register_clkdev(clk, NULL, "20201000.uart"); if (ret) pr_err("uart0_pclk alias not registered\n"); - clk = clk_register_fixed_rate(NULL, "uart1_pclk", NULL, CLK_IS_ROOT, - 125000000); + clk = clk_register_fixed_rate(NULL, "uart1_pclk", NULL, 0, 125000000); if (IS_ERR(clk)) pr_err("uart1_pclk not registered\n"); ret = clk_register_clkdev(clk, NULL, "20215000.uart"); @@ -634,6 +646,7 @@ struct bcm2835_clock_data { u32 frac_bits; bool is_vpu_clock; + bool is_mash_clock; }; static const char *const bcm2835_clock_per_parents[] = { @@ -815,6 +828,7 @@ static const struct bcm2835_clock_data bcm2835_clock_pwm_data = { .div_reg = CM_PWMDIV, .int_bits = 12, .frac_bits = 12, + .is_mash_clock = true, }; struct bcm2835_pll { @@ -900,8 +914,14 @@ static void bcm2835_pll_off(struct clk_hw *hw) struct bcm2835_cprman *cprman = pll->cprman; const struct bcm2835_pll_data *data = pll->data; - cprman_write(cprman, data->cm_ctrl_reg, CM_PLL_ANARST); - cprman_write(cprman, data->a2w_ctrl_reg, A2W_PLL_CTRL_PWRDN); + spin_lock(&cprman->regs_lock); + cprman_write(cprman, data->cm_ctrl_reg, + cprman_read(cprman, data->cm_ctrl_reg) | + CM_PLL_ANARST); + cprman_write(cprman, data->a2w_ctrl_reg, + cprman_read(cprman, data->a2w_ctrl_reg) | + A2W_PLL_CTRL_PWRDN); + spin_unlock(&cprman->regs_lock); } static int bcm2835_pll_on(struct clk_hw *hw) @@ -911,6 +931,10 @@ static int bcm2835_pll_on(struct clk_hw *hw) const struct bcm2835_pll_data *data = pll->data; ktime_t timeout; + cprman_write(cprman, data->a2w_ctrl_reg, + cprman_read(cprman, data->a2w_ctrl_reg) & + ~A2W_PLL_CTRL_PWRDN); + /* Take the PLL out of reset. */ cprman_write(cprman, data->cm_ctrl_reg, cprman_read(cprman, data->cm_ctrl_reg) & ~CM_PLL_ANARST); @@ -1060,16 +1084,7 @@ static long bcm2835_pll_divider_round_rate(struct clk_hw *hw, static unsigned long bcm2835_pll_divider_get_rate(struct clk_hw *hw, unsigned long parent_rate) { - struct bcm2835_pll_divider *divider = bcm2835_pll_divider_from_hw(hw); - struct bcm2835_cprman *cprman = divider->cprman; - const struct bcm2835_pll_divider_data *data = divider->data; - u32 div = cprman_read(cprman, data->a2w_reg); - - div &= (1 << A2W_PLL_DIV_BITS) - 1; - if (div == 0) - div = 256; - - return parent_rate / div; + return clk_divider_ops.recalc_rate(hw, parent_rate); } static void bcm2835_pll_divider_off(struct clk_hw *hw) @@ -1078,10 +1093,12 @@ static void bcm2835_pll_divider_off(struct clk_hw *hw) struct bcm2835_cprman *cprman = divider->cprman; const struct bcm2835_pll_divider_data *data = divider->data; + spin_lock(&cprman->regs_lock); cprman_write(cprman, data->cm_reg, (cprman_read(cprman, data->cm_reg) & ~data->load_mask) | data->hold_mask); cprman_write(cprman, data->a2w_reg, A2W_PLL_CHANNEL_DISABLE); + spin_unlock(&cprman->regs_lock); } static int bcm2835_pll_divider_on(struct clk_hw *hw) @@ -1090,12 +1107,14 @@ static int bcm2835_pll_divider_on(struct clk_hw *hw) struct bcm2835_cprman *cprman = divider->cprman; const struct bcm2835_pll_divider_data *data = divider->data; + spin_lock(&cprman->regs_lock); cprman_write(cprman, data->a2w_reg, cprman_read(cprman, data->a2w_reg) & ~A2W_PLL_CHANNEL_DISABLE); cprman_write(cprman, data->cm_reg, cprman_read(cprman, data->cm_reg) & ~data->hold_mask); + spin_unlock(&cprman->regs_lock); return 0; } @@ -1169,7 +1188,7 @@ static u32 bcm2835_clock_choose_div(struct clk_hw *hw, GENMASK(CM_DIV_FRAC_BITS - data->frac_bits, 0) >> 1; u64 temp = (u64)parent_rate << CM_DIV_FRAC_BITS; u64 rem; - u32 div; + u32 div, mindiv, maxdiv; rem = do_div(temp, rate); div = temp; @@ -1179,10 +1198,23 @@ static u32 bcm2835_clock_choose_div(struct clk_hw *hw, div += unused_frac_mask + 1; div &= ~unused_frac_mask; - /* Clamp to the limits. */ - div = max(div, unused_frac_mask + 1); - div = min_t(u32, div, GENMASK(data->int_bits + CM_DIV_FRAC_BITS - 1, - CM_DIV_FRAC_BITS - data->frac_bits)); + /* different clamping limits apply for a mash clock */ + if (data->is_mash_clock) { + /* clamp to min divider of 2 */ + mindiv = 2 << CM_DIV_FRAC_BITS; + /* clamp to the highest possible integer divider */ + maxdiv = (BIT(data->int_bits) - 1) << CM_DIV_FRAC_BITS; + } else { + /* clamp to min divider of 1 */ + mindiv = 1 << CM_DIV_FRAC_BITS; + /* clamp to the highest possible fractional divider */ + maxdiv = GENMASK(data->int_bits + CM_DIV_FRAC_BITS - 1, + CM_DIV_FRAC_BITS - data->frac_bits); + } + + /* apply the clamping limits */ + div = max_t(u32, div, mindiv); + div = min_t(u32, div, maxdiv); return div; } @@ -1276,9 +1308,26 @@ static int bcm2835_clock_set_rate(struct clk_hw *hw, struct bcm2835_cprman *cprman = clock->cprman; const struct bcm2835_clock_data *data = clock->data; u32 div = bcm2835_clock_choose_div(hw, rate, parent_rate, false); + u32 ctl; + + spin_lock(&cprman->regs_lock); + + /* + * Setting up frac support + * + * In principle it is recommended to stop/start the clock first, + * but as we set CLK_SET_RATE_GATE during registration of the + * clock this requirement should be take care of by the + * clk-framework. + */ + ctl = cprman_read(cprman, data->ctl_reg) & ~CM_FRAC; + ctl |= (div & CM_DIV_FRAC_MASK) ? CM_FRAC : 0; + cprman_write(cprman, data->ctl_reg, ctl); cprman_write(cprman, data->div_reg, div); + spin_unlock(&cprman->regs_lock); + return 0; } @@ -1430,7 +1479,7 @@ bcm2835_register_pll_divider(struct bcm2835_cprman *cprman, divider->div.reg = cprman->regs + data->a2w_reg; divider->div.shift = A2W_PLL_DIV_SHIFT; divider->div.width = A2W_PLL_DIV_BITS; - divider->div.flags = 0; + divider->div.flags = CLK_DIVIDER_MAX_AT_ZERO; divider->div.lock = &cprman->regs_lock; divider->div.hw.init = &init; divider->div.table = NULL; diff --git a/drivers/clk/bcm/clk-cygnus.c b/drivers/clk/bcm/clk-cygnus.c index 3a228b6d4..464fdc4bc 100644 --- a/drivers/clk/bcm/clk-cygnus.c +++ b/drivers/clk/bcm/clk-cygnus.c @@ -268,3 +268,62 @@ static void __init cygnus_asiu_init(struct device_node *node) iproc_asiu_setup(node, asiu_div, asiu_gate, ARRAY_SIZE(asiu_div)); } CLK_OF_DECLARE(cygnus_asiu_clk, "brcm,cygnus-asiu-clk", cygnus_asiu_init); + +/* + * AUDIO PLL VCO frequency parameter table + * + * PLL output frequency = ((ndiv_int + ndiv_frac / 2^20) * + * (parent clock rate / pdiv) + * + * On Cygnus, parent is the 25MHz oscillator + */ +static const struct iproc_pll_vco_param audiopll_vco_params[] = { + /* rate (Hz) ndiv_int ndiv_frac pdiv */ + { 1354750204UL, 54, 199238, 1 }, + { 1769470191UL, 70, 816639, 1 }, +}; + +static const struct iproc_pll_ctrl audiopll = { + .flags = IPROC_CLK_PLL_NEEDS_SW_CFG | IPROC_CLK_PLL_HAS_NDIV_FRAC | + IPROC_CLK_PLL_USER_MODE_ON | IPROC_CLK_PLL_RESET_ACTIVE_LOW, + .reset = RESET_VAL(0x5c, 0, 1), + .dig_filter = DF_VAL(0x48, 0, 3, 6, 4, 3, 3), + .sw_ctrl = SW_CTRL_VAL(0x4, 0), + .ndiv_int = REG_VAL(0x8, 0, 10), + .ndiv_frac = REG_VAL(0x8, 10, 20), + .pdiv = REG_VAL(0x44, 0, 4), + .vco_ctrl = VCO_CTRL_VAL(0x0c, 0x10), + .status = REG_VAL(0x54, 0, 1), + .macro_mode = REG_VAL(0x0, 0, 3), +}; + +static const struct iproc_clk_ctrl audiopll_clk[] = { + [BCM_CYGNUS_AUDIOPLL_CH0] = { + .channel = BCM_CYGNUS_AUDIOPLL_CH0, + .flags = IPROC_CLK_AON | + IPROC_CLK_MCLK_DIV_BY_2, + .enable = ENABLE_VAL(0x14, 8, 10, 9), + .mdiv = REG_VAL(0x14, 0, 8), + }, + [BCM_CYGNUS_AUDIOPLL_CH1] = { + .channel = BCM_CYGNUS_AUDIOPLL_CH1, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x18, 8, 10, 9), + .mdiv = REG_VAL(0x18, 0, 8), + }, + [BCM_CYGNUS_AUDIOPLL_CH2] = { + .channel = BCM_CYGNUS_AUDIOPLL_CH2, + .flags = IPROC_CLK_AON, + .enable = ENABLE_VAL(0x1c, 8, 10, 9), + .mdiv = REG_VAL(0x1c, 0, 8), + }, +}; + +static void __init cygnus_audiopll_clk_init(struct device_node *node) +{ + iproc_pll_clk_setup(node, &audiopll, audiopll_vco_params, + ARRAY_SIZE(audiopll_vco_params), audiopll_clk, + ARRAY_SIZE(audiopll_clk)); +} +CLK_OF_DECLARE(cygnus_audiopll, "brcm,cygnus-audiopll", + cygnus_audiopll_clk_init); diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c index afd5891ac..fd492a5da 100644 --- a/drivers/clk/bcm/clk-iproc-pll.c +++ b/drivers/clk/bcm/clk-iproc-pll.c @@ -25,6 +25,12 @@ #define PLL_VCO_HIGH_SHIFT 19 #define PLL_VCO_LOW_SHIFT 30 +/* + * PLL MACRO_SELECT modes 0 to 5 choose pre-calculated PLL output frequencies + * from a look-up table. Mode 7 allows user to manipulate PLL clock dividers + */ +#define PLL_USER_MODE 7 + /* number of delay loops waiting for PLL to lock */ #define LOCK_DELAY 100 @@ -215,7 +221,10 @@ static void __pll_put_in_reset(struct iproc_pll *pll) const struct iproc_pll_reset_ctrl *reset = &ctrl->reset; val = readl(pll->control_base + reset->offset); - val &= ~(1 << reset->reset_shift | 1 << reset->p_reset_shift); + if (ctrl->flags & IPROC_CLK_PLL_RESET_ACTIVE_LOW) + val |= BIT(reset->reset_shift) | BIT(reset->p_reset_shift); + else + val &= ~(BIT(reset->reset_shift) | BIT(reset->p_reset_shift)); iproc_pll_write(pll, pll->control_base, reset->offset, val); } @@ -236,7 +245,10 @@ static void __pll_bring_out_reset(struct iproc_pll *pll, unsigned int kp, iproc_pll_write(pll, pll->control_base, dig_filter->offset, val); val = readl(pll->control_base + reset->offset); - val |= 1 << reset->reset_shift | 1 << reset->p_reset_shift; + if (ctrl->flags & IPROC_CLK_PLL_RESET_ACTIVE_LOW) + val &= ~(BIT(reset->reset_shift) | BIT(reset->p_reset_shift)); + else + val |= BIT(reset->reset_shift) | BIT(reset->p_reset_shift); iproc_pll_write(pll, pll->control_base, reset->offset, val); } @@ -292,6 +304,16 @@ static int pll_set_rate(struct iproc_clk *clk, unsigned int rate_index, /* put PLL in reset */ __pll_put_in_reset(pll); + /* set PLL in user mode before modifying PLL controls */ + if (ctrl->flags & IPROC_CLK_PLL_USER_MODE_ON) { + val = readl(pll->control_base + ctrl->macro_mode.offset); + val &= ~(bit_mask(ctrl->macro_mode.width) << + ctrl->macro_mode.shift); + val |= PLL_USER_MODE << ctrl->macro_mode.shift; + iproc_pll_write(pll, pll->control_base, + ctrl->macro_mode.offset, val); + } + iproc_pll_write(pll, pll->control_base, ctrl->vco_ctrl.u_offset, 0); val = readl(pll->control_base + ctrl->vco_ctrl.l_offset); @@ -505,7 +527,10 @@ static unsigned long iproc_clk_recalc_rate(struct clk_hw *hw, if (mdiv == 0) mdiv = 256; - clk->rate = parent_rate / mdiv; + if (ctrl->flags & IPROC_CLK_MCLK_DIV_BY_2) + clk->rate = parent_rate / (mdiv * 2); + else + clk->rate = parent_rate / mdiv; return clk->rate; } @@ -543,7 +568,10 @@ static int iproc_clk_set_rate(struct clk_hw *hw, unsigned long rate, if (rate == 0 || parent_rate == 0) return -EINVAL; - div = DIV_ROUND_UP(parent_rate, rate); + if (ctrl->flags & IPROC_CLK_MCLK_DIV_BY_2) + div = DIV_ROUND_UP(parent_rate, rate * 2); + else + div = DIV_ROUND_UP(parent_rate, rate); if (div > 256) return -EINVAL; @@ -555,7 +583,10 @@ static int iproc_clk_set_rate(struct clk_hw *hw, unsigned long rate, val |= div << ctrl->mdiv.shift; } iproc_pll_write(pll, pll->control_base, ctrl->mdiv.offset, val); - clk->rate = parent_rate / div; + if (ctrl->flags & IPROC_CLK_MCLK_DIV_BY_2) + clk->rate = parent_rate / (div * 2); + else + clk->rate = parent_rate / div; return 0; } diff --git a/drivers/clk/bcm/clk-iproc.h b/drivers/clk/bcm/clk-iproc.h index 8988de70a..2148b4ea9 100644 --- a/drivers/clk/bcm/clk-iproc.h +++ b/drivers/clk/bcm/clk-iproc.h @@ -61,6 +61,26 @@ #define IPROC_CLK_PLL_SPLIT_STAT_CTRL BIT(6) /* + * Some PLLs have an additional divide by 2 in master clock calculation; + * MCLK = VCO_freq / (Mdiv * 2). Identify this to let the driver know + * of modified calculations + */ +#define IPROC_CLK_MCLK_DIV_BY_2 BIT(7) + +/* + * Some PLLs provide a look up table for the leaf clock frequencies and + * auto calculates VCO frequency parameters based on the provided leaf + * clock frequencies. They have a user mode that allows the divider + * controls to be determined by the user + */ +#define IPROC_CLK_PLL_USER_MODE_ON BIT(8) + +/* + * Some PLLs have an active low reset + */ +#define IPROC_CLK_PLL_RESET_ACTIVE_LOW BIT(9) + +/* * Parameters for VCO frequency configuration * * VCO frequency = @@ -149,6 +169,7 @@ struct iproc_pll_ctrl { struct iproc_clk_reg_op pdiv; struct iproc_pll_vco_ctrl vco_ctrl; struct iproc_clk_reg_op status; + struct iproc_clk_reg_op macro_mode; }; /* @@ -183,16 +204,16 @@ struct iproc_asiu_div { unsigned int low_width; }; -void __init iproc_armpll_setup(struct device_node *node); -void __init iproc_pll_clk_setup(struct device_node *node, - const struct iproc_pll_ctrl *pll_ctrl, - const struct iproc_pll_vco_param *vco, - unsigned int num_vco_entries, - const struct iproc_clk_ctrl *clk_ctrl, - unsigned int num_clks); -void __init iproc_asiu_setup(struct device_node *node, - const struct iproc_asiu_div *div, - const struct iproc_asiu_gate *gate, - unsigned int num_clks); +void iproc_armpll_setup(struct device_node *node); +void iproc_pll_clk_setup(struct device_node *node, + const struct iproc_pll_ctrl *pll_ctrl, + const struct iproc_pll_vco_param *vco, + unsigned int num_vco_entries, + const struct iproc_clk_ctrl *clk_ctrl, + unsigned int num_clks); +void iproc_asiu_setup(struct device_node *node, + const struct iproc_asiu_div *div, + const struct iproc_asiu_gate *gate, + unsigned int num_clks); #endif /* _CLK_IPROC_H */ |