blob: 181ee359c39a5c44d71f04671d65bf10f8239c50 [file] [log] [blame]
/*
* Copyright (c) 2013 Marvell Technology Group Ltd.
*
* Author: Jisheng Zhang <jszhang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/io.h>
#include <linux/of.h>
#include <linux/kernel.h>
#include <linux/clk-provider.h>
#include <linux/delay.h>
#include "clk.h"
#define PLL_CTRL0 0x0
#define PLL_CTRL1 0x4
#define PLL_CTRL2 0x8
#define PLL_CTRL3 0xC
#define PLL_CTRL4 0x10
#define PLL_STATUS 0x14
#define AVPLL_CTRL0 0
#define AVPLL_CTRL1 4
#define AVPLL_CTRL2 8
#define AVPLL_CTRL3 12
#define AVPLLCH 28
#define AVPLLCH_SIZE 16
#define AVPLLCH_CTRL0 0
#define AVPLLCH_CTRL1 4
#define AVPLLCH_CTRL2 8
#define AVPLLCH_CTRL3 12
static u8 vcodiv_berlin2cdp[] = {1, 2, 4, 8, 16, 32, 64, 128};
static unsigned long berlin2cdp_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
u32 val, fbdiv, rfdiv, vcodivsel;
struct berlin_pll *pll = to_berlin_pll(hw);
val = readl_relaxed(pll->ctrl + PLL_CTRL0);
fbdiv = (val >> 12) & 0x1FF;
rfdiv = (val >> 3) & 0x1FF;
val = readl_relaxed(pll->ctrl + PLL_CTRL1);
vcodivsel = (val >> 9) & 0x7;
return parent_rate * fbdiv * 4 / rfdiv /
vcodiv_berlin2cdp[vcodivsel];
}
static long berlin2cdp_pll_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
int vcodivsel, pllintpi, kvco;
u32 vco, rfdiv, fbdiv, ctrl0;
struct berlin_pll *pll = to_berlin_pll(hw);
long ret;
rate /= 1000000;
if (rate <= 3000 && rate >= 1200)
vcodivsel = 0;
else if (rate < 1200 && rate >= 600)
vcodivsel = 1;
else if (rate < 600 && rate >= 300)
vcodivsel = 2;
else if (rate < 300 && rate >= 150)
vcodivsel = 3;
else if (rate < 150 && rate >= 75)
vcodivsel = 4;
else if (rate < 75 && rate >= 37)
vcodivsel = 5;
else
return berlin2cdp_pll_recalc_rate(hw, *parent_rate);
vco = rate * vcodiv_berlin2cdp[vcodivsel];
if (vco >= 1200 && vco <= 1350)
kvco = 8;
else if (vco <= 1500)
kvco = 9;
else if (vco <= 1750)
kvco = 0xA;
else if (vco <= 2000)
kvco = 0xB;
else if (vco <= 2200)
kvco = 0xC;
else if (vco <= 2400)
kvco = 0xD;
else if (vco <= 2600)
kvco = 0xE;
else if (vco <= 3000)
kvco = 0xF;
else
return berlin2cdp_pll_recalc_rate(hw, *parent_rate);
if (vco>= 1500 && vco <= 2000)
pllintpi = 5;
else if (vco <= 2500)
pllintpi = 6;
else if (vco <= 3000)
pllintpi = 8;
else
return berlin2cdp_pll_recalc_rate(hw, *parent_rate);
ctrl0 = readl_relaxed(pll->ctrl);
rfdiv = (ctrl0 >> 3) & 0x1FF;
fbdiv = (vco * rfdiv) / (4 * *parent_rate / 1000000);
fbdiv &= 0x1FF;
ret = *parent_rate * fbdiv * 4 / rfdiv /
vcodiv_berlin2cdp[vcodivsel];
return ret;
}
static int berlin2cdp_pll_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
int vcodivsel, pllintpi, kvco;
u32 vco, rfdiv, fbdiv, ctrl0, ctrl1, bypass;
struct berlin_pll *pll = to_berlin_pll(hw);
rate /= 1000000;
if (rate <= 3000 && rate >= 1200)
vcodivsel = 0;
else if (rate < 1200 && rate >= 600)
vcodivsel = 1;
else if (rate < 600 && rate >= 300)
vcodivsel = 2;
else if (rate < 300 && rate >= 150)
vcodivsel = 3;
else if (rate < 150 && rate >= 75)
vcodivsel = 4;
else if (rate < 75 && rate >= 37)
vcodivsel = 5;
else
return -EPERM;
vco = rate * vcodiv_berlin2cdp[vcodivsel];
if (vco >= 1200 && vco <= 1350)
kvco = 8;
else if (vco <= 1500)
kvco = 9;
else if (vco <= 1750)
kvco = 0xA;
else if (vco <= 2000)
kvco = 0xB;
else if (vco <= 2200)
kvco = 0xC;
else if (vco <= 2400)
kvco = 0xD;
else if (vco <= 2600)
kvco = 0xE;
else if (vco <= 3000)
kvco = 0xF;
else
return -EPERM;
if (vco>= 1500 && vco <= 2000)
pllintpi = 5;
else if (vco <= 2500)
pllintpi = 6;
else if (vco <= 3000)
pllintpi = 8;
else
return -EPERM;
ctrl0 = readl_relaxed(pll->ctrl + PLL_CTRL0);
ctrl1 = readl_relaxed(pll->ctrl + PLL_CTRL1);
rfdiv = (ctrl0 >> 3) & 0x1FF;
fbdiv = (vco * rfdiv) / (4 * parent_rate / 1000000);
fbdiv &= 0x1FF;
ctrl0 &= ~(0x1FF << 12);
ctrl0 |= (fbdiv << 12);
ctrl1 &= ~(0xf << 0);
ctrl1 |= (kvco << 0);
ctrl1 &= ~(0x7 << 6);
ctrl1 |= (vcodivsel << 6);
ctrl1 &= ~(0x7 << 9);
ctrl1 |= (vcodivsel << 9);
ctrl1 &= ~(0xf << 26);
ctrl1 |= (pllintpi << 26);
/* Pll bypass enable */
bypass = readl_relaxed(pll->bypass);
bypass |= (1 << pll->bypass_shift);
writel_relaxed(bypass, pll->bypass);
ctrl1 |= (1 << 14);
writel_relaxed(ctrl1, pll->ctrl + PLL_CTRL1);
/* reset on */
ctrl0 |= (1 << 1);
writel_relaxed(ctrl0, pll->ctrl + PLL_CTRL0);
/* make sure RESET is high for at least 2us */
udelay(2);
/* clear reset */
ctrl0 &= ~(1 << 1);
writel_relaxed(ctrl0, pll->ctrl + PLL_CTRL0);
/* wait 50us */
udelay(50);
/* make sure pll locked */
while (!(readl_relaxed(pll->ctrl + PLL_STATUS) & (1 << 0)))
cpu_relax();
/* pll bypass disable */
ctrl1 &= ~(1 << 14);
writel_relaxed(ctrl1, pll->ctrl + PLL_CTRL1);
bypass &= ~(1 << pll->bypass_shift);
writel_relaxed(bypass, pll->bypass);
return 0;
}
static const struct clk_ops berlin2cdp_pll_ops = {
.recalc_rate = berlin2cdp_pll_recalc_rate,
.round_rate = berlin2cdp_pll_round_rate,
.set_rate = berlin2cdp_pll_set_rate,
};
void __init berlin2cdp_pll_setup(struct device_node *np)
{
berlin_pll_setup(np, &berlin2cdp_pll_ops);
}
static unsigned long berlin2cdp_avpllch_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
u32 val;
void __iomem *addr;
unsigned long pll;
int endpll, sync1, sync2, post_div, post_0p5_div;
struct berlin_avpllch *avpllch = to_berlin_avpllch(hw);
addr = avpllch->base + AVPLLCH + avpllch->which * AVPLLCH_SIZE;
val = readl_relaxed(addr + AVPLLCH_CTRL0);
endpll = val & (1 << 14);
post_div = val & 0x1FFF;
post_0p5_div = (val >> 13) & 1;
sync1 = readl_relaxed(addr + AVPLLCH_CTRL2) & 0xFFFFF;
sync2 = readl_relaxed(addr + AVPLLCH_CTRL3) & 0xFFFFF;
parent_rate /= 1000000;
if (endpll)
pll = parent_rate * sync2 / sync1;
else
pll = parent_rate;
return 1000000 * 2 * pll / (2 * post_div + post_0p5_div);
}
static const struct clk_ops berlin2cdp_avpllch_ops = {
.recalc_rate = berlin2cdp_avpllch_recalc_rate,
};
static struct berlin_avplldata avpll_data = {
.offset = 140,
.offset_mask = 0x7FFFF,
.offset_shift = 0,
.fbdiv = 4,
.fbdiv_mask = 0x1FF,
.fbdiv_shift = 17,
.ops = &berlin2cdp_avpllch_ops,
};
void __init berlin2cdp_avpll_setup(struct device_node *np)
{
berlin_avpll_setup(np, &avpll_data);
}