diff options
Diffstat (limited to 'drivers/clk/mvebu')
-rw-r--r-- | drivers/clk/mvebu/Kconfig | 8 | ||||
-rw-r--r-- | drivers/clk/mvebu/Makefile | 2 | ||||
-rw-r--r-- | drivers/clk/mvebu/ap-cpu-clk.c | 356 | ||||
-rw-r--r-- | drivers/clk/mvebu/ap806-system-controller.c | 178 | ||||
-rw-r--r-- | drivers/clk/mvebu/armada_ap_cp_helper.c | 30 | ||||
-rw-r--r-- | drivers/clk/mvebu/armada_ap_cp_helper.h | 11 | ||||
-rw-r--r-- | drivers/clk/mvebu/cp110-system-controller.c | 32 |
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 &= ~(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, ®); - 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, ®); + 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++) { |