diff options
Diffstat (limited to 'drivers/clk/clk-axi-clkgen.c')
-rw-r--r-- | drivers/clk/clk-axi-clkgen.c | 159 |
1 files changed, 112 insertions, 47 deletions
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c index 934e53a96ddd..aec62301fa06 100644 --- a/drivers/clk/clk-axi-clkgen.c +++ b/drivers/clk/clk-axi-clkgen.c @@ -6,14 +6,17 @@ * Author: Lars-Peter Clausen <lars@metafoo.de> */ -#include <linux/platform_device.h> +#include <linux/adi-axi-common.h> +#include <linux/bits.h> #include <linux/clk.h> #include <linux/clk-provider.h> -#include <linux/slab.h> +#include <linux/err.h> #include <linux/io.h> -#include <linux/of.h> #include <linux/module.h> -#include <linux/err.h> +#include <linux/mod_devicetable.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> #define AXI_CLKGEN_V2_REG_RESET 0x40 #define AXI_CLKGEN_V2_REG_CLKSEL 0x44 @@ -28,6 +31,9 @@ #define AXI_CLKGEN_V2_DRP_STATUS_BUSY BIT(16) +#define ADI_CLKGEN_REG_FPGA_VOLTAGE 0x0140 +#define ADI_CLKGEN_INFO_FPGA_VOLTAGE(val) ((val) & GENMASK(15, 0)) + #define MMCM_REG_CLKOUT5_2 0x07 #define MMCM_REG_CLKOUT0_1 0x08 #define MMCM_REG_CLKOUT0_2 0x09 @@ -90,7 +96,7 @@ static uint32_t axi_clkgen_lookup_filter(unsigned int m) } } -static const uint32_t axi_clkgen_lock_table[] = { +static const u32 axi_clkgen_lock_table[] = { 0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8, 0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8, 0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339, @@ -102,7 +108,7 @@ static const uint32_t axi_clkgen_lock_table[] = { 0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113, }; -static uint32_t axi_clkgen_lookup_lock(unsigned int m) +static u32 axi_clkgen_lookup_lock(unsigned int m) { if (m < ARRAY_SIZE(axi_clkgen_lock_table)) return axi_clkgen_lock_table[m]; @@ -118,14 +124,15 @@ static const struct axi_clkgen_limits axi_clkgen_zynqmp_default_limits = { static const struct axi_clkgen_limits axi_clkgen_zynq_default_limits = { .fpfd_min = 10000, - .fpfd_max = 300000, + .fpfd_max = 450000, .fvco_min = 600000, .fvco_max = 1200000, }; static void axi_clkgen_calc_params(const struct axi_clkgen_limits *limits, - unsigned long fin, unsigned long fout, - unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout) + unsigned long fin, unsigned long fout, + unsigned int *best_d, unsigned int *best_m, + unsigned int *best_dout) { unsigned long d, d_min, d_max, _d_min, _d_max; unsigned long m, m_min, m_max; @@ -141,15 +148,15 @@ static void axi_clkgen_calc_params(const struct axi_clkgen_limits *limits, *best_m = 0; *best_dout = 0; - d_min = max_t(unsigned long, DIV_ROUND_UP(fin, limits->fpfd_max), 1); - d_max = min_t(unsigned long, fin / limits->fpfd_min, 80); + d_min = max(DIV_ROUND_UP(fin, limits->fpfd_max), 1); + d_max = min(fin / limits->fpfd_min, 80); again: fvco_min_fract = limits->fvco_min << fract_shift; fvco_max_fract = limits->fvco_max << fract_shift; - m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min_fract, fin) * d_min, 1); - m_max = min_t(unsigned long, fvco_max_fract * d_max / fin, 64 << fract_shift); + m_min = max(DIV_ROUND_UP(fvco_min_fract, fin) * d_min, 1); + m_max = min(fvco_max_fract * d_max / fin, 64 << fract_shift); for (m = m_min; m <= m_max; m++) { _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max_fract)); @@ -172,7 +179,7 @@ again: } } - /* Lets see if we find a better setting in fractional mode */ + /* Let's see if we find a better setting in fractional mode */ if (fract_shift == 0) { fract_shift = 3; goto again; @@ -192,9 +199,9 @@ struct axi_clkgen_div_params { }; static void axi_clkgen_calc_clk_params(unsigned int divider, - unsigned int frac_divider, struct axi_clkgen_div_params *params) + unsigned int frac_divider, + struct axi_clkgen_div_params *params) { - memset(params, 0x0, sizeof(*params)); if (divider == 1) { @@ -222,7 +229,7 @@ static void axi_clkgen_calc_clk_params(unsigned int divider, if (params->edge == 0 || frac_divider == 1) params->low--; if (((params->edge == 0) ^ (frac_divider == 1)) || - (divider == 2 && frac_divider == 1)) + (divider == 2 && frac_divider == 1)) params->frac_wf_f = 1; params->frac_phase = params->edge * 4 + frac_divider / 2; @@ -230,13 +237,13 @@ static void axi_clkgen_calc_clk_params(unsigned int divider, } static void axi_clkgen_write(struct axi_clkgen *axi_clkgen, - unsigned int reg, unsigned int val) + unsigned int reg, unsigned int val) { writel(val, axi_clkgen->base + reg); } static void axi_clkgen_read(struct axi_clkgen *axi_clkgen, - unsigned int reg, unsigned int *val) + unsigned int reg, unsigned int *val) { *val = readl(axi_clkgen->base + reg); } @@ -257,7 +264,7 @@ static int axi_clkgen_wait_non_busy(struct axi_clkgen *axi_clkgen) } static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen, - unsigned int reg, unsigned int *val) + unsigned int reg, unsigned int *val) { unsigned int reg_val; int ret; @@ -281,7 +288,8 @@ static int axi_clkgen_mmcm_read(struct axi_clkgen *axi_clkgen, } static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen, - unsigned int reg, unsigned int val, unsigned int mask) + unsigned int reg, unsigned int val, + unsigned int mask) { unsigned int reg_val = 0; int ret; @@ -302,8 +310,7 @@ static int axi_clkgen_mmcm_write(struct axi_clkgen *axi_clkgen, return 0; } -static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen, - bool enable) +static void axi_clkgen_mmcm_enable(struct axi_clkgen *axi_clkgen, bool enable) { unsigned int val = AXI_CLKGEN_V2_RESET_ENABLE; @@ -319,31 +326,31 @@ static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) } static void axi_clkgen_set_div(struct axi_clkgen *axi_clkgen, - unsigned int reg1, unsigned int reg2, unsigned int reg3, - struct axi_clkgen_div_params *params) + unsigned int reg1, unsigned int reg2, + unsigned int reg3, + struct axi_clkgen_div_params *params) { axi_clkgen_mmcm_write(axi_clkgen, reg1, - (params->high << 6) | params->low, 0xefff); + (params->high << 6) | params->low, 0xefff); axi_clkgen_mmcm_write(axi_clkgen, reg2, - (params->frac << 12) | (params->frac_en << 11) | - (params->frac_wf_r << 10) | (params->edge << 7) | - (params->nocount << 6), 0x7fff); + (params->frac << 12) | (params->frac_en << 11) | + (params->frac_wf_r << 10) | (params->edge << 7) | + (params->nocount << 6), 0x7fff); if (reg3 != 0) { axi_clkgen_mmcm_write(axi_clkgen, reg3, - (params->frac_phase << 11) | (params->frac_wf_f << 10), 0x3c00); + (params->frac_phase << 11) | (params->frac_wf_f << 10), + 0x3c00); } } -static int axi_clkgen_set_rate(struct clk_hw *clk_hw, - unsigned long rate, unsigned long parent_rate) +static int axi_clkgen_set_rate(struct clk_hw *clk_hw, unsigned long rate, + unsigned long parent_rate) { struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); const struct axi_clkgen_limits *limits = &axi_clkgen->limits; unsigned int d, m, dout; struct axi_clkgen_div_params params; - uint32_t power = 0; - uint32_t filter; - uint32_t lock; + u32 power = 0, filter, lock; if (parent_rate == 0 || rate == 0) return -EINVAL; @@ -363,22 +370,22 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw, axi_clkgen_calc_clk_params(dout >> 3, dout & 0x7, ¶ms); axi_clkgen_set_div(axi_clkgen, MMCM_REG_CLKOUT0_1, MMCM_REG_CLKOUT0_2, - MMCM_REG_CLKOUT5_2, ¶ms); + MMCM_REG_CLKOUT5_2, ¶ms); axi_clkgen_calc_clk_params(d, 0, ¶ms); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_CLK_DIV, - (params.edge << 13) | (params.nocount << 12) | - (params.high << 6) | params.low, 0x3fff); + (params.edge << 13) | (params.nocount << 12) | + (params.high << 6) | params.low, 0x3fff); axi_clkgen_calc_clk_params(m >> 3, m & 0x7, ¶ms); axi_clkgen_set_div(axi_clkgen, MMCM_REG_CLK_FB1, MMCM_REG_CLK_FB2, - MMCM_REG_CLKOUT6_2, ¶ms); + MMCM_REG_CLKOUT6_2, ¶ms); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK1, lock & 0x3ff, 0x3ff); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK2, - (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff); + (((lock >> 16) & 0x1f) << 10) | 0x1, 0x7fff); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_LOCK3, - (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff); + (((lock >> 24) & 0x1f) << 10) | 0x3e9, 0x7fff); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER1, filter >> 16, 0x9900); axi_clkgen_mmcm_write(axi_clkgen, MMCM_REG_FILTER2, filter, 0x9900); @@ -407,7 +414,7 @@ static int axi_clkgen_determine_rate(struct clk_hw *hw, } static unsigned int axi_clkgen_get_div(struct axi_clkgen *axi_clkgen, - unsigned int reg1, unsigned int reg2) + unsigned int reg1, unsigned int reg2) { unsigned int val1, val2; unsigned int div; @@ -434,7 +441,7 @@ static unsigned int axi_clkgen_get_div(struct axi_clkgen *axi_clkgen, } static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, - unsigned long parent_rate) + unsigned long parent_rate) { struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); unsigned int d, m, dout; @@ -442,9 +449,9 @@ static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, unsigned int val; dout = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLKOUT0_1, - MMCM_REG_CLKOUT0_2); + MMCM_REG_CLKOUT0_2); m = axi_clkgen_get_div(axi_clkgen, MMCM_REG_CLK_FB1, - MMCM_REG_CLK_FB2); + MMCM_REG_CLK_FB2); axi_clkgen_mmcm_read(axi_clkgen, MMCM_REG_CLK_DIV, &val); if (val & MMCM_CLK_DIV_NOCOUNT) @@ -496,6 +503,54 @@ static u8 axi_clkgen_get_parent(struct clk_hw *clk_hw) return parent; } +static int axi_clkgen_setup_limits(struct axi_clkgen *axi_clkgen, + struct device *dev) +{ + unsigned int tech, family, speed_grade, reg_value; + + axi_clkgen_read(axi_clkgen, ADI_AXI_REG_FPGA_INFO, ®_value); + tech = ADI_AXI_INFO_FPGA_TECH(reg_value); + family = ADI_AXI_INFO_FPGA_FAMILY(reg_value); + speed_grade = ADI_AXI_INFO_FPGA_SPEED_GRADE(reg_value); + + axi_clkgen->limits.fpfd_min = 10000; + axi_clkgen->limits.fvco_min = 600000; + + switch (speed_grade) { + case ADI_AXI_FPGA_SPEED_1 ... ADI_AXI_FPGA_SPEED_1LV: + axi_clkgen->limits.fvco_max = 1200000; + axi_clkgen->limits.fpfd_max = 450000; + break; + case ADI_AXI_FPGA_SPEED_2 ... ADI_AXI_FPGA_SPEED_2LV: + axi_clkgen->limits.fvco_max = 1440000; + axi_clkgen->limits.fpfd_max = 500000; + if (family == ADI_AXI_FPGA_FAMILY_KINTEX || family == ADI_AXI_FPGA_FAMILY_ARTIX) { + axi_clkgen_read(axi_clkgen, ADI_CLKGEN_REG_FPGA_VOLTAGE, + ®_value); + if (ADI_CLKGEN_INFO_FPGA_VOLTAGE(reg_value) < 950) { + axi_clkgen->limits.fvco_max = 1200000; + axi_clkgen->limits.fpfd_max = 450000; + } + } + break; + case ADI_AXI_FPGA_SPEED_3: + axi_clkgen->limits.fvco_max = 1600000; + axi_clkgen->limits.fpfd_max = 550000; + break; + default: + return dev_err_probe(dev, -ENODEV, "Unknown speed grade %d\n", + speed_grade); + }; + + /* Overwrite vco limits for ultrascale+ */ + if (tech == ADI_AXI_FPGA_TECH_ULTRASCALE_PLUS) { + axi_clkgen->limits.fvco_max = 1600000; + axi_clkgen->limits.fvco_min = 800000; + } + + return 0; +} + static const struct clk_ops axi_clkgen_ops = { .recalc_rate = axi_clkgen_recalc_rate, .determine_rate = axi_clkgen_determine_rate, @@ -510,6 +565,7 @@ static int axi_clkgen_probe(struct platform_device *pdev) { const struct axi_clkgen_limits *dflt_limits; struct axi_clkgen *axi_clkgen; + unsigned int pcore_version; struct clk_init_data init; const char *parent_names[2]; const char *clk_name; @@ -555,11 +611,20 @@ static int axi_clkgen_probe(struct platform_device *pdev) return -EINVAL; } - memcpy(&axi_clkgen->limits, dflt_limits, sizeof(axi_clkgen->limits)); + axi_clkgen_read(axi_clkgen, ADI_AXI_REG_VERSION, &pcore_version); + + if (ADI_AXI_PCORE_VER_MAJOR(pcore_version) > 0x04) { + ret = axi_clkgen_setup_limits(axi_clkgen, &pdev->dev); + if (ret) + return ret; + } else { + memcpy(&axi_clkgen->limits, dflt_limits, + sizeof(axi_clkgen->limits)); + } clk_name = pdev->dev.of_node->name; of_property_read_string(pdev->dev.of_node, "clock-output-names", - &clk_name); + &clk_name); init.name = clk_name; init.ops = &axi_clkgen_ops; |