2017-08-08 18:24:32

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 00/12] Misc patches for QCOM clocks

* v2:

1. Discussion is going on with HW team to fix the offset issue in next chip
version so removed the RCG patch
2. Made “fix 16 bit alpha support calculation” generic to support all cases.

3. Clubbed the [1] and Patch [2] for "support for dynamic updating the PLL"
a. The PLL_LATCH_INTERFACE bit is not available in every Alpha PLL and it
will be turned off after default power on. If the PLL user want to turn
off the LATCH_INTERFACE then they can directly set this bit in
PLL_USER_CTL_U during alpha_pll_configure and clear the dynamic update
flag bit
b. The latching procedure is only required if PLL is enabled
c. The handling is different according to PLL_UPDATE_BYPASS bit

4. Added the patch for “2 bit PLL post divider”

[1] https://www.spinics.net/lists/linux-arm-msm/msg24569.html
[2] https://www.spinics.net/lists/kernel/msg2566714.html

* v1:

https://www.spinics.net/lists/kernel/msg2566710.html

Abhishek Sahu (12):
clk: qcom: flag for 64 bit CONFIG_CTL
clk: qcom: support for alpha mode configuration
clk: qcom: use offset from alpha pll node
clk: qcom: fix 16 bit alpha support calculation
clk: qcom: support for dynamic updating the PLL
clk: qcom: add flag for VCO operation
clk: qcom: support for Huayra PLL
clk: qcom: support for Brammo PLL
clk: qcom: support for 2 bit PLL post divider
clk: qcom: add read-only alpha pll post divider operations
clk: qcom: add read-only divider operations
clk: qcom: add parent map for regmap mux

drivers/clk/qcom/clk-alpha-pll.c | 507 +++++++++++++++++++++++++++-------
drivers/clk/qcom/clk-alpha-pll.h | 49 +++-
drivers/clk/qcom/clk-rcg.h | 10 -
drivers/clk/qcom/clk-regmap-divider.c | 29 ++
drivers/clk/qcom/clk-regmap-divider.h | 1 +
drivers/clk/qcom/clk-regmap-mux.c | 6 +
drivers/clk/qcom/clk-regmap-mux.h | 2 +
drivers/clk/qcom/common.h | 11 +-
drivers/clk/qcom/gcc-ipq8074.c | 6 +-
drivers/clk/qcom/gcc-msm8994.c | 12 +-
drivers/clk/qcom/gcc-msm8996.c | 12 +-
drivers/clk/qcom/mmcc-msm8996.c | 48 ++--
12 files changed, 554 insertions(+), 139 deletions(-)

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation


2017-08-08 18:24:39

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 02/12] clk: qcom: support for alpha mode configuration

The current configuration does not fully configure PLL alpha mode
and values so this patch

1. Configures PLL_ALPHA_VAL_U for PLL which supports 40 bit alpha.
2. Adds alpha enable and alpha mode configuration support.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/clk/qcom/clk-alpha-pll.c | 5 +++++
drivers/clk/qcom/clk-alpha-pll.h | 3 +++
2 files changed, 8 insertions(+)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index e6cde2d..6291048 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -123,6 +123,9 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
regmap_write(regmap, off + PLL_CONFIG_CTL_U,
config->config_ctl_hi_val);

+ if (!(pll->flags & SUPPORTS_16BIT_ALPHA))
+ regmap_write(regmap, off + PLL_ALPHA_VAL_U, config->alpha_hi);
+
val = config->main_output_mask;
val |= config->aux_output_mask;
val |= config->aux2_output_mask;
@@ -130,6 +133,8 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
val |= config->pre_div_val;
val |= config->post_div_val;
val |= config->vco_val;
+ val |= config->alpha_en_mask;
+ val |= config->alpha_mode_mask;

mask = config->main_output_mask;
mask |= config->aux_output_mask;
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index bbd6aa9..39686db 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -59,12 +59,15 @@ struct clk_alpha_pll_postdiv {
struct alpha_pll_config {
u32 l;
u32 alpha;
+ u32 alpha_hi;
u32 config_ctl_val;
u32 config_ctl_hi_val;
u32 main_output_mask;
u32 aux_output_mask;
u32 aux2_output_mask;
u32 early_output_mask;
+ u32 alpha_en_mask;
+ u32 alpha_mode_mask;
u32 pre_div_val;
u32 pre_div_mask;
u32 post_div_val;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-08 18:24:44

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 03/12] clk: qcom: use offset from alpha pll node

Alpha PLL is a generic name used for Qualcomm PLL’s which uses L
and Alpha values for configuring the integer and fractional part.
Qualcomm SoC’s use different types of Alpha PLL’s for which basic
software configuration part is common. These PLL’s will have same
basic registers like PLL_MODE, L_VAL, ALPHA_VAL but some of the
register offsets are different in each PLL type.
Also, the offsets are not same in different instances of same
type of PLL in some cases so it’s better to get the offsets
from PLL node itself instead of hardcoding it.

This patch adds the support for giving the PLL offsets array in
PLL node itself and uses the same for calculating the offsets.
Now, this offsets array will be mandatory for all alpha PLL nodes.
This patch provides the default array of offsets which driver can use
where the PLL offsets are same. Some of the existing
Qualcomm SoC’s uses the alpha PLL nodes so this patch added the
default offsets in its nodes.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/clk/qcom/clk-alpha-pll.c | 130 ++++++++++++++++++++-------------------
drivers/clk/qcom/clk-alpha-pll.h | 28 +++++++--
drivers/clk/qcom/gcc-ipq8074.c | 6 +-
drivers/clk/qcom/gcc-msm8994.c | 12 ++--
drivers/clk/qcom/gcc-msm8996.c | 12 ++--
drivers/clk/qcom/mmcc-msm8996.c | 48 ++++++++++-----
6 files changed, 142 insertions(+), 94 deletions(-)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 6291048..084fbdc 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -20,7 +20,6 @@
#include "clk-alpha-pll.h"
#include "common.h"

-#define PLL_MODE 0x00
# define PLL_OUTCTRL BIT(0)
# define PLL_BYPASSNL BIT(1)
# define PLL_RESET_N BIT(2)
@@ -36,25 +35,12 @@
# define PLL_ACTIVE_FLAG BIT(30)
# define PLL_LOCK_DET BIT(31)

-#define PLL_L_VAL 0x04
-#define PLL_ALPHA_VAL 0x08
-#define PLL_ALPHA_VAL_U 0x0c
-
-#define PLL_USER_CTL 0x10
# define PLL_POST_DIV_SHIFT 8
# define PLL_POST_DIV_MASK 0xf
# define PLL_ALPHA_EN BIT(24)
# define PLL_VCO_SHIFT 20
# define PLL_VCO_MASK 0x3

-#define PLL_USER_CTL_U 0x14
-
-#define PLL_CONFIG_CTL 0x18
-#define PLL_CONFIG_CTL_U 0x20
-#define PLL_TEST_CTL 0x1c
-#define PLL_TEST_CTL_U 0x20
-#define PLL_STATUS 0x24
-
/*
* Even though 40 bits are present, use only 32 for ease of calculation.
*/
@@ -62,27 +48,52 @@
#define ALPHA_BITWIDTH 32
#define ALPHA_16BIT_MASK 0xffff

+#define pll_mode(pll) (pll->base + pll->offsets[ALPHA_PLL_MODE])
+#define pll_l(pll) (pll->base + pll->offsets[ALPHA_PLL_L_VAL])
+#define pll_alpha(pll) (pll->base + pll->offsets[ALPHA_PLL_ALPHA_VAL])
+#define pll_alpha_u(pll) (pll->base + pll->offsets[ALPHA_PLL_ALPHA_VAL_U])
+#define pll_user_ctl(pll) (pll->base + pll->offsets[ALPHA_PLL_USER_CTL])
+#define pll_user_ctl_u(pll) (pll->base + pll->offsets[ALPHA_PLL_USER_CTL_U])
+#define pll_cfg_ctl(pll) (pll->base + pll->offsets[ALPHA_PLL_CONFIG_CTL])
+#define pll_test_ctl(pll) (pll->base + pll->offsets[ALPHA_PLL_TEST_CTL])
+#define pll_test_ctl_u(pll) (pll->base + pll->offsets[ALPHA_PLL_TEST_CTL_U])
+#define pll_status(pll) (pll->base + pll->offsets[ALPHA_PLL_STATUS])
+#define pll_cfg_ctl_u(pll) (pll->base + pll->offsets[ALPHA_PLL_CONFIG_CTL_U])
+
#define to_clk_alpha_pll(_hw) container_of(to_clk_regmap(_hw), \
struct clk_alpha_pll, clkr)

#define to_clk_alpha_pll_postdiv(_hw) container_of(to_clk_regmap(_hw), \
struct clk_alpha_pll_postdiv, clkr)

