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:
Stephen Boyd 2018-01-26 16:41:47 -08:00
34 changed files with 8152 additions and 292 deletions

View file

@ -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"

View file

@ -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/

View file

@ -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);

View file

@ -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));

View file

@ -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, &reg, &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, &reg, &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, &reg, &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, &reg, &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, &reg, &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]);

View file

@ -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

View file

@ -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 @@ 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);

View file

@ -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

View file

@ -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;

View file

@ -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;
};

View file

@ -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
View 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
View 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
View 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
View 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_ */

View 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);

View 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
View 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, &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);
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
View 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
View 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, &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, &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
View 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
View 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, &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);
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
View 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
View 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, &reg);
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
View 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_ */

File diff suppressed because it is too large Load diff

View file

@ -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;

View file

@ -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;