2f9f64bc5a
The order of arguments in the call to vco_set() for the ICST clocks appears to have been switched in error, which results in the VCO not being initialised correctly. This in turn stops the integrated LCD on things like Integrator/CP from working correctly. This patch fixes the order and restores the expected functionality. Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Signed-off-by: Jonathan Austin <jonathan.austin@arm.com> Signed-off-by: Mike Turquette <mturquette@linaro.org> Cc: stable@vger.kernel.org
149 lines
3.5 KiB
C
149 lines
3.5 KiB
C
/*
|
|
* Driver for the ICST307 VCO clock found in the ARM Reference designs.
|
|
* We wrap the custom interface from <asm/hardware/icst.h> into the generic
|
|
* clock framework.
|
|
*
|
|
* Copyright (C) 2012 Linus Walleij
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* TODO: when all ARM reference designs are migrated to generic clocks, the
|
|
* ICST clock code from the ARM tree should probably be merged into this
|
|
* file.
|
|
*/
|
|
#include <linux/clk.h>
|
|
#include <linux/clkdev.h>
|
|
#include <linux/err.h>
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/io.h>
|
|
|
|
#include "clk-icst.h"
|
|
|
|
/**
|
|
* struct clk_icst - ICST VCO clock wrapper
|
|
* @hw: corresponding clock hardware entry
|
|
* @vcoreg: VCO register address
|
|
* @lockreg: VCO lock register address
|
|
* @params: parameters for this ICST instance
|
|
* @rate: current rate
|
|
*/
|
|
struct clk_icst {
|
|
struct clk_hw hw;
|
|
void __iomem *vcoreg;
|
|
void __iomem *lockreg;
|
|
const struct icst_params *params;
|
|
unsigned long rate;
|
|
};
|
|
|
|
#define to_icst(_hw) container_of(_hw, struct clk_icst, hw)
|
|
|
|
/**
|
|
* vco_get() - get ICST VCO settings from a certain register
|
|
* @vcoreg: register containing the VCO settings
|
|
*/
|
|
static struct icst_vco vco_get(void __iomem *vcoreg)
|
|
{
|
|
u32 val;
|
|
struct icst_vco vco;
|
|
|
|
val = readl(vcoreg);
|
|
vco.v = val & 0x1ff;
|
|
vco.r = (val >> 9) & 0x7f;
|
|
vco.s = (val >> 16) & 03;
|
|
return vco;
|
|
}
|
|
|
|
/**
|
|
* vco_set() - commit changes to an ICST VCO
|
|
* @locreg: register to poke to unlock the VCO for writing
|
|
* @vcoreg: register containing the VCO settings
|
|
* @vco: ICST VCO parameters to commit
|
|
*/
|
|
static void vco_set(void __iomem *lockreg,
|
|
void __iomem *vcoreg,
|
|
struct icst_vco vco)
|
|
{
|
|
u32 val;
|
|
|
|
val = readl(vcoreg) & ~0x7ffff;
|
|
val |= vco.v | (vco.r << 9) | (vco.s << 16);
|
|
|
|
/* This magic unlocks the VCO so it can be controlled */
|
|
writel(0xa05f, lockreg);
|
|
writel(val, vcoreg);
|
|
/* This locks the VCO again */
|
|
writel(0, lockreg);
|
|
}
|
|
|
|
|
|
static unsigned long icst_recalc_rate(struct clk_hw *hw,
|
|
unsigned long parent_rate)
|
|
{
|
|
struct clk_icst *icst = to_icst(hw);
|
|
struct icst_vco vco;
|
|
|
|
vco = vco_get(icst->vcoreg);
|
|
icst->rate = icst_hz(icst->params, vco);
|
|
return icst->rate;
|
|
}
|
|
|
|
static long icst_round_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long *prate)
|
|
{
|
|
struct clk_icst *icst = to_icst(hw);
|
|
struct icst_vco vco;
|
|
|
|
vco = icst_hz_to_vco(icst->params, rate);
|
|
return icst_hz(icst->params, vco);
|
|
}
|
|
|
|
static int icst_set_rate(struct clk_hw *hw, unsigned long rate,
|
|
unsigned long parent_rate)
|
|
{
|
|
struct clk_icst *icst = to_icst(hw);
|
|
struct icst_vco vco;
|
|
|
|
vco = icst_hz_to_vco(icst->params, rate);
|
|
icst->rate = icst_hz(icst->params, vco);
|
|
vco_set(icst->lockreg, icst->vcoreg, vco);
|
|
return 0;
|
|
}
|
|
|
|
static const struct clk_ops icst_ops = {
|
|
.recalc_rate = icst_recalc_rate,
|
|
.round_rate = icst_round_rate,
|
|
.set_rate = icst_set_rate,
|
|
};
|
|
|
|
struct clk *icst_clk_register(struct device *dev,
|
|
const struct clk_icst_desc *desc,
|
|
void __iomem *base)
|
|
{
|
|
struct clk *clk;
|
|
struct clk_icst *icst;
|
|
struct clk_init_data init;
|
|
|
|
icst = kzalloc(sizeof(struct clk_icst), GFP_KERNEL);
|
|
if (!icst) {
|
|
pr_err("could not allocate ICST clock!\n");
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
init.name = "icst";
|
|
init.ops = &icst_ops;
|
|
init.flags = CLK_IS_ROOT;
|
|
init.parent_names = NULL;
|
|
init.num_parents = 0;
|
|
icst->hw.init = &init;
|
|
icst->params = desc->params;
|
|
icst->vcoreg = base + desc->vco_offset;
|
|
icst->lockreg = base + desc->lock_offset;
|
|
|
|
clk = clk_register(dev, &icst->hw);
|
|
if (IS_ERR(clk))
|
|
kfree(icst);
|
|
|
|
return clk;
|
|
}
|