+const u8 clk_alpha_pll_offsets[] = {
+ [ALPHA_PLL_MODE] = 0x00,
+ [ALPHA_PLL_L_VAL] = 0x04,
+ [ALPHA_PLL_ALPHA_VAL] = 0x08,
+ [ALPHA_PLL_ALPHA_VAL_U] = 0x0c,
+ [ALPHA_PLL_USER_CTL] = 0x10,
+ [ALPHA_PLL_USER_CTL_U] = 0x14,
+ [ALPHA_PLL_CONFIG_CTL] = 0x18,
+ [ALPHA_PLL_TEST_CTL] = 0x1c,
+ [ALPHA_PLL_TEST_CTL_U] = 0x20,
+ [ALPHA_PLL_STATUS] = 0x24,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_pll_offsets);
+
static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
const char *action)
{
- u32 val, off;
+ u32 val;
int count;
int ret;
const char *name = clk_hw_get_name(&pll->clkr.hw);

- off = pll->offset;
- ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
+ ret = regmap_read(pll->clkr.regmap, pll_mode(pll), &val);
if (ret)
return ret;

for (count = 100; count > 0; count--) {
- ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
+ ret = regmap_read(pll->clkr.regmap, pll_mode(pll), &val);
if (ret)
return ret;
if (inverse && !(val & mask))
@@ -113,18 +124,17 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
const struct alpha_pll_config *config)
{
u32 val, mask;
- u32 off = pll->offset;

- regmap_write(regmap, off + PLL_L_VAL, config->l);
- regmap_write(regmap, off + PLL_ALPHA_VAL, config->alpha);
- regmap_write(regmap, off + PLL_CONFIG_CTL, config->config_ctl_val);
+ regmap_write(regmap, pll_l(pll), config->l);
+ regmap_write(regmap, pll_alpha(pll), config->alpha);
+ regmap_write(regmap, pll_cfg_ctl(pll), config->config_ctl_val);

if (pll->flags & SUPPORTS_64BIT_CONFIG_CTL)
- regmap_write(regmap, off + PLL_CONFIG_CTL_U,
+ regmap_write(regmap, pll_cfg_ctl_u(pll),
config->config_ctl_hi_val);

if (!(pll->flags & SUPPORTS_16BIT_ALPHA))
- regmap_write(regmap, off + PLL_ALPHA_VAL_U, config->alpha_hi);
+ regmap_write(regmap, pll_alpha_u(pll), config->alpha_hi);

val = config->main_output_mask;
val |= config->aux_output_mask;
@@ -144,20 +154,19 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
mask |= config->post_div_mask;
mask |= config->vco_mask;

- regmap_update_bits(regmap, off + PLL_USER_CTL, mask, val);
+ regmap_update_bits(regmap, pll_user_ctl(pll), mask, val);

if (pll->flags & SUPPORTS_FSM_MODE)
- qcom_pll_set_fsm_mode(regmap, off + PLL_MODE, 6, 0);
+ qcom_pll_set_fsm_mode(regmap, pll_mode(pll), 6, 0);
}

static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw)
{
int ret;
- u32 val, off;
+ u32 val;
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);

- off = pll->offset;
- ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
+ ret = regmap_read(pll->clkr.regmap, pll_mode(pll), &val);
if (ret)
return ret;

@@ -166,7 +175,7 @@ static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw)
if (pll->flags & SUPPORTS_OFFLINE_REQ)
val &= ~PLL_OFFLINE_REQ;

- ret = regmap_write(pll->clkr.regmap, off + PLL_MODE, val);
+ ret = regmap_write(pll->clkr.regmap, pll_mode(pll), val);
if (ret)
return ret;

@@ -179,16 +188,15 @@ static int clk_alpha_pll_hwfsm_enable(struct clk_hw *hw)
static void clk_alpha_pll_hwfsm_disable(struct clk_hw *hw)
{
int ret;
- u32 val, off;
+ u32 val;
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);

- off = pll->offset;
- ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
+ ret = regmap_read(pll->clkr.regmap, pll_mode(pll), &val);
if (ret)
return;

if (pll->flags & SUPPORTS_OFFLINE_REQ) {
- ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
+ ret = regmap_update_bits(pll->clkr.regmap, pll_mode(pll),
PLL_OFFLINE_REQ, PLL_OFFLINE_REQ);
if (ret)
return;
@@ -199,7 +207,7 @@ static void clk_alpha_pll_hwfsm_disable(struct clk_hw *hw)
}

/* Disable hwfsm */
- ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
+ ret = regmap_update_bits(pll->clkr.regmap, pll_mode(pll),
PLL_FSM_ENA, 0);
if (ret)
return;
@@ -210,11 +218,10 @@ static void clk_alpha_pll_hwfsm_disable(struct clk_hw *hw)
static int pll_is_enabled(struct clk_hw *hw, u32 mask)
{
int ret;
- u32 val, off;
+ u32 val;
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);

- off = pll->offset;
- ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
+ ret = regmap_read(pll->clkr.regmap, pll_mode(pll), &val);
if (ret)
return ret;

@@ -235,12 +242,10 @@ static int clk_alpha_pll_enable(struct clk_hw *hw)
{
int ret;
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
- u32 val, mask, off;
-
- off = pll->offset;
+ u32 val, mask;

mask = PLL_OUTCTRL | PLL_RESET_N | PLL_BYPASSNL;
- ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
+ ret = regmap_read(pll->clkr.regmap, pll_mode(pll), &val);
if (ret)
return ret;

@@ -256,7 +261,7 @@ static int clk_alpha_pll_enable(struct clk_hw *hw)
if ((val & mask) == mask)
return 0;

- ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
+ ret = regmap_update_bits(pll->clkr.regmap, pll_mode(pll),
PLL_BYPASSNL, PLL_BYPASSNL);
if (ret)
return ret;
@@ -268,7 +273,7 @@ static int clk_alpha_pll_enable(struct clk_hw *hw)
mb();
udelay(5);

- ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
+ ret = regmap_update_bits(pll->clkr.regmap, pll_mode(pll),
PLL_RESET_N, PLL_RESET_N);
if (ret)
return ret;
@@ -277,7 +282,7 @@ static int clk_alpha_pll_enable(struct clk_hw *hw)
if (ret)
return ret;

- ret = regmap_update_bits(pll->clkr.regmap, off + PLL_MODE,
+ ret = regmap_update_bits(pll->clkr.regmap, pll_mode(pll),
PLL_OUTCTRL, PLL_OUTCTRL);

/* Ensure that the write above goes through before returning. */
@@ -289,11 +294,9 @@ static void clk_alpha_pll_disable(struct clk_hw *hw)
{
int ret;
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
- u32 val, mask, off;
-
- off = pll->offset;
+ u32 val, mask;

- ret = regmap_read(pll->clkr.regmap, off + PLL_MODE, &val);
+ ret = regmap_read(pll->clkr.regmap, pll_mode(pll), &val);
if (ret)
return;

@@ -304,14 +307,14 @@ static void clk_alpha_pll_disable(struct clk_hw *hw)
}

mask = PLL_OUTCTRL;
- regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, mask, 0);
+ regmap_update_bits(pll->clkr.regmap, pll_mode(pll), mask, 0);

/* Delay of 2 output clock ticks required until output is disabled */
mb();
udelay(1);

mask = PLL_RESET_N | PLL_BYPASSNL;
- regmap_update_bits(pll->clkr.regmap, off + PLL_MODE, mask, 0);
+ regmap_update_bits(pll->clkr.regmap, pll_mode(pll), mask, 0);
}

static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a)
@@ -364,17 +367,16 @@ static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a)
u32 l, low, high, ctl;
u64 a = 0, prate = parent_rate;
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
- u32 off = pll->offset;

- regmap_read(pll->clkr.regmap, off + PLL_L_VAL, &l);
+ regmap_read(pll->clkr.regmap, pll_l(pll), &l);

