mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 18:51:56 -04:00
Merge branches 'clk-spreadtrum', 'clk-mvebu-dvfs', 'clk-qoriq', 'clk-imx' and 'clk-qcom-ipq8074' into clk-next
* clk-spreadtrum: clk: sprd: add clocks support for SC9860 clk: sprd: Add dt-bindings include file for SC9860 dt-bindings: Add Spreadtrum clock binding documentation clk: sprd: add adjustable pll support clk: sprd: add composite clock support clk: sprd: add divider clock support clk: sprd: add mux clock support clk: sprd: add gate clock support clk: sprd: Add common infrastructure clk: move clock common macros out from vendor directories * clk-mvebu-dvfs: clk: mvebu: armada-37xx-periph: add DVFS support for cpu clocks clk: mvebu: armada-37xx-periph: prepare cpu clk to be used with DVFS clk: mvebu: armada-37xx-periph: cosmetic changes * clk-qoriq: clk: qoriq: add more divider clocks support * clk-imx: clk: imx51: uart4, uart5 gates only exist on imx50, imx53 * clk-qcom-ipq8074: clk: qcom: ipq8074: add misc resets for PCIE and NSS dt-bindings: clock: qcom: add misc resets for PCIE and NSS clk: qcom: ipq8074: add GP and Crypto clocks clk: qcom: ipq8074: add NSS ethernet port clocks clk: qcom: ipq8074: add NSS clocks clk: qcom: ipq8074: add PCIE, USB and SDCC clocks clk: qcom: ipq8074: add remaining PLL’s dt-bindings: clock: qcom: add remaining clocks for IPQ8074 clk: qcom: ipq8074: fix missing GPLL0 divider width clk: qcom: add parent map for regmap mux clk: qcom: add read-only divider operations
This commit is contained in:
commit
21170e3bda
34 changed files with 8152 additions and 292 deletions
|
|
@ -236,6 +236,7 @@ source "drivers/clk/mvebu/Kconfig"
|
|||
source "drivers/clk/qcom/Kconfig"
|
||||
source "drivers/clk/renesas/Kconfig"
|
||||
source "drivers/clk/samsung/Kconfig"
|
||||
source "drivers/clk/sprd/Kconfig"
|
||||
source "drivers/clk/sunxi-ng/Kconfig"
|
||||
source "drivers/clk/tegra/Kconfig"
|
||||
source "drivers/clk/ti/Kconfig"
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
|
|||
obj-$(CONFIG_ARCH_SIRF) += sirf/
|
||||
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
|
||||
obj-$(CONFIG_PLAT_SPEAR) += spear/
|
||||
obj-$(CONFIG_ARCH_SPRD) += sprd/
|
||||
obj-$(CONFIG_ARCH_STI) += st/
|
||||
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
|
||||
obj-$(CONFIG_ARCH_SUNXI) += sunxi-ng/
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ struct clockgen_pll_div {
|
|||
};
|
||||
|
||||
struct clockgen_pll {
|
||||
struct clockgen_pll_div div[4];
|
||||
struct clockgen_pll_div div[8];
|
||||
};
|
||||
|
||||
#define CLKSEL_VALID 1
|
||||
|
|
@ -1127,6 +1127,13 @@ static void __init create_one_pll(struct clockgen *cg, int idx)
|
|||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* For platform PLL, there are 8 divider clocks.
|
||||
* For core PLL, there are 4 divider clocks at most.
|
||||
*/
|
||||
if (idx != PLATFORM_PLL && i >= 4)
|
||||
break;
|
||||
|
||||
snprintf(pll->div[i].name, sizeof(pll->div[i].name),
|
||||
"cg-pll%d-div%d", idx, i + 1);
|
||||
|
||||
|
|
|
|||
|
|
@ -257,10 +257,6 @@ static void __init mx5_clocks_common_init(void __iomem *ccm_base)
|
|||
clk[IMX5_CLK_VPU_SEL] = imx_clk_mux("vpu_sel", MXC_CCM_CBCMR, 14, 2, vpu_sel, ARRAY_SIZE(vpu_sel));
|
||||
clk[IMX5_CLK_VPU_GATE] = imx_clk_gate2("vpu_gate", "vpu_sel", MXC_CCM_CCGR5, 6);
|
||||
clk[IMX5_CLK_VPU_REFERENCE_GATE] = imx_clk_gate2("vpu_reference_gate", "osc", MXC_CCM_CCGR5, 8);
|
||||
clk[IMX5_CLK_UART4_IPG_GATE] = imx_clk_gate2("uart4_ipg_gate", "ipg", MXC_CCM_CCGR7, 8);
|
||||
clk[IMX5_CLK_UART4_PER_GATE] = imx_clk_gate2("uart4_per_gate", "uart_root", MXC_CCM_CCGR7, 10);
|
||||
clk[IMX5_CLK_UART5_IPG_GATE] = imx_clk_gate2("uart5_ipg_gate", "ipg", MXC_CCM_CCGR7, 12);
|
||||
clk[IMX5_CLK_UART5_PER_GATE] = imx_clk_gate2("uart5_per_gate", "uart_root", MXC_CCM_CCGR7, 14);
|
||||
clk[IMX5_CLK_GPC_DVFS] = imx_clk_gate2("gpc_dvfs", "dummy", MXC_CCM_CCGR5, 24);
|
||||
|
||||
clk[IMX5_CLK_SSI_APM] = imx_clk_mux("ssi_apm", MXC_CCM_CSCMR1, 8, 2, ssi_apm_sels, ARRAY_SIZE(ssi_apm_sels));
|
||||
|
|
@ -361,6 +357,10 @@ static void __init mx50_clocks_init(struct device_node *np)
|
|||
clk[IMX5_CLK_USB_PHY1_GATE] = imx_clk_gate2("usb_phy1_gate", "usb_phy_sel", MXC_CCM_CCGR4, 10);
|
||||
clk[IMX5_CLK_USB_PHY2_GATE] = imx_clk_gate2("usb_phy2_gate", "usb_phy_sel", MXC_CCM_CCGR4, 12);
|
||||
clk[IMX5_CLK_I2C3_GATE] = imx_clk_gate2("i2c3_gate", "per_root", MXC_CCM_CCGR1, 22);
|
||||
clk[IMX5_CLK_UART4_IPG_GATE] = imx_clk_gate2("uart4_ipg_gate", "ipg", MXC_CCM_CCGR7, 8);
|
||||
clk[IMX5_CLK_UART4_PER_GATE] = imx_clk_gate2("uart4_per_gate", "uart_root", MXC_CCM_CCGR7, 10);
|
||||
clk[IMX5_CLK_UART5_IPG_GATE] = imx_clk_gate2("uart5_ipg_gate", "ipg", MXC_CCM_CCGR7, 12);
|
||||
clk[IMX5_CLK_UART5_PER_GATE] = imx_clk_gate2("uart5_per_gate", "uart_root", MXC_CCM_CCGR7, 14);
|
||||
|
||||
clk[IMX5_CLK_CKO1_SEL] = imx_clk_mux("cko1_sel", MXC_CCM_CCOSR, 0, 4,
|
||||
mx53_cko1_sel, ARRAY_SIZE(mx53_cko1_sel));
|
||||
|
|
@ -562,6 +562,10 @@ static void __init mx53_clocks_init(struct device_node *np)
|
|||
clk[IMX5_CLK_IEEE1588_PRED] = imx_clk_divider("ieee1588_pred", "ieee1588_sel", MXC_CCM_CSCDR2, 6, 3);
|
||||
clk[IMX5_CLK_IEEE1588_PODF] = imx_clk_divider("ieee1588_podf", "ieee1588_pred", MXC_CCM_CSCDR2, 0, 6);
|
||||
clk[IMX5_CLK_IEEE1588_GATE] = imx_clk_gate2("ieee1588_serial_gate", "ieee1588_podf", MXC_CCM_CCGR7, 6);
|
||||
clk[IMX5_CLK_UART4_IPG_GATE] = imx_clk_gate2("uart4_ipg_gate", "ipg", MXC_CCM_CCGR7, 8);
|
||||
clk[IMX5_CLK_UART4_PER_GATE] = imx_clk_gate2("uart4_per_gate", "uart_root", MXC_CCM_CCGR7, 10);
|
||||
clk[IMX5_CLK_UART5_IPG_GATE] = imx_clk_gate2("uart5_ipg_gate", "ipg", MXC_CCM_CCGR7, 12);
|
||||
clk[IMX5_CLK_UART5_PER_GATE] = imx_clk_gate2("uart5_per_gate", "uart_root", MXC_CCM_CCGR7, 14);
|
||||
|
||||
clk[IMX5_CLK_CKO1_SEL] = imx_clk_mux("cko1_sel", MXC_CCM_CCOSR, 0, 4,
|
||||
mx53_cko1_sel, ARRAY_SIZE(mx53_cko1_sel));
|
||||
|
|
|
|||
|
|
@ -21,9 +21,11 @@
|
|||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define TBG_SEL 0x0
|
||||
|
|
@ -33,6 +35,26 @@
|
|||
#define CLK_SEL 0x10
|
||||
#define CLK_DIS 0x14
|
||||
|
||||
#define LOAD_LEVEL_NR 4
|
||||
|
||||
#define ARMADA_37XX_NB_L0L1 0x18
|
||||
#define ARMADA_37XX_NB_L2L3 0x1C
|
||||
#define ARMADA_37XX_NB_TBG_DIV_OFF 13
|
||||
#define ARMADA_37XX_NB_TBG_DIV_MASK 0x7
|
||||
#define ARMADA_37XX_NB_CLK_SEL_OFF 11
|
||||
#define ARMADA_37XX_NB_CLK_SEL_MASK 0x1
|
||||
#define ARMADA_37XX_NB_TBG_SEL_OFF 9
|
||||
#define ARMADA_37XX_NB_TBG_SEL_MASK 0x3
|
||||
#define ARMADA_37XX_NB_CONFIG_SHIFT 16
|
||||
#define ARMADA_37XX_NB_DYN_MOD 0x24
|
||||
#define ARMADA_37XX_NB_DFS_EN 31
|
||||
#define ARMADA_37XX_NB_CPU_LOAD 0x30
|
||||
#define ARMADA_37XX_NB_CPU_LOAD_MASK 0x3
|
||||
#define ARMADA_37XX_DVFS_LOAD_0 0
|
||||
#define ARMADA_37XX_DVFS_LOAD_1 1
|
||||
#define ARMADA_37XX_DVFS_LOAD_2 2
|
||||
#define ARMADA_37XX_DVFS_LOAD_3 3
|
||||
|
||||
struct clk_periph_driver_data {
|
||||
struct clk_hw_onecell_data *hw_data;
|
||||
spinlock_t lock;
|
||||
|
|
@ -46,7 +68,18 @@ struct clk_double_div {
|
|||
u8 shift2;
|
||||
};
|
||||
|
||||
struct clk_pm_cpu {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg_mux;
|
||||
u8 shift_mux;
|
||||
u32 mask_mux;
|
||||
void __iomem *reg_div;
|
||||
u8 shift_div;
|
||||
struct regmap *nb_pm_base;
|
||||
};
|
||||
|
||||
#define to_clk_double_div(_hw) container_of(_hw, struct clk_double_div, hw)
|
||||
#define to_clk_pm_cpu(_hw) container_of(_hw, struct clk_pm_cpu, hw)
|
||||
|
||||
struct clk_periph_data {
|
||||
const char *name;
|
||||
|
|
@ -55,6 +88,7 @@ struct clk_periph_data {
|
|||
struct clk_hw *mux_hw;
|
||||
struct clk_hw *rate_hw;
|
||||
struct clk_hw *gate_hw;
|
||||
struct clk_hw *muxrate_hw;
|
||||
bool is_double_div;
|
||||
};
|
||||
|
||||
|
|
@ -79,7 +113,9 @@ static const struct clk_div_table clk_table2[] = {
|
|||
{ .val = 1, .div = 4, },
|
||||
{ .val = 0, .div = 0, }, /* last entry */
|
||||
};
|
||||
|
||||
static const struct clk_ops clk_double_div_ops;
|
||||
static const struct clk_ops clk_pm_cpu_ops;
|
||||
|
||||
#define PERIPH_GATE(_name, _bit) \
|
||||
struct clk_gate gate_##_name = { \
|
||||
|
|
@ -121,6 +157,18 @@ struct clk_divider rate_##_name = { \
|
|||
} \
|
||||
};
|
||||
|
||||
#define PERIPH_PM_CPU(_name, _shift1, _reg, _shift2) \
|
||||
struct clk_pm_cpu muxrate_##_name = { \
|
||||
.reg_mux = (void *)TBG_SEL, \
|
||||
.mask_mux = 3, \
|
||||
.shift_mux = _shift1, \
|
||||
.reg_div = (void *)_reg, \
|
||||
.shift_div = _shift2, \
|
||||
.hw.init = &(struct clk_init_data){ \
|
||||
.ops = &clk_pm_cpu_ops, \
|
||||
} \
|
||||
};
|
||||
|
||||
#define PERIPH_CLK_FULL_DD(_name, _bit, _shift, _reg1, _reg2, _shift1, _shift2)\
|
||||
static PERIPH_GATE(_name, _bit); \
|
||||
static PERIPH_MUX(_name, _shift); \
|
||||
|
|
@ -135,10 +183,6 @@ static PERIPH_DIV(_name, _reg, _shift1, _table);
|
|||
static PERIPH_GATE(_name, _bit); \
|
||||
static PERIPH_DIV(_name, _reg, _shift, _table);
|
||||
|
||||
#define PERIPH_CLK_MUX_DIV(_name, _shift, _reg, _shift_div, _table) \
|
||||
static PERIPH_MUX(_name, _shift); \
|
||||
static PERIPH_DIV(_name, _reg, _shift_div, _table);
|
||||
|
||||
#define PERIPH_CLK_MUX_DD(_name, _shift, _reg1, _reg2, _shift1, _shift2)\
|
||||
static PERIPH_MUX(_name, _shift); \
|
||||
static PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2);
|
||||
|
|
@ -179,13 +223,12 @@ static PERIPH_DOUBLEDIV(_name, _reg1, _reg2, _shift1, _shift2);
|
|||
.rate_hw = &rate_##_name.hw, \
|
||||
}
|
||||
|
||||
#define REF_CLK_MUX_DIV(_name) \
|
||||
#define REF_CLK_PM_CPU(_name) \
|
||||
{ .name = #_name, \
|
||||
.parent_names = (const char *[]){ "TBG-A-P", \
|
||||
"TBG-B-P", "TBG-A-S", "TBG-B-S"}, \
|
||||
.num_parents = 4, \
|
||||
.mux_hw = &mux_##_name.hw, \
|
||||
.rate_hw = &rate_##_name.hw, \
|
||||
.muxrate_hw = &muxrate_##_name.hw, \
|
||||
}
|
||||
|
||||
#define REF_CLK_MUX_DD(_name) \
|
||||
|
|
@ -215,9 +258,9 @@ PERIPH_CLK_FULL_DD(ddr_fclk, 21, 16, DIV_SEL0, DIV_SEL0, 15, 12);
|
|||
PERIPH_CLK_FULL(trace, 22, 18, DIV_SEL0, 20, clk_table6);
|
||||
PERIPH_CLK_FULL(counter, 23, 20, DIV_SEL0, 23, clk_table6);
|
||||
PERIPH_CLK_FULL_DD(eip97, 24, 24, DIV_SEL2, DIV_SEL2, 22, 19);
|
||||
PERIPH_CLK_MUX_DIV(cpu, 22, DIV_SEL0, 28, clk_table6);
|
||||
static PERIPH_PM_CPU(cpu, 22, DIV_SEL0, 28);
|
||||
|
||||
static struct clk_periph_data data_nb[] ={
|
||||
static struct clk_periph_data data_nb[] = {
|
||||
REF_CLK_FULL_DD(mmc),
|
||||
REF_CLK_FULL_DD(sata_host),
|
||||
REF_CLK_FULL_DD(sec_at),
|
||||
|
|
@ -234,7 +277,7 @@ static struct clk_periph_data data_nb[] ={
|
|||
REF_CLK_FULL(trace),
|
||||
REF_CLK_FULL(counter),
|
||||
REF_CLK_FULL_DD(eip97),
|
||||
REF_CLK_MUX_DIV(cpu),
|
||||
REF_CLK_PM_CPU(cpu),
|
||||
{ },
|
||||
};
|
||||
|
||||
|
|
@ -281,7 +324,7 @@ static unsigned int get_div(void __iomem *reg, int shift)
|
|||
}
|
||||
|
||||
static unsigned long clk_double_div_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_double_div *double_div = to_clk_double_div(hw);
|
||||
unsigned int div;
|
||||
|
|
@ -296,6 +339,222 @@ static const struct clk_ops clk_double_div_ops = {
|
|||
.recalc_rate = clk_double_div_recalc_rate,
|
||||
};
|
||||
|
||||
static void armada_3700_pm_dvfs_update_regs(unsigned int load_level,
|
||||
unsigned int *reg,
|
||||
unsigned int *offset)
|
||||
{
|
||||
if (load_level <= ARMADA_37XX_DVFS_LOAD_1)
|
||||
*reg = ARMADA_37XX_NB_L0L1;
|
||||
else
|
||||
*reg = ARMADA_37XX_NB_L2L3;
|
||||
|
||||
if (load_level == ARMADA_37XX_DVFS_LOAD_0 ||
|
||||
load_level == ARMADA_37XX_DVFS_LOAD_2)
|
||||
*offset += ARMADA_37XX_NB_CONFIG_SHIFT;
|
||||
}
|
||||
|
||||
static bool armada_3700_pm_dvfs_is_enabled(struct regmap *base)
|
||||
{
|
||||
unsigned int val, reg = ARMADA_37XX_NB_DYN_MOD;
|
||||
|
||||
if (IS_ERR(base))
|
||||
return false;
|
||||
|
||||
regmap_read(base, reg, &val);
|
||||
|
||||
return !!(val & BIT(ARMADA_37XX_NB_DFS_EN));
|
||||
}
|
||||
|
||||
static unsigned int armada_3700_pm_dvfs_get_cpu_div(struct regmap *base)
|
||||
{
|
||||
unsigned int reg = ARMADA_37XX_NB_CPU_LOAD;
|
||||
unsigned int offset = ARMADA_37XX_NB_TBG_DIV_OFF;
|
||||
unsigned int load_level, div;
|
||||
|
||||
/*
|
||||
* This function is always called after the function
|
||||
* armada_3700_pm_dvfs_is_enabled, so no need to check again
|
||||
* if the base is valid.
|
||||
*/
|
||||
regmap_read(base, reg, &load_level);
|
||||
|
||||
/*
|
||||
* The register and the offset inside this register accessed to
|
||||
* read the current divider depend on the load level
|
||||
*/
|
||||
load_level &= ARMADA_37XX_NB_CPU_LOAD_MASK;
|
||||
armada_3700_pm_dvfs_update_regs(load_level, ®, &offset);
|
||||
|
||||
regmap_read(base, reg, &div);
|
||||
|
||||
return (div >> offset) & ARMADA_37XX_NB_TBG_DIV_MASK;
|
||||
}
|
||||
|
||||
static unsigned int armada_3700_pm_dvfs_get_cpu_parent(struct regmap *base)
|
||||
{
|
||||
unsigned int reg = ARMADA_37XX_NB_CPU_LOAD;
|
||||
unsigned int offset = ARMADA_37XX_NB_TBG_SEL_OFF;
|
||||
unsigned int load_level, sel;
|
||||
|
||||
/*
|
||||
* This function is always called after the function
|
||||
* armada_3700_pm_dvfs_is_enabled, so no need to check again
|
||||
* if the base is valid
|
||||
*/
|
||||
regmap_read(base, reg, &load_level);
|
||||
|
||||
/*
|
||||
* The register and the offset inside this register accessed to
|
||||
* read the current divider depend on the load level
|
||||
*/
|
||||
load_level &= ARMADA_37XX_NB_CPU_LOAD_MASK;
|
||||
armada_3700_pm_dvfs_update_regs(load_level, ®, &offset);
|
||||
|
||||
regmap_read(base, reg, &sel);
|
||||
|
||||
return (sel >> offset) & ARMADA_37XX_NB_TBG_SEL_MASK;
|
||||
}
|
||||
|
||||
static u8 clk_pm_cpu_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct clk_pm_cpu *pm_cpu = to_clk_pm_cpu(hw);
|
||||
int num_parents = clk_hw_get_num_parents(hw);
|
||||
u32 val;
|
||||
|
||||
if (armada_3700_pm_dvfs_is_enabled(pm_cpu->nb_pm_base)) {
|
||||
val = armada_3700_pm_dvfs_get_cpu_parent(pm_cpu->nb_pm_base);
|
||||
} else {
|
||||
val = readl(pm_cpu->reg_mux) >> pm_cpu->shift_mux;
|
||||
val &= pm_cpu->mask_mux;
|
||||
}
|
||||
|
||||
if (val >= num_parents)
|
||||
return -EINVAL;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int clk_pm_cpu_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct clk_pm_cpu *pm_cpu = to_clk_pm_cpu(hw);
|
||||
struct regmap *base = pm_cpu->nb_pm_base;
|
||||
int load_level;
|
||||
|
||||
/*
|
||||
* We set the clock parent only if the DVFS is available but
|
||||
* not enabled.
|
||||
*/
|
||||
if (IS_ERR(base) || armada_3700_pm_dvfs_is_enabled(base))
|
||||
return -EINVAL;
|
||||
|
||||
/* Set the parent clock for all the load level */
|
||||
for (load_level = 0; load_level < LOAD_LEVEL_NR; load_level++) {
|
||||
unsigned int reg, mask, val,
|
||||
offset = ARMADA_37XX_NB_TBG_SEL_OFF;
|
||||
|
||||
armada_3700_pm_dvfs_update_regs(load_level, ®, &offset);
|
||||
|
||||
val = index << offset;
|
||||
mask = ARMADA_37XX_NB_TBG_SEL_MASK << offset;
|
||||
regmap_update_bits(base, reg, mask, val);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned long clk_pm_cpu_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pm_cpu *pm_cpu = to_clk_pm_cpu(hw);
|
||||
unsigned int div;
|
||||
|
||||
if (armada_3700_pm_dvfs_is_enabled(pm_cpu->nb_pm_base))
|
||||
div = armada_3700_pm_dvfs_get_cpu_div(pm_cpu->nb_pm_base);
|
||||
else
|
||||
div = get_div(pm_cpu->reg_div, pm_cpu->shift_div);
|
||||
return DIV_ROUND_UP_ULL((u64)parent_rate, div);
|
||||
}
|
||||
|
||||
static long clk_pm_cpu_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct clk_pm_cpu *pm_cpu = to_clk_pm_cpu(hw);
|
||||
struct regmap *base = pm_cpu->nb_pm_base;
|
||||
unsigned int div = *parent_rate / rate;
|
||||
unsigned int load_level;
|
||||
/* only available when DVFS is enabled */
|
||||
if (!armada_3700_pm_dvfs_is_enabled(base))
|
||||
return -EINVAL;
|
||||
|
||||
for (load_level = 0; load_level < LOAD_LEVEL_NR; load_level++) {
|
||||
unsigned int reg, val, offset = ARMADA_37XX_NB_TBG_DIV_OFF;
|
||||
|
||||
armada_3700_pm_dvfs_update_regs(load_level, ®, &offset);
|
||||
|
||||
regmap_read(base, reg, &val);
|
||||
|
||||
val >>= offset;
|
||||
val &= ARMADA_37XX_NB_TBG_DIV_MASK;
|
||||
if (val == div)
|
||||
/*
|
||||
* We found a load level matching the target
|
||||
* divider, switch to this load level and
|
||||
* return.
|
||||
*/
|
||||
return *parent_rate / div;
|
||||
}
|
||||
|
||||
/* We didn't find any valid divider */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int clk_pm_cpu_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct clk_pm_cpu *pm_cpu = to_clk_pm_cpu(hw);
|
||||
struct regmap *base = pm_cpu->nb_pm_base;
|
||||
unsigned int div = parent_rate / rate;
|
||||
unsigned int load_level;
|
||||
|
||||
/* only available when DVFS is enabled */
|
||||
if (!armada_3700_pm_dvfs_is_enabled(base))
|
||||
return -EINVAL;
|
||||
|
||||
for (load_level = 0; load_level < LOAD_LEVEL_NR; load_level++) {
|
||||
unsigned int reg, mask, val,
|
||||
offset = ARMADA_37XX_NB_TBG_DIV_OFF;
|
||||
|
||||
armada_3700_pm_dvfs_update_regs(load_level, ®, &offset);
|
||||
|
||||
regmap_read(base, reg, &val);
|
||||
val >>= offset;
|
||||
val &= ARMADA_37XX_NB_TBG_DIV_MASK;
|
||||
|
||||
if (val == div) {
|
||||
/*
|
||||
* We found a load level matching the target
|
||||
* divider, switch to this load level and
|
||||
* return.
|
||||
*/
|
||||
reg = ARMADA_37XX_NB_CPU_LOAD;
|
||||
mask = ARMADA_37XX_NB_CPU_LOAD_MASK;
|
||||
regmap_update_bits(base, reg, mask, load_level);
|
||||
|
||||
return rate;
|
||||
}
|
||||
}
|
||||
|
||||
/* We didn't find any valid divider */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct clk_ops clk_pm_cpu_ops = {
|
||||
.get_parent = clk_pm_cpu_get_parent,
|
||||
.set_parent = clk_pm_cpu_set_parent,
|
||||
.round_rate = clk_pm_cpu_round_rate,
|
||||
.set_rate = clk_pm_cpu_set_rate,
|
||||
.recalc_rate = clk_pm_cpu_recalc_rate,
|
||||
};
|
||||
|
||||
static const struct of_device_id armada_3700_periph_clock_of_match[] = {
|
||||
{ .compatible = "marvell,armada-3700-periph-clock-nb",
|
||||
.data = data_nb, },
|
||||
|
|
@ -303,6 +562,7 @@ static const struct of_device_id armada_3700_periph_clock_of_match[] = {
|
|||
.data = data_sb, },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int armada_3700_add_composite_clk(const struct clk_periph_data *data,
|
||||
void __iomem *reg, spinlock_t *lock,
|
||||
struct device *dev, struct clk_hw **hw)
|
||||
|
|
@ -354,10 +614,29 @@ static int armada_3700_add_composite_clk(const struct clk_periph_data *data,
|
|||
}
|
||||
}
|
||||
|
||||
if (data->muxrate_hw) {
|
||||
struct clk_pm_cpu *pmcpu_clk;
|
||||
struct clk_hw *muxrate_hw = data->muxrate_hw;
|
||||
struct regmap *map;
|
||||
|
||||
pmcpu_clk = to_clk_pm_cpu(muxrate_hw);
|
||||
pmcpu_clk->reg_mux = reg + (u64)pmcpu_clk->reg_mux;
|
||||
pmcpu_clk->reg_div = reg + (u64)pmcpu_clk->reg_div;
|
||||
|
||||
mux_hw = muxrate_hw;
|
||||
rate_hw = muxrate_hw;
|
||||
mux_ops = muxrate_hw->init->ops;
|
||||
rate_ops = muxrate_hw->init->ops;
|
||||
|
||||
map = syscon_regmap_lookup_by_compatible(
|
||||
"marvell,armada-3700-nb-pm");
|
||||
pmcpu_clk->nb_pm_base = map;
|
||||
}
|
||||
|
||||
*hw = clk_hw_register_composite(dev, data->name, data->parent_names,
|
||||
data->num_parents, mux_hw,
|
||||
mux_ops, rate_hw, rate_ops,
|
||||
gate_hw, gate_ops, CLK_IGNORE_UNUSED);
|
||||
data->num_parents, mux_hw,
|
||||
mux_ops, rate_hw, rate_ops,
|
||||
gate_hw, gate_ops, CLK_IGNORE_UNUSED);
|
||||
|
||||
if (IS_ERR(*hw))
|
||||
return PTR_ERR(*hw);
|
||||
|
|
@ -406,12 +685,11 @@ static int armada_3700_periph_clock_probe(struct platform_device *pdev)
|
|||
if (armada_3700_add_composite_clk(&data[i], reg,
|
||||
&driver_data->lock, dev, hw))
|
||||
dev_err(dev, "Can't register periph clock %s\n",
|
||||
data[i].name);
|
||||
|
||||
data[i].name);
|
||||
}
|
||||
|
||||
ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
|
||||
driver_data->hw_data);
|
||||
driver_data->hw_data);
|
||||
if (ret) {
|
||||
for (i = 0; i < num_periph; i++)
|
||||
clk_hw_unregister(driver_data->hw_data->hws[i]);
|
||||
|
|
|
|||
|
|
@ -25,16 +25,6 @@ struct freq_tbl {
|
|||
u16 n;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
|
|||
|
|
@ -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 = ÷r->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 @@ const struct clk_ops clk_regmap_div_ops = {
|
|||
.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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ struct qcom_reset_map;
|
|||
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,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
14
drivers/clk/sprd/Kconfig
Normal file
14
drivers/clk/sprd/Kconfig
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
config SPRD_COMMON_CLK
|
||||
tristate "Clock support for Spreadtrum SoCs"
|
||||
depends on ARCH_SPRD || COMPILE_TEST
|
||||
default ARCH_SPRD
|
||||
|
||||
if SPRD_COMMON_CLK
|
||||
|
||||
# SoC Drivers
|
||||
|
||||
config SPRD_SC9860_CLK
|
||||
tristate "Support for the Spreadtrum SC9860 clocks"
|
||||
depends on (ARM64 && ARCH_SPRD) || COMPILE_TEST
|
||||
default ARM64 && ARCH_SPRD
|
||||
endif
|
||||
11
drivers/clk/sprd/Makefile
Normal file
11
drivers/clk/sprd/Makefile
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
obj-$(CONFIG_SPRD_COMMON_CLK) += clk-sprd.o
|
||||
|
||||
clk-sprd-y += common.o
|
||||
clk-sprd-y += gate.o
|
||||
clk-sprd-y += mux.o
|
||||
clk-sprd-y += div.o
|
||||
clk-sprd-y += composite.o
|
||||
clk-sprd-y += pll.o
|
||||
|
||||
## SoC support
|
||||
obj-$(CONFIG_SPRD_SC9860_CLK) += sc9860-clk.o
|
||||
96
drivers/clk/sprd/common.c
Normal file
96
drivers/clk/sprd/common.c
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Spreadtrum clock infrastructure
|
||||
//
|
||||
// Copyright (C) 2017 Spreadtrum, Inc.
|
||||
// Author: Chunyan Zhang <[email protected]>
|
||||
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
static const struct regmap_config sprdclk_regmap_config = {
|
||||
.reg_bits = 32,
|
||||
.reg_stride = 4,
|
||||
.val_bits = 32,
|
||||
.max_register = 0xffff,
|
||||
.fast_io = true,
|
||||
};
|
||||
|
||||
static void sprd_clk_set_regmap(const struct sprd_clk_desc *desc,
|
||||
struct regmap *regmap)
|
||||
{
|
||||
int i;
|
||||
struct sprd_clk_common *cclk;
|
||||
|
||||
for (i = 0; i < desc->num_clk_clks; i++) {
|
||||
cclk = desc->clk_clks[i];
|
||||
if (!cclk)
|
||||
continue;
|
||||
|
||||
cclk->regmap = regmap;
|
||||
}
|
||||
}
|
||||
|
||||
int sprd_clk_regmap_init(struct platform_device *pdev,
|
||||
const struct sprd_clk_desc *desc)
|
||||
{
|
||||
void __iomem *base;
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct regmap *regmap;
|
||||
|
||||
if (of_find_property(node, "sprd,syscon", NULL)) {
|
||||
regmap = syscon_regmap_lookup_by_phandle(node, "sprd,syscon");
|
||||
if (IS_ERR_OR_NULL(regmap)) {
|
||||
pr_err("%s: failed to get syscon regmap\n", __func__);
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
} else {
|
||||
base = of_iomap(node, 0);
|
||||
regmap = devm_regmap_init_mmio(&pdev->dev, base,
|
||||
&sprdclk_regmap_config);
|
||||
if (IS_ERR_OR_NULL(regmap)) {
|
||||
pr_err("failed to init regmap\n");
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
}
|
||||
|
||||
sprd_clk_set_regmap(desc, regmap);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sprd_clk_regmap_init);
|
||||
|
||||
int sprd_clk_probe(struct device *dev, struct clk_hw_onecell_data *clkhw)
|
||||
{
|
||||
int i, ret;
|
||||
struct clk_hw *hw;
|
||||
|
||||
for (i = 0; i < clkhw->num; i++) {
|
||||
|
||||
hw = clkhw->hws[i];
|
||||
|
||||
if (!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, clkhw);
|
||||
if (ret)
|
||||
dev_err(dev, "Failed to add clock provider\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sprd_clk_probe);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
38
drivers/clk/sprd/common.h
Normal file
38
drivers/clk/sprd/common.h
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Spreadtrum clock infrastructure
|
||||
//
|
||||
// Copyright (C) 2017 Spreadtrum, Inc.
|
||||
// Author: Chunyan Zhang <[email protected]>
|
||||
|
||||
#ifndef _SPRD_CLK_COMMON_H_
|
||||
#define _SPRD_CLK_COMMON_H_
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
struct device_node;
|
||||
|
||||
struct sprd_clk_common {
|
||||
struct regmap *regmap;
|
||||
u32 reg;
|
||||
struct clk_hw hw;
|
||||
};
|
||||
|
||||
struct sprd_clk_desc {
|
||||
struct sprd_clk_common **clk_clks;
|
||||
unsigned long num_clk_clks;
|
||||
struct clk_hw_onecell_data *hw_clks;
|
||||
};
|
||||
|
||||
static inline struct sprd_clk_common *
|
||||
hw_to_sprd_clk_common(const struct clk_hw *hw)
|
||||
{
|
||||
return container_of(hw, struct sprd_clk_common, hw);
|
||||
}
|
||||
int sprd_clk_regmap_init(struct platform_device *pdev,
|
||||
const struct sprd_clk_desc *desc);
|
||||
int sprd_clk_probe(struct device *dev, struct clk_hw_onecell_data *clkhw);
|
||||
|
||||
#endif /* _SPRD_CLK_COMMON_H_ */
|
||||
60
drivers/clk/sprd/composite.c
Normal file
60
drivers/clk/sprd/composite.c
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Spreadtrum composite clock driver
|
||||
//
|
||||
// Copyright (C) 2017 Spreadtrum, Inc.
|
||||
// Author: Chunyan Zhang <[email protected]>
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#include "composite.h"
|
||||
|
||||
static long sprd_comp_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct sprd_comp *cc = hw_to_sprd_comp(hw);
|
||||
|
||||
return sprd_div_helper_round_rate(&cc->common, &cc->div,
|
||||
rate, parent_rate);
|
||||
}
|
||||
|
||||
static unsigned long sprd_comp_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sprd_comp *cc = hw_to_sprd_comp(hw);
|
||||
|
||||
return sprd_div_helper_recalc_rate(&cc->common, &cc->div, parent_rate);
|
||||
}
|
||||
|
||||
static int sprd_comp_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sprd_comp *cc = hw_to_sprd_comp(hw);
|
||||
|
||||
return sprd_div_helper_set_rate(&cc->common, &cc->div,
|
||||
rate, parent_rate);
|
||||
}
|
||||
|
||||
static u8 sprd_comp_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_comp *cc = hw_to_sprd_comp(hw);
|
||||
|
||||
return sprd_mux_helper_get_parent(&cc->common, &cc->mux);
|
||||
}
|
||||
|
||||
static int sprd_comp_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct sprd_comp *cc = hw_to_sprd_comp(hw);
|
||||
|
||||
return sprd_mux_helper_set_parent(&cc->common, &cc->mux, index);
|
||||
}
|
||||
|
||||
const struct clk_ops sprd_comp_ops = {
|
||||
.get_parent = sprd_comp_get_parent,
|
||||
.set_parent = sprd_comp_set_parent,
|
||||
|
||||
.round_rate = sprd_comp_round_rate,
|
||||
.recalc_rate = sprd_comp_recalc_rate,
|
||||
.set_rate = sprd_comp_set_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(sprd_comp_ops);
|
||||
51
drivers/clk/sprd/composite.h
Normal file
51
drivers/clk/sprd/composite.h
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Spreadtrum composite clock driver
|
||||
//
|
||||
// Copyright (C) 2017 Spreadtrum, Inc.
|
||||
// Author: Chunyan Zhang <[email protected]>
|
||||
|
||||
#ifndef _SPRD_COMPOSITE_H_
|
||||
#define _SPRD_COMPOSITE_H_
|
||||
|
||||
#include "common.h"
|
||||
#include "mux.h"
|
||||
#include "div.h"
|
||||
|
||||
struct sprd_comp {
|
||||
struct sprd_mux_ssel mux;
|
||||
struct sprd_div_internal div;
|
||||
struct sprd_clk_common common;
|
||||
};
|
||||
|
||||
#define SPRD_COMP_CLK_TABLE(_struct, _name, _parent, _reg, _table, \
|
||||
_mshift, _mwidth, _dshift, _dwidth, _flags) \
|
||||
struct sprd_comp _struct = { \
|
||||
.mux = _SPRD_MUX_CLK(_mshift, _mwidth, _table), \
|
||||
.div = _SPRD_DIV_CLK(_dshift, _dwidth), \
|
||||
.common = { \
|
||||
.regmap = NULL, \
|
||||
.reg = _reg, \
|
||||
.hw.init = CLK_HW_INIT_PARENTS(_name, \
|
||||
_parent, \
|
||||
&sprd_comp_ops, \
|
||||
_flags), \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SPRD_COMP_CLK(_struct, _name, _parent, _reg, _mshift, \
|
||||
_mwidth, _dshift, _dwidth, _flags) \
|
||||
SPRD_COMP_CLK_TABLE(_struct, _name, _parent, _reg, \
|
||||
NULL, _mshift, _mwidth, \
|
||||
_dshift, _dwidth, _flags)
|
||||
|
||||
static inline struct sprd_comp *hw_to_sprd_comp(const struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_clk_common *common = hw_to_sprd_clk_common(hw);
|
||||
|
||||
return container_of(common, struct sprd_comp, common);
|
||||
}
|
||||
|
||||
extern const struct clk_ops sprd_comp_ops;
|
||||
|
||||
#endif /* _SPRD_COMPOSITE_H_ */
|
||||
90
drivers/clk/sprd/div.c
Normal file
90
drivers/clk/sprd/div.c
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Spreadtrum divider clock driver
|
||||
//
|
||||
// Copyright (C) 2017 Spreadtrum, Inc.
|
||||
// Author: Chunyan Zhang <[email protected]>
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
|
||||
#include "div.h"
|
||||
|
||||
long sprd_div_helper_round_rate(struct sprd_clk_common *common,
|
||||
const struct sprd_div_internal *div,
|
||||
unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
return divider_round_rate(&common->hw, rate, parent_rate,
|
||||
NULL, div->width, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sprd_div_helper_round_rate);
|
||||
|
||||
static long sprd_div_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
struct sprd_div *cd = hw_to_sprd_div(hw);
|
||||
|
||||
return sprd_div_helper_round_rate(&cd->common, &cd->div,
|
||||
rate, parent_rate);
|
||||
}
|
||||
|
||||
unsigned long sprd_div_helper_recalc_rate(struct sprd_clk_common *common,
|
||||
const struct sprd_div_internal *div,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned long val;
|
||||
unsigned int reg;
|
||||
|
||||
regmap_read(common->regmap, common->reg, ®);
|
||||
val = reg >> div->shift;
|
||||
val &= (1 << div->width) - 1;
|
||||
|
||||
return divider_recalc_rate(&common->hw, parent_rate, val, NULL, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sprd_div_helper_recalc_rate);
|
||||
|
||||
static unsigned long sprd_div_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sprd_div *cd = hw_to_sprd_div(hw);
|
||||
|
||||
return sprd_div_helper_recalc_rate(&cd->common, &cd->div, parent_rate);
|
||||
}
|
||||
|
||||
int sprd_div_helper_set_rate(const struct sprd_clk_common *common,
|
||||
const struct sprd_div_internal *div,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned long val;
|
||||
unsigned int reg;
|
||||
|
||||
val = divider_get_val(rate, parent_rate, NULL,
|
||||
div->width, 0);
|
||||
|
||||
regmap_read(common->regmap, common->reg, ®);
|
||||
reg &= ~GENMASK(div->width + div->shift - 1, div->shift);
|
||||
|
||||
regmap_write(common->regmap, common->reg,
|
||||
reg | (val << div->shift));
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sprd_div_helper_set_rate);
|
||||
|
||||
static int sprd_div_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sprd_div *cd = hw_to_sprd_div(hw);
|
||||
|
||||
return sprd_div_helper_set_rate(&cd->common, &cd->div,
|
||||
rate, parent_rate);
|
||||
}
|
||||
|
||||
const struct clk_ops sprd_div_ops = {
|
||||
.recalc_rate = sprd_div_recalc_rate,
|
||||
.round_rate = sprd_div_round_rate,
|
||||
.set_rate = sprd_div_set_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(sprd_div_ops);
|
||||
75
drivers/clk/sprd/div.h
Normal file
75
drivers/clk/sprd/div.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Spreadtrum divider clock driver
|
||||
//
|
||||
// Copyright (C) 2017 Spreadtrum, Inc.
|
||||
// Author: Chunyan Zhang <[email protected]>
|
||||
|
||||
#ifndef _SPRD_DIV_H_
|
||||
#define _SPRD_DIV_H_
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/**
|
||||
* struct sprd_div_internal - Internal divider description
|
||||
* @shift: Bit offset of the divider in its register
|
||||
* @width: Width of the divider field in its register
|
||||
*
|
||||
* That structure represents a single divider, and is meant to be
|
||||
* embedded in other structures representing the various clock
|
||||
* classes.
|
||||
*/
|
||||
struct sprd_div_internal {
|
||||
u8 shift;
|
||||
u8 width;
|
||||
};
|
||||
|
||||
#define _SPRD_DIV_CLK(_shift, _width) \
|
||||
{ \
|
||||
.shift = _shift, \
|
||||
.width = _width, \
|
||||
}
|
||||
|
||||
struct sprd_div {
|
||||
struct sprd_div_internal div;
|
||||
struct sprd_clk_common common;
|
||||
};
|
||||
|
||||
#define SPRD_DIV_CLK(_struct, _name, _parent, _reg, \
|
||||
_shift, _width, _flags) \
|
||||
struct sprd_div _struct = { \
|
||||
.div = _SPRD_DIV_CLK(_shift, _width), \
|
||||
.common = { \
|
||||
.regmap = NULL, \
|
||||
.reg = _reg, \
|
||||
.hw.init = CLK_HW_INIT(_name, \
|
||||
_parent, \
|
||||
&sprd_div_ops, \
|
||||
_flags), \
|
||||
} \
|
||||
}
|
||||
|
||||
static inline struct sprd_div *hw_to_sprd_div(const struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_clk_common *common = hw_to_sprd_clk_common(hw);
|
||||
|
||||
return container_of(common, struct sprd_div, common);
|
||||
}
|
||||
|
||||
long sprd_div_helper_round_rate(struct sprd_clk_common *common,
|
||||
const struct sprd_div_internal *div,
|
||||
unsigned long rate,
|
||||
unsigned long *parent_rate);
|
||||
|
||||
unsigned long sprd_div_helper_recalc_rate(struct sprd_clk_common *common,
|
||||
const struct sprd_div_internal *div,
|
||||
unsigned long parent_rate);
|
||||
|
||||
int sprd_div_helper_set_rate(const struct sprd_clk_common *common,
|
||||
const struct sprd_div_internal *div,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate);
|
||||
|
||||
extern const struct clk_ops sprd_div_ops;
|
||||
|
||||
#endif /* _SPRD_DIV_H_ */
|
||||
111
drivers/clk/sprd/gate.c
Normal file
111
drivers/clk/sprd/gate.c
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Spreadtrum gate clock driver
|
||||
//
|
||||
// Copyright (C) 2017 Spreadtrum, Inc.
|
||||
// Author: Chunyan Zhang <[email protected]>
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "gate.h"
|
||||
|
||||
static void clk_gate_toggle(const struct sprd_gate *sg, bool en)
|
||||
{
|
||||
const struct sprd_clk_common *common = &sg->common;
|
||||
unsigned int reg;
|
||||
bool set = sg->flags & CLK_GATE_SET_TO_DISABLE ? true : false;
|
||||
|
||||
set ^= en;
|
||||
|
||||
regmap_read(common->regmap, common->reg, ®);
|
||||
|
||||
if (set)
|
||||
reg |= sg->enable_mask;
|
||||
else
|
||||
reg &= ~sg->enable_mask;
|
||||
|
||||
regmap_write(common->regmap, common->reg, reg);
|
||||
}
|
||||
|
||||
static void clk_sc_gate_toggle(const struct sprd_gate *sg, bool en)
|
||||
{
|
||||
const struct sprd_clk_common *common = &sg->common;
|
||||
bool set = sg->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
|
||||
unsigned int offset;
|
||||
|
||||
set ^= en;
|
||||
|
||||
/*
|
||||
* Each set/clear gate clock has three registers:
|
||||
* common->reg - base register
|
||||
* common->reg + offset - set register
|
||||
* common->reg + 2 * offset - clear register
|
||||
*/
|
||||
offset = set ? sg->sc_offset : sg->sc_offset * 2;
|
||||
|
||||
regmap_write(common->regmap, common->reg + offset,
|
||||
sg->enable_mask);
|
||||
}
|
||||
|
||||
static void sprd_gate_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_gate *sg = hw_to_sprd_gate(hw);
|
||||
|
||||
clk_gate_toggle(sg, false);
|
||||
}
|
||||
|
||||
static int sprd_gate_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_gate *sg = hw_to_sprd_gate(hw);
|
||||
|
||||
clk_gate_toggle(sg, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sprd_sc_gate_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_gate *sg = hw_to_sprd_gate(hw);
|
||||
|
||||
clk_sc_gate_toggle(sg, false);
|
||||
}
|
||||
|
||||
static int sprd_sc_gate_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_gate *sg = hw_to_sprd_gate(hw);
|
||||
|
||||
clk_sc_gate_toggle(sg, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
static int sprd_gate_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_gate *sg = hw_to_sprd_gate(hw);
|
||||
struct sprd_clk_common *common = &sg->common;
|
||||
unsigned int reg;
|
||||
|
||||
regmap_read(common->regmap, common->reg, ®);
|
||||
|
||||
if (sg->flags & CLK_GATE_SET_TO_DISABLE)
|
||||
reg ^= sg->enable_mask;
|
||||
|
||||
reg &= sg->enable_mask;
|
||||
|
||||
return reg ? 1 : 0;
|
||||
}
|
||||
|
||||
const struct clk_ops sprd_gate_ops = {
|
||||
.disable = sprd_gate_disable,
|
||||
.enable = sprd_gate_enable,
|
||||
.is_enabled = sprd_gate_is_enabled,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(sprd_gate_ops);
|
||||
|
||||
const struct clk_ops sprd_sc_gate_ops = {
|
||||
.disable = sprd_sc_gate_disable,
|
||||
.enable = sprd_sc_gate_enable,
|
||||
.is_enabled = sprd_gate_is_enabled,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(sprd_sc_gate_ops);
|
||||
|
||||
59
drivers/clk/sprd/gate.h
Normal file
59
drivers/clk/sprd/gate.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Spreadtrum gate clock driver
|
||||
//
|
||||
// Copyright (C) 2017 Spreadtrum, Inc.
|
||||
// Author: Chunyan Zhang <[email protected]>
|
||||
|
||||
#ifndef _SPRD_GATE_H_
|
||||
#define _SPRD_GATE_H_
|
||||
|
||||
#include "common.h"
|
||||
|
||||
struct sprd_gate {
|
||||
u32 enable_mask;
|
||||
u16 flags;
|
||||
u16 sc_offset;
|
||||
|
||||
struct sprd_clk_common common;
|
||||
};
|
||||
|
||||
#define SPRD_SC_GATE_CLK_OPS(_struct, _name, _parent, _reg, _sc_offset, \
|
||||
_enable_mask, _flags, _gate_flags, _ops) \
|
||||
struct sprd_gate _struct = { \
|
||||
.enable_mask = _enable_mask, \
|
||||
.sc_offset = _sc_offset, \
|
||||
.flags = _gate_flags, \
|
||||
.common = { \
|
||||
.regmap = NULL, \
|
||||
.reg = _reg, \
|
||||
.hw.init = CLK_HW_INIT(_name, \
|
||||
_parent, \
|
||||
_ops, \
|
||||
_flags), \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SPRD_GATE_CLK(_struct, _name, _parent, _reg, \
|
||||
_enable_mask, _flags, _gate_flags) \
|
||||
SPRD_SC_GATE_CLK_OPS(_struct, _name, _parent, _reg, 0, \
|
||||
_enable_mask, _flags, _gate_flags, \
|
||||
&sprd_gate_ops)
|
||||
|
||||
#define SPRD_SC_GATE_CLK(_struct, _name, _parent, _reg, _sc_offset, \
|
||||
_enable_mask, _flags, _gate_flags) \
|
||||
SPRD_SC_GATE_CLK_OPS(_struct, _name, _parent, _reg, _sc_offset, \
|
||||
_enable_mask, _flags, _gate_flags, \
|
||||
&sprd_sc_gate_ops)
|
||||
|
||||
static inline struct sprd_gate *hw_to_sprd_gate(const struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_clk_common *common = hw_to_sprd_clk_common(hw);
|
||||
|
||||
return container_of(common, struct sprd_gate, common);
|
||||
}
|
||||
|
||||
extern const struct clk_ops sprd_gate_ops;
|
||||
extern const struct clk_ops sprd_sc_gate_ops;
|
||||
|
||||
#endif /* _SPRD_GATE_H_ */
|
||||
76
drivers/clk/sprd/mux.c
Normal file
76
drivers/clk/sprd/mux.c
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Spreadtrum multiplexer clock driver
|
||||
//
|
||||
// Copyright (C) 2017 Spreadtrum, Inc.
|
||||
// Author: Chunyan Zhang <[email protected]>
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#include "mux.h"
|
||||
|
||||
u8 sprd_mux_helper_get_parent(const struct sprd_clk_common *common,
|
||||
const struct sprd_mux_ssel *mux)
|
||||
{
|
||||
unsigned int reg;
|
||||
u8 parent;
|
||||
int num_parents;
|
||||
int i;
|
||||
|
||||
regmap_read(common->regmap, common->reg, ®);
|
||||
parent = reg >> mux->shift;
|
||||
parent &= (1 << mux->width) - 1;
|
||||
|
||||
if (!mux->table)
|
||||
return parent;
|
||||
|
||||
num_parents = clk_hw_get_num_parents(&common->hw);
|
||||
|
||||
for (i = 0; i < num_parents - 1; i++)
|
||||
if (parent >= mux->table[i] && parent < mux->table[i + 1])
|
||||
return i;
|
||||
|
||||
return num_parents - 1;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sprd_mux_helper_get_parent);
|
||||
|
||||
static u8 sprd_mux_get_parent(struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_mux *cm = hw_to_sprd_mux(hw);
|
||||
|
||||
return sprd_mux_helper_get_parent(&cm->common, &cm->mux);
|
||||
}
|
||||
|
||||
int sprd_mux_helper_set_parent(const struct sprd_clk_common *common,
|
||||
const struct sprd_mux_ssel *mux,
|
||||
u8 index)
|
||||
{
|
||||
unsigned int reg;
|
||||
|
||||
if (mux->table)
|
||||
index = mux->table[index];
|
||||
|
||||
regmap_read(common->regmap, common->reg, ®);
|
||||
reg &= ~GENMASK(mux->width + mux->shift - 1, mux->shift);
|
||||
regmap_write(common->regmap, common->reg,
|
||||
reg | (index << mux->shift));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sprd_mux_helper_set_parent);
|
||||
|
||||
static int sprd_mux_set_parent(struct clk_hw *hw, u8 index)
|
||||
{
|
||||
struct sprd_mux *cm = hw_to_sprd_mux(hw);
|
||||
|
||||
return sprd_mux_helper_set_parent(&cm->common, &cm->mux, index);
|
||||
}
|
||||
|
||||
const struct clk_ops sprd_mux_ops = {
|
||||
.get_parent = sprd_mux_get_parent,
|
||||
.set_parent = sprd_mux_set_parent,
|
||||
.determine_rate = __clk_mux_determine_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(sprd_mux_ops);
|
||||
74
drivers/clk/sprd/mux.h
Normal file
74
drivers/clk/sprd/mux.h
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Spreadtrum multiplexer clock driver
|
||||
//
|
||||
// Copyright (C) 2017 Spreadtrum, Inc.
|
||||
// Author: Chunyan Zhang <[email protected]>
|
||||
|
||||
#ifndef _SPRD_MUX_H_
|
||||
#define _SPRD_MUX_H_
|
||||
|
||||
#include "common.h"
|
||||
|
||||
/**
|
||||
* struct sprd_mux_ssel - Mux clock's source select bits in its register
|
||||
* @shift: Bit offset of the divider in its register
|
||||
* @width: Width of the divider field in its register
|
||||
* @table: For some mux clocks, not all sources are used on some special
|
||||
* chips, this matches the value of mux clock's register and the
|
||||
* sources which are used for this mux clock
|
||||
*/
|
||||
struct sprd_mux_ssel {
|
||||
u8 shift;
|
||||
u8 width;
|
||||
const u8 *table;
|
||||
};
|
||||
|
||||
struct sprd_mux {
|
||||
struct sprd_mux_ssel mux;
|
||||
struct sprd_clk_common common;
|
||||
};
|
||||
|
||||
#define _SPRD_MUX_CLK(_shift, _width, _table) \
|
||||
{ \
|
||||
.shift = _shift, \
|
||||
.width = _width, \
|
||||
.table = _table, \
|
||||
}
|
||||
|
||||
#define SPRD_MUX_CLK_TABLE(_struct, _name, _parents, _table, \
|
||||
_reg, _shift, _width, \
|
||||
_flags) \
|
||||
struct sprd_mux _struct = { \
|
||||
.mux = _SPRD_MUX_CLK(_shift, _width, _table), \
|
||||
.common = { \
|
||||
.regmap = NULL, \
|
||||
.reg = _reg, \
|
||||
.hw.init = CLK_HW_INIT_PARENTS(_name, \
|
||||
_parents, \
|
||||
&sprd_mux_ops, \
|
||||
_flags), \
|
||||
} \
|
||||
}
|
||||
|
||||
#define SPRD_MUX_CLK(_struct, _name, _parents, _reg, \
|
||||
_shift, _width, _flags) \
|
||||
SPRD_MUX_CLK_TABLE(_struct, _name, _parents, NULL, \
|
||||
_reg, _shift, _width, _flags)
|
||||
|
||||
static inline struct sprd_mux *hw_to_sprd_mux(const struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_clk_common *common = hw_to_sprd_clk_common(hw);
|
||||
|
||||
return container_of(common, struct sprd_mux, common);
|
||||
}
|
||||
|
||||
extern const struct clk_ops sprd_mux_ops;
|
||||
|
||||
u8 sprd_mux_helper_get_parent(const struct sprd_clk_common *common,
|
||||
const struct sprd_mux_ssel *mux);
|
||||
int sprd_mux_helper_set_parent(const struct sprd_clk_common *common,
|
||||
const struct sprd_mux_ssel *mux,
|
||||
u8 index);
|
||||
|
||||
#endif /* _SPRD_MUX_H_ */
|
||||
266
drivers/clk/sprd/pll.c
Normal file
266
drivers/clk/sprd/pll.c
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Spreadtrum pll clock driver
|
||||
//
|
||||
// Copyright (C) 2015~2017 Spreadtrum, Inc.
|
||||
// Author: Chunyan Zhang <[email protected]>
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "pll.h"
|
||||
|
||||
#define CLK_PLL_1M 1000000
|
||||
#define CLK_PLL_10M (CLK_PLL_1M * 10)
|
||||
|
||||
#define pindex(pll, member) \
|
||||
(pll->factors[member].shift / (8 * sizeof(pll->regs_num)))
|
||||
|
||||
#define pshift(pll, member) \
|
||||
(pll->factors[member].shift % (8 * sizeof(pll->regs_num)))
|
||||
|
||||
#define pwidth(pll, member) \
|
||||
pll->factors[member].width
|
||||
|
||||
#define pmask(pll, member) \
|
||||
((pwidth(pll, member)) ? \
|
||||
GENMASK(pwidth(pll, member) + pshift(pll, member) - 1, \
|
||||
pshift(pll, member)) : 0)
|
||||
|
||||
#define pinternal(pll, cfg, member) \
|
||||
(cfg[pindex(pll, member)] & pmask(pll, member))
|
||||
|
||||
#define pinternal_val(pll, cfg, member) \
|
||||
(pinternal(pll, cfg, member) >> pshift(pll, member))
|
||||
|
||||
static inline unsigned int
|
||||
sprd_pll_read(const struct sprd_pll *pll, u8 index)
|
||||
{
|
||||
const struct sprd_clk_common *common = &pll->common;
|
||||
unsigned int val = 0;
|
||||
|
||||
if (WARN_ON(index >= pll->regs_num))
|
||||
return 0;
|
||||
|
||||
regmap_read(common->regmap, common->reg + index * 4, &val);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static inline void
|
||||
sprd_pll_write(const struct sprd_pll *pll, u8 index,
|
||||
u32 msk, u32 val)
|
||||
{
|
||||
const struct sprd_clk_common *common = &pll->common;
|
||||
unsigned int offset, reg;
|
||||
int ret = 0;
|
||||
|
||||
if (WARN_ON(index >= pll->regs_num))
|
||||
return;
|
||||
|
||||
offset = common->reg + index * 4;
|
||||
ret = regmap_read(common->regmap, offset, ®);
|
||||
if (!ret)
|
||||
regmap_write(common->regmap, offset, (reg & ~msk) | val);
|
||||
}
|
||||
|
||||
static unsigned long pll_get_refin(const struct sprd_pll *pll)
|
||||
{
|
||||
u32 shift, mask, index, refin_id = 3;
|
||||
const unsigned long refin[4] = { 2, 4, 13, 26 };
|
||||
|
||||
if (pwidth(pll, PLL_REFIN)) {
|
||||
index = pindex(pll, PLL_REFIN);
|
||||
shift = pshift(pll, PLL_REFIN);
|
||||
mask = pmask(pll, PLL_REFIN);
|
||||
refin_id = (sprd_pll_read(pll, index) & mask) >> shift;
|
||||
if (refin_id > 3)
|
||||
refin_id = 3;
|
||||
}
|
||||
|
||||
return refin[refin_id];
|
||||
}
|
||||
|
||||
static u32 pll_get_ibias(u64 rate, const u64 *table)
|
||||
{
|
||||
u32 i, num = table[0];
|
||||
|
||||
for (i = 1; i < num + 1; i++)
|
||||
if (rate <= table[i])
|
||||
break;
|
||||
|
||||
return (i == num + 1) ? num : i;
|
||||
}
|
||||
|
||||
static unsigned long _sprd_pll_recalc_rate(const struct sprd_pll *pll,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
u32 *cfg;
|
||||
u32 i, mask, regs_num = pll->regs_num;
|
||||
unsigned long rate, nint, kint = 0;
|
||||
u64 refin;
|
||||
u16 k1, k2;
|
||||
|
||||
cfg = kcalloc(regs_num, sizeof(*cfg), GFP_KERNEL);
|
||||
if (!cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < regs_num; i++)
|
||||
cfg[i] = sprd_pll_read(pll, i);
|
||||
|
||||
refin = pll_get_refin(pll);
|
||||
|
||||
if (pinternal(pll, cfg, PLL_PREDIV))
|
||||
refin = refin * 2;
|
||||
|
||||
if (pwidth(pll, PLL_POSTDIV) &&
|
||||
((pll->fflag == 1 && pinternal(pll, cfg, PLL_POSTDIV)) ||
|
||||
(!pll->fflag && !pinternal(pll, cfg, PLL_POSTDIV))))
|
||||
refin = refin / 2;
|
||||
|
||||
if (!pinternal(pll, cfg, PLL_DIV_S)) {
|
||||
rate = refin * pinternal_val(pll, cfg, PLL_N) * CLK_PLL_10M;
|
||||
} else {
|
||||
nint = pinternal_val(pll, cfg, PLL_NINT);
|
||||
if (pinternal(pll, cfg, PLL_SDM_EN))
|
||||
kint = pinternal_val(pll, cfg, PLL_KINT);
|
||||
|
||||
mask = pmask(pll, PLL_KINT);
|
||||
|
||||
k1 = pll->k1;
|
||||
k2 = pll->k2;
|
||||
rate = DIV_ROUND_CLOSEST_ULL(refin * kint * k1,
|
||||
((mask >> __ffs(mask)) + 1)) *
|
||||
k2 + refin * nint * CLK_PLL_1M;
|
||||
}
|
||||
|
||||
return rate;
|
||||
}
|
||||
|
||||
#define SPRD_PLL_WRITE_CHECK(pll, i, mask, val) \
|
||||
(((sprd_pll_read(pll, i) & mask) == val) ? 0 : (-EFAULT))
|
||||
|
||||
static int _sprd_pll_set_rate(const struct sprd_pll *pll,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct reg_cfg *cfg;
|
||||
int ret = 0;
|
||||
u32 mask, shift, width, ibias_val, index;
|
||||
u32 regs_num = pll->regs_num, i = 0;
|
||||
unsigned long kint, nint;
|
||||
u64 tmp, refin, fvco = rate;
|
||||
|
||||
cfg = kcalloc(regs_num, sizeof(*cfg), GFP_KERNEL);
|
||||
if (!cfg)
|
||||
return -ENOMEM;
|
||||
|
||||
refin = pll_get_refin(pll);
|
||||
|
||||
mask = pmask(pll, PLL_PREDIV);
|
||||
index = pindex(pll, PLL_PREDIV);
|
||||
width = pwidth(pll, PLL_PREDIV);
|
||||
if (width && (sprd_pll_read(pll, index) & mask))
|
||||
refin = refin * 2;
|
||||
|
||||
mask = pmask(pll, PLL_POSTDIV);
|
||||
index = pindex(pll, PLL_POSTDIV);
|
||||
width = pwidth(pll, PLL_POSTDIV);
|
||||
cfg[index].msk = mask;
|
||||
if (width && ((pll->fflag == 1 && fvco <= pll->fvco) ||
|
||||
(pll->fflag == 0 && fvco > pll->fvco)))
|
||||
cfg[index].val |= mask;
|
||||
|
||||
if (width && fvco <= pll->fvco)
|
||||
fvco = fvco * 2;
|
||||
|
||||
mask = pmask(pll, PLL_DIV_S);
|
||||
index = pindex(pll, PLL_DIV_S);
|
||||
cfg[index].val |= mask;
|
||||
cfg[index].msk |= mask;
|
||||
|
||||
mask = pmask(pll, PLL_SDM_EN);
|
||||
index = pindex(pll, PLL_SDM_EN);
|
||||
cfg[index].val |= mask;
|
||||
cfg[index].msk |= mask;
|
||||
|
||||
nint = do_div(fvco, refin * CLK_PLL_1M);
|
||||
mask = pmask(pll, PLL_NINT);
|
||||
index = pindex(pll, PLL_NINT);
|
||||
shift = pshift(pll, PLL_NINT);
|
||||
cfg[index].val |= (nint << shift) & mask;
|
||||
cfg[index].msk |= mask;
|
||||
|
||||
mask = pmask(pll, PLL_KINT);
|
||||
index = pindex(pll, PLL_KINT);
|
||||
width = pwidth(pll, PLL_KINT);
|
||||
shift = pshift(pll, PLL_KINT);
|
||||
tmp = fvco - refin * nint * CLK_PLL_1M;
|
||||
tmp = do_div(tmp, 10000) * ((mask >> shift) + 1);
|
||||
kint = DIV_ROUND_CLOSEST_ULL(tmp, refin * 100);
|
||||
cfg[index].val |= (kint << shift) & mask;
|
||||
cfg[index].msk |= mask;
|
||||
|
||||
ibias_val = pll_get_ibias(fvco, pll->itable);
|
||||
|
||||
mask = pmask(pll, PLL_IBIAS);
|
||||
index = pindex(pll, PLL_IBIAS);
|
||||
shift = pshift(pll, PLL_IBIAS);
|
||||
cfg[index].val |= ibias_val << shift & mask;
|
||||
cfg[index].msk |= mask;
|
||||
|
||||
for (i = 0; i < regs_num; i++) {
|
||||
if (cfg[i].msk) {
|
||||
sprd_pll_write(pll, i, cfg[i].msk, cfg[i].val);
|
||||
ret |= SPRD_PLL_WRITE_CHECK(pll, i, cfg[i].msk,
|
||||
cfg[i].val);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ret)
|
||||
udelay(pll->udelay);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned long sprd_pll_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sprd_pll *pll = hw_to_sprd_pll(hw);
|
||||
|
||||
return _sprd_pll_recalc_rate(pll, parent_rate);
|
||||
}
|
||||
|
||||
static int sprd_pll_set_rate(struct clk_hw *hw,
|
||||
unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct sprd_pll *pll = hw_to_sprd_pll(hw);
|
||||
|
||||
return _sprd_pll_set_rate(pll, rate, parent_rate);
|
||||
}
|
||||
|
||||
static int sprd_pll_clk_prepare(struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_pll *pll = hw_to_sprd_pll(hw);
|
||||
|
||||
udelay(pll->udelay);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long sprd_pll_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
return rate;
|
||||
}
|
||||
|
||||
const struct clk_ops sprd_pll_ops = {
|
||||
.prepare = sprd_pll_clk_prepare,
|
||||
.recalc_rate = sprd_pll_recalc_rate,
|
||||
.round_rate = sprd_pll_round_rate,
|
||||
.set_rate = sprd_pll_set_rate,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(sprd_pll_ops);
|
||||
108
drivers/clk/sprd/pll.h
Normal file
108
drivers/clk/sprd/pll.h
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
//
|
||||
// Spreadtrum pll clock driver
|
||||
//
|
||||
// Copyright (C) 2015~2017 Spreadtrum, Inc.
|
||||
// Author: Chunyan Zhang <[email protected]>
|
||||
|
||||
#ifndef _SPRD_PLL_H_
|
||||
#define _SPRD_PLL_H_
|
||||
|
||||
#include "common.h"
|
||||
|
||||
struct reg_cfg {
|
||||
u32 val;
|
||||
u32 msk;
|
||||
};
|
||||
|
||||
struct clk_bit_field {
|
||||
u8 shift;
|
||||
u8 width;
|
||||
};
|
||||
|
||||
enum {
|
||||
PLL_LOCK_DONE,
|
||||
PLL_DIV_S,
|
||||
PLL_MOD_EN,
|
||||
PLL_SDM_EN,
|
||||
PLL_REFIN,
|
||||
PLL_IBIAS,
|
||||
PLL_N,
|
||||
PLL_NINT,
|
||||
PLL_KINT,
|
||||
PLL_PREDIV,
|
||||
PLL_POSTDIV,
|
||||
|
||||
PLL_FACT_MAX
|
||||
};
|
||||
|
||||
/*
|
||||
* struct sprd_pll - definition of adjustable pll clock
|
||||
*
|
||||
* @reg: registers used to set the configuration of pll clock,
|
||||
* reg[0] shows how many registers this pll clock uses.
|
||||
* @itable: pll ibias table, itable[0] means how many items this
|
||||
* table includes
|
||||
* @udelay delay time after setting rate
|
||||
* @factors used to calculate the pll clock rate
|
||||
* @fvco: fvco threshold rate
|
||||
* @fflag: fvco flag
|
||||
*/
|
||||
struct sprd_pll {
|
||||
u32 regs_num;
|
||||
const u64 *itable;
|
||||
const struct clk_bit_field *factors;
|
||||
u16 udelay;
|
||||
u16 k1;
|
||||
u16 k2;
|
||||
u16 fflag;
|
||||
u64 fvco;
|
||||
|
||||
struct sprd_clk_common common;
|
||||
};
|
||||
|
||||
#define SPRD_PLL_WITH_ITABLE_K_FVCO(_struct, _name, _parent, _reg, \
|
||||
_regs_num, _itable, _factors, \
|
||||
_udelay, _k1, _k2, _fflag, _fvco) \
|
||||
struct sprd_pll _struct = { \
|
||||
.regs_num = _regs_num, \
|
||||
.itable = _itable, \
|
||||
.factors = _factors, \
|
||||
.udelay = _udelay, \
|
||||
.k1 = _k1, \
|
||||
.k2 = _k2, \
|
||||
.fflag = _fflag, \
|
||||
.fvco = _fvco, \
|
||||
.common = { \
|
||||
.regmap = NULL, \
|
||||
.reg = _reg, \
|
||||
.hw.init = CLK_HW_INIT(_name, \
|
||||
_parent, \
|
||||
&sprd_pll_ops, \
|
||||
0), \
|
||||
}, \
|
||||
}
|
||||
|
||||
#define SPRD_PLL_WITH_ITABLE_K(_struct, _name, _parent, _reg, \
|
||||
_regs_num, _itable, _factors, \
|
||||
_udelay, _k1, _k2) \
|
||||
SPRD_PLL_WITH_ITABLE_K_FVCO(_struct, _name, _parent, _reg, \
|
||||
_regs_num, _itable, _factors, \
|
||||
_udelay, _k1, _k2, 0, 0)
|
||||
|
||||
#define SPRD_PLL_WITH_ITABLE_1K(_struct, _name, _parent, _reg, \
|
||||
_regs_num, _itable, _factors, _udelay) \
|
||||
SPRD_PLL_WITH_ITABLE_K_FVCO(_struct, _name, _parent, _reg, \
|
||||
_regs_num, _itable, _factors, \
|
||||
_udelay, 1000, 1000, 0, 0)
|
||||
|
||||
static inline struct sprd_pll *hw_to_sprd_pll(struct clk_hw *hw)
|
||||
{
|
||||
struct sprd_clk_common *common = hw_to_sprd_clk_common(hw);
|
||||
|
||||
return container_of(common, struct sprd_pll, common);
|
||||
}
|
||||
|
||||
extern const struct clk_ops sprd_pll_ops;
|
||||
|
||||
#endif /* _SPRD_PLL_H_ */
|
||||
1974
drivers/clk/sprd/sc9860-clk.c
Normal file
1974
drivers/clk/sprd/sc9860-clk.c
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -31,35 +31,6 @@
|
|||
|
||||
struct device_node;
|
||||
|
||||
#define CLK_HW_INIT(_name, _parent, _ops, _flags) \
|
||||
&(struct clk_init_data) { \
|
||||
.flags = _flags, \
|
||||
.name = _name, \
|
||||
.parent_names = (const char *[]) { _parent }, \
|
||||
.num_parents = 1, \
|
||||
.ops = _ops, \
|
||||
}
|
||||
|
||||
#define CLK_HW_INIT_PARENTS(_name, _parents, _ops, _flags) \
|
||||
&(struct clk_init_data) { \
|
||||
.flags = _flags, \
|
||||
.name = _name, \
|
||||
.parent_names = _parents, \
|
||||
.num_parents = ARRAY_SIZE(_parents), \
|
||||
.ops = _ops, \
|
||||
}
|
||||
|
||||
#define CLK_FIXED_FACTOR(_struct, _name, _parent, \
|
||||
_div, _mult, _flags) \
|
||||
struct clk_fixed_factor _struct = { \
|
||||
.div = _div, \
|
||||
.mult = _mult, \
|
||||
.hw.init = CLK_HW_INIT(_name, \
|
||||
_parent, \
|
||||
&clk_fixed_factor_ops, \
|
||||
_flags), \
|
||||
}
|
||||
|
||||
struct ccu_common {
|
||||
void __iomem *base;
|
||||
u16 reg;
|
||||
|
|
|
|||
|
|
@ -14,24 +14,6 @@
|
|||
|
||||
#define PNAME(x) static const char *x[]
|
||||
|
||||
#define CLK_HW_INIT(_name, _parent, _ops, _flags) \
|
||||
&(struct clk_init_data) { \
|
||||
.flags = _flags, \
|
||||
.name = _name, \
|
||||
.parent_names = (const char *[]) { _parent }, \
|
||||
.num_parents = 1, \
|
||||
.ops = _ops, \
|
||||
}
|
||||
|
||||
#define CLK_HW_INIT_PARENTS(_name, _parents, _ops, _flags) \
|
||||
&(struct clk_init_data) { \
|
||||
.flags = _flags, \
|
||||
.name = _name, \
|
||||
.parent_names = _parents, \
|
||||
.num_parents = ARRAY_SIZE(_parents), \
|
||||
.ops = _ops, \
|
||||
}
|
||||
|
||||
struct zx_pll_config {
|
||||
unsigned long rate;
|
||||
u32 cfg0;
|
||||
|
|
|
|||
Loading…
Reference in a new issue