aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/clk/mvebu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk/mvebu')
-rw-r--r--drivers/clk/mvebu/Kconfig8
-rw-r--r--drivers/clk/mvebu/Makefile2
-rw-r--r--drivers/clk/mvebu/ap-cpu-clk.c356
-rw-r--r--drivers/clk/mvebu/ap806-system-controller.c178
-rw-r--r--drivers/clk/mvebu/armada_ap_cp_helper.c30
-rw-r--r--drivers/clk/mvebu/armada_ap_cp_helper.h11
-rw-r--r--drivers/clk/mvebu/cp110-system-controller.c32
7 files changed, 541 insertions, 76 deletions
diff --git a/drivers/clk/mvebu/Kconfig b/drivers/clk/mvebu/Kconfig
index b09f6ded0a30..415e6906a113 100644
--- a/drivers/clk/mvebu/Kconfig
+++ b/drivers/clk/mvebu/Kconfig
@@ -8,6 +8,9 @@ config MVEBU_CLK_CPU
config MVEBU_CLK_COREDIV
bool
+config ARMADA_AP_CP_HELPER
+ bool
+
config ARMADA_370_CLK
bool
select MVEBU_CLK_COMMON
@@ -35,9 +38,14 @@ config ARMADA_XP_CLK
config ARMADA_AP806_SYSCON
bool
+ select ARMADA_AP_CP_HELPER
+
+config ARMADA_AP_CPU_CLK
+ bool
config ARMADA_CP110_SYSCON
bool
+ select ARMADA_AP_CP_HELPER
config DOVE_CLK
bool
diff --git a/drivers/clk/mvebu/Makefile b/drivers/clk/mvebu/Makefile
index 93ac3685271f..04464cef0f06 100644
--- a/drivers/clk/mvebu/Makefile
+++ b/drivers/clk/mvebu/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_MVEBU_CLK_COMMON) += common.o
obj-$(CONFIG_MVEBU_CLK_CPU) += clk-cpu.o
obj-$(CONFIG_MVEBU_CLK_COREDIV) += clk-corediv.o
+obj-$(CONFIG_ARMADA_AP_CP_HELPER) += armada_ap_cp_helper.o
obj-$(CONFIG_ARMADA_370_CLK) += armada-370.o
obj-$(CONFIG_ARMADA_375_CLK) += armada-375.o
@@ -12,6 +13,7 @@ obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-tbg.o
obj-$(CONFIG_ARMADA_37XX_CLK) += armada-37xx-periph.o
obj-$(CONFIG_ARMADA_XP_CLK) += armada-xp.o mv98dx3236.o
obj-$(CONFIG_ARMADA_AP806_SYSCON) += ap806-system-controller.o
+obj-$(CONFIG_ARMADA_AP_CPU_CLK) += ap-cpu-clk.o
obj-$(CONFIG_ARMADA_CP110_SYSCON) += cp110-system-controller.o
obj-$(CONFIG_DOVE_CLK) += dove.o dove-divider.o
obj-$(CONFIG_KIRKWOOD_CLK) += kirkwood.o
diff --git a/drivers/clk/mvebu/ap-cpu-clk.c b/drivers/clk/mvebu/ap-cpu-clk.c
new file mode 100644
index 000000000000..af5e5acad370
--- /dev/null
+++ b/drivers/clk/mvebu/ap-cpu-clk.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Marvell Armada AP CPU Clock Controller
+ *
+ * Copyright (C) 2018 Marvell
+ *
+ * Omri Itach <omrii@marvell.com>
+ * Gregory Clement <gregory.clement@bootlin.com>
+ */
+
+#define pr_fmt(fmt) "ap-cpu-clk: " fmt
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include "armada_ap_cp_helper.h"
+
+#define AP806_CPU_CLUSTER0 0
+#define AP806_CPU_CLUSTER1 1
+#define AP806_CPUS_PER_CLUSTER 2
+#define APN806_CPU1_MASK 0x1
+
+#define APN806_CLUSTER_NUM_OFFSET 8
+#define APN806_CLUSTER_NUM_MASK BIT(APN806_CLUSTER_NUM_OFFSET)
+
+#define APN806_MAX_DIVIDER 32
+
+/**
+ * struct cpu_dfs_regs: CPU DFS register mapping
+ * @divider_reg: full integer ratio from PLL frequency to CPU clock frequency
+ * @force_reg: request to force new ratio regardless of relation to other clocks
+ * @ratio_reg: central request to switch ratios
+ */
+struct cpu_dfs_regs {
+ unsigned int divider_reg;
+ unsigned int force_reg;
+ unsigned int ratio_reg;
+ unsigned int ratio_state_reg;
+ unsigned int divider_mask;
+ unsigned int cluster_offset;
+ unsigned int force_mask;
+ int divider_offset;
+ int divider_ratio;
+ int ratio_offset;
+ int ratio_state_offset;
+ int ratio_state_cluster_offset;
+};
+
+/* AP806 CPU DFS register mapping*/
+#define AP806_CA72MP2_0_PLL_CR_0_REG_OFFSET 0x278
+#define AP806_CA72MP2_0_PLL_CR_1_REG_OFFSET 0x280
+#define AP806_CA72MP2_0_PLL_CR_2_REG_OFFSET 0x284
+#define AP806_CA72MP2_0_PLL_SR_REG_OFFSET 0xC94
+
+#define AP806_CA72MP2_0_PLL_CR_CLUSTER_OFFSET 0x14
+#define AP806_PLL_CR_0_CPU_CLK_DIV_RATIO_OFFSET 0
+#define AP806_PLL_CR_CPU_CLK_DIV_RATIO 0
+#define AP806_PLL_CR_0_CPU_CLK_DIV_RATIO_MASK \
+ (0x3f << AP806_PLL_CR_0_CPU_CLK_DIV_RATIO_OFFSET)
+#define AP806_PLL_CR_0_CPU_CLK_RELOAD_FORCE_OFFSET 24
+#define AP806_PLL_CR_0_CPU_CLK_RELOAD_FORCE_MASK \
+ (0x1 << AP806_PLL_CR_0_CPU_CLK_RELOAD_FORCE_OFFSET)
+#define AP806_PLL_CR_0_CPU_CLK_RELOAD_RATIO_OFFSET 16
+#define AP806_CA72MP2_0_PLL_RATIO_STABLE_OFFSET 0
+#define AP806_CA72MP2_0_PLL_RATIO_STATE 11
+
+#define STATUS_POLL_PERIOD_US 1
+#define STATUS_POLL_TIMEOUT_US 1000000
+
+#define to_ap_cpu_clk(_hw) container_of(_hw, struct ap_cpu_clk, hw)
+
+static const struct cpu_dfs_regs ap806_dfs_regs = {
+ .divider_reg = AP806_CA72MP2_0_PLL_CR_0_REG_OFFSET,
+ .force_reg = AP806_CA72MP2_0_PLL_CR_1_REG_OFFSET,
+ .ratio_reg = AP806_CA72MP2_0_PLL_CR_2_REG_OFFSET,
+ .ratio_state_reg = AP806_CA72MP2_0_PLL_SR_REG_OFFSET,
+ .divider_mask = AP806_PLL_CR_0_CPU_CLK_DIV_RATIO_MASK,
+ .cluster_offset = AP806_CA72MP2_0_PLL_CR_CLUSTER_OFFSET,
+ .force_mask = AP806_PLL_CR_0_CPU_CLK_RELOAD_FORCE_MASK,
+ .divider_offset = AP806_PLL_CR_0_CPU_CLK_DIV_RATIO_OFFSET,
+ .divider_ratio = AP806_PLL_CR_CPU_CLK_DIV_RATIO,
+ .ratio_offset = AP806_PLL_CR_0_CPU_CLK_RELOAD_RATIO_OFFSET,
+ .ratio_state_offset = AP806_CA72MP2_0_PLL_RATIO_STABLE_OFFSET,
+ .ratio_state_cluster_offset = AP806_CA72MP2_0_PLL_RATIO_STABLE_OFFSET,
+};
+
+/* AP807 CPU DFS register mapping */
+#define AP807_DEVICE_GENERAL_CONTROL_10_REG_OFFSET 0x278
+#define AP807_DEVICE_GENERAL_CONTROL_11_REG_OFFSET 0x27c
+#define AP807_DEVICE_GENERAL_STATUS_6_REG_OFFSET 0xc98
+#define AP807_CA72MP2_0_PLL_CR_CLUSTER_OFFSET 0x8
+#define AP807_PLL_CR_0_CPU_CLK_DIV_RATIO_OFFSET 18
+#define AP807_PLL_CR_0_CPU_CLK_DIV_RATIO_MASK \
+ (0x3f << AP807_PLL_CR_0_CPU_CLK_DIV_RATIO_OFFSET)
+#define AP807_PLL_CR_1_CPU_CLK_DIV_RATIO_OFFSET 12
+#define AP807_PLL_CR_1_CPU_CLK_DIV_RATIO_MASK \
+ (0x3f << AP807_PLL_CR_1_CPU_CLK_DIV_RATIO_OFFSET)
+#define AP807_PLL_CR_CPU_CLK_DIV_RATIO 3
+#define AP807_PLL_CR_0_CPU_CLK_RELOAD_FORCE_OFFSET 0
+#define AP807_PLL_CR_0_CPU_CLK_RELOAD_FORCE_MASK \
+ (0x3 << AP807_PLL_CR_0_CPU_CLK_RELOAD_FORCE_OFFSET)
+#define AP807_PLL_CR_0_CPU_CLK_RELOAD_RATIO_OFFSET 6
+#define AP807_CA72MP2_0_PLL_CLKDIV_RATIO_STABLE_OFFSET 20
+#define AP807_CA72MP2_0_PLL_CLKDIV_RATIO_STABLE_CLUSTER_OFFSET 3
+
+static const struct cpu_dfs_regs ap807_dfs_regs = {
+ .divider_reg = AP807_DEVICE_GENERAL_CONTROL_10_REG_OFFSET,
+ .force_reg = AP807_DEVICE_GENERAL_CONTROL_11_REG_OFFSET,
+ .ratio_reg = AP807_DEVICE_GENERAL_CONTROL_11_REG_OFFSET,
+ .ratio_state_reg = AP807_DEVICE_GENERAL_STATUS_6_REG_OFFSET,
+ .divider_mask = AP807_PLL_CR_0_CPU_CLK_DIV_RATIO_MASK,
+ .cluster_offset = AP807_CA72MP2_0_PLL_CR_CLUSTER_OFFSET,
+ .force_mask = AP807_PLL_CR_0_CPU_CLK_RELOAD_FORCE_MASK,
+ .divider_offset = AP807_PLL_CR_0_CPU_CLK_DIV_RATIO_OFFSET,
+ .divider_ratio = AP807_PLL_CR_CPU_CLK_DIV_RATIO,
+ .ratio_offset = AP807_PLL_CR_0_CPU_CLK_RELOAD_RATIO_OFFSET,
+ .ratio_state_offset = AP807_CA72MP2_0_PLL_CLKDIV_RATIO_STABLE_OFFSET,
+ .ratio_state_cluster_offset =
+ AP807_CA72MP2_0_PLL_CLKDIV_RATIO_STABLE_CLUSTER_OFFSET
+};
+
+/*
+ * struct ap806_clk: CPU cluster clock controller instance
+ * @cluster: Cluster clock controller index
+ * @clk_name: Cluster clock controller name
+ * @dev : Cluster clock device
+ * @hw: HW specific structure of Cluster clock controller
+ * @pll_cr_base: CA72MP2 Register base (Device Sample at Reset register)
+ */
+struct ap_cpu_clk {
+ unsigned int cluster;
+ const char *clk_name;
+ struct device *dev;
+ struct clk_hw hw;
+ struct regmap *pll_cr_base;
+ const struct cpu_dfs_regs *pll_regs;
+};
+
+static unsigned long ap_cpu_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ap_cpu_clk *clk = to_ap_cpu_clk(hw);
+ unsigned int cpu_clkdiv_reg;
+ int cpu_clkdiv_ratio;
+
+ cpu_clkdiv_reg = clk->pll_regs->divider_reg +
+ (clk->cluster * clk->pll_regs->cluster_offset);
+ regmap_read(clk->pll_cr_base, cpu_clkdiv_reg, &cpu_clkdiv_ratio);
+ cpu_clkdiv_ratio &= clk->pll_regs->divider_mask;
+ cpu_clkdiv_ratio >>= clk->pll_regs->divider_offset;
+
+ return parent_rate / cpu_clkdiv_ratio;
+}
+
+static int ap_cpu_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct ap_cpu_clk *clk = to_ap_cpu_clk(hw);
+ int ret, reg, divider = parent_rate / rate;
+ unsigned int cpu_clkdiv_reg, cpu_force_reg, cpu_ratio_reg, stable_bit;
+
+ cpu_clkdiv_reg = clk->pll_regs->divider_reg +
+ (clk->cluster * clk->pll_regs->cluster_offset);
+ cpu_force_reg = clk->pll_regs->force_reg +
+ (clk->cluster * clk->pll_regs->cluster_offset);
+ cpu_ratio_reg = clk->pll_regs->ratio_reg +
+ (clk->cluster * clk->pll_regs->cluster_offset);
+
+ regmap_read(clk->pll_cr_base, cpu_clkdiv_reg, &reg);
+ reg &= ~(clk->pll_regs->divider_mask);
+ reg |= (divider << clk->pll_regs->divider_offset);
+
+ /*
+ * AP807 CPU divider has two channels with ratio 1:3 and divider_ratio
+ * is 1. Otherwise, in the case of the AP806, divider_ratio is 0.
+ */
+ if (clk->pll_regs->divider_ratio) {
+ reg &= ~(AP807_PLL_CR_1_CPU_CLK_DIV_RATIO_MASK);
+ reg |= ((divider * clk->pll_regs->divider_ratio) <<
+ AP807_PLL_CR_1_CPU_CLK_DIV_RATIO_OFFSET);
+ }
+ regmap_write(clk->pll_cr_base, cpu_clkdiv_reg, reg);
+
+
+ regmap_update_bits(clk->pll_cr_base, cpu_force_reg,
+ clk->pll_regs->force_mask,
+ clk->pll_regs->force_mask);
+
+ regmap_update_bits(clk->pll_cr_base, cpu_ratio_reg,
+ BIT(clk->pll_regs->ratio_offset),
+ BIT(clk->pll_regs->ratio_offset));
+
+ stable_bit = BIT(clk->pll_regs->ratio_state_offset +
+ clk->cluster *
+ clk->pll_regs->ratio_state_cluster_offset),
+ ret = regmap_read_poll_timeout(clk->pll_cr_base,
+ clk->pll_regs->ratio_state_reg, reg,
+ reg & stable_bit, STATUS_POLL_PERIOD_US,
+ STATUS_POLL_TIMEOUT_US);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(clk->pll_cr_base, cpu_ratio_reg,
+ BIT(clk->pll_regs->ratio_offset), 0);
+
+ return 0;
+}
+
+static long ap_cpu_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ int divider = *parent_rate / rate;
+
+ divider = min(divider, APN806_MAX_DIVIDER);
+
+ return *parent_rate / divider;
+}
+
+static const struct clk_ops ap_cpu_clk_ops = {
+ .recalc_rate = ap_cpu_clk_recalc_rate,
+ .round_rate = ap_cpu_clk_round_rate,
+ .set_rate = ap_cpu_clk_set_rate,
+};
+
+static int ap_cpu_clock_probe(struct platform_device *pdev)
+{
+ int ret, nclusters = 0, cluster_index = 0;
+ struct device *dev = &pdev->dev;
+ struct device_node *dn, *np = dev->of_node;
+ struct clk_hw_onecell_data *ap_cpu_data;
+ struct ap_cpu_clk *ap_cpu_clk;
+ struct regmap *regmap;
+
+ regmap = syscon_node_to_regmap(np->parent);
+ if (IS_ERR(regmap)) {
+ pr_err("cannot get pll_cr_base regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ /*
+ * AP806 has 4 cpus and DFS for AP806 is controlled per
+ * cluster (2 CPUs per cluster), cpu0 and cpu1 are fixed to
+ * cluster0 while cpu2 and cpu3 are fixed to cluster1 whether
+ * they are enabled or not. Since cpu0 is the boot cpu, then
+ * cluster0 must exist. If cpu2 or cpu3 is enabled, cluster1
+ * will exist and the cluster number is 2; otherwise the
+ * cluster number is 1.
+ */
+ nclusters = 1;
+ for_each_of_cpu_node(dn) {
+ int cpu, err;
+
+ err = of_property_read_u32(dn, "reg", &cpu);
+ if (WARN_ON(err))
+ return err;
+
+ /* If cpu2 or cpu3 is enabled */
+ if (cpu & APN806_CLUSTER_NUM_MASK) {
+ nclusters = 2;
+ break;
+ }
+ }
+ /*
+ * DFS for AP806 is controlled per cluster (2 CPUs per cluster),
+ * so allocate structs per cluster
+ */
+ ap_cpu_clk = devm_kcalloc(dev, nclusters, sizeof(*ap_cpu_clk),
+ GFP_KERNEL);
+ if (!ap_cpu_clk)
+ return -ENOMEM;
+
+ ap_cpu_data = devm_kzalloc(dev, sizeof(*ap_cpu_data) +
+ sizeof(struct clk_hw *) * nclusters,
+ GFP_KERNEL);
+ if (!ap_cpu_data)
+ return -ENOMEM;
+
+ for_each_of_cpu_node(dn) {
+ char *clk_name = "cpu-cluster-0";
+ struct clk_init_data init;
+ const char *parent_name;
+ struct clk *parent;
+ int cpu, err;
+
+ err = of_property_read_u32(dn, "reg", &cpu);
+ if (WARN_ON(err))
+ return err;
+
+ cluster_index = cpu & APN806_CLUSTER_NUM_MASK;
+ cluster_index >>= APN806_CLUSTER_NUM_OFFSET;
+
+ /* Initialize once for one cluster */
+ if (ap_cpu_data->hws[cluster_index])
+ continue;
+
+ parent = of_clk_get(np, cluster_index);
+ if (IS_ERR(parent)) {
+ dev_err(dev, "Could not get the clock parent\n");
+ return -EINVAL;
+ }
+ parent_name = __clk_get_name(parent);
+ clk_name[12] += cluster_index;
+ ap_cpu_clk[cluster_index].clk_name =
+ ap_cp_unique_name(dev, np->parent, clk_name);
+ ap_cpu_clk[cluster_index].cluster = cluster_index;
+ ap_cpu_clk[cluster_index].pll_cr_base = regmap;
+ ap_cpu_clk[cluster_index].hw.init = &init;
+ ap_cpu_clk[cluster_index].dev = dev;
+ ap_cpu_clk[cluster_index].pll_regs = of_device_get_match_data(&pdev->dev);
+
+ init.name = ap_cpu_clk[cluster_index].clk_name;
+ init.ops = &ap_cpu_clk_ops;
+ init.num_parents = 1;
+ init.parent_names = &parent_name;
+
+ ret = devm_clk_hw_register(dev, &ap_cpu_clk[cluster_index].hw);
+ if (ret)
+ return ret;
+ ap_cpu_data->hws[cluster_index] = &ap_cpu_clk[cluster_index].hw;
+ }
+
+ ap_cpu_data->num = cluster_index + 1;
+
+ ret = of_clk_add_hw_provider(np, of_clk_hw_onecell_get, ap_cpu_data);
+ if (ret)
+ dev_err(dev, "failed to register OF clock provider\n");
+
+ return ret;
+}
+
+static const struct of_device_id ap_cpu_clock_of_match[] = {
+ {
+ .compatible = "marvell,ap806-cpu-clock",
+ .data = &ap806_dfs_regs,
+ },
+ {
+ .compatible = "marvell,ap807-cpu-clock",
+ .data = &ap807_dfs_regs,
+ },
+ { }
+};
+
+static struct platform_driver ap_cpu_clock_driver = {
+ .probe = ap_cpu_clock_probe,
+ .driver = {
+ .name = "marvell-ap-cpu-clock",
+ .of_match_table = ap_cpu_clock_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver(ap_cpu_clock_driver);
diff --git a/drivers/clk/mvebu/ap806-system-controller.c b/drivers/clk/mvebu/ap806-system-controller.c
index ea54a874bbda..948bd1e71aea 100644
--- a/drivers/clk/mvebu/ap806-system-controller.c
+++ b/drivers/clk/mvebu/ap806-system-controller.c
@@ -10,18 +10,18 @@
#define pr_fmt(fmt) "ap806-system-controller: " fmt
+#include "armada_ap_cp_helper.h"
#include <linux/clk-provider.h>
#include <linux/mfd/syscon.h>
#include <linux/init.h>
#include <linux/of.h>
-#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#define AP806_SAR_REG 0x400
#define AP806_SAR_CLKFREQ_MODE_MASK 0x1f
-#define AP806_CLK_NUM 5
+#define AP806_CLK_NUM 6
static struct clk *ap806_clks[AP806_CLK_NUM];
@@ -30,86 +30,149 @@ static struct clk_onecell_data ap806_clk_data = {
.clk_num = AP806_CLK_NUM,
};
-static char *ap806_unique_name(struct device *dev, struct device_node *np,
- char *name)
+static int ap806_get_sar_clocks(unsigned int freq_mode,
+ unsigned int *cpuclk_freq,
+ unsigned int *dclk_freq)
{
- const __be32 *reg;
- u64 addr;
-
- reg = of_get_property(np, "reg", NULL);
- addr = of_translate_address(np, reg);
- return devm_kasprintf(dev, GFP_KERNEL, "%llx-%s",
- (unsigned long long)addr, name);
-}
-
-static int ap806_syscon_common_probe(struct platform_device *pdev,
- struct device_node *syscon_node)
-{
- unsigned int freq_mode, cpuclk_freq;
- const char *name, *fixedclk_name;
- struct device *dev = &pdev->dev;
- struct device_node *np = dev->of_node;
- struct regmap *regmap;
- u32 reg;
- int ret;
-
- regmap = syscon_node_to_regmap(syscon_node);
- if (IS_ERR(regmap)) {
- dev_err(dev, "cannot get regmap\n");
- return PTR_ERR(regmap);
- }
-
- ret = regmap_read(regmap, AP806_SAR_REG, &reg);
- if (ret) {
- dev_err(dev, "cannot read from regmap\n");
- return ret;
- }
-
- freq_mode = reg & AP806_SAR_CLKFREQ_MODE_MASK;
switch (freq_mode) {
case 0x0:
+ *cpuclk_freq = 2000;
+ *dclk_freq = 600;
+ break;
case 0x1:
- cpuclk_freq = 2000;
+ *cpuclk_freq = 2000;
+ *dclk_freq = 525;
break;
case 0x6:
+ *cpuclk_freq = 1800;
+ *dclk_freq = 600;
+ break;
case 0x7:
- cpuclk_freq = 1800;
+ *cpuclk_freq = 1800;
+ *dclk_freq = 525;
break;
case 0x4:
+ *cpuclk_freq = 1600;
+ *dclk_freq = 400;
+ break;
case 0xB:
+ *cpuclk_freq = 1600;
+ *dclk_freq = 450;
+ break;
case 0xD:
- cpuclk_freq = 1600;
+ *cpuclk_freq = 1600;
+ *dclk_freq = 525;
break;
case 0x1a:
- cpuclk_freq = 1400;
+ *cpuclk_freq = 1400;
+ *dclk_freq = 400;
break;
case 0x14:
+ *cpuclk_freq = 1300;
+ *dclk_freq = 400;
+ break;
case 0x17:
- cpuclk_freq = 1300;
+ *cpuclk_freq = 1300;
+ *dclk_freq = 325;
break;
case 0x19:
- cpuclk_freq = 1200;
+ *cpuclk_freq = 1200;
+ *dclk_freq = 400;
break;
case 0x13:
+ *cpuclk_freq = 1000;
+ *dclk_freq = 325;
+ break;
case 0x1d:
- cpuclk_freq = 1000;
+ *cpuclk_freq = 1000;
+ *dclk_freq = 400;
break;
case 0x1c:
- cpuclk_freq = 800;
+ *cpuclk_freq = 800;
+ *dclk_freq = 400;
break;
case 0x1b:
- cpuclk_freq = 600;
+ *cpuclk_freq = 600;
+ *dclk_freq = 400;
break;
default:
- dev_err(dev, "invalid SAR value\n");
return -EINVAL;
}
+ return 0;
+}
+
+static int ap807_get_sar_clocks(unsigned int freq_mode,
+ unsigned int *cpuclk_freq,
+ unsigned int *dclk_freq)
+{
+ switch (freq_mode) {
+ case 0x0:
+ *cpuclk_freq = 2000;
+ *dclk_freq = 1200;
+ break;
+ case 0x6:
+ *cpuclk_freq = 2200;
+ *dclk_freq = 1200;
+ break;
+ case 0xD:
+ *cpuclk_freq = 1600;
+ *dclk_freq = 1200;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ap806_syscon_common_probe(struct platform_device *pdev,
+ struct device_node *syscon_node)
+{
+ unsigned int freq_mode, cpuclk_freq, dclk_freq;
+ const char *name, *fixedclk_name;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regmap *regmap;
+ u32 reg;
+ int ret;
+
+ regmap = syscon_node_to_regmap(syscon_node);
+ if (IS_ERR(regmap)) {
+ dev_err(dev, "cannot get regmap\n");
+ return PTR_ERR(regmap);
+ }
+
+ ret = regmap_read(regmap, AP806_SAR_REG, &reg);
+ if (ret) {
+ dev_err(dev, "cannot read from regmap\n");
+ return ret;
+ }
+
+ freq_mode = reg & AP806_SAR_CLKFREQ_MODE_MASK;
+
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "marvell,ap806-clock")) {
+ ret = ap806_get_sar_clocks(freq_mode, &cpuclk_freq, &dclk_freq);
+ } else if (of_device_is_compatible(pdev->dev.of_node,
+ "marvell,ap807-clock")) {
+ ret = ap807_get_sar_clocks(freq_mode, &cpuclk_freq, &dclk_freq);
+ } else {
+ dev_err(dev, "compatible not supported\n");
+ return -EINVAL;
+ }
+
+ if (ret) {
+ dev_err(dev, "invalid Sample at Reset value\n");
+ return ret;
+ }
+
/* Convert to hertz */
cpuclk_freq *= 1000 * 1000;
+ dclk_freq *= 1000 * 1000;
/* CPU clocks depend on the Sample At Reset configuration */
- name = ap806_unique_name(dev, syscon_node, "cpu-cluster-0");
+ name = ap_cp_unique_name(dev, syscon_node, "pll-cluster-0");
ap806_clks[0] = clk_register_fixed_rate(dev, name, NULL,
0, cpuclk_freq);
if (IS_ERR(ap806_clks[0])) {
@@ -117,7 +180,7 @@ static int ap806_syscon_common_probe(struct platform_device *pdev,
goto fail0;
}
- name = ap806_unique_name(dev, syscon_node, "cpu-cluster-1");
+ name = ap_cp_unique_name(dev, syscon_node, "pll-cluster-1");
ap806_clks[1] = clk_register_fixed_rate(dev, name, NULL, 0,
cpuclk_freq);
if (IS_ERR(ap806_clks[1])) {
@@ -126,7 +189,7 @@ static int ap806_syscon_common_probe(struct platform_device *pdev,
}
/* Fixed clock is always 1200 Mhz */
- fixedclk_name = ap806_unique_name(dev, syscon_node, "fixed");
+ fixedclk_name = ap_cp_unique_name(dev, syscon_node, "fixed");
ap806_clks[2] = clk_register_fixed_rate(dev, fixedclk_name, NULL,
0, 1200 * 1000 * 1000);
if (IS_ERR(ap806_clks[2])) {
@@ -135,7 +198,7 @@ static int ap806_syscon_common_probe(struct platform_device *pdev,
}
/* MSS Clock is fixed clock divided by 6 */
- name = ap806_unique_name(dev, syscon_node, "mss");
+ name = ap_cp_unique_name(dev, syscon_node, "mss");
ap806_clks[3] = clk_register_fixed_factor(NULL, name, fixedclk_name,
0, 1, 6);
if (IS_ERR(ap806_clks[3])) {
@@ -144,7 +207,7 @@ static int ap806_syscon_common_probe(struct platform_device *pdev,
}
/* SDIO(/eMMC) Clock is fixed clock divided by 3 */
- name = ap806_unique_name(dev, syscon_node, "sdio");
+ name = ap_cp_unique_name(dev, syscon_node, "sdio");
ap806_clks[4] = clk_register_fixed_factor(NULL, name,
fixedclk_name,
0, 1, 3);
@@ -153,6 +216,14 @@ static int ap806_syscon_common_probe(struct platform_device *pdev,
goto fail4;
}
+ /* AP-DCLK(HCLK) Clock is DDR clock divided by 2 */
+ name = ap_cp_unique_name(dev, syscon_node, "ap-dclk");
+ ap806_clks[5] = clk_register_fixed_rate(dev, name, NULL, 0, dclk_freq);
+ if (IS_ERR(ap806_clks[5])) {
+ ret = PTR_ERR(ap806_clks[5]);
+ goto fail5;
+ }
+
ret = of_clk_add_provider(np, of_clk_src_onecell_get, &ap806_clk_data);
if (ret)
goto fail_clk_add;
@@ -160,6 +231,8 @@ static int ap806_syscon_common_probe(struct platform_device *pdev,
return 0;
fail_clk_add:
+ clk_unregister_fixed_factor(ap806_clks[5]);
+fail5:
clk_unregister_fixed_factor(ap806_clks[4]);
fail4:
clk_unregister_fixed_factor(ap806_clks[3]);
@@ -206,6 +279,7 @@ builtin_platform_driver(ap806_syscon_legacy_driver);
static const struct of_device_id ap806_clock_of_match[] = {
{ .compatible = "marvell,ap806-clock", },
+ { .compatible = "marvell,ap807-clock", },
{ }
};
diff --git a/drivers/clk/mvebu/armada_ap_cp_helper.c b/drivers/clk/mvebu/armada_ap_cp_helper.c
new file mode 100644
index 000000000000..6a930f697ee5
--- /dev/null
+++ b/drivers/clk/mvebu/armada_ap_cp_helper.c
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Marvell Armada AP and CP110 helper
+ *
+ * Copyright (C) 2018 Marvell
+ *
+ * Gregory Clement <gregory.clement@bootlin.com>
+ *
+ */
+
+#include "armada_ap_cp_helper.h"
+#include <linux/device.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+
+char *ap_cp_unique_name(struct device *dev, struct device_node *np,
+ const char *name)
+{
+ const __be32 *reg;
+ u64 addr;
+
+ /* Do not create a name if there is no clock */
+ if (!name)
+ return NULL;
+
+ reg = of_get_property(np, "reg", NULL);
+ addr = of_translate_address(np, reg);
+ return devm_kasprintf(dev, GFP_KERNEL, "%llx-%s",
+ (unsigned long long)addr, name);
+}
diff --git a/drivers/clk/mvebu/armada_ap_cp_helper.h b/drivers/clk/mvebu/armada_ap_cp_helper.h
new file mode 100644
index 000000000000..810af1e5dfa4
--- /dev/null
+++ b/drivers/clk/mvebu/armada_ap_cp_helper.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef __ARMADA_AP_CP_HELPER_H
+#define __ARMADA_AP_CP_HELPER_H
+
+struct device;
+struct device_node;
+
+char *ap_cp_unique_name(struct device *dev, struct device_node *np,
+ const char *name);
+#endif
diff --git a/drivers/clk/mvebu/cp110-system-controller.c b/drivers/clk/mvebu/cp110-system-controller.c
index b6de283f45e3..808463276145 100644
--- a/drivers/clk/mvebu/cp110-system-controller.c
+++ b/drivers/clk/mvebu/cp110-system-controller.c
@@ -26,11 +26,11 @@
#define pr_fmt(fmt) "cp110-system-controller: " fmt
+#include "armada_ap_cp_helper.h"
#include <linux/clk-provider.h>
#include <linux/mfd/syscon.h>
#include <linux/init.h>
#include <linux/of.h>
-#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -212,22 +212,6 @@ static struct clk_hw *cp110_of_clk_get(struct of_phandle_args *clkspec,
return ERR_PTR(-EINVAL);
}
-static char *cp110_unique_name(struct device *dev, struct device_node *np,
- const char *name)
-{
- const __be32 *reg;
- u64 addr;
-
- /* Do not create a name if there is no clock */
- if (!name)
- return NULL;
-
- reg = of_get_property(np, "reg", NULL);
- addr = of_translate_address(np, reg);
- return devm_kasprintf(dev, GFP_KERNEL, "%llx-%s",
- (unsigned long long)addr, name);
-}
-
static int cp110_syscon_common_probe(struct platform_device *pdev,
struct device_node *syscon_node)
{
@@ -261,7 +245,7 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
cp110_clk_data->num = CP110_CLK_NUM;
/* Register the PLL0 which is the root of the hw tree */
- pll0_name = cp110_unique_name(dev, syscon_node, "pll0");
+ pll0_name = ap_cp_unique_name(dev, syscon_node, "pll0");
hw = clk_hw_register_fixed_rate(NULL, pll0_name, NULL, 0,
1000 * 1000 * 1000);
if (IS_ERR(hw)) {
@@ -272,7 +256,7 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
cp110_clks[CP110_CORE_PLL0] = hw;
/* PPv2 is PLL0/3 */
- ppv2_name = cp110_unique_name(dev, syscon_node, "ppv2-core");
+ ppv2_name = ap_cp_unique_name(dev, syscon_node, "ppv2-core");
hw = clk_hw_register_fixed_factor(NULL, ppv2_name, pll0_name, 0, 1, 3);
if (IS_ERR(hw)) {
ret = PTR_ERR(hw);
@@ -282,7 +266,7 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
cp110_clks[CP110_CORE_PPV2] = hw;
/* X2CORE clock is PLL0/2 */
- x2core_name = cp110_unique_name(dev, syscon_node, "x2core");
+ x2core_name = ap_cp_unique_name(dev, syscon_node, "x2core");
hw = clk_hw_register_fixed_factor(NULL, x2core_name, pll0_name,
0, 1, 2);
if (IS_ERR(hw)) {
@@ -293,7 +277,7 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
cp110_clks[CP110_CORE_X2CORE] = hw;
/* Core clock is X2CORE/2 */
- core_name = cp110_unique_name(dev, syscon_node, "core");
+ core_name = ap_cp_unique_name(dev, syscon_node, "core");
hw = clk_hw_register_fixed_factor(NULL, core_name, x2core_name,
0, 1, 2);
if (IS_ERR(hw)) {
@@ -303,7 +287,7 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
cp110_clks[CP110_CORE_CORE] = hw;
/* NAND can be either PLL0/2.5 or core clock */
- nand_name = cp110_unique_name(dev, syscon_node, "nand-core");
+ nand_name = ap_cp_unique_name(dev, syscon_node, "nand-core");
if (nand_clk_ctrl & NF_CLOCK_SEL_400_MASK)
hw = clk_hw_register_fixed_factor(NULL, nand_name,
pll0_name, 0, 2, 5);
@@ -318,7 +302,7 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
cp110_clks[CP110_CORE_NAND] = hw;
/* SDIO clock is PLL0/2.5 */
- sdio_name = cp110_unique_name(dev, syscon_node, "sdio-core");
+ sdio_name = ap_cp_unique_name(dev, syscon_node, "sdio-core");
hw = clk_hw_register_fixed_factor(NULL, sdio_name,
pll0_name, 0, 2, 5);
if (IS_ERR(hw)) {
@@ -330,7 +314,7 @@ static int cp110_syscon_common_probe(struct platform_device *pdev,
/* create the unique name for all the gate clocks */
for (i = 0; i < ARRAY_SIZE(gate_base_names); i++)
- gate_name[i] = cp110_unique_name(dev, syscon_node,
+ gate_name[i] = ap_cp_unique_name(dev, syscon_node,
gate_base_names[i]);
for (i = 0; i < ARRAY_SIZE(gate_base_names); i++) {