- regmap_read(pll->clkr.regmap, off + PLL_USER_CTL, &ctl);
+ regmap_read(pll->clkr.regmap, pll_user_ctl(pll), &ctl);
if (ctl & PLL_ALPHA_EN) {
- regmap_read(pll->clkr.regmap, off + PLL_ALPHA_VAL, &low);
+ regmap_read(pll->clkr.regmap, pll_alpha(pll), &low);
if (pll->flags & SUPPORTS_16BIT_ALPHA) {
a = low & ALPHA_16BIT_MASK;
} else {
- regmap_read(pll->clkr.regmap, off + PLL_ALPHA_VAL_U,
+ regmap_read(pll->clkr.regmap, pll_alpha_u(pll),
&high);
a = (u64)high << 32 | low;
a >>= ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH;
@@ -389,7 +391,7 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
const struct pll_vco *vco;
- u32 l, off = pll->offset;
+ u32 l;
u64 a;

rate = alpha_pll_round_rate(rate, prate, &l, &a);
@@ -399,21 +401,21 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
return -EINVAL;
}

- regmap_write(pll->clkr.regmap, off + PLL_L_VAL, l);
+ regmap_write(pll->clkr.regmap, pll_l(pll), l);

if (pll->flags & SUPPORTS_16BIT_ALPHA) {
- regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL,
+ regmap_write(pll->clkr.regmap, pll_alpha(pll),
a & ALPHA_16BIT_MASK);
} else {
a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);
- regmap_write(pll->clkr.regmap, off + PLL_ALPHA_VAL_U, a >> 32);
+ regmap_write(pll->clkr.regmap, pll_alpha_u(pll), a >> 32);
}

- regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL,
+ regmap_update_bits(pll->clkr.regmap, pll_user_ctl(pll),
PLL_VCO_MASK << PLL_VCO_SHIFT,
vco->val << PLL_VCO_SHIFT);

- regmap_update_bits(pll->clkr.regmap, off + PLL_USER_CTL, PLL_ALPHA_EN,
+ regmap_update_bits(pll->clkr.regmap, pll_user_ctl(pll), PLL_ALPHA_EN,
PLL_ALPHA_EN);

return 0;
@@ -463,7 +465,7 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
u32 ctl;

- regmap_read(pll->clkr.regmap, pll->offset + PLL_USER_CTL, &ctl);
+ regmap_read(pll->clkr.regmap, pll_user_ctl(pll), &ctl);

ctl >>= PLL_POST_DIV_SHIFT;
ctl &= PLL_POST_DIV_MASK;
@@ -499,7 +501,7 @@ static int clk_alpha_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate,
/* 16 -> 0xf, 8 -> 0x7, 4 -> 0x3, 2 -> 0x1, 1 -> 0x0 */
div = DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1;

- return regmap_update_bits(pll->clkr.regmap, pll->offset + PLL_USER_CTL,
+ return regmap_update_bits(pll->clkr.regmap, pll_user_ctl(pll),
PLL_POST_DIV_MASK << PLL_POST_DIV_SHIFT,
div << PLL_POST_DIV_SHIFT);
}
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index 39686db..4901d92 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -17,6 +17,20 @@
#include <linux/clk-provider.h>
#include "clk-regmap.h"

+enum {
+ ALPHA_PLL_MODE,
+ ALPHA_PLL_L_VAL,
+ ALPHA_PLL_ALPHA_VAL,
+ ALPHA_PLL_ALPHA_VAL_U,
+ ALPHA_PLL_USER_CTL,
+ ALPHA_PLL_USER_CTL_U,
+ ALPHA_PLL_CONFIG_CTL,
+ ALPHA_PLL_CONFIG_CTL_U,
+ ALPHA_PLL_TEST_CTL,
+ ALPHA_PLL_TEST_CTL_U,
+ ALPHA_PLL_STATUS,
+};
+
struct pll_vco {
unsigned long min_freq;
unsigned long max_freq;
@@ -25,12 +39,14 @@ struct pll_vco {

/**
* struct clk_alpha_pll - phase locked loop (PLL)
- * @offset: base address of registers
+ * @base: base address of registers
+ * @offsets: array containing offsets of all PLL registers from base address
* @vco_table: array of VCO settings
* @clkr: regmap clock handle
*/
struct clk_alpha_pll {
- u32 offset;
+ u32 base;
+ const u8 *offsets;

const struct pll_vco *vco_table;
size_t num_vco;
@@ -45,12 +61,14 @@ struct clk_alpha_pll {

/**
* struct clk_alpha_pll_postdiv - phase locked loop (PLL) post-divider
- * @offset: base address of registers
+ * @base: base address of registers
+ * @offsets: array containing offsets of all PLL registers from base address
* @width: width of post-divider
* @clkr: regmap clock handle
*/
struct clk_alpha_pll_postdiv {
- u32 offset;
+ u32 base;
+ const u8 *offsets;
u8 width;

struct clk_regmap clkr;
@@ -76,6 +94,8 @@ struct alpha_pll_config {
u32 vco_mask;
};

+extern const u8 clk_alpha_pll_offsets[];
+
extern const struct clk_ops clk_alpha_pll_ops;
extern const struct clk_ops clk_alpha_pll_hwfsm_ops;
extern const struct clk_ops clk_alpha_pll_postdiv_ops;
diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c
index 0f735d3..29380d2 100644
--- a/drivers/clk/qcom/gcc-ipq8074.c
+++ b/drivers/clk/qcom/gcc-ipq8074.c
@@ -51,7 +51,8 @@ enum {
};

static struct clk_alpha_pll gpll0_main = {
- .offset = 0x21000,
+ .base = 0x21000,
+ .offsets = clk_alpha_pll_offsets,
.clkr = {
.enable_reg = 0x0b000,
.enable_mask = BIT(0),
@@ -81,7 +82,8 @@ enum {
};

static struct clk_alpha_pll_postdiv gpll0 = {
- .offset = 0x21000,
+ .base = 0x21000,
+ .offsets = clk_alpha_pll_offsets,
.clkr.hw.init = &(struct clk_init_data){
.name = "gpll0",
.parent_names = (const char *[]){
diff --git a/drivers/clk/qcom/gcc-msm8994.c b/drivers/clk/qcom/gcc-msm8994.c
index 7983288..da6b0d1 100644
--- a/drivers/clk/qcom/gcc-msm8994.c
+++ b/drivers/clk/qcom/gcc-msm8994.c
@@ -72,7 +72,8 @@ enum {
};

static struct clk_alpha_pll gpll0_early = {
- .offset = 0x00000,
+ .base = 0x00000,
+ .offsets = clk_alpha_pll_offsets,
.clkr = {
.enable_reg = 0x1480,
.enable_mask = BIT(0),
@@ -87,7 +88,8 @@ enum {
};

static struct clk_alpha_pll_postdiv gpll0 = {
- .offset = 0x00000,
+ .base = 0x00000,
+ .offsets = clk_alpha_pll_offsets,
.clkr.hw.init = &(struct clk_init_data)
{
.name = "gpll0",
@@ -98,7 +100,8 @@ enum {
};

static struct clk_alpha_pll gpll4_early = {
- .offset = 0x1dc0,
+ .base = 0x1dc0,
+ .offsets = clk_alpha_pll_offsets,
.clkr = {
.enable_reg = 0x1480,
.enable_mask = BIT(4),
@@ -113,7 +116,8 @@ enum {
};

static struct clk_alpha_pll_postdiv gpll4 = {
- .offset = 0x1dc0,
+ .base = 0x1dc0,
+ .offsets = clk_alpha_pll_offsets,
.clkr.hw.init = &(struct clk_init_data)
{
.name = "gpll4",
diff --git a/drivers/clk/qcom/gcc-msm8996.c b/drivers/clk/qcom/gcc-msm8996.c
index 8abc200..fcf1b15 100644
--- a/drivers/clk/qcom/gcc-msm8996.c
+++ b/drivers/clk/qcom/gcc-msm8996.c
@@ -226,7 +226,8 @@ enum {
};

static struct clk_alpha_pll gpll0_early = {
- .offset = 0x00000,
+ .base = 0x00000,
+ .offsets = clk_alpha_pll_offsets,
.clkr = {
.enable_reg = 0x52000,
.enable_mask = BIT(0),
@@ -251,7 +252,8 @@ enum {
};

static struct clk_alpha_pll_postdiv gpll0 = {
- .offset = 0x00000,
+ .base = 0x00000,
+ .offsets = clk_alpha_pll_offsets,
.clkr.hw.init = &(struct clk_init_data){
.name = "gpll0",
.parent_names = (const char *[]){ "gpll0_early" },
@@ -261,7 +263,8 @@ enum {
};

static struct clk_alpha_pll gpll4_early = {
- .offset = 0x77000,
+ .base = 0x77000,
+ .offsets = clk_alpha_pll_offsets,
.clkr = {
.enable_reg = 0x52000,
.enable_mask = BIT(4),
@@ -275,7 +278,8 @@ enum {
};

static struct clk_alpha_pll_postdiv gpll4 = {
- .offset = 0x77000,
+ .base = 0x77000,
+ .offsets = clk_alpha_pll_offsets,
.clkr.hw.init = &(struct clk_init_data){
.name = "gpll4",
.parent_names = (const char *[]){ "gpll4_early" },
diff --git a/drivers/clk/qcom/mmcc-msm8996.c b/drivers/clk/qcom/mmcc-msm8996.c
index 352394d..2447740 100644
--- a/drivers/clk/qcom/mmcc-msm8996.c
+++ b/drivers/clk/qcom/mmcc-msm8996.c
@@ -266,7 +266,8 @@ enum {
};

static struct clk_alpha_pll mmpll0_early = {
- .offset = 0x0,
+ .base = 0x0,
+ .offsets = clk_alpha_pll_offsets,
.vco_table = mmpll_p_vco,
.num_vco = ARRAY_SIZE(mmpll_p_vco),
.clkr = {
@@ -282,7 +283,8 @@ enum {
};

static struct clk_alpha_pll_postdiv mmpll0 = {
- .offset = 0x0,
+ .base = 0x0,
+ .offsets = clk_alpha_pll_offsets,
.width = 4,
.clkr.hw.init = &(struct clk_init_data){
.name = "mmpll0",
@@ -294,7 +296,8 @@ enum {
};

static struct clk_alpha_pll mmpll1_early = {
- .offset = 0x30,
+ .base = 0x30,
+ .offsets = clk_alpha_pll_offsets,
.vco_table = mmpll_p_vco,
.num_vco = ARRAY_SIZE(mmpll_p_vco),
.clkr = {
@@ -310,7 +313,8 @@ enum {
};

static struct clk_alpha_pll_postdiv mmpll1 = {
- .offset = 0x30,
+ .base = 0x30,
+ .offsets = clk_alpha_pll_offsets,
.width = 4,
.clkr.hw.init = &(struct clk_init_data){
.name = "mmpll1",
@@ -322,7 +326,8 @@ enum {
};

static struct clk_alpha_pll mmpll2_early = {
- .offset = 0x4100,
+ .base = 0x4100,
+ .offsets = clk_alpha_pll_offsets,
.vco_table = mmpll_gfx_vco,
.num_vco = ARRAY_SIZE(mmpll_gfx_vco),
.clkr.hw.init = &(struct clk_init_data){
@@ -334,7 +339,8 @@ enum {
};

static struct clk_alpha_pll_postdiv mmpll2 = {
- .offset = 0x4100,
+ .base = 0x4100,
+ .offsets = clk_alpha_pll_offsets,
.width = 4,
.clkr.hw.init = &(struct clk_init_data){
.name = "mmpll2",
@@ -346,7 +352,8 @@ enum {
};

static struct clk_alpha_pll mmpll3_early = {
- .offset = 0x60,
+ .base = 0x60,
+ .offsets = clk_alpha_pll_offsets,
.vco_table = mmpll_p_vco,
.num_vco = ARRAY_SIZE(mmpll_p_vco),
.clkr.hw.init = &(struct clk_init_data){
@@ -358,7 +365,8 @@ enum {
};

static struct clk_alpha_pll_postdiv mmpll3 = {
- .offset = 0x60,
+ .base = 0x60,
+ .offsets = clk_alpha_pll_offsets,
.width = 4,
.clkr.hw.init = &(struct clk_init_data){
.name = "mmpll3",
@@ -370,7 +378,8 @@ enum {
};

static struct clk_alpha_pll mmpll4_early = {
- .offset = 0x90,
+ .base = 0x90,
+ .offsets = clk_alpha_pll_offsets,
.vco_table = mmpll_t_vco,
.num_vco = ARRAY_SIZE(mmpll_t_vco),
.clkr.hw.init = &(struct clk_init_data){
@@ -382,7 +391,8 @@ enum {
};

static struct clk_alpha_pll_postdiv mmpll4 = {
- .offset = 0x90,
+ .base = 0x90,
+ .offsets = clk_alpha_pll_offsets,
.width = 2,
.clkr.hw.init = &(struct clk_init_data){
.name = "mmpll4",
@@ -394,7 +404,8 @@ enum {
};

static struct clk_alpha_pll mmpll5_early = {
- .offset = 0xc0,
+ .base = 0xc0,
+ .offsets = clk_alpha_pll_offsets,
.vco_table = mmpll_p_vco,
.num_vco = ARRAY_SIZE(mmpll_p_vco),
.clkr.hw.init = &(struct clk_init_data){
@@ -406,7 +417,8 @@ enum {
};

static struct clk_alpha_pll_postdiv mmpll5 = {
- .offset = 0xc0,
+ .base = 0xc0,
+ .offsets = clk_alpha_pll_offsets,
.width = 4,
.clkr.hw.init = &(struct clk_init_data){
.name = "mmpll5",
@@ -418,7 +430,8 @@ enum {
};

static struct clk_alpha_pll mmpll8_early = {
- .offset = 0x4130,
+ .base = 0x4130,
+ .offsets = clk_alpha_pll_offsets,
.vco_table = mmpll_gfx_vco,
.num_vco = ARRAY_SIZE(mmpll_gfx_vco),
.clkr.hw.init = &(struct clk_init_data){
@@ -430,7 +443,8 @@ enum {
};

static struct clk_alpha_pll_postdiv mmpll8 = {
- .offset = 0x4130,
+ .base = 0x4130,
+ .offsets = clk_alpha_pll_offsets,
.width = 4,
.clkr.hw.init = &(struct clk_init_data){
.name = "mmpll8",
@@ -442,7 +456,8 @@ enum {
};

static struct clk_alpha_pll mmpll9_early = {
- .offset = 0x4200,
+ .base = 0x4200,
+ .offsets = clk_alpha_pll_offsets,
.vco_table = mmpll_t_vco,
.num_vco = ARRAY_SIZE(mmpll_t_vco),
.clkr.hw.init = &(struct clk_init_data){
@@ -454,7 +469,8 @@ enum {
};

static struct clk_alpha_pll_postdiv mmpll9 = {
- .offset = 0x4200,
+ .base = 0x4200,
+ .offsets = clk_alpha_pll_offsets,
.width = 2,
.clkr.hw.init = &(struct clk_init_data){
.name = "mmpll9",
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-08 18:24:52

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 05/12] clk: qcom: support for dynamic updating the PLL

Some of the Alpha PLL’s support dynamic update in which the
frequency can be changed dynamically without turning off the PLL.

This dynamic update requires the following sequence

1. Write the desired values to pll_l_val and pll_alpha_val
2. Toggle pll_latch_input from low to high
3. Wait for pll_ack_latch to transition from low to high
The new L and alpha values have been latched. It make
take some time for the PLL to fully settle with these
new values
4. Pull pll_latch_input low

Signed-off-by: Rajendra Nayak <[email protected]>
Signed-off-by: Taniya Das <[email protected]>
Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/clk/qcom/clk-alpha-pll.c | 67 +++++++++++++++++++++++++++++++++++++++-
drivers/clk/qcom/clk-alpha-pll.h | 1 +
2 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 6694fd5..1f37bf8a 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -31,7 +31,10 @@
# define PLL_VOTE_FSM_ENA BIT(20)
# define PLL_FSM_ENA BIT(20)
# define PLL_VOTE_FSM_RESET BIT(21)
+# define PLL_UPDATE BIT(22)
+# define PLL_UPDATE_BYPASS BIT(23)
# define PLL_OFFLINE_ACK BIT(28)
+# define ALPHA_PLL_ACK_LATCH BIT(29)
# define PLL_ACTIVE_FLAG BIT(30)
# define PLL_LOCK_DET BIT(31)

@@ -123,6 +126,15 @@ static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
#define wait_for_pll_offline(pll) \
wait_for_pll(pll, PLL_OFFLINE_ACK, 0, "offline")

+#define wait_for_pll_update(pll) \
+ wait_for_pll(pll, PLL_UPDATE, 1, "update")
+
+#define wait_for_pll_update_ack_set(pll) \
+ wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 0, "update_ack_set")
+
+#define wait_for_pll_update_ack_clear(pll) \
+ wait_for_pll(pll, ALPHA_PLL_ACK_LATCH, 1, "update_ack_clear")
+
void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
const struct alpha_pll_config *config)
{
@@ -396,6 +408,56 @@ static void clk_alpha_pll_disable(struct clk_hw *hw)
return alpha_pll_calc_rate(prate, l, a, alpha_width);
}

+static int clk_alpha_pll_update_latch(struct clk_alpha_pll *pll)
+{
+ u32 mode;
+ int ret;
+
+ regmap_read(pll->clkr.regmap, pll_mode(pll), &mode);
+
+ /* Latch the input to the PLL */
+ regmap_update_bits(pll->clkr.regmap, pll_mode(pll), PLL_UPDATE,
+ PLL_UPDATE);
+
+ /* Make sure PLL_UPDATE request goes through*/
+ mb();
+
+ /* Wait for 2 reference cycle before checking ACK bit */
+ udelay(1);
+
+ /*
+ * PLL will latch the new L, Alpha and freq control word.
+ * PLL will respond by raising PLL_ACK_LATCH output when new programming
+ * has been latched in and PLL is being updated. When
+ * UPDATE_LOGIC_BYPASS bit is not set, PLL_UPDATE will be cleared
+ * automatically by hardware when PLL_ACK_LATCH is asserted by PLL.
+ */
+ if (mode & PLL_UPDATE_BYPASS) {
+ ret = wait_for_pll_update_ack_set(pll);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(pll->clkr.regmap, pll_mode(pll),
+ PLL_UPDATE, 0);
+
+ /* Make sure PLL_UPDATE request goes through*/
+ mb();
+ } else {
+ ret = wait_for_pll_update(pll);
+ if (ret)
+ return ret;
+ }
+
+ ret = wait_for_pll_update_ack_clear(pll);
+ if (ret)
+ return ret;
+
+ /* Wait for PLL output to stabilize */
+ udelay(10);
+
+ return 0;
+}
+
static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long prate)
{
@@ -428,7 +490,10 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
regmap_update_bits(pll->clkr.regmap, pll_user_ctl(pll), PLL_ALPHA_EN,
PLL_ALPHA_EN);

- return 0;
+ if (!clk_hw_is_enabled(hw) || !(pll->flags & SUPPORTS_DYNAMIC_UPDATE))
+ return 0;
+
+ return clk_alpha_pll_update_latch(pll);
}

static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index 4901d92..8b27c05 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -54,6 +54,7 @@ struct clk_alpha_pll {
#define SUPPORTS_16BIT_ALPHA BIT(1)
#define SUPPORTS_FSM_MODE BIT(2)
#define SUPPORTS_64BIT_CONFIG_CTL BIT(3)
+#define SUPPORTS_DYNAMIC_UPDATE BIT(4)
u8 flags;

struct clk_regmap clkr;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-08 18:25:01

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 06/12] clk: qcom: add flag for VCO operation

Some of the Alpha PLL’s does not have VCO configuration so this
patch adds the flag and does not perform VCO operation if this
flag is set.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/clk/qcom/clk-alpha-pll.c | 19 +++++++++++--------
drivers/clk/qcom/clk-alpha-pll.h | 1 +
2 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 1f37bf8a..c368e7c 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -467,10 +467,12 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
u64 a;

rate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
- vco = alpha_pll_find_vco(pll, rate);
- if (!vco) {
- pr_err("alpha pll not in a valid vco range\n");
- return -EINVAL;
+ if (!(pll->flags & SUPPORTS_NO_VCO)) {
+ vco = alpha_pll_find_vco(pll, rate);
+ if (!vco) {
+ pr_err("alpha pll not in a valid vco range\n");
+ return -EINVAL;
+ }
}

regmap_write(pll->clkr.regmap, pll_l(pll), l);
@@ -483,9 +485,10 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,

regmap_write(pll->clkr.regmap, pll_alpha(pll), a);

- regmap_update_bits(pll->clkr.regmap, pll_user_ctl(pll),
- PLL_VCO_MASK << PLL_VCO_SHIFT,
- vco->val << PLL_VCO_SHIFT);
+ if (!(pll->flags & SUPPORTS_NO_VCO))
+ regmap_update_bits(pll->clkr.regmap, pll_user_ctl(pll),
+ PLL_VCO_MASK << PLL_VCO_SHIFT,
+ vco->val << PLL_VCO_SHIFT);

regmap_update_bits(pll->clkr.regmap, pll_user_ctl(pll), PLL_ALPHA_EN,
PLL_ALPHA_EN);
@@ -505,7 +508,7 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long min_freq, max_freq;

rate = alpha_pll_round_rate(rate, *prate, &l, &a, alpha_width);
- if (alpha_pll_find_vco(pll, rate))
+ if ((pll->flags & SUPPORTS_NO_VCO) || alpha_pll_find_vco(pll, rate))
return rate;

min_freq = pll->vco_table[0].min_freq;
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index 8b27c05..973673b 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -55,6 +55,7 @@ struct clk_alpha_pll {
#define SUPPORTS_FSM_MODE BIT(2)
#define SUPPORTS_64BIT_CONFIG_CTL BIT(3)
#define SUPPORTS_DYNAMIC_UPDATE BIT(4)
+#define SUPPORTS_NO_VCO BIT(5)
u8 flags;

struct clk_regmap clkr;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-08 18:25:05

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 09/12] clk: qcom: support for 2 bit PLL post divider

Current PLL driver only supports 4 bit PLL post divider so
modified the PLL divider operations to support 2 bit PLL
post divider.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/clk/qcom/clk-alpha-pll.c | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index b491dbe..4725f80 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -39,7 +39,6 @@
# define PLL_LOCK_DET BIT(31)

# define PLL_POST_DIV_SHIFT 8
-# define PLL_POST_DIV_MASK 0xf
# define PLL_ALPHA_EN BIT(24)
# define PLL_ALPHA_MODE BIT(25)
# define PLL_VCO_SHIFT 20
@@ -738,7 +737,7 @@ static long clk_alpha_huayra_pll_round_rate(struct clk_hw *hw,
regmap_read(pll->clkr.regmap, pll_user_ctl(pll), &ctl);

ctl >>= PLL_POST_DIV_SHIFT;
- ctl &= PLL_POST_DIV_MASK;
+ ctl &= BIT(pll->width) - 1;

return parent_rate >> fls(ctl);
}
@@ -752,13 +751,26 @@ static long clk_alpha_huayra_pll_round_rate(struct clk_hw *hw,
{ }
};

+static const struct clk_div_table clk_alpha_2bit_div_table[] = {
+ { 0x0, 1 },
+ { 0x1, 2 },
+ { 0x3, 4 },
+ { }
+};
+
static long
clk_alpha_pll_postdiv_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
+ const struct clk_div_table *table;
+
+ if (pll->width == 2)
+ table = clk_alpha_2bit_div_table;
+ else
+ table = clk_alpha_div_table;

- return divider_round_rate(hw, rate, prate, clk_alpha_div_table,
+ return divider_round_rate(hw, rate, prate, table,
pll->width, CLK_DIVIDER_POWER_OF_TWO);
}

@@ -772,7 +784,7 @@ static int clk_alpha_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate,
div = DIV_ROUND_UP_ULL((u64)parent_rate, rate) - 1;

return regmap_update_bits(pll->clkr.regmap, pll_user_ctl(pll),
- PLL_POST_DIV_MASK << PLL_POST_DIV_SHIFT,
+ (BIT(pll->width) - 1) << PLL_POST_DIV_SHIFT,
div << PLL_POST_DIV_SHIFT);
}

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-08 18:25:12

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 10/12] clk: qcom: add read-only alpha pll post divider operations

Some of the divider settings are preconfigured and should not
be changed by the clock framework during frequency change. This
patch adds the read-only divider operation for QCOM alpha pll
post divider which is equivalent to generic divider operations in
'commit 79c6ab509558 ("clk: divider: add CLK_DIVIDER_READ_ONLY flag")'.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/clk/qcom/clk-alpha-pll.c | 25 +++++++++++++++++++++++++
drivers/clk/qcom/clk-alpha-pll.h | 1 +
2 files changed, 26 insertions(+)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 4725f80..69d069f 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -774,6 +774,25 @@ static long clk_alpha_huayra_pll_round_rate(struct clk_hw *hw,
pll->width, CLK_DIVIDER_POWER_OF_TWO);
}

+static long
+clk_alpha_pll_postdiv_round_ro_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_alpha_pll_postdiv *pll = to_clk_alpha_pll_postdiv(hw);
+ u32 ctl, div;
+
+ regmap_read(pll->clkr.regmap, pll_user_ctl(pll), &ctl);
+
+ ctl >>= PLL_POST_DIV_SHIFT;
+ ctl &= BIT(pll->width) - 1;
+ div = 1 << fls(ctl);
+
+ if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT)
+ *prate = clk_hw_round_rate(clk_hw_get_parent(hw), div * rate);
+
+ return DIV_ROUND_UP_ULL((u64)*prate, div);
+}
+
static int clk_alpha_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
@@ -794,3 +813,9 @@ static int clk_alpha_pll_postdiv_set_rate(struct clk_hw *hw, unsigned long rate,
.set_rate = clk_alpha_pll_postdiv_set_rate,
};
EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_ops);
+
+const struct clk_ops clk_alpha_pll_postdiv_ro_ops = {
+ .round_rate = clk_alpha_pll_postdiv_round_ro_rate,
+ .recalc_rate = clk_alpha_pll_postdiv_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_ro_ops);
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index 8a0dab6..2b96beb 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -109,6 +109,7 @@ struct alpha_pll_config {
extern const struct clk_ops clk_alpha_pll_hwfsm_ops;
extern const struct clk_ops clk_alpha_huayra_pll_ops;
extern const struct clk_ops clk_alpha_pll_postdiv_ops;
+extern const struct clk_ops clk_alpha_pll_postdiv_ro_ops;

void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
const struct alpha_pll_config *config);
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-08 18:25:18

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 12/12] clk: qcom: add parent map for regmap mux

Currently the driver assumes the register configuration value
is identical to its index in the parent map. This patch adds
the parent map field in regmap mux clock node which contains
the mapping of parent index with actual register configuration
value. If regmap node contains this parent map then the
configuration value will be taken from this
parent map instead of simply writing the index value.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/clk/qcom/clk-rcg.h | 10 ----------
drivers/clk/qcom/clk-regmap-mux.c | 6 ++++++
drivers/clk/qcom/clk-regmap-mux.h | 2 ++
drivers/clk/qcom/common.h | 11 ++++++++++-
4 files changed, 18 insertions(+), 11 deletions(-)

diff --git a/drivers/clk/qcom/clk-rcg.h b/drivers/clk/qcom/clk-rcg.h
index 1b3e8d2..ee66857 100644
--- a/drivers/clk/qcom/clk-rcg.h
+++ b/drivers/clk/qcom/clk-rcg.h
@@ -26,16 +26,6 @@ struct freq_tbl {
};

/**
- * struct parent_map - map table for PLL source select configuration values
- * @src: source PLL
- * @cfg: configuration value
- */
-struct parent_map {
- u8 src;
- u8 cfg;
-};
-
-/**
* struct mn - M/N:D counter
* @mnctr_en_bit: bit to enable mn counter
* @mnctr_reset_bit: bit to assert mn counter reset
diff --git a/drivers/clk/qcom/clk-regmap-mux.c b/drivers/clk/qcom/clk-regmap-mux.c
index cae3071..0f3a1bd 100644
--- a/drivers/clk/qcom/clk-regmap-mux.c
+++ b/drivers/clk/qcom/clk-regmap-mux.c
@@ -35,6 +35,9 @@ static u8 mux_get_parent(struct clk_hw *hw)
val >>= mux->shift;
val &= mask;

+ if (mux->parent_map)
+ return qcom_find_src_index(hw, mux->parent_map, val);
+
return val;
}

@@ -45,6 +48,9 @@ static int mux_set_parent(struct clk_hw *hw, u8 index)
unsigned int mask = GENMASK(mux->width + mux->shift - 1, mux->shift);
unsigned int val;

+ if (mux->parent_map)
+ index = mux->parent_map[index].cfg;
+
val = index;
val <<= mux->shift;

diff --git a/drivers/clk/qcom/clk-regmap-mux.h b/drivers/clk/qcom/clk-regmap-mux.h
index 5cec761..7797cdd 100644
--- a/drivers/clk/qcom/clk-regmap-mux.h
+++ b/drivers/clk/qcom/clk-regmap-mux.h
@@ -16,11 +16,13 @@

#include <linux/clk-provider.h>
#include "clk-regmap.h"
+#include "common.h"

struct clk_regmap_mux {
u32 reg;
u32 shift;
u32 width;
+ const struct parent_map *parent_map;
struct clk_regmap clkr;
};

diff --git a/drivers/clk/qcom/common.h b/drivers/clk/qcom/common.h
index 23c1927..00196ee 100644
--- a/drivers/clk/qcom/common.h
+++ b/drivers/clk/qcom/common.h
@@ -20,7 +20,6 @@
struct regmap;
struct freq_tbl;
struct clk_hw;
-struct parent_map;

#define PLL_LOCK_COUNT_SHIFT 8
#define PLL_LOCK_COUNT_MASK 0x3f
@@ -39,6 +38,16 @@ struct qcom_cc_desc {
size_t num_gdscs;
};

+/**
+ * struct parent_map - map table for source select configuration values
+ * @src: source
+ * @cfg: configuration value
+ */
+struct parent_map {
+ u8 src;
+ u8 cfg;
+};
+
extern const struct freq_tbl *qcom_find_freq(const struct freq_tbl *f,
unsigned long rate);
extern const struct freq_tbl *qcom_find_freq_floor(const struct freq_tbl *f,
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-08 18:25:42

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 11/12] clk: qcom: add read-only divider operations

Some of the divider settings are preconfigured and should not
be changed by the clock framework during frequency change. This
patch adds the read-only divider operation for QCOM dividers
which is equivalent to generic divider operations in
'commit 79c6ab509558 ("clk: divider: add CLK_DIVIDER_READ_ONLY flag")'.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/clk/qcom/clk-regmap-divider.c | 29 +++++++++++++++++++++++++++++
drivers/clk/qcom/clk-regmap-divider.h | 1 +
2 files changed, 30 insertions(+)

diff --git a/drivers/clk/qcom/clk-regmap-divider.c b/drivers/clk/qcom/clk-regmap-divider.c
index 5348491..6cf9005 100644
--- a/drivers/clk/qcom/clk-regmap-divider.c
+++ b/drivers/clk/qcom/clk-regmap-divider.c
@@ -23,6 +23,29 @@ static inline struct clk_regmap_div *to_clk_regmap_div(struct clk_hw *hw)
return container_of(to_clk_regmap(hw), struct clk_regmap_div, clkr);
}

+static long div_round_ro_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_regmap_div *divider = to_clk_regmap_div(hw);
+ struct clk_regmap *clkr = &divider->clkr;
+ u32 div;
+ struct clk_hw *hw_parent = clk_hw_get_parent(hw);
+
+ regmap_read(clkr->regmap, divider->reg, &div);
+ div >>= divider->shift;
+ div &= BIT(divider->width) - 1;
+ div += 1;
+
+ if (clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) {
+ if (!hw_parent)
+ return -EINVAL;
+
+ *prate = clk_hw_round_rate(hw_parent, rate * div);
+ }
+
+ return DIV_ROUND_UP_ULL((u64)*prate, div);
+}
+
static long div_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
@@ -68,3 +91,9 @@ static unsigned long div_recalc_rate(struct clk_hw *hw,
.recalc_rate = div_recalc_rate,
};
EXPORT_SYMBOL_GPL(clk_regmap_div_ops);
+
+const struct clk_ops clk_regmap_div_ro_ops = {
+ .round_rate = div_round_ro_rate,
+ .recalc_rate = div_recalc_rate,
+};
+EXPORT_SYMBOL_GPL(clk_regmap_div_ro_ops);
diff --git a/drivers/clk/qcom/clk-regmap-divider.h b/drivers/clk/qcom/clk-regmap-divider.h
index fc4492e..8c39c27 100644
--- a/drivers/clk/qcom/clk-regmap-divider.h
+++ b/drivers/clk/qcom/clk-regmap-divider.h
@@ -25,5 +25,6 @@ struct clk_regmap_div {
};

extern const struct clk_ops clk_regmap_div_ops;
+extern const struct clk_ops clk_regmap_div_ro_ops;

#endif
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-08 18:24:59

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 07/12] clk: qcom: support for Huayra PLL

Following are the major differences in Huayra PLL

1. PLL Alpha Value is 16 bits
2. Depending on alpha_mode, the pll_alpha_val can be treated as
M/N value or as a two’s compliment number.
3. Huayra PLL supports PLL dynamic programming. User can change
L_VAL, without having to go through the power on sequence.

Since the decoding of alpha val and dynamic programming are
completely different from other Alpha PLL’s so this patch
made adds separate functions for Huayra PLL.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/clk/qcom/clk-alpha-pll.c | 180 +++++++++++++++++++++++++++++++++++++++
drivers/clk/qcom/clk-alpha-pll.h | 6 ++
2 files changed, 186 insertions(+)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index c368e7c..23e2bb7 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -41,9 +41,17 @@
# define PLL_POST_DIV_SHIFT 8
# define PLL_POST_DIV_MASK 0xf
# define PLL_ALPHA_EN BIT(24)
+# define PLL_ALPHA_MODE BIT(25)
# define PLL_VCO_SHIFT 20
# define PLL_VCO_MASK 0x3

+#define PLL_HUAYRA_M_WIDTH 8
+#define PLL_HUAYRA_M_SHIFT 8
+#define PLL_HUAYRA_M_MASK 0xff
+#define PLL_HUAYRA_N_SHIFT 0
+#define PLL_HUAYRA_N_MASK 0xff
+#define PLL_HUAYRA_ALPHA_WIDTH 16
+
/*
* Even though 40 bits are present, use only 32 for ease of calculation.
*/
@@ -86,6 +94,19 @@
};
EXPORT_SYMBOL_GPL(clk_alpha_pll_offsets);

+const u8 clk_alpha_huayra_pll_offsets[] = {
+ [ALPHA_PLL_MODE] = 0x00,
+ [ALPHA_PLL_L_VAL] = 0x04,
+ [ALPHA_PLL_ALPHA_VAL] = 0x08,
+ [ALPHA_PLL_USER_CTL] = 0x10,
+ [ALPHA_PLL_CONFIG_CTL] = 0x14,
+ [ALPHA_PLL_CONFIG_CTL_U] = 0x18,
+ [ALPHA_PLL_TEST_CTL] = 0x1c,
+ [ALPHA_PLL_TEST_CTL_U] = 0x20,
+ [ALPHA_PLL_STATUS] = 0x24,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_huayra_pll_offsets);
+
static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
const char *action)
{
@@ -517,6 +538,155 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
return clamp(rate, min_freq, max_freq);
}

+static unsigned long
+alpha_huayra_pll_calc_rate(u64 prate, u32 l, u32 a)
+{
+ /*
+ * a contains 16 bit alpha_val in two’s compliment number in the range
+ * of [-0.5, 0.5).
+ */
+ if (a >= BIT(PLL_HUAYRA_ALPHA_WIDTH - 1))
+ l -= 1;
+
+ return (prate * l) + (prate * a >> PLL_HUAYRA_ALPHA_WIDTH);
+}
+
+static unsigned long
+alpha_huayra_pll_round_rate(unsigned long rate, unsigned long prate,
+ u32 *l, u32 *a)
+{
+ u64 remainder;
+ u64 quotient;
+
+ quotient = rate;
+ remainder = do_div(quotient, prate);
+ *l = quotient;
+
+ if (!remainder) {
+ *a = 0;
+ return rate;
+ }
+
+ quotient = remainder << PLL_HUAYRA_ALPHA_WIDTH;
+ remainder = do_div(quotient, prate);
+
+ if (remainder)
+ quotient++;
+
+ /*
+ * alpha_val should be in two’s compliment number in the range
+ * of [-0.5, 0.5) so if quotient >= 0.5 then increment the l value
+ * since alpha value will be subtracted in this case.
+ */
+ if (quotient >= BIT(PLL_HUAYRA_ALPHA_WIDTH - 1))
+ *l += 1;
+
+ *a = quotient;
+ return alpha_huayra_pll_calc_rate(prate, *l, *a);
+}
+
+static unsigned long
+clk_alpha_huayra_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ u32 l, alpha = 0, ctl, alpha_m, alpha_n;
+ u64 rate = parent_rate, tmp;
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+
+ regmap_read(pll->clkr.regmap, pll_l(pll), &l);
+ regmap_read(pll->clkr.regmap, pll_user_ctl(pll), &ctl);
+
+ if (ctl & PLL_ALPHA_EN) {
+ regmap_read(pll->clkr.regmap, pll_alpha(pll), &alpha);
+ /*
+ * Depending upon alpha_mode, it can be treated as M/N value or
+ * as a two’s compliment number. When
+ * alpha_mode=1 pll_alpha_val<15:8>=M & pll_apla_val<7:0>=N
+ * Fout=FIN*(L+(M/N))
+ * M is a signed number (-128 to 127) and N is unsigned
+ * (0 to 255). M/N has to be within +/-0.5.
+ *
+ * alpha_mode=0, it is a two’s compliment number in the range
+ * of [-0.5, 0.5).
+ * Fout=FIN*(L+(alpha_val)/2^16),where alpha_val is
+ * two’s compliment number.
+ */
+ if (!(ctl & PLL_ALPHA_MODE))
+ return alpha_huayra_pll_calc_rate(rate, l, alpha);
+
+ alpha_m = alpha >> PLL_HUAYRA_M_SHIFT & PLL_HUAYRA_M_MASK;
+ alpha_n = alpha >> PLL_HUAYRA_N_SHIFT & PLL_HUAYRA_N_MASK;
+
+ rate *= l;
+ tmp = parent_rate;
+ if (alpha_m >= BIT(PLL_HUAYRA_M_WIDTH - 1)) {
+ alpha_m = BIT(PLL_HUAYRA_M_WIDTH) - alpha_m;
+ tmp *= alpha_m;
+ do_div(tmp, alpha_n);
+ rate -= tmp;
+ } else {
+ tmp *= alpha_m;
+ do_div(tmp, alpha_n);
+ rate += tmp;
+ }
+
+ return rate;
+ }
+
+ return alpha_huayra_pll_calc_rate(rate, l, alpha);
+}
+
+static int clk_alpha_huayra_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long prate)
+{
+ struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+ u32 l, a, ctl, cur_alpha = 0;
+
+ rate = alpha_huayra_pll_round_rate(rate, prate, &l, &a);
+
+ regmap_read(pll->clkr.regmap, pll_user_ctl(pll), &ctl);
+
+ if (ctl & PLL_ALPHA_EN)
+ regmap_read(pll->clkr.regmap, pll_alpha(pll), &cur_alpha);
+
+ /*
+ * Huayra PLL supports PLL dynamic programming. User can change L_VAL,
+ * without having to go through the power on sequence.
+ */
+ if (clk_hw_is_enabled(hw)) {
+ if (cur_alpha != a) {
+ pr_err("clock needs to be gated %s\n",
+ clk_hw_get_name(hw));
+ return -EBUSY;
+ }
+
+ regmap_write(pll->clkr.regmap, pll_l(pll), l);
+ /* Ensure that the write above goes to detect L val change. */
+ mb();
+ return wait_for_pll_enable_lock(pll);
+ }
+
+ regmap_write(pll->clkr.regmap, pll_l(pll), l);
+ regmap_write(pll->clkr.regmap, pll_alpha(pll), a);
+
+ if (a == 0)
+ regmap_update_bits(pll->clkr.regmap, pll_user_ctl(pll),
+ PLL_ALPHA_EN, 0x0);
+ else
+ regmap_update_bits(pll->clkr.regmap, pll_user_ctl(pll),
+ PLL_ALPHA_EN | PLL_ALPHA_MODE, PLL_ALPHA_EN);
+
+ return 0;
+}
+
+static long clk_alpha_huayra_pll_round_rate(struct clk_hw *hw,
+ unsigned long rate,
+ unsigned long *prate)
+{
+ u32 l, a;
+
+ return alpha_huayra_pll_round_rate(rate, *prate, &l, &a);
+}
+
const struct clk_ops clk_alpha_pll_ops = {
.enable = clk_alpha_pll_enable,
.disable = clk_alpha_pll_disable,
@@ -537,6 +707,16 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
};
EXPORT_SYMBOL_GPL(clk_alpha_pll_hwfsm_ops);

+const struct clk_ops clk_alpha_huayra_pll_ops = {
+ .enable = clk_alpha_pll_enable,
+ .disable = clk_alpha_pll_disable,
+ .is_enabled = clk_alpha_pll_is_enabled,
+ .recalc_rate = clk_alpha_huayra_pll_recalc_rate,
+ .round_rate = clk_alpha_huayra_pll_round_rate,
+ .set_rate = clk_alpha_huayra_pll_set_rate,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_huayra_pll_ops);
+
static unsigned long
clk_alpha_pll_postdiv_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index 973673b..af74abf 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -61,6 +61,10 @@ struct clk_alpha_pll {
struct clk_regmap clkr;
};

+#define CLK_HUAYRA_PLL_FLAGS (SUPPORTS_NO_VCO | SUPPORTS_DYNAMIC_UPDATE | \
+ SUPPORTS_64BIT_CONFIG_CTL | \
+ SUPPORTS_16BIT_ALPHA)
+
/**
* struct clk_alpha_pll_postdiv - phase locked loop (PLL) post-divider
* @base: base address of registers
@@ -97,9 +101,11 @@ struct alpha_pll_config {
};

extern const u8 clk_alpha_pll_offsets[];
+extern const u8 clk_alpha_huayra_pll_offsets[];

extern const struct clk_ops clk_alpha_pll_ops;
extern const struct clk_ops clk_alpha_pll_hwfsm_ops;
+extern const struct clk_ops clk_alpha_huayra_pll_ops;
extern const struct clk_ops clk_alpha_pll_postdiv_ops;

void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-08 18:27:29

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 08/12] clk: qcom: support for Brammo PLL

1. Brammo PLL does not allow configuration of VCO
2. Supports the dynamic update in which the frequency can
be changed dynamically without turning off the PLL
3. The register offsets are different from normal Alpha PLL

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/clk/qcom/clk-alpha-pll.c | 12 ++++++++++++
drivers/clk/qcom/clk-alpha-pll.h | 2 ++
2 files changed, 14 insertions(+)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 23e2bb7..b491dbe 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -107,6 +107,18 @@
};
EXPORT_SYMBOL_GPL(clk_alpha_huayra_pll_offsets);

+const u8 clk_alpha_brammo_pll_offsets[] = {
+ [ALPHA_PLL_MODE] = 0x00,
+ [ALPHA_PLL_L_VAL] = 0x04,
+ [ALPHA_PLL_ALPHA_VAL] = 0x08,
+ [ALPHA_PLL_ALPHA_VAL_U] = 0x0c,
+ [ALPHA_PLL_USER_CTL] = 0x10,
+ [ALPHA_PLL_CONFIG_CTL] = 0x18,
+ [ALPHA_PLL_TEST_CTL] = 0x1c,
+ [ALPHA_PLL_STATUS] = 0x24,
+};
+EXPORT_SYMBOL_GPL(clk_alpha_brammo_pll_offsets);
+
static int wait_for_pll(struct clk_alpha_pll *pll, u32 mask, bool inverse,
const char *action)
{
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index af74abf..8a0dab6 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -64,6 +64,7 @@ struct clk_alpha_pll {
#define CLK_HUAYRA_PLL_FLAGS (SUPPORTS_NO_VCO | SUPPORTS_DYNAMIC_UPDATE | \
SUPPORTS_64BIT_CONFIG_CTL | \
SUPPORTS_16BIT_ALPHA)
+#define CLK_BRAMMO_PLL_FLAGS (SUPPORTS_NO_VCO | SUPPORTS_DYNAMIC_UPDATE)

/**
* struct clk_alpha_pll_postdiv - phase locked loop (PLL) post-divider
@@ -102,6 +103,7 @@ struct alpha_pll_config {

extern const u8 clk_alpha_pll_offsets[];
extern const u8 clk_alpha_huayra_pll_offsets[];
+extern const u8 clk_alpha_brammo_pll_offsets[];

extern const struct clk_ops clk_alpha_pll_ops;
extern const struct clk_ops clk_alpha_pll_hwfsm_ops;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-08 18:28:16

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 04/12] clk: qcom: fix 16 bit alpha support calculation

The alpha value calculation function has been written for 40 bit
alpha which is not coming properly for 16 bit

1. Alpha value is being calculated on the basis of
ALPHA_BITWIDTH to make the computation easy for 40 bit alpha.
After calculating the 32 bit alpha, It is being converted to 40
bit alpha by making making lower bits zero. But if actual alpha
register width is less than ALPHA_BITWIDTH, then the actual width
can be used for calculation

2. During 40 bit alpha pll set rate, the lower alpha register is
not being configured

Now the changes have been made to calculate the rate and register
values from alpha_width instead hardcoding it so that it can work
for all the cases.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/clk/qcom/clk-alpha-pll.c | 56 +++++++++++++++++++++++-----------------
1 file changed, 33 insertions(+), 23 deletions(-)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 084fbdc..6694fd5 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -45,8 +45,11 @@
* Even though 40 bits are present, use only 32 for ease of calculation.
*/
#define ALPHA_REG_BITWIDTH 40
+#define ALPHA_REG_16BIT_WIDTH 16
#define ALPHA_BITWIDTH 32
-#define ALPHA_16BIT_MASK 0xffff
+
+#define pll_alpha_width(pll) (pll->flags & SUPPORTS_16BIT_ALPHA ? \
+ ALPHA_REG_16BIT_WIDTH : ALPHA_REG_BITWIDTH)

#define pll_mode(pll) (pll->base + pll->offsets[ALPHA_PLL_MODE])
#define pll_l(pll) (pll->base + pll->offsets[ALPHA_PLL_L_VAL])
@@ -317,13 +320,16 @@ static void clk_alpha_pll_disable(struct clk_hw *hw)
regmap_update_bits(pll->clkr.regmap, pll_mode(pll), mask, 0);
}

-static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a)
+static unsigned long
+alpha_pll_calc_rate(u64 prate, u32 l, u32 a, u32 alpha_width)
{
- return (prate * l) + ((prate * a) >> ALPHA_BITWIDTH);
+ return (prate * l) + ((prate * a) >>
+ (alpha_width < ALPHA_BITWIDTH ? alpha_width : ALPHA_BITWIDTH));
}

static unsigned long
-alpha_pll_round_rate(unsigned long rate, unsigned long prate, u32 *l, u64 *a)
+alpha_pll_round_rate(unsigned long rate, unsigned long prate, u32 *l, u64 *a,
+ u32 alpha_width)
{
u64 remainder;
u64 quotient;
@@ -338,14 +344,16 @@ static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a)
}

/* Upper ALPHA_BITWIDTH bits of Alpha */
- quotient = remainder << ALPHA_BITWIDTH;
+ quotient = remainder << (alpha_width < ALPHA_BITWIDTH ?
+ alpha_width : ALPHA_BITWIDTH);
+
remainder = do_div(quotient, prate);

if (remainder)
quotient++;

*a = quotient;
- return alpha_pll_calc_rate(prate, *l, *a);
+ return alpha_pll_calc_rate(prate, *l, *a, alpha_width);
}

static const struct pll_vco *
@@ -364,26 +372,28 @@ static unsigned long alpha_pll_calc_rate(u64 prate, u32 l, u32 a)
static unsigned long
clk_alpha_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
- u32 l, low, high, ctl;
- u64 a = 0, prate = parent_rate;
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
+ u32 l, low, high, ctl, alpha_width = pll_alpha_width(pll);
+ u64 a = 0, prate = parent_rate;

regmap_read(pll->clkr.regmap, pll_l(pll), &l);

regmap_read(pll->clkr.regmap, pll_user_ctl(pll), &ctl);
if (ctl & PLL_ALPHA_EN) {
regmap_read(pll->clkr.regmap, pll_alpha(pll), &low);
- if (pll->flags & SUPPORTS_16BIT_ALPHA) {
- a = low & ALPHA_16BIT_MASK;
- } else {
+ if (alpha_width > 32) {
regmap_read(pll->clkr.regmap, pll_alpha_u(pll),
&high);
a = (u64)high << 32 | low;
- a >>= ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH;
+ } else {
+ a = low & GENMASK(0, alpha_width - 1);
}
+
+ if (alpha_width > ALPHA_BITWIDTH)
+ a >>= alpha_width - ALPHA_BITWIDTH;
}

- return alpha_pll_calc_rate(prate, l, a);
+ return alpha_pll_calc_rate(prate, l, a, alpha_width);
}

static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -391,10 +401,10 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
const struct pll_vco *vco;
- u32 l;
+ u32 l, alpha_width = pll_alpha_width(pll);
u64 a;

- rate = alpha_pll_round_rate(rate, prate, &l, &a);
+ rate = alpha_pll_round_rate(rate, prate, &l, &a, alpha_width);
vco = alpha_pll_find_vco(pll, rate);
if (!vco) {
pr_err("alpha pll not in a valid vco range\n");
@@ -403,13 +413,13 @@ static int clk_alpha_pll_set_rate(struct clk_hw *hw, unsigned long rate,

regmap_write(pll->clkr.regmap, pll_l(pll), l);

- if (pll->flags & SUPPORTS_16BIT_ALPHA) {
- regmap_write(pll->clkr.regmap, pll_alpha(pll),
- a & ALPHA_16BIT_MASK);
- } else {
- a <<= (ALPHA_REG_BITWIDTH - ALPHA_BITWIDTH);
+ if (alpha_width > ALPHA_BITWIDTH)
+ a <<= alpha_width - ALPHA_BITWIDTH;
+
+ if (alpha_width > 32)
regmap_write(pll->clkr.regmap, pll_alpha_u(pll), a >> 32);
- }
+
+ regmap_write(pll->clkr.regmap, pll_alpha(pll), a);

regmap_update_bits(pll->clkr.regmap, pll_user_ctl(pll),
PLL_VCO_MASK << PLL_VCO_SHIFT,
@@ -425,11 +435,11 @@ static long clk_alpha_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_alpha_pll *pll = to_clk_alpha_pll(hw);
- u32 l;
+ u32 l, alpha_width = pll_alpha_width(pll);
u64 a;
unsigned long min_freq, max_freq;

- rate = alpha_pll_round_rate(rate, *prate, &l, &a);
+ rate = alpha_pll_round_rate(rate, *prate, &l, &a, alpha_width);
if (alpha_pll_find_vco(pll, rate))
return rate;

--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation

2017-08-08 18:28:44

by Abhishek Sahu

[permalink] [raw]
Subject: [RFC v2 01/12] clk: qcom: flag for 64 bit CONFIG_CTL

Some of the Alpha PLL’s (like Spark, Brammo PLL) do not have
CONFIG_CTL_U register. This patch adds the flag for PLL’s
which have CONFIG_CTL_U register and checks the same while
doing PLL initial configuration.

Signed-off-by: Abhishek Sahu <[email protected]>
---
drivers/clk/qcom/clk-alpha-pll.c | 5 ++++-
drivers/clk/qcom/clk-alpha-pll.h | 7 ++++---
2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c
index 47a1da3..e6cde2d 100644
--- a/drivers/clk/qcom/clk-alpha-pll.c
+++ b/drivers/clk/qcom/clk-alpha-pll.c
@@ -118,7 +118,10 @@ void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap,
regmap_write(regmap, off + PLL_L_VAL, config->l);
regmap_write(regmap, off + PLL_ALPHA_VAL, config->alpha);
regmap_write(regmap, off + PLL_CONFIG_CTL, config->config_ctl_val);
- regmap_write(regmap, off + PLL_CONFIG_CTL_U, config->config_ctl_hi_val);
+
+ if (pll->flags & SUPPORTS_64BIT_CONFIG_CTL)
+ regmap_write(regmap, off + PLL_CONFIG_CTL_U,
+ config->config_ctl_hi_val);

val = config->main_output_mask;
val |= config->aux_output_mask;
diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h
index d6e1ee2..bbd6aa9 100644
--- a/drivers/clk/qcom/clk-alpha-pll.h
+++ b/drivers/clk/qcom/clk-alpha-pll.h
@@ -34,9 +34,10 @@ struct clk_alpha_pll {

const struct pll_vco *vco_table;
size_t num_vco;
-#define SUPPORTS_OFFLINE_REQ BIT(0)
-#define SUPPORTS_16BIT_ALPHA BIT(1)
-#define SUPPORTS_FSM_MODE BIT(2)
+#define SUPPORTS_OFFLINE_REQ BIT(0)
+#define SUPPORTS_16BIT_ALPHA BIT(1)
+#define SUPPORTS_FSM_MODE BIT(2)
+#define SUPPORTS_64BIT_CONFIG_CTL BIT(3)
u8 flags;

struct clk_regmap clkr;
--
QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation