Commit 91d695d7 authored by Shubhrajyoti Datta's avatar Shubhrajyoti Datta Committed by Greg Kroah-Hartman
Browse files

staging: clocking-wizard: Add support for fractional support



Currently the set rate granularity is to integral divisors.
Add support for the fractional divisors.
Only the first output0 is fractional in the hardware.

Signed-off-by: default avatarShubhrajyoti Datta <shubhrajyoti.datta@xilinx.com>
Link: https://lore.kernel.org/r/1614172241-17326-7-git-send-email-shubhrajyoti.datta@xilinx.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 5a853722
Loading
Loading
Loading
Loading
+137 −16
Original line number Diff line number Diff line
@@ -27,11 +27,15 @@

#define WZRD_CLKFBOUT_MULT_SHIFT	8
#define WZRD_CLKFBOUT_MULT_MASK		(0xff << WZRD_CLKFBOUT_MULT_SHIFT)
#define WZRD_CLKFBOUT_FRAC_SHIFT	16
#define WZRD_CLKFBOUT_FRAC_MASK		(0x3ff << WZRD_CLKFBOUT_FRAC_SHIFT)
#define WZRD_DIVCLK_DIVIDE_SHIFT	0
#define WZRD_DIVCLK_DIVIDE_MASK		(0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
#define WZRD_CLKOUT_DIVIDE_SHIFT	0
#define WZRD_CLKOUT_DIVIDE_WIDTH	8
#define WZRD_CLKOUT_DIVIDE_MASK		(0xff << WZRD_DIVCLK_DIVIDE_SHIFT)
#define WZRD_CLKOUT_FRAC_SHIFT		8
#define WZRD_CLKOUT_FRAC_MASK		0x3ff

#define WZRD_DR_MAX_INT_DIV_VALUE	255
#define WZRD_DR_STATUS_REG_OFFSET	0x04
@@ -51,6 +55,7 @@
enum clk_wzrd_int_clks {
	wzrd_clk_mul,
	wzrd_clk_mul_div,
	wzrd_clk_mul_frac,
	wzrd_clk_int_max
};

@@ -194,6 +199,117 @@ static const struct clk_ops clk_wzrd_clk_divider_ops = {
	.recalc_rate = clk_wzrd_recalc_rate,
};

static unsigned long clk_wzrd_recalc_ratef(struct clk_hw *hw,
					   unsigned long parent_rate)
{
	unsigned int val;
	u32 div, frac;
	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
	void __iomem *div_addr = divider->base + divider->offset;

	val = readl(div_addr);
	div = val & div_mask(divider->width);
	frac = (val >> WZRD_CLKOUT_FRAC_SHIFT) & WZRD_CLKOUT_FRAC_MASK;

	return mult_frac(parent_rate, 1000, (div * 1000) + frac);
}

static int clk_wzrd_dynamic_reconfig_f(struct clk_hw *hw, unsigned long rate,
				       unsigned long parent_rate)
{
	int err;
	u32 value, pre;
	unsigned long rate_div, f, clockout0_div;
	struct clk_wzrd_divider *divider = to_clk_wzrd_divider(hw);
	void __iomem *div_addr = divider->base + divider->offset;

	rate_div = ((parent_rate * 1000) / rate);
	clockout0_div = rate_div / 1000;

	pre = DIV_ROUND_CLOSEST((parent_rate * 1000), rate);
	f = (u32)(pre - (clockout0_div * 1000));
	f = f & WZRD_CLKOUT_FRAC_MASK;
	f = f << WZRD_CLKOUT_DIVIDE_WIDTH;

	value = (f  | (clockout0_div & WZRD_CLKOUT_DIVIDE_MASK));

	/* Set divisor and clear phase offset */
	writel(value, div_addr);
	writel(0x0, div_addr + WZRD_DR_DIV_TO_PHASE_OFFSET);

	/* Check status register */
	err = readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
				 value & WZRD_DR_LOCK_BIT_MASK,
				 WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
	if (err)
		return err;

	/* Initiate reconfiguration */
	writel(WZRD_DR_BEGIN_DYNA_RECONF,
	       divider->base + WZRD_DR_INIT_REG_OFFSET);

	/* Check status register */
	return readl_poll_timeout(divider->base + WZRD_DR_STATUS_REG_OFFSET, value,
				value & WZRD_DR_LOCK_BIT_MASK,
				WZRD_USEC_POLL, WZRD_TIMEOUT_POLL);
}

static long clk_wzrd_round_rate_f(struct clk_hw *hw, unsigned long rate,
				  unsigned long *prate)
{
	return rate;
}

static const struct clk_ops clk_wzrd_clk_divider_ops_f = {
	.round_rate = clk_wzrd_round_rate_f,
	.set_rate = clk_wzrd_dynamic_reconfig_f,
	.recalc_rate = clk_wzrd_recalc_ratef,
};

