From ab525dcccfd6ad11a0313fd58222b72cee4ca7ad Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 16 Jan 2018 10:50:34 -0200 Subject: clk: Print the clock name and warning cause When getting the clock related warnings, it is useful to know what is the clock name that is causing the problem and the cause of the problem. Add the clock name and the the warning cause to the log, so that the output becomes clearer like this: [ 2.383969] ------------[ cut here ]------------ [ 2.388720] WARNING: CPU: 0 PID: 1 at drivers/clk/clk.c:814 clk_core_disable+0xd4/0xf8 [ 2.396658] uart4_ipg_gate already disabled Signed-off-by: Fabio Estevam Reviewed-by: Geert Uytterhoeven Signed-off-by: Stephen Boyd --- drivers/clk/clk.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0f686a9dac3e..c95dc09a1ca7 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -548,7 +548,8 @@ static void clk_core_rate_unprotect(struct clk_core *core) if (!core) return; - if (WARN_ON(core->protect_count == 0)) + if (WARN(core->protect_count == 0, + "%s already unprotected\n", core->name)) return; if (--core->protect_count > 0) @@ -681,16 +682,18 @@ static void clk_core_unprepare(struct clk_core *core) if (!core) return; - if (WARN_ON(core->prepare_count == 0)) + if (WARN(core->prepare_count == 0, + "%s already unprepared\n", core->name)) return; - if (WARN_ON(core->prepare_count == 1 && core->flags & CLK_IS_CRITICAL)) + if (WARN(core->prepare_count == 1 && core->flags & CLK_IS_CRITICAL, + "Unpreparing critical %s\n", core->name)) return; if (--core->prepare_count > 0) return; - WARN_ON(core->enable_count > 0); + WARN(core->enable_count > 0, "Unpreparing enabled %s\n", core->name); trace_clk_unprepare(core); @@ -808,10 +811,11 @@ static void clk_core_disable(struct clk_core *core) if (!core) return; - if (WARN_ON(core->enable_count == 0)) + if (WARN(core->enable_count == 0, "%s already disabled\n", core->name)) return; - if (WARN_ON(core->enable_count == 1 && core->flags & CLK_IS_CRITICAL)) + if (WARN(core->enable_count == 1 && core->flags & CLK_IS_CRITICAL, + "Disabling critical %s\n", core->name)) return; if (--core->enable_count > 0) @@ -866,7 +870,8 @@ static int clk_core_enable(struct clk_core *core) if (!core) return 0; - if (WARN_ON(core->prepare_count == 0)) + if (WARN(core->prepare_count == 0, + "Enabling unprepared %s\n", core->name)) return -ESHUTDOWN; if (core->enable_count == 0) { -- cgit v1.2.3 From bdc3bbdd40ba90ce2953af01007036191572720b Mon Sep 17 00:00:00 2001 From: Amit Nischal Date: Thu, 8 Mar 2018 12:48:12 +0530 Subject: clk: qcom: Clear hardware clock control bit of RCG For upcoming targets like sdm845, POR value of the hardware clock control bit is set for most of root clocks which needs to be cleared for software to be able to control. For older targets like MSM8996, this bit is reserved bit and having POR value as 0 so this patch will work for the older targets too. So update the configuration mask to take care of the same to clear hardware clock control bit. Signed-off-by: Amit Nischal Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-rcg2.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index bbeaf9c09dbb..e63db103f24e 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2013, 2018, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -42,6 +42,7 @@ #define CFG_MODE_SHIFT 12 #define CFG_MODE_MASK (0x3 << CFG_MODE_SHIFT) #define CFG_MODE_DUAL_EDGE (0x2 << CFG_MODE_SHIFT) +#define CFG_HW_CLK_CTRL_MASK BIT(20) #define M_REG 0x8 #define N_REG 0xc @@ -276,7 +277,7 @@ static int clk_rcg2_configure(struct clk_rcg2 *rcg, const struct freq_tbl *f) } mask = BIT(rcg->hid_width) - 1; - mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK; + mask |= CFG_SRC_SEL_MASK | CFG_MODE_MASK | CFG_HW_CLK_CTRL_MASK; cfg = f->pre_div << CFG_SRC_DIV_SHIFT; cfg |= rcg->parent_map[index].cfg << CFG_SRC_SEL_SHIFT; if (rcg->mnd_width && f->n && (f->m != f->n)) -- cgit v1.2.3 From 687d7a0caa7736de53e3c5efdef6b5343d1470ac Mon Sep 17 00:00:00 2001 From: Amit Nischal Date: Thu, 8 Mar 2018 12:48:14 +0530 Subject: clk: qcom: Add support for controlling Fabia PLL Fabia PLL is a Digital Frequency Locked Loop (DFLL) clock generator which has a wide range of frequency output. It supports dynamic updating of the output frequency ("frequency slewing") without need to turn off the PLL before configuration. Add support for initial configuration and programming sequence to control fabia PLLs. Signed-off-by: Amit Nischal [sboyd: Shorten code a little] Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-alpha-pll.c | 301 ++++++++++++++++++++++++++++++++++++++- drivers/clk/qcom/clk-alpha-pll.h | 18 ++- 2 files changed, 311 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 6d04cd96482a..9722b701fbdb 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -58,6 +58,8 @@ #define PLL_TEST_CTL(p) ((p)->offset + (p)->regs[PLL_OFF_TEST_CTL]) #define PLL_TEST_CTL_U(p) ((p)->offset + (p)->regs[PLL_OFF_TEST_CTL_U]) #define PLL_STATUS(p) ((p)->offset + (p)->regs[PLL_OFF_STATUS]) +#define PLL_OPMODE(p) ((p)->offset + (p)->regs[PLL_OFF_OPMODE]) +#define PLL_FRAC(p) ((p)->offset + (p)->regs[PLL_OFF_FRAC]) const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { [CLK_ALPHA_PLL_TYPE_DEFAULT] = { @@ -90,6 +92,18 @@ const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { [PLL_OFF_TEST_CTL] = 0x1c, [PLL_OFF_STATUS] = 0x24, }, + [CLK_ALPHA_PLL_TYPE_FABIA] = { + [PLL_OFF_L_VAL] = 0x04, + [PLL_OFF_USER_CTL] = 0x0c, + [PLL_OFF_USER_CTL_U] = 0x10, + [PLL_OFF_CONFIG_CTL] = 0x14, + [PLL_OFF_CONFIG_CTL_U] = 0x18, + [PLL_OFF_TEST_CTL] = 0x1c, + [PLL_OFF_TEST_CTL_U] = 0x20, + [PLL_OFF_STATUS] = 0x24, + [PLL_OFF_OPMODE] = 0x2c, + [PLL_OFF_FRAC] = 0x38, + }, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); @@ -108,6 +122,12 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); #define PLL_HUAYRA_N_MASK 0xff #define PLL_HUAYRA_ALPHA_WIDTH 16 +#define FABIA_OPMODE_STANDBY 0x0 +#define FABIA_OPMODE_RUN 0x1 + +#define FABIA_PLL_OUT_MASK 0x7 +#define FABIA_PLL_RATE_MARGIN 500 + #define pll_alpha_width(p) \ ((PLL_ALPHA_VAL_U(p) - PLL_ALPHA_VAL(p) == 4) ? \ ALPHA_REG_BITWIDTH : ALPHA_REG_16BIT_WIDTH) @@ -441,16 +461,12 @@ clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) return alpha_pll_calc_rate(prate, l, a, alpha_width); } -static int clk_alpha_pll_update_latch(struct clk_alpha_pll *pll, - int (*is_enabled)(struct clk_hw *)) + +static int __clk_alpha_pll_update_latch(struct clk_alpha_pll *pll) { int ret; u32 mode; - if (!is_enabled(&pll->clkr.hw) || - !(pll->flags & SUPPORTS_DYNAMIC_UPDATE)) - return 0; - regmap_read(pll->clkr.regmap, PLL_MODE(pll), &mode); /* Latch the input to the PLL */ @@ -489,6 +505,16 @@ static int clk_alpha_pll_update_latch(struct clk_alpha_pll *pll, return 0; } +static int clk_alpha_pll_update_latch(struct clk_alpha_pll *pll, + int (*is_enabled)(struct clk_hw *)) +{ + if (!is_enabled(&pll->clkr.hw) || + !(pll->flags & SUPPORTS_DYNAMIC_UPDATE)) + return 0; + + return __clk_alpha_pll_update_latch(pll); +} + static int __clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long prate, int (*is_enabled)(struct clk_hw *)) @@ -832,3 +858,264 @@ const struct clk_ops clk_alpha_pll_postdiv_ro_ops = { .recalc_rate = clk_alpha_pll_postdiv_recalc_rate, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_ro_ops); + +void clk_fabia_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, + const struct alpha_pll_config *config) +{ + u32 val, mask; + + if (config->l) + regmap_write(regmap, PLL_L_VAL(pll), config->l); + + if (config->alpha) + regmap_write(regmap, PLL_FRAC(pll), config->alpha); + + if (config->config_ctl_val) + regmap_write(regmap, PLL_CONFIG_CTL(pll), + config->config_ctl_val); + + if (config->post_div_mask) { + mask = config->post_div_mask; + val = config->post_div_val; + regmap_update_bits(regmap, PLL_USER_CTL(pll), mask, val); + } + + regmap_update_bits(regmap, PLL_MODE(pll), PLL_UPDATE_BYPASS, + PLL_UPDATE_BYPASS); + + regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N, PLL_RESET_N); +} + +static int alpha_pll_fabia_enable(struct clk_hw *hw) +{ + int ret; + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + u32 val, opmode_val; + struct regmap *regmap = pll->clkr.regmap; + + ret = regmap_read(regmap, PLL_MODE(pll), &val); + if (ret) + return ret; + + /* If in FSM mode, just vote for it */ + if (val & PLL_VOTE_FSM_ENA) { + ret = clk_enable_regmap(hw); + if (ret) + return ret; + return wait_for_pll_enable_active(pll); + } + + ret = regmap_read(regmap, PLL_OPMODE(pll), &opmode_val); + if (ret) + return ret; + + /* Skip If PLL is already running */ + if ((opmode_val & FABIA_OPMODE_RUN) && (val & PLL_OUTCTRL)) + return 0; + + ret = regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0); + if (ret) + return ret; + + ret = regmap_write(regmap, PLL_OPMODE(pll), FABIA_OPMODE_STANDBY); + if (ret) + return ret; + + ret = regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N, + PLL_RESET_N); + if (ret) + return ret; + + ret = regmap_write(regmap, PLL_OPMODE(pll), FABIA_OPMODE_RUN); + if (ret) + return ret; + + ret = wait_for_pll_enable_lock(pll); + if (ret) + return ret; + + ret = regmap_update_bits(regmap, PLL_USER_CTL(pll), + FABIA_PLL_OUT_MASK, FABIA_PLL_OUT_MASK); + if (ret) + return ret; + + return regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, + PLL_OUTCTRL); +} + +static void alpha_pll_fabia_disable(struct clk_hw *hw) +{ + int ret; + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + u32 val; + struct regmap *regmap = pll->clkr.regmap; + + ret = regmap_read(regmap, PLL_MODE(pll), &val); + if (ret) + return; + + /* If in FSM mode, just unvote it */ + if (val & PLL_FSM_ENA) { + clk_disable_regmap(hw); + return; + } + + ret = regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0); + if (ret) + return; + + /* Disable main outputs */ + ret = regmap_update_bits(regmap, PLL_USER_CTL(pll), FABIA_PLL_OUT_MASK, + 0); + if (ret) + return; + + /* Place the PLL in STANDBY */ + regmap_write(regmap, PLL_OPMODE(pll), FABIA_OPMODE_STANDBY); +} + +static unsigned long alpha_pll_fabia_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + u32 l, frac, alpha_width = pll_alpha_width(pll); + + regmap_read(pll->clkr.regmap, PLL_L_VAL(pll), &l); + regmap_read(pll->clkr.regmap, PLL_FRAC(pll), &frac); + + return alpha_pll_calc_rate(parent_rate, l, frac, alpha_width); +} + +static int alpha_pll_fabia_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long prate) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + u32 val, l, alpha_width = pll_alpha_width(pll); + u64 a; + unsigned long rrate; + int ret = 0; + + ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); + if (ret) + return ret; + + rrate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width); + + /* + * Due to limited number of bits for fractional rate programming, the + * rounded up rate could be marginally higher than the requested rate. + */ + if (rrate > (rate + FABIA_PLL_RATE_MARGIN) || rrate < rate) { + pr_err("Call set rate on the PLL with rounded rates!\n"); + return -EINVAL; + } + + regmap_write(pll->clkr.regmap, PLL_L_VAL(pll), l); + regmap_write(pll->clkr.regmap, PLL_FRAC(pll), a); + + return __clk_alpha_pll_update_latch(pll); +} + +const struct clk_ops clk_alpha_pll_fabia_ops = { + .enable = alpha_pll_fabia_enable, + .disable = alpha_pll_fabia_disable, + .is_enabled = clk_alpha_pll_is_enabled, + .set_rate = alpha_pll_fabia_set_rate, + .recalc_rate = alpha_pll_fabia_recalc_rate, + .round_rate = clk_alpha_pll_round_rate, +}; +EXPORT_SYMBOL_GPL(clk_alpha_pll_fabia_ops); + +const struct clk_ops clk_alpha_pll_fixed_fabia_ops = { + .enable = alpha_pll_fabia_enable, + .disable = alpha_pll_fabia_disable, + .is_enabled = clk_alpha_pll_is_enabled, + .recalc_rate = alpha_pll_fabia_recalc_rate, + .round_rate = clk_alpha_pll_round_rate, +}; +EXPORT_SYMBOL_GPL(clk_alpha_pll_fixed_fabia_ops); + +static unsigned long clk_alpha_pll_postdiv_fabia_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw); + u32 i, div = 1, val; + int ret; + + if (!pll->post_div_table) { + pr_err("Missing the post_div_table for the PLL\n"); + return -EINVAL; + } + + ret = regmap_read(pll->clkr.regmap, PLL_USER_CTL(pll), &val); + if (ret) + return ret; + + val >>= pll->post_div_shift; + val &= BIT(pll->width) - 1; + + for (i = 0; i < pll->num_post_div; i++) { + if (pll->post_div_table[i].val == val) { + div = pll->post_div_table[i].div; + break; + } + } + + return (parent_rate / div); +} + +static long clk_alpha_pll_postdiv_fabia_round_rate(struct clk_hw *hw, + unsigned long rate, unsigned long *prate) +{ + struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw); + + if (!pll->post_div_table) { + pr_err("Missing the post_div_table for the PLL\n"); + return -EINVAL; + } + + return divider_round_rate(hw, rate, prate, pll->post_div_table, + pll->width, CLK_DIVIDER_ROUND_CLOSEST); +} + +static int clk_alpha_pll_postdiv_fabia_set_rate(struct clk_hw *hw, + unsigned long rate, unsigned long parent_rate) +{ + struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw); + int i, val = 0, div, ret; + + /* + * If the PLL is in FSM mode, then treat set_rate callback as a + * no-operation. + */ + ret = regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); + if (ret) + return ret; + + if (val & PLL_VOTE_FSM_ENA) + return 0; + + if (!pll->post_div_table) { + pr_err("Missing the post_div_table for the PLL\n"); + return -EINVAL; + } + + div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); + for (i = 0; i < pll->num_post_div; i++) { + if (pll->post_div_table[i].div == div) { + val = pll->post_div_table[i].val; + break; + } + } + + return regmap_update_bits(pll->clkr.regmap, PLL_USER_CTL(pll), + (BIT(pll->width) - 1) << pll->post_div_shift, + val << pll->post_div_shift); +} + +const struct clk_ops clk_alpha_pll_postdiv_fabia_ops = { + .recalc_rate = clk_alpha_pll_postdiv_fabia_recalc_rate, + .round_rate = clk_alpha_pll_postdiv_fabia_round_rate, + .set_rate = clk_alpha_pll_postdiv_fabia_set_rate, +}; +EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_fabia_ops); diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index 7593e8a56cf2..f981b486c468 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2015, 2018, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -22,6 +22,7 @@ enum { CLK_ALPHA_PLL_TYPE_DEFAULT, CLK_ALPHA_PLL_TYPE_HUAYRA, CLK_ALPHA_PLL_TYPE_BRAMMO, + CLK_ALPHA_PLL_TYPE_FABIA, CLK_ALPHA_PLL_TYPE_MAX, }; @@ -36,6 +37,8 @@ enum { PLL_OFF_TEST_CTL, PLL_OFF_TEST_CTL_U, PLL_OFF_STATUS, + PLL_OFF_OPMODE, + PLL_OFF_FRAC, PLL_OFF_MAX_REGS }; @@ -73,6 +76,10 @@ struct clk_alpha_pll { * @offset: base address of registers * @regs: alpha pll register map (see @clk_alpha_pll_regs) * @width: width of post-divider + * @post_div_shift: shift to differentiate between odd & even post-divider + * @post_div_table: table with PLL odd and even post-divider settings + * @num_post_div: Number of PLL post-divider settings + * * @clkr: regmap clock handle */ struct clk_alpha_pll_postdiv { @@ -81,6 +88,9 @@ struct clk_alpha_pll_postdiv { const u8 *regs; struct clk_regmap clkr; + int post_div_shift; + const struct clk_div_table *post_div_table; + size_t num_post_div; }; struct alpha_pll_config { @@ -109,7 +119,13 @@ extern const struct clk_ops clk_alpha_pll_postdiv_ops; extern const struct clk_ops clk_alpha_pll_huayra_ops; extern const struct clk_ops clk_alpha_pll_postdiv_ro_ops; +extern const struct clk_ops clk_alpha_pll_fabia_ops; +extern const struct clk_ops clk_alpha_pll_fixed_fabia_ops; +extern const struct clk_ops clk_alpha_pll_postdiv_fabia_ops; + void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); +void clk_fabia_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, + const struct alpha_pll_config *config); #endif -- cgit v1.2.3 From 1af897dff60aff3f421f12c5b9a3ebadc9e2d8a3 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Fri, 6 Apr 2018 09:50:45 +0200 Subject: clk: spear: fix WDT clock definition on SPEAr600 There is no SPEAr600 device named "wdt". Instead, the description of the WDT (watchdog) was recently added to the Device Tree, and the device name is "fc880000.wdt", so we should associate the WDT fixed rate clock to this device name. Signed-off-by: Quentin Schulz Acked-by: Viresh Kumar Signed-off-by: Stephen Boyd --- drivers/clk/spear/spear6xx_clock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/clk/spear/spear6xx_clock.c b/drivers/clk/spear/spear6xx_clock.c index f911d9f77763..47810be7f15c 100644 --- a/drivers/clk/spear/spear6xx_clock.c +++ b/drivers/clk/spear/spear6xx_clock.c @@ -147,7 +147,7 @@ void __init spear6xx_clk_init(void __iomem *misc_base) clk = clk_register_fixed_factor(NULL, "wdt_clk", "osc_30m_clk", 0, 1, 1); - clk_register_clkdev(clk, NULL, "wdt"); + clk_register_clkdev(clk, NULL, "fc880000.wdt"); /* clock derived from pll1 clk */ clk = clk_register_fixed_factor(NULL, "cpu_clk", "pll1_clk", -- cgit v1.2.3 From 3495e29565986aac23efa5d5153e424e15ed2a3c Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 26 Mar 2018 23:08:57 +0530 Subject: clk: actions: Add common clock driver support Add support for Actions Semi common clock driver with generic structures and interface functions. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Stephen Boyd --- drivers/clk/Kconfig | 1 + drivers/clk/Makefile | 1 + drivers/clk/actions/Kconfig | 4 ++ drivers/clk/actions/Makefile | 3 ++ drivers/clk/actions/owl-common.c | 89 ++++++++++++++++++++++++++++++++++++++++ drivers/clk/actions/owl-common.h | 41 ++++++++++++++++++ 6 files changed, 139 insertions(+) create mode 100644 drivers/clk/actions/Kconfig create mode 100644 drivers/clk/actions/Makefile create mode 100644 drivers/clk/actions/owl-common.c create mode 100644 drivers/clk/actions/owl-common.h (limited to 'drivers') diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 98ce9fc6e6c0..6313a4f4327a 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -238,6 +238,7 @@ config COMMON_CLK_VC5 This driver supports the IDT VersaClock 5 and VersaClock 6 programmable clock generators. +source "drivers/clk/actions/Kconfig" source "drivers/clk/bcm/Kconfig" source "drivers/clk/hisilicon/Kconfig" source "drivers/clk/imgtec/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 71ec41e6364f..b7909df532ed 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_XGENE) += clk-xgene.o # please keep this section sorted lexicographically by directory path name +obj-y += actions/ obj-$(CONFIG_COMMON_CLK_AT91) += at91/ obj-$(CONFIG_ARCH_ARTPEC) += axis/ obj-$(CONFIG_ARC_PLAT_AXS10X) += axs10x/ diff --git a/drivers/clk/actions/Kconfig b/drivers/clk/actions/Kconfig new file mode 100644 index 000000000000..13a3e5083d43 --- /dev/null +++ b/drivers/clk/actions/Kconfig @@ -0,0 +1,4 @@ +config CLK_ACTIONS + bool "Clock driver for Actions Semi SoCs" + depends on ARCH_ACTIONS || COMPILE_TEST + default ARCH_ACTIONS diff --git a/drivers/clk/actions/Makefile b/drivers/clk/actions/Makefile new file mode 100644 index 000000000000..64a50fc2d335 --- /dev/null +++ b/drivers/clk/actions/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_CLK_ACTIONS) += clk-owl.o + +clk-owl-y += owl-common.o diff --git a/drivers/clk/actions/owl-common.c b/drivers/clk/actions/owl-common.c new file mode 100644 index 000000000000..61c1071b5180 --- /dev/null +++ b/drivers/clk/actions/owl-common.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL common clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#include +#include +#include +#include + +#include "owl-common.h" + +static const struct regmap_config owl_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x00cc, + .fast_io = true, +}; + +static void owl_clk_set_regmap(const struct owl_clk_desc *desc, + struct regmap *regmap) +{ + int i; + struct owl_clk_common *clks; + + for (i = 0; i < desc->num_clks; i++) { + clks = desc->clks[i]; + if (!clks) + continue; + + clks->regmap = regmap; + } +} + +int owl_clk_regmap_init(struct platform_device *pdev, + const struct owl_clk_desc *desc) +{ + void __iomem *base; + struct regmap *regmap; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(&pdev->dev, base, &owl_regmap_config); + if (IS_ERR(regmap)) { + pr_err("failed to init regmap\n"); + return PTR_ERR(regmap); + } + + owl_clk_set_regmap(desc, regmap); + + return 0; +} + +int owl_clk_probe(struct device *dev, struct clk_hw_onecell_data *hw_clks) +{ + int i, ret; + struct clk_hw *hw; + + for (i = 0; i < hw_clks->num; i++) { + + hw = hw_clks->hws[i]; + + if (IS_ERR_OR_NULL(hw)) + continue; + + ret = devm_clk_hw_register(dev, hw); + if (ret) { + dev_err(dev, "Couldn't register clock %d - %s\n", + i, hw->init->name); + return ret; + } + } + + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get, hw_clks); + if (ret) + dev_err(dev, "Failed to add clock provider\n"); + + return ret; +} diff --git a/drivers/clk/actions/owl-common.h b/drivers/clk/actions/owl-common.h new file mode 100644 index 000000000000..4fd726ec54a6 --- /dev/null +++ b/drivers/clk/actions/owl-common.h @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL common clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#ifndef _OWL_COMMON_H_ +#define _OWL_COMMON_H_ + +#include +#include +#include + +struct device_node; + +struct owl_clk_common { + struct regmap *regmap; + struct clk_hw hw; +}; + +struct owl_clk_desc { + struct owl_clk_common **clks; + unsigned long num_clks; + struct clk_hw_onecell_data *hw_clks; +}; + +static inline struct owl_clk_common * + hw_to_owl_clk_common(const struct clk_hw *hw) +{ + return container_of(hw, struct owl_clk_common, hw); +} + +int owl_clk_regmap_init(struct platform_device *pdev, + const struct owl_clk_desc *desc); +int owl_clk_probe(struct device *dev, struct clk_hw_onecell_data *hw_clks); + +#endif /* _OWL_COMMON_H_ */ -- cgit v1.2.3 From 103c5e1b1026e4b3b025bbec9f4c586708269f3e Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 26 Mar 2018 23:08:58 +0530 Subject: clk: actions: Add gate clock support Add support for Actions Semi gate clock together with helper functions to be used in composite clock. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Stephen Boyd --- drivers/clk/actions/Makefile | 1 + drivers/clk/actions/owl-gate.c | 77 ++++++++++++++++++++++++++++++++++++++++++ drivers/clk/actions/owl-gate.h | 73 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 drivers/clk/actions/owl-gate.c create mode 100644 drivers/clk/actions/owl-gate.h (limited to 'drivers') diff --git a/drivers/clk/actions/Makefile b/drivers/clk/actions/Makefile index 64a50fc2d335..1f0917872c9d 100644 --- a/drivers/clk/actions/Makefile +++ b/drivers/clk/actions/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_CLK_ACTIONS) += clk-owl.o clk-owl-y += owl-common.o +clk-owl-y += owl-gate.o diff --git a/drivers/clk/actions/owl-gate.c b/drivers/clk/actions/owl-gate.c new file mode 100644 index 000000000000..f11500ba46a7 --- /dev/null +++ b/drivers/clk/actions/owl-gate.c @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL gate clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#include +#include + +#include "owl-gate.h" + +void owl_gate_set(const struct owl_clk_common *common, + const struct owl_gate_hw *gate_hw, bool enable) +{ + int set = gate_hw->gate_flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; + u32 reg; + + set ^= enable; + + regmap_read(common->regmap, gate_hw->reg, ®); + + if (set) + reg |= BIT(gate_hw->bit_idx); + else + reg &= ~BIT(gate_hw->bit_idx); + + regmap_write(common->regmap, gate_hw->reg, reg); +} + +static void owl_gate_disable(struct clk_hw *hw) +{ + struct owl_gate *gate = hw_to_owl_gate(hw); + struct owl_clk_common *common = &gate->common; + + owl_gate_set(common, &gate->gate_hw, false); +} + +static int owl_gate_enable(struct clk_hw *hw) +{ + struct owl_gate *gate = hw_to_owl_gate(hw); + struct owl_clk_common *common = &gate->common; + + owl_gate_set(common, &gate->gate_hw, true); + + return 0; +} + +int owl_gate_clk_is_enabled(const struct owl_clk_common *common, + const struct owl_gate_hw *gate_hw) +{ + u32 reg; + + regmap_read(common->regmap, gate_hw->reg, ®); + + if (gate_hw->gate_flags & CLK_GATE_SET_TO_DISABLE) + reg ^= BIT(gate_hw->bit_idx); + + return !!(reg & BIT(gate_hw->bit_idx)); +} + +static int owl_gate_is_enabled(struct clk_hw *hw) +{ + struct owl_gate *gate = hw_to_owl_gate(hw); + struct owl_clk_common *common = &gate->common; + + return owl_gate_clk_is_enabled(common, &gate->gate_hw); +} + +const struct clk_ops owl_gate_ops = { + .disable = owl_gate_disable, + .enable = owl_gate_enable, + .is_enabled = owl_gate_is_enabled, +}; diff --git a/drivers/clk/actions/owl-gate.h b/drivers/clk/actions/owl-gate.h new file mode 100644 index 000000000000..c2d61ceebce2 --- /dev/null +++ b/drivers/clk/actions/owl-gate.h @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL gate clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#ifndef _OWL_GATE_H_ +#define _OWL_GATE_H_ + +#include "owl-common.h" + +struct owl_gate_hw { + u32 reg; + u8 bit_idx; + u8 gate_flags; +}; + +struct owl_gate { + struct owl_gate_hw gate_hw; + struct owl_clk_common common; +}; + +#define OWL_GATE_HW(_reg, _bit_idx, _gate_flags) \ + { \ + .reg = _reg, \ + .bit_idx = _bit_idx, \ + .gate_flags = _gate_flags, \ + } + +#define OWL_GATE(_struct, _name, _parent, _reg, \ + _bit_idx, _gate_flags, _flags) \ + struct owl_gate _struct = { \ + .gate_hw = OWL_GATE_HW(_reg, _bit_idx, _gate_flags), \ + .common = { \ + .regmap = NULL, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &owl_gate_ops, \ + _flags), \ + } \ + } \ + +#define OWL_GATE_NO_PARENT(_struct, _name, _reg, \ + _bit_idx, _gate_flags, _flags) \ + struct owl_gate _struct = { \ + .gate_hw = OWL_GATE_HW(_reg, _bit_idx, _gate_flags), \ + .common = { \ + .regmap = NULL, \ + .hw.init = CLK_HW_INIT_NO_PARENT(_name, \ + &owl_gate_ops, \ + _flags), \ + }, \ + } \ + +static inline struct owl_gate *hw_to_owl_gate(const struct clk_hw *hw) +{ + struct owl_clk_common *common = hw_to_owl_clk_common(hw); + + return container_of(common, struct owl_gate, common); +} + +void owl_gate_set(const struct owl_clk_common *common, + const struct owl_gate_hw *gate_hw, bool enable); +int owl_gate_clk_is_enabled(const struct owl_clk_common *common, + const struct owl_gate_hw *gate_hw); + +extern const struct clk_ops owl_gate_ops; + +#endif /* _OWL_GATE_H_ */ -- cgit v1.2.3 From a83387729af8a7ded74e7155efb9468cefe081bd Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 26 Mar 2018 23:08:59 +0530 Subject: clk: actions: Add mux clock support Add support for Actions Semi mux clock together with helper functions to be used in composite clock. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Stephen Boyd --- drivers/clk/actions/Makefile | 1 + drivers/clk/actions/owl-mux.c | 60 ++++++++++++++++++++++++++++++++++++++++++ drivers/clk/actions/owl-mux.h | 61 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 drivers/clk/actions/owl-mux.c create mode 100644 drivers/clk/actions/owl-mux.h (limited to 'drivers') diff --git a/drivers/clk/actions/Makefile b/drivers/clk/actions/Makefile index 1f0917872c9d..2d4aa8f35d90 100644 --- a/drivers/clk/actions/Makefile +++ b/drivers/clk/actions/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_CLK_ACTIONS) += clk-owl.o clk-owl-y += owl-common.o clk-owl-y += owl-gate.o +clk-owl-y += owl-mux.o diff --git a/drivers/clk/actions/owl-mux.c b/drivers/clk/actions/owl-mux.c new file mode 100644 index 000000000000..f9c6cf2540e4 --- /dev/null +++ b/drivers/clk/actions/owl-mux.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL mux clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#include +#include + +#include "owl-mux.h" + +u8 owl_mux_helper_get_parent(const struct owl_clk_common *common, + const struct owl_mux_hw *mux_hw) +{ + u32 reg; + u8 parent; + + regmap_read(common->regmap, mux_hw->reg, ®); + parent = reg >> mux_hw->shift; + parent &= BIT(mux_hw->width) - 1; + + return parent; +} + +static u8 owl_mux_get_parent(struct clk_hw *hw) +{ + struct owl_mux *mux = hw_to_owl_mux(hw); + + return owl_mux_helper_get_parent(&mux->common, &mux->mux_hw); +} + +int owl_mux_helper_set_parent(const struct owl_clk_common *common, + struct owl_mux_hw *mux_hw, u8 index) +{ + u32 reg; + + regmap_read(common->regmap, mux_hw->reg, ®); + reg &= ~GENMASK(mux_hw->width + mux_hw->shift - 1, mux_hw->shift); + regmap_write(common->regmap, mux_hw->reg, + reg | (index << mux_hw->shift)); + + return 0; +} + +static int owl_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct owl_mux *mux = hw_to_owl_mux(hw); + + return owl_mux_helper_set_parent(&mux->common, &mux->mux_hw, index); +} + +const struct clk_ops owl_mux_ops = { + .get_parent = owl_mux_get_parent, + .set_parent = owl_mux_set_parent, + .determine_rate = __clk_mux_determine_rate, +}; diff --git a/drivers/clk/actions/owl-mux.h b/drivers/clk/actions/owl-mux.h new file mode 100644 index 000000000000..834284c8c3ae --- /dev/null +++ b/drivers/clk/actions/owl-mux.h @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL mux clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#ifndef _OWL_MUX_H_ +#define _OWL_MUX_H_ + +#include "owl-common.h" + +struct owl_mux_hw { + u32 reg; + u8 shift; + u8 width; +}; + +struct owl_mux { + struct owl_mux_hw mux_hw; + struct owl_clk_common common; +}; + +#define OWL_MUX_HW(_reg, _shift, _width) \ + { \ + .reg = _reg, \ + .shift = _shift, \ + .width = _width, \ + } + +#define OWL_MUX(_struct, _name, _parents, _reg, \ + _shift, _width, _flags) \ + struct owl_mux _struct = { \ + .mux_hw = OWL_MUX_HW(_reg, _shift, _width), \ + .common = { \ + .regmap = NULL, \ + .hw.init = CLK_HW_INIT_PARENTS(_name, \ + _parents, \ + &owl_mux_ops, \ + _flags), \ + }, \ + } + +static inline struct owl_mux *hw_to_owl_mux(const struct clk_hw *hw) +{ + struct owl_clk_common *common = hw_to_owl_clk_common(hw); + + return container_of(common, struct owl_mux, common); +} + +u8 owl_mux_helper_get_parent(const struct owl_clk_common *common, + const struct owl_mux_hw *mux_hw); +int owl_mux_helper_set_parent(const struct owl_clk_common *common, + struct owl_mux_hw *mux_hw, u8 index); + +extern const struct clk_ops owl_mux_ops; + +#endif /* _OWL_MUX_H_ */ -- cgit v1.2.3 From e10e291833024f30cc577c14e1ae909333ca2bfd Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 26 Mar 2018 23:09:00 +0530 Subject: clk: actions: Add divider clock support Add support for Actions Semi divider clock together with helper functions to be used in composite clock. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Stephen Boyd --- drivers/clk/actions/Makefile | 1 + drivers/clk/actions/owl-divider.c | 94 +++++++++++++++++++++++++++++++++++++++ drivers/clk/actions/owl-divider.h | 75 +++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 drivers/clk/actions/owl-divider.c create mode 100644 drivers/clk/actions/owl-divider.h (limited to 'drivers') diff --git a/drivers/clk/actions/Makefile b/drivers/clk/actions/Makefile index 2d4aa8f35d90..5ce75df57e1a 100644 --- a/drivers/clk/actions/Makefile +++ b/drivers/clk/actions/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_CLK_ACTIONS) += clk-owl.o clk-owl-y += owl-common.o clk-owl-y += owl-gate.o clk-owl-y += owl-mux.o +clk-owl-y += owl-divider.o diff --git a/drivers/clk/actions/owl-divider.c b/drivers/clk/actions/owl-divider.c new file mode 100644 index 000000000000..cddac00fe324 --- /dev/null +++ b/drivers/clk/actions/owl-divider.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL divider clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#include +#include + +#include "owl-divider.h" + +long owl_divider_helper_round_rate(struct owl_clk_common *common, + const struct owl_divider_hw *div_hw, + unsigned long rate, + unsigned long *parent_rate) +{ + return divider_round_rate(&common->hw, rate, parent_rate, + div_hw->table, div_hw->width, + div_hw->div_flags); +} + +static long owl_divider_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct owl_divider *div = hw_to_owl_divider(hw); + + return owl_divider_helper_round_rate(&div->common, &div->div_hw, + rate, parent_rate); +} + +unsigned long owl_divider_helper_recalc_rate(struct owl_clk_common *common, + const struct owl_divider_hw *div_hw, + unsigned long parent_rate) +{ + unsigned long val; + unsigned int reg; + + regmap_read(common->regmap, div_hw->reg, ®); + val = reg >> div_hw->shift; + val &= (1 << div_hw->width) - 1; + + return divider_recalc_rate(&common->hw, parent_rate, + val, div_hw->table, + div_hw->div_flags, + div_hw->width); +} + +static unsigned long owl_divider_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct owl_divider *div = hw_to_owl_divider(hw); + + return owl_divider_helper_recalc_rate(&div->common, + &div->div_hw, parent_rate); +} + +int owl_divider_helper_set_rate(const struct owl_clk_common *common, + const struct owl_divider_hw *div_hw, + unsigned long rate, + unsigned long parent_rate) +{ + unsigned long val; + unsigned int reg; + + val = divider_get_val(rate, parent_rate, div_hw->table, + div_hw->width, 0); + + regmap_read(common->regmap, div_hw->reg, ®); + reg &= ~GENMASK(div_hw->width + div_hw->shift - 1, div_hw->shift); + + regmap_write(common->regmap, div_hw->reg, + reg | (val << div_hw->shift)); + + return 0; +} + +static int owl_divider_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct owl_divider *div = hw_to_owl_divider(hw); + + return owl_divider_helper_set_rate(&div->common, &div->div_hw, + rate, parent_rate); +} + +const struct clk_ops owl_divider_ops = { + .recalc_rate = owl_divider_recalc_rate, + .round_rate = owl_divider_round_rate, + .set_rate = owl_divider_set_rate, +}; diff --git a/drivers/clk/actions/owl-divider.h b/drivers/clk/actions/owl-divider.h new file mode 100644 index 000000000000..92d3e3d23967 --- /dev/null +++ b/drivers/clk/actions/owl-divider.h @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL divider clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#ifndef _OWL_DIVIDER_H_ +#define _OWL_DIVIDER_H_ + +#include "owl-common.h" + +struct owl_divider_hw { + u32 reg; + u8 shift; + u8 width; + u8 div_flags; + struct clk_div_table *table; +}; + +struct owl_divider { + struct owl_divider_hw div_hw; + struct owl_clk_common common; +}; + +#define OWL_DIVIDER_HW(_reg, _shift, _width, _div_flags, _table) \ + { \ + .reg = _reg, \ + .shift = _shift, \ + .width = _width, \ + .div_flags = _div_flags, \ + .table = _table, \ + } + +#define OWL_DIVIDER(_struct, _name, _parent, _reg, \ + _shift, _width, _table, _div_flags, _flags) \ + struct owl_divider _struct = { \ + .div_hw = OWL_DIVIDER_HW(_reg, _shift, _width, \ + _div_flags, _table), \ + .common = { \ + .regmap = NULL, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &owl_divider_ops, \ + _flags), \ + }, \ + } + +static inline struct owl_divider *hw_to_owl_divider(const struct clk_hw *hw) +{ + struct owl_clk_common *common = hw_to_owl_clk_common(hw); + + return container_of(common, struct owl_divider, common); +} + +long owl_divider_helper_round_rate(struct owl_clk_common *common, + const struct owl_divider_hw *div_hw, + unsigned long rate, + unsigned long *parent_rate); + +unsigned long owl_divider_helper_recalc_rate(struct owl_clk_common *common, + const struct owl_divider_hw *div_hw, + unsigned long parent_rate); + +int owl_divider_helper_set_rate(const struct owl_clk_common *common, + const struct owl_divider_hw *div_hw, + unsigned long rate, + unsigned long parent_rate); + +extern const struct clk_ops owl_divider_ops; + +#endif /* _OWL_DIVIDER_H_ */ -- cgit v1.2.3 From 4bb78fc9744a7597b8f847ca7343add5a31fa1b3 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 26 Mar 2018 23:09:01 +0530 Subject: clk: actions: Add factor clock support Add support for Actions Semi factor clock together with helper functions to be used in composite clock. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Stephen Boyd --- drivers/clk/actions/Makefile | 1 + drivers/clk/actions/owl-factor.c | 222 +++++++++++++++++++++++++++++++++++++++ drivers/clk/actions/owl-factor.h | 83 +++++++++++++++ 3 files changed, 306 insertions(+) create mode 100644 drivers/clk/actions/owl-factor.c create mode 100644 drivers/clk/actions/owl-factor.h (limited to 'drivers') diff --git a/drivers/clk/actions/Makefile b/drivers/clk/actions/Makefile index 5ce75df57e1a..994357fa560b 100644 --- a/drivers/clk/actions/Makefile +++ b/drivers/clk/actions/Makefile @@ -4,3 +4,4 @@ clk-owl-y += owl-common.o clk-owl-y += owl-gate.o clk-owl-y += owl-mux.o clk-owl-y += owl-divider.o +clk-owl-y += owl-factor.o diff --git a/drivers/clk/actions/owl-factor.c b/drivers/clk/actions/owl-factor.c new file mode 100644 index 000000000000..317d4a9e112e --- /dev/null +++ b/drivers/clk/actions/owl-factor.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL factor clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#include +#include +#include + +#include "owl-factor.h" + +static unsigned int _get_table_maxval(const struct clk_factor_table *table) +{ + unsigned int maxval = 0; + const struct clk_factor_table *clkt; + + for (clkt = table; clkt->div; clkt++) + if (clkt->val > maxval) + maxval = clkt->val; + return maxval; +} + +static int _get_table_div_mul(const struct clk_factor_table *table, + unsigned int val, unsigned int *mul, unsigned int *div) +{ + const struct clk_factor_table *clkt; + + for (clkt = table; clkt->div; clkt++) { + if (clkt->val == val) { + *mul = clkt->mul; + *div = clkt->div; + return 1; + } + } + + return 0; +} + +static unsigned int _get_table_val(const struct clk_factor_table *table, + unsigned long rate, unsigned long parent_rate) +{ + const struct clk_factor_table *clkt; + int val = -1; + u64 calc_rate; + + for (clkt = table; clkt->div; clkt++) { + calc_rate = parent_rate * clkt->mul; + do_div(calc_rate, clkt->div); + + if ((unsigned long)calc_rate <= rate) { + val = clkt->val; + break; + } + } + + if (val == -1) + val = _get_table_maxval(table); + + return val; +} + +static int clk_val_best(struct clk_hw *hw, unsigned long rate, + unsigned long *best_parent_rate) +{ + struct owl_factor *factor = hw_to_owl_factor(hw); + struct owl_factor_hw *factor_hw = &factor->factor_hw; + const struct clk_factor_table *clkt = factor_hw->table; + unsigned long parent_rate, try_parent_rate, best = 0, cur_rate; + unsigned long parent_rate_saved = *best_parent_rate; + int bestval = 0; + + if (!rate) + rate = 1; + + if (!(clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)) { + parent_rate = *best_parent_rate; + bestval = _get_table_val(clkt, rate, parent_rate); + return bestval; + } + + for (clkt = factor_hw->table; clkt->div; clkt++) { + try_parent_rate = rate * clkt->div / clkt->mul; + + if (try_parent_rate == parent_rate_saved) { + pr_debug("%s: [%d %d %d] found try_parent_rate %ld\n", + __func__, clkt->val, clkt->mul, clkt->div, + try_parent_rate); + /* + * It's the most ideal case if the requested rate can be + * divided from parent clock without any need to change + * parent rate, so return the divider immediately. + */ + *best_parent_rate = parent_rate_saved; + return clkt->val; + } + + parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), + try_parent_rate); + cur_rate = DIV_ROUND_UP(parent_rate, clkt->div) * clkt->mul; + if (cur_rate <= rate && cur_rate > best) { + bestval = clkt->val; + best = cur_rate; + *best_parent_rate = parent_rate; + } + } + + if (!bestval) { + bestval = _get_table_maxval(clkt); + *best_parent_rate = clk_hw_round_rate( + clk_hw_get_parent(hw), 1); + } + + return bestval; +} + +long owl_factor_helper_round_rate(struct owl_clk_common *common, + const struct owl_factor_hw *factor_hw, + unsigned long rate, + unsigned long *parent_rate) +{ + const struct clk_factor_table *clkt = factor_hw->table; + unsigned int val, mul = 0, div = 1; + + val = clk_val_best(&common->hw, rate, parent_rate); + _get_table_div_mul(clkt, val, &mul, &div); + + return *parent_rate * mul / div; +} + +static long owl_factor_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct owl_factor *factor = hw_to_owl_factor(hw); + struct owl_factor_hw *factor_hw = &factor->factor_hw; + + return owl_factor_helper_round_rate(&factor->common, factor_hw, + rate, parent_rate); +} + +unsigned long owl_factor_helper_recalc_rate(struct owl_clk_common *common, + const struct owl_factor_hw *factor_hw, + unsigned long parent_rate) +{ + const struct clk_factor_table *clkt = factor_hw->table; + unsigned long long int rate; + u32 reg, val, mul, div; + + div = 0; + mul = 0; + + regmap_read(common->regmap, factor_hw->reg, ®); + + val = reg >> factor_hw->shift; + val &= div_mask(factor_hw); + + _get_table_div_mul(clkt, val, &mul, &div); + if (!div) { + WARN(!(factor_hw->fct_flags & CLK_DIVIDER_ALLOW_ZERO), + "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", + __clk_get_name(common->hw.clk)); + return parent_rate; + } + + rate = (unsigned long long int)parent_rate * mul; + do_div(rate, div); + + return rate; +} + +static unsigned long owl_factor_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct owl_factor *factor = hw_to_owl_factor(hw); + struct owl_factor_hw *factor_hw = &factor->factor_hw; + struct owl_clk_common *common = &factor->common; + + return owl_factor_helper_recalc_rate(common, factor_hw, parent_rate); +} + +int owl_factor_helper_set_rate(const struct owl_clk_common *common, + const struct owl_factor_hw *factor_hw, + unsigned long rate, + unsigned long parent_rate) +{ + u32 val, reg; + + val = _get_table_val(factor_hw->table, rate, parent_rate); + + if (val > div_mask(factor_hw)) + val = div_mask(factor_hw); + + regmap_read(common->regmap, factor_hw->reg, ®); + + reg &= ~(div_mask(factor_hw) << factor_hw->shift); + reg |= val << factor_hw->shift; + + regmap_write(common->regmap, factor_hw->reg, reg); + + return 0; +} + +static int owl_factor_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct owl_factor *factor = hw_to_owl_factor(hw); + struct owl_factor_hw *factor_hw = &factor->factor_hw; + struct owl_clk_common *common = &factor->common; + + return owl_factor_helper_set_rate(common, factor_hw, + rate, parent_rate); +} + +const struct clk_ops owl_factor_ops = { + .round_rate = owl_factor_round_rate, + .recalc_rate = owl_factor_recalc_rate, + .set_rate = owl_factor_set_rate, +}; diff --git a/drivers/clk/actions/owl-factor.h b/drivers/clk/actions/owl-factor.h new file mode 100644 index 000000000000..f1a7ffe896e1 --- /dev/null +++ b/drivers/clk/actions/owl-factor.h @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL factor clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#ifndef _OWL_FACTOR_H_ +#define _OWL_FACTOR_H_ + +#include "owl-common.h" + +struct clk_factor_table { + unsigned int val; + unsigned int mul; + unsigned int div; +}; + +struct owl_factor_hw { + u32 reg; + u8 shift; + u8 width; + u8 fct_flags; + struct clk_factor_table *table; +}; + +struct owl_factor { + struct owl_factor_hw factor_hw; + struct owl_clk_common common; +}; + +#define OWL_FACTOR_HW(_reg, _shift, _width, _fct_flags, _table) \ + { \ + .reg = _reg, \ + .shift = _shift, \ + .width = _width, \ + .fct_flags = _fct_flags, \ + .table = _table, \ + } + +#define OWL_FACTOR(_struct, _name, _parent, _reg, \ + _shift, _width, _table, _fct_flags, _flags) \ + struct owl_factor _struct = { \ + .factor_hw = OWL_FACTOR_HW(_reg, _shift, \ + _width, _fct_flags, _table), \ + .common = { \ + .regmap = NULL, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &owl_factor_ops, \ + _flags), \ + }, \ + } + +#define div_mask(d) ((1 << ((d)->width)) - 1) + +static inline struct owl_factor *hw_to_owl_factor(const struct clk_hw *hw) +{ + struct owl_clk_common *common = hw_to_owl_clk_common(hw); + + return container_of(common, struct owl_factor, common); +} + +long owl_factor_helper_round_rate(struct owl_clk_common *common, + const struct owl_factor_hw *factor_hw, + unsigned long rate, + unsigned long *parent_rate); + +unsigned long owl_factor_helper_recalc_rate(struct owl_clk_common *common, + const struct owl_factor_hw *factor_hw, + unsigned long parent_rate); + +int owl_factor_helper_set_rate(const struct owl_clk_common *common, + const struct owl_factor_hw *factor_hw, + unsigned long rate, + unsigned long parent_rate); + +extern const struct clk_ops owl_factor_ops; + +#endif /* _OWL_FACTOR_H_ */ -- cgit v1.2.3 From c53519644620028b81cefa3228bfc70661dd56e4 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 26 Mar 2018 23:09:02 +0530 Subject: clk: actions: Add fixed factor clock support Add support for Actions Semi fixed factor clock reusing the clk ops from common clock driver. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Stephen Boyd --- drivers/clk/actions/owl-fixed-factor.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 drivers/clk/actions/owl-fixed-factor.h (limited to 'drivers') diff --git a/drivers/clk/actions/owl-fixed-factor.h b/drivers/clk/actions/owl-fixed-factor.h new file mode 100644 index 000000000000..cc9fe36c0964 --- /dev/null +++ b/drivers/clk/actions/owl-fixed-factor.h @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL fixed factor clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#ifndef _OWL_FIXED_FACTOR_H_ +#define _OWL_FIXED_FACTOR_H_ + +#include "owl-common.h" + +#define OWL_FIX_FACT(_struct, _name, _parent, _mul, _div, _flags) \ + struct clk_fixed_factor _struct = { \ + .mult = _mul, \ + .div = _div, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &clk_fixed_factor_ops, \ + _flags), \ + } + +extern const struct clk_ops clk_fixed_factor_ops; + +#endif /* _OWL_FIXED_FACTOR_H_ */ -- cgit v1.2.3 From bb150645abfa2950d1c66dfe8d7e8c8e03d2a644 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 26 Mar 2018 23:09:03 +0530 Subject: clk: actions: Add composite clock support Add support for Actions Semi composite clock. This clock consists of gate, mux, divider, factor and fixed factor clocks. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Stephen Boyd --- drivers/clk/actions/Makefile | 1 + drivers/clk/actions/owl-composite.c | 199 ++++++++++++++++++++++++++++++++++++ drivers/clk/actions/owl-composite.h | 124 ++++++++++++++++++++++ 3 files changed, 324 insertions(+) create mode 100644 drivers/clk/actions/owl-composite.c create mode 100644 drivers/clk/actions/owl-composite.h (limited to 'drivers') diff --git a/drivers/clk/actions/Makefile b/drivers/clk/actions/Makefile index 994357fa560b..53431aef6e9c 100644 --- a/drivers/clk/actions/Makefile +++ b/drivers/clk/actions/Makefile @@ -5,3 +5,4 @@ clk-owl-y += owl-gate.o clk-owl-y += owl-mux.o clk-owl-y += owl-divider.o clk-owl-y += owl-factor.o +clk-owl-y += owl-composite.o diff --git a/drivers/clk/actions/owl-composite.c b/drivers/clk/actions/owl-composite.c new file mode 100644 index 000000000000..101706e0c66f --- /dev/null +++ b/drivers/clk/actions/owl-composite.c @@ -0,0 +1,199 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL composite clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#include +#include + +#include "owl-composite.h" + +static u8 owl_comp_get_parent(struct clk_hw *hw) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + + return owl_mux_helper_get_parent(&comp->common, &comp->mux_hw); +} + +static int owl_comp_set_parent(struct clk_hw *hw, u8 index) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + + return owl_mux_helper_set_parent(&comp->common, &comp->mux_hw, index); +} + +static void owl_comp_disable(struct clk_hw *hw) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + struct owl_clk_common *common = &comp->common; + + owl_gate_set(common, &comp->gate_hw, false); +} + +static int owl_comp_enable(struct clk_hw *hw) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + struct owl_clk_common *common = &comp->common; + + owl_gate_set(common, &comp->gate_hw, true); + + return 0; +} + +static int owl_comp_is_enabled(struct clk_hw *hw) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + struct owl_clk_common *common = &comp->common; + + return owl_gate_clk_is_enabled(common, &comp->gate_hw); +} + +static long owl_comp_div_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + + return owl_divider_helper_round_rate(&comp->common, &comp->rate.div_hw, + rate, parent_rate); +} + +static unsigned long owl_comp_div_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + + return owl_divider_helper_recalc_rate(&comp->common, &comp->rate.div_hw, + parent_rate); +} + +static int owl_comp_div_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + + return owl_divider_helper_set_rate(&comp->common, &comp->rate.div_hw, + rate, parent_rate); +} + +static long owl_comp_fact_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + + return owl_factor_helper_round_rate(&comp->common, + &comp->rate.factor_hw, + rate, parent_rate); +} + +static unsigned long owl_comp_fact_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + + return owl_factor_helper_recalc_rate(&comp->common, + &comp->rate.factor_hw, + parent_rate); +} + +static int owl_comp_fact_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + + return owl_factor_helper_set_rate(&comp->common, + &comp->rate.factor_hw, + rate, parent_rate); +} + +static long owl_comp_fix_fact_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + struct clk_fixed_factor *fix_fact_hw = &comp->rate.fix_fact_hw; + + return comp->fix_fact_ops->round_rate(&fix_fact_hw->hw, rate, parent_rate); +} + +static unsigned long owl_comp_fix_fact_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct owl_composite *comp = hw_to_owl_comp(hw); + struct clk_fixed_factor *fix_fact_hw = &comp->rate.fix_fact_hw; + + return comp->fix_fact_ops->recalc_rate(&fix_fact_hw->hw, parent_rate); + +} + +static int owl_comp_fix_fact_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + /* + * We must report success but we can do so unconditionally because + * owl_comp_fix_fact_round_rate returns values that ensure this call is + * a nop. + */ + + return 0; +} + +const struct clk_ops owl_comp_div_ops = { + /* mux_ops */ + .get_parent = owl_comp_get_parent, + .set_parent = owl_comp_set_parent, + + /* gate_ops */ + .disable = owl_comp_disable, + .enable = owl_comp_enable, + .is_enabled = owl_comp_is_enabled, + + /* div_ops */ + .round_rate = owl_comp_div_round_rate, + .recalc_rate = owl_comp_div_recalc_rate, + .set_rate = owl_comp_div_set_rate, +}; + + +const struct clk_ops owl_comp_fact_ops = { + /* mux_ops */ + .get_parent = owl_comp_get_parent, + .set_parent = owl_comp_set_parent, + + /* gate_ops */ + .disable = owl_comp_disable, + .enable = owl_comp_enable, + .is_enabled = owl_comp_is_enabled, + + /* fact_ops */ + .round_rate = owl_comp_fact_round_rate, + .recalc_rate = owl_comp_fact_recalc_rate, + .set_rate = owl_comp_fact_set_rate, +}; + +const struct clk_ops owl_comp_fix_fact_ops = { + /* gate_ops */ + .disable = owl_comp_disable, + .enable = owl_comp_enable, + .is_enabled = owl_comp_is_enabled, + + /* fix_fact_ops */ + .round_rate = owl_comp_fix_fact_round_rate, + .recalc_rate = owl_comp_fix_fact_recalc_rate, + .set_rate = owl_comp_fix_fact_set_rate, +}; + + +const struct clk_ops owl_comp_pass_ops = { + /* mux_ops */ + .get_parent = owl_comp_get_parent, + .set_parent = owl_comp_set_parent, + + /* gate_ops */ + .disable = owl_comp_disable, + .enable = owl_comp_enable, + .is_enabled = owl_comp_is_enabled, +}; diff --git a/drivers/clk/actions/owl-composite.h b/drivers/clk/actions/owl-composite.h new file mode 100644 index 000000000000..b410ed5bf308 --- /dev/null +++ b/drivers/clk/actions/owl-composite.h @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL composite clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#ifndef _OWL_COMPOSITE_H_ +#define _OWL_COMPOSITE_H_ + +#include "owl-common.h" +#include "owl-mux.h" +#include "owl-gate.h" +#include "owl-factor.h" +#include "owl-fixed-factor.h" +#include "owl-divider.h" + +union owl_rate { + struct owl_divider_hw div_hw; + struct owl_factor_hw factor_hw; + struct clk_fixed_factor fix_fact_hw; +}; + +struct owl_composite { + struct owl_mux_hw mux_hw; + struct owl_gate_hw gate_hw; + union owl_rate rate; + + const struct clk_ops *fix_fact_ops; + + struct owl_clk_common common; +}; + +#define OWL_COMP_DIV(_struct, _name, _parent, \ + _mux, _gate, _div, _flags) \ + struct owl_composite _struct = { \ + .mux_hw = _mux, \ + .gate_hw = _gate, \ + .rate.div_hw = _div, \ + .common = { \ + .regmap = NULL, \ + .hw.init = CLK_HW_INIT_PARENTS(_name, \ + _parent, \ + &owl_comp_div_ops,\ + _flags), \ + }, \ + } + +#define OWL_COMP_DIV_FIXED(_struct, _name, _parent, \ + _gate, _div, _flags) \ + struct owl_composite _struct = { \ + .gate_hw = _gate, \ + .rate.div_hw = _div, \ + .common = { \ + .regmap = NULL, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &owl_comp_div_ops,\ + _flags), \ + }, \ + } + +#define OWL_COMP_FACTOR(_struct, _name, _parent, \ + _mux, _gate, _factor, _flags) \ + struct owl_composite _struct = { \ + .mux_hw = _mux, \ + .gate_hw = _gate, \ + .rate.factor_hw = _factor, \ + .common = { \ + .regmap = NULL, \ + .hw.init = CLK_HW_INIT_PARENTS(_name, \ + _parent, \ + &owl_comp_fact_ops,\ + _flags), \ + }, \ + } + +#define OWL_COMP_FIXED_FACTOR(_struct, _name, _parent, \ + _gate, _mul, _div, _flags) \ + struct owl_composite _struct = { \ + .gate_hw = _gate, \ + .rate.fix_fact_hw.mult = _mul, \ + .rate.fix_fact_hw.div = _div, \ + .fix_fact_ops = &clk_fixed_factor_ops, \ + .common = { \ + .regmap = NULL, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &owl_comp_fix_fact_ops,\ + _flags), \ + }, \ + } + +#define OWL_COMP_PASS(_struct, _name, _parent, \ + _mux, _gate, _flags) \ + struct owl_composite _struct = { \ + .mux_hw = _mux, \ + .gate_hw = _gate, \ + .common = { \ + .regmap = NULL, \ + .hw.init = CLK_HW_INIT_PARENTS(_name, \ + _parent, \ + &owl_comp_pass_ops,\ + _flags), \ + }, \ + } + +static inline struct owl_composite *hw_to_owl_comp(const struct clk_hw *hw) +{ + struct owl_clk_common *common = hw_to_owl_clk_common(hw); + + return container_of(common, struct owl_composite, common); +} + +extern const struct clk_ops owl_comp_div_ops; +extern const struct clk_ops owl_comp_fact_ops; +extern const struct clk_ops owl_comp_fix_fact_ops; +extern const struct clk_ops owl_comp_pass_ops; +extern const struct clk_ops clk_fixed_factor_ops; + +#endif /* _OWL_COMPOSITE_H_ */ -- cgit v1.2.3 From 2792c37e94c80ae374ddabe24d624286566d4f3d Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 26 Mar 2018 23:09:04 +0530 Subject: clk: actions: Add pll clock support Add support for Actions Semi PLL clock. Signed-off-by: Manivannan Sadhasivam Signed-off-by: Stephen Boyd --- drivers/clk/actions/Makefile | 1 + drivers/clk/actions/owl-pll.c | 194 ++++++++++++++++++++++++++++++++++++++++++ drivers/clk/actions/owl-pll.h | 92 ++++++++++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 drivers/clk/actions/owl-pll.c create mode 100644 drivers/clk/actions/owl-pll.h (limited to 'drivers') diff --git a/drivers/clk/actions/Makefile b/drivers/clk/actions/Makefile index 53431aef6e9c..31b68eab9309 100644 --- a/drivers/clk/actions/Makefile +++ b/drivers/clk/actions/Makefile @@ -6,3 +6,4 @@ clk-owl-y += owl-mux.o clk-owl-y += owl-divider.o clk-owl-y += owl-factor.o clk-owl-y += owl-composite.o +clk-owl-y += owl-pll.o diff --git a/drivers/clk/actions/owl-pll.c b/drivers/clk/actions/owl-pll.c new file mode 100644 index 000000000000..058e06d7099f --- /dev/null +++ b/drivers/clk/actions/owl-pll.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0+ +// +// OWL pll clock driver +// +// Copyright (c) 2014 Actions Semi Inc. +// Author: David Liu +// +// Copyright (c) 2018 Linaro Ltd. +// Author: Manivannan Sadhasivam + +#include +#include +#include +#include + +#include "owl-pll.h" + +static u32 owl_pll_calculate_mul(struct owl_pll_hw *pll_hw, unsigned long rate) +{ + u32 mul; + + mul = DIV_ROUND_CLOSEST(rate, pll_hw->bfreq); + if (mul < pll_hw->min_mul) + mul = pll_hw->min_mul; + else if (mul > pll_hw->max_mul) + mul = pll_hw->max_mul; + + return mul &= mul_mask(pll_hw); +} + +static unsigned long _get_table_rate(const struct clk_pll_table *table, + unsigned int val) +{ + const struct clk_pll_table *clkt; + + for (clkt = table; clkt->rate; clkt++) + if (clkt->val == val) + return clkt->rate; + + return 0; +} + +static const struct clk_pll_table *_get_pll_table( + const struct clk_pll_table *table, unsigned long rate) +{ + const struct clk_pll_table *clkt; + + for (clkt = table; clkt->rate; clkt++) { + if (clkt->rate == rate) { + table = clkt; + break; + } else if (clkt->rate < rate) + table = clkt; + } + + return table; +} + +static long owl_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct owl_pll *pll = hw_to_owl_pll(hw); + struct owl_pll_hw *pll_hw = &pll->pll_hw; + const struct clk_pll_table *clkt; + u32 mul; + + if (p