Commit ac1ee86a authored by Alexandru Ardelean's avatar Alexandru Ardelean Committed by Stephen Boyd
Browse files

clk: axi-clkgen: wrap limits in a struct and keep copy on the state object



Up until the these limits were global/hard-coded, since they are typically
limits of the fabric.

However, since this is an FPGA generated clock, this may run on setups
where one clock is on a fabric, and another one synthesized on another
fabric connected via PCIe (or some other inter-connect, and then these
limits need to be adjusted for each instance of the AXI CLKGEN.

This change wraps the current constants in 'axi_clkgen_limits' struct and
the 'axi_clkgen' instance keeps a copy of these limits, which is
initialized at probe from the default limits.

The limits are stored on the device-tree OF table, so that we can adjust
them via the compatible string.

Signed-off-by: default avatarAlexandru Ardelean <alexandru.ardelean@analog.com>
Link: https://lore.kernel.org/r/20201203074037.26940-1-alexandru.ardelean@analog.com


Signed-off-by: default avatarStephen Boyd <sboyd@kernel.org>
parent bd91abb2
Loading
Loading
Loading
Loading
+31 −17
Original line number Diff line number Diff line
@@ -46,9 +46,17 @@
#define MMCM_CLK_DIV_DIVIDE	BIT(11)
#define MMCM_CLK_DIV_NOCOUNT	BIT(12)

struct axi_clkgen_limits {
	unsigned int fpfd_min;
	unsigned int fpfd_max;
	unsigned int fvco_min;
	unsigned int fvco_max;
};

struct axi_clkgen {
	void __iomem *base;
	struct clk_hw clk_hw;
	struct axi_clkgen_limits limits;
};

static uint32_t axi_clkgen_lookup_filter(unsigned int m)
@@ -100,12 +108,15 @@ static uint32_t axi_clkgen_lookup_lock(unsigned int m)
	return 0x1f1f00fa;
}

static const unsigned int fpfd_min = 10000;
static const unsigned int fpfd_max = 300000;
static const unsigned int fvco_min = 600000;
static const unsigned int fvco_max = 1200000;
static const struct axi_clkgen_limits axi_clkgen_zynq_default_limits = {
	.fpfd_min = 10000,
	.fpfd_max = 300000,
	.fvco_min = 600000,
	.fvco_max = 1200000,
};

static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
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 d, d_min, d_max, _d_min, _d_max;
@@ -122,12 +133,12 @@ static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout,
	*best_m = 0;
	*best_dout = 0;

	d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1);
	d_max = min_t(unsigned long, fin / fpfd_min, 80);
	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);

again:
	fvco_min_fract = fvco_min << fract_shift;
	fvco_max_fract = fvco_max << fract_shift;
	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);
@@ -319,6 +330,7 @@ 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;
@@ -328,7 +340,7 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
	if (parent_rate == 0 || rate == 0)
		return -EINVAL;

	axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout);
	axi_clkgen_calc_params(limits, parent_rate, rate, &d, &m, &dout);

	if (d == 0 || dout == 0 || m == 0)
		return -EINVAL;
@@ -368,10 +380,12 @@ static int axi_clkgen_set_rate(struct clk_hw *clk_hw,
static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
	unsigned long *parent_rate)
{
	struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(hw);
	const struct axi_clkgen_limits *limits = &axi_clkgen->limits;
	unsigned int d, m, dout;
	unsigned long long tmp;

	axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout);
	axi_clkgen_calc_params(limits, *parent_rate, rate, &d, &m, &dout);

	if (d == 0 || dout == 0 || m == 0)
		return -EINVAL;
@@ -485,6 +499,7 @@ static const struct clk_ops axi_clkgen_ops = {
static const struct of_device_id axi_clkgen_ids[] = {
	{
		.compatible = "adi,axi-clkgen-2.00.a",
		.data = &axi_clkgen_zynq_default_limits,
	},
	{ },
};
@@ -492,7 +507,7 @@ MODULE_DEVICE_TABLE(of, axi_clkgen_ids);

static int axi_clkgen_probe(struct platform_device *pdev)
{
	const struct of_device_id *id;
	const struct axi_clkgen_limits *dflt_limits;
	struct axi_clkgen *axi_clkgen;
	struct clk_init_data init;
	const char *parent_names[2];
@@ -501,11 +516,8 @@ static int axi_clkgen_probe(struct platform_device *pdev)
	unsigned int i;
	int ret;

	if (!pdev->dev.of_node)
		return -ENODEV;

	id = of_match_node(axi_clkgen_ids, pdev->dev.of_node);
	if (!id)
	dflt_limits = device_get_match_data(&pdev->dev);
	if (!dflt_limits)
		return -ENODEV;

	axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
@@ -527,6 +539,8 @@ static int axi_clkgen_probe(struct platform_device *pdev)
			return -EINVAL;
	}

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