static struct clk *clk_wzrd_register_divf(struct device *dev,
					  const char *name,
					  const char *parent_name,
					  unsigned long flags,
					  void __iomem *base, u16 offset,
					  u8 shift, u8 width,
					  u8 clk_divider_flags,
					  const struct clk_div_table *table,
					  spinlock_t *lock)
{
	struct clk_wzrd_divider *div;
	struct clk_hw *hw;
	struct clk_init_data init;
	int ret;

	div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
	if (!div)
		return ERR_PTR(-ENOMEM);

	init.name = name;

	init.ops = &clk_wzrd_clk_divider_ops_f;

	init.flags = flags;
	init.parent_names = &parent_name;
	init.num_parents = 1;

	div->base = base;
	div->offset = offset;
	div->shift = shift;
	div->width = width;
	div->flags = clk_divider_flags;
	div->lock = lock;
	div->hw.init = &init;
	div->table = table;

	hw = &div->hw;
	ret =  devm_clk_hw_register(dev, hw);
	if (ret)
		return ERR_PTR(ret);

	return hw->clk;
}

static struct clk *clk_wzrd_register_divider(struct device *dev,
					     const char *name,
					     const char *parent_name,
@@ -297,7 +413,7 @@ static SIMPLE_DEV_PM_OPS(clk_wzrd_dev_pm_ops, clk_wzrd_suspend,
static int clk_wzrd_probe(struct platform_device *pdev)
{
	int i, ret;
	u32 reg;
	u32 reg, reg_f, mult;
	unsigned long rate;
	const char *clk_name;
	void __iomem *ctrl_reg;
@@ -350,17 +466,13 @@ static int clk_wzrd_probe(struct platform_device *pdev)
		goto err_disable_clk;
	}

	/* we don't support fractional div/mul yet */
	reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) &
		    WZRD_CLKFBOUT_FRAC_EN;
	reg |= readl(clk_wzrd->base + WZRD_CLK_CFG_REG(2)) &
		     WZRD_CLKOUT0_FRAC_EN;
	if (reg)
		dev_warn(&pdev->dev, "fractional div/mul not supported\n");

	/* register multiplier */
	reg = (readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0)) &
		     WZRD_CLKFBOUT_MULT_MASK) >> WZRD_CLKFBOUT_MULT_SHIFT;
	reg = readl(clk_wzrd->base + WZRD_CLK_CFG_REG(0));
	reg_f = reg & WZRD_CLKFBOUT_FRAC_MASK;
	reg_f =  reg_f >> WZRD_CLKFBOUT_FRAC_SHIFT;

	reg = reg & WZRD_CLKFBOUT_MULT_MASK;
	reg =  reg >> WZRD_CLKFBOUT_MULT_SHIFT;
	mult = (reg * 1000) + reg_f;
	clk_name = kasprintf(GFP_KERNEL, "%s_mul", dev_name(&pdev->dev));
	if (!clk_name) {
		ret = -ENOMEM;
@@ -378,8 +490,7 @@ static int clk_wzrd_probe(struct platform_device *pdev)
	clk_wzrd->clks_internal[wzrd_clk_mul] = clk_register_fixed_factor
			(&pdev->dev, clk_name,
			 __clk_get_name(clk_wzrd->clk_in1),
			 0, reg, 1);
	kfree(clk_name);
			0, mult, 1000);
	if (IS_ERR(clk_wzrd->clks_internal[wzrd_clk_mul])) {
		dev_err(&pdev->dev, "unable to register fixed-factor clock\n");
		ret = PTR_ERR(clk_wzrd->clks_internal[wzrd_clk_mul]);
@@ -416,8 +527,18 @@ static int clk_wzrd_probe(struct platform_device *pdev)
			ret = -EINVAL;
			goto err_rm_int_clks;
		}
		clk_wzrd->clkout[i] = clk_wzrd_register_divider(&pdev->dev,
								clkout_name,
		if (!i)
			clk_wzrd->clkout[i] = clk_wzrd_register_divf
				(&pdev->dev, clkout_name,
				clk_name, flags,
				clk_wzrd->base, (WZRD_CLK_CFG_REG(2) + i * 12),
				WZRD_CLKOUT_DIVIDE_SHIFT,
				WZRD_CLKOUT_DIVIDE_WIDTH,
				CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO,
				NULL, &clkwzrd_lock);
		else
			clk_wzrd->clkout[i] = clk_wzrd_register_divider
				(&pdev->dev, clkout_name,
				clk_name, 0,
				clk_wzrd->base, (WZRD_CLK_CFG_REG(2) + i * 12),
				WZRD_CLKOUT_DIVIDE_SHIFT,