Skip to content

Commit

Permalink
Merge tag 'v3.19-rockchip-clk2' of git://git.kernel.org/pub/scm/linux…
Browse files Browse the repository at this point in the history
…/kernel/git/mmind/linux-rockchip into clk-next

- clock phase setting capability for the rk3288 mmc clocks
- pll init to allow syncing to actual rate table values
- some more exported clocks
- fixes for some clocks (typos etc) all of them not yet used
  in actual drivers
  • Loading branch information
Michael Turquette committed Nov 29, 2014
2 parents 250d07d + 89bf26c commit b572b5f
Show file tree
Hide file tree
Showing 8 changed files with 315 additions and 50 deletions.
1 change: 1 addition & 0 deletions drivers/clk/rockchip/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ obj-y += clk-rockchip.o
obj-y += clk.o
obj-y += clk-pll.o
obj-y += clk-cpu.o
obj-y += clk-mmc-phase.o
obj-$(CONFIG_RESET_CONTROLLER) += softrst.o

obj-y += clk-rk3188.o
Expand Down
154 changes: 154 additions & 0 deletions drivers/clk/rockchip/clk-mmc-phase.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/*
* Copyright 2014 Google, Inc
* Author: Alexandru M Stan <amstan@chromium.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that 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.
*/

#include <linux/slab.h>
#include <linux/clk-provider.h>
#include "clk.h"

struct rockchip_mmc_clock {
struct clk_hw hw;
void __iomem *reg;
int id;
int shift;
};

#define to_mmc_clock(_hw) container_of(_hw, struct rockchip_mmc_clock, hw)

#define RK3288_MMC_CLKGEN_DIV 2

static unsigned long rockchip_mmc_recalc(struct clk_hw *hw,
unsigned long parent_rate)
{
return parent_rate / RK3288_MMC_CLKGEN_DIV;
}

#define ROCKCHIP_MMC_DELAY_SEL BIT(10)
#define ROCKCHIP_MMC_DEGREE_MASK 0x3
#define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
#define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)

#define PSECS_PER_SEC 1000000000000LL

/*
* Each fine delay is between 40ps-80ps. Assume each fine delay is 60ps to
* simplify calculations. So 45degs could be anywhere between 33deg and 66deg.
*/
#define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60

static int rockchip_mmc_get_phase(struct clk_hw *hw)
{
struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw);
unsigned long rate = clk_get_rate(hw->clk);
u32 raw_value;
u16 degrees;
u32 delay_num = 0;

raw_value = readl(mmc_clock->reg) >> (mmc_clock->shift);

degrees = (raw_value & ROCKCHIP_MMC_DEGREE_MASK) * 90;

if (raw_value & ROCKCHIP_MMC_DELAY_SEL) {
/* degrees/delaynum * 10000 */
unsigned long factor = (ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10) *
36 * (rate / 1000000);

delay_num = (raw_value & ROCKCHIP_MMC_DELAYNUM_MASK);
delay_num >>= ROCKCHIP_MMC_DELAYNUM_OFFSET;
degrees += delay_num * factor / 10000;
}

return degrees % 360;
}

static int rockchip_mmc_set_phase(struct clk_hw *hw, int degrees)
{
struct rockchip_mmc_clock *mmc_clock = to_mmc_clock(hw);
unsigned long rate = clk_get_rate(hw->clk);
u8 nineties, remainder;
u8 delay_num;
u32 raw_value;
u64 delay;

/* allow 22 to be 22.5 */
degrees++;
/* floor to 22.5 increment */
degrees -= ((degrees) * 10 % 225) / 10;

nineties = degrees / 90;
/* 22.5 multiples */
remainder = (degrees % 90) / 22;

delay = PSECS_PER_SEC;
do_div(delay, rate);
/* / 360 / 22.5 */
do_div(delay, 16);
do_div(delay, ROCKCHIP_MMC_DELAY_ELEMENT_PSEC);

delay *= remainder;
delay_num = (u8) min(delay, 255ULL);

raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0;
raw_value |= delay_num << ROCKCHIP_MMC_DELAYNUM_OFFSET;
raw_value |= nineties;
writel(HIWORD_UPDATE(raw_value, 0x07ff, mmc_clock->shift), mmc_clock->reg);

pr_debug("%s->set_phase(%d) delay_nums=%u reg[0x%p]=0x%03x actual_degrees=%d\n",
__clk_get_name(hw->clk), degrees, delay_num,
mmc_clock->reg, raw_value>>(mmc_clock->shift),
rockchip_mmc_get_phase(hw)
);

return 0;
}

static const struct clk_ops rockchip_mmc_clk_ops = {
.recalc_rate = rockchip_mmc_recalc,
.get_phase = rockchip_mmc_get_phase,
.set_phase = rockchip_mmc_set_phase,
};

struct clk *rockchip_clk_register_mmc(const char *name,
const char **parent_names, u8 num_parents,
void __iomem *reg, int shift)
{
struct clk_init_data init;
struct rockchip_mmc_clock *mmc_clock;
struct clk *clk;

mmc_clock = kmalloc(sizeof(*mmc_clock), GFP_KERNEL);
if (!mmc_clock)
return NULL;

init.num_parents = num_parents;
init.parent_names = parent_names;
init.ops = &rockchip_mmc_clk_ops;

mmc_clock->hw.init = &init;
mmc_clock->reg = reg;
mmc_clock->shift = shift;

if (name)
init.name = name;

clk = clk_register(NULL, &mmc_clock->hw);
if (IS_ERR(clk))
goto err_free;

return clk;

err_free:
kfree(mmc_clock);
return NULL;
}
81 changes: 66 additions & 15 deletions drivers/clk/rockchip/clk-pll.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct rockchip_clk_pll {
int lock_offset;
unsigned int lock_shift;
enum rockchip_pll_type type;
u8 flags;
const struct rockchip_pll_rate_table *rate_table;
unsigned int rate_count;
spinlock_t *lock;
Expand Down Expand Up @@ -257,6 +258,55 @@ static int rockchip_rk3066_pll_is_enabled(struct clk_hw *hw)
return !(pllcon & RK3066_PLLCON3_PWRDOWN);
}

static void rockchip_rk3066_pll_init(struct clk_hw *hw)
{
struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
const struct rockchip_pll_rate_table *rate;
unsigned int nf, nr, no, bwadj;
unsigned long drate;
u32 pllcon;

if (!(pll->flags & ROCKCHIP_PLL_SYNC_RATE))
return;

drate = __clk_get_rate(hw->clk);
rate = rockchip_get_pll_settings(pll, drate);

/* when no rate setting for the current rate, rely on clk_set_rate */
if (!rate)
return;

pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(0));
nr = ((pllcon >> RK3066_PLLCON0_NR_SHIFT) & RK3066_PLLCON0_NR_MASK) + 1;
no = ((pllcon >> RK3066_PLLCON0_OD_SHIFT) & RK3066_PLLCON0_OD_MASK) + 1;

pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(1));
nf = ((pllcon >> RK3066_PLLCON1_NF_SHIFT) & RK3066_PLLCON1_NF_MASK) + 1;

pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(2));
bwadj = (pllcon >> RK3066_PLLCON2_BWADJ_SHIFT) & RK3066_PLLCON2_BWADJ_MASK;

pr_debug("%s: pll %s@%lu: nr (%d:%d); no (%d:%d); nf(%d:%d), bwadj(%d:%d)\n",
__func__, __clk_get_name(hw->clk), drate, rate->nr, nr,
rate->no, no, rate->nf, nf, rate->bwadj, bwadj);
if (rate->nr != nr || rate->no != no || rate->nf != nf
|| rate->bwadj != bwadj) {
struct clk *parent = __clk_get_parent(hw->clk);
unsigned long prate;

if (!parent) {
pr_warn("%s: parent of %s not available\n",
__func__, __clk_get_name(hw->clk));
return;
}

pr_debug("%s: pll %s: rate params do not match rate table, adjusting\n",
__func__, __clk_get_name(hw->clk));
prate = __clk_get_rate(parent);
rockchip_rk3066_pll_set_rate(hw, drate, prate);
}
}

static const struct clk_ops rockchip_rk3066_pll_clk_norate_ops = {
.recalc_rate = rockchip_rk3066_pll_recalc_rate,
.enable = rockchip_rk3066_pll_enable,
Expand All @@ -271,6 +321,7 @@ static const struct clk_ops rockchip_rk3066_pll_clk_ops = {
.enable = rockchip_rk3066_pll_enable,
.disable = rockchip_rk3066_pll_disable,
.is_enabled = rockchip_rk3066_pll_is_enabled,
.init = rockchip_rk3066_pll_init,
};

/*
Expand All @@ -282,7 +333,7 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
void __iomem *base, int con_offset, int grf_lock_offset,
int lock_shift, int mode_offset, int mode_shift,
struct rockchip_pll_rate_table *rate_table,
spinlock_t *lock)
u8 clk_pll_flags, spinlock_t *lock)
{
const char *pll_parents[3];
struct clk_init_data init;
Expand Down Expand Up @@ -345,8 +396,22 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
pll->reg_base = base + con_offset;
pll->lock_offset = grf_lock_offset;
pll->lock_shift = lock_shift;
pll->flags = clk_pll_flags;
pll->lock = lock;

/* create the mux on top of the real pll */
pll->pll_mux_ops = &clk_mux_ops;
pll_mux = &pll->pll_mux;
pll_mux->reg = base + mode_offset;
pll_mux->shift = mode_shift;
pll_mux->mask = PLL_MODE_MASK;
pll_mux->flags = 0;
pll_mux->lock = lock;
pll_mux->hw.init = &init;

if (pll_type == pll_rk3066)
pll_mux->flags |= CLK_MUX_HIWORD_MASK;

pll_clk = clk_register(NULL, &pll->hw);
if (IS_ERR(pll_clk)) {
pr_err("%s: failed to register pll clock %s : %ld\n",
Expand All @@ -355,10 +420,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
goto err_pll;
}

/* create the mux on top of the real pll */
pll->pll_mux_ops = &clk_mux_ops;
pll_mux = &pll->pll_mux;

/* the actual muxing is xin24m, pll-output, xin32k */
pll_parents[0] = parent_names[0];
pll_parents[1] = pll_name;
Expand All @@ -370,16 +431,6 @@ struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
init.parent_names = pll_parents;
init.num_parents = ARRAY_SIZE(pll_parents);

pll_mux->reg = base + mode_offset;
pll_mux->shift = mode_shift;
pll_mux->mask = PLL_MODE_MASK;
pll_mux->flags = 0;
pll_mux->lock = lock;
pll_mux->hw.init = &init;

if (pll_type == pll_rk3066)
pll_mux->flags |= CLK_MUX_HIWORD_MASK;

mux_clk = clk_register(NULL, &pll_mux->hw);
if (IS_ERR(mux_clk))
goto err_mux;
Expand Down
37 changes: 15 additions & 22 deletions drivers/clk/rockchip/clk-rk3188.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,13 +212,13 @@ PNAME(mux_sclk_macref_p) = { "mac_src", "ext_rmii" };

static struct rockchip_pll_clock rk3188_pll_clks[] __initdata = {
[apll] = PLL(pll_rk3066, PLL_APLL, "apll", mux_pll_p, 0, RK2928_PLL_CON(0),
RK2928_MODE_CON, 0, 6, rk3188_pll_rates),
RK2928_MODE_CON, 0, 6, 0, rk3188_pll_rates),
[dpll] = PLL(pll_rk3066, PLL_DPLL, "dpll", mux_pll_p, 0, RK2928_PLL_CON(4),
RK2928_MODE_CON, 4, 5, NULL),
RK2928_MODE_CON, 4, 5, 0, NULL),
[cpll] = PLL(pll_rk3066, PLL_CPLL, "cpll", mux_pll_p, 0, RK2928_PLL_CON(8),
RK2928_MODE_CON, 8, 7, rk3188_pll_rates),
RK2928_MODE_CON, 8, 7, ROCKCHIP_PLL_SYNC_RATE, rk3188_pll_rates),
[gpll] = PLL(pll_rk3066, PLL_GPLL, "gpll", mux_pll_p, 0, RK2928_PLL_CON(12),
RK2928_MODE_CON, 12, 8, rk3188_pll_rates),
RK2928_MODE_CON, 12, 8, ROCKCHIP_PLL_SYNC_RATE, rk3188_pll_rates),
};

#define MFLAGS CLK_MUX_HIWORD_MASK
Expand Down Expand Up @@ -330,6 +330,15 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = {
RK2928_CLKSEL_CON(24), 8, 8, DFLAGS,
RK2928_CLKGATE_CON(2), 8, GFLAGS),

COMPOSITE_NOMUX(0, "spdif_pre", "i2s_src", 0,
RK2928_CLKSEL_CON(5), 0, 7, DFLAGS,
RK2928_CLKGATE_CON(0), 13, GFLAGS),
COMPOSITE_FRAC(0, "spdif_frac", "spdif_pll", 0,
RK2928_CLKSEL_CON(9), 0,
RK2928_CLKGATE_CON(0), 14, GFLAGS),
MUX(SCLK_SPDIF, "sclk_spdif", mux_sclk_spdif_p, 0,
RK2928_CLKSEL_CON(5), 8, 2, MFLAGS),

/*
* Clock-Architecture Diagram 4
*/
Expand Down Expand Up @@ -410,7 +419,7 @@ static struct rockchip_clk_branch common_clk_branches[] __initdata = {
/* hclk_ahb2apb is part of a clk branch */
GATE(0, "hclk_vio_bus", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 12, GFLAGS),
GATE(HCLK_LCDC0, "hclk_lcdc0", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 1, GFLAGS),
GATE(HCLK_LCDC1, "hclk_lcdc1", "aclk_cpu", 0, RK2928_CLKGATE_CON(6), 2, GFLAGS),
GATE(HCLK_LCDC1, "hclk_lcdc1", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 2, GFLAGS),
GATE(HCLK_CIF0, "hclk_cif0", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 4, GFLAGS),
GATE(HCLK_IPP, "hclk_ipp", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 9, GFLAGS),
GATE(HCLK_RGA, "hclk_rga", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 10, GFLAGS),
Expand Down Expand Up @@ -577,14 +586,6 @@ static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = {
RK2928_CLKGATE_CON(0), 12, GFLAGS),
MUX(SCLK_I2S2, "sclk_i2s2", mux_sclk_i2s2_p, 0,
RK2928_CLKSEL_CON(4), 8, 2, MFLAGS),
COMPOSITE_NOMUX(0, "spdif_pre", "i2s_src", 0,
RK2928_CLKSEL_CON(5), 0, 7, DFLAGS,
RK2928_CLKGATE_CON(0), 13, GFLAGS),
COMPOSITE_FRAC(0, "spdif_frac", "spdif_pll", 0,
RK2928_CLKSEL_CON(9), 0,
RK2928_CLKGATE_CON(0), 14, GFLAGS),
MUX(SCLK_SPDIF, "sclk_spdif", mux_sclk_spdif_p, 0,
RK2928_CLKSEL_CON(5), 8, 2, MFLAGS),

GATE(HCLK_I2S1, "hclk_i2s1", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 3, GFLAGS),
GATE(HCLK_I2S2, "hclk_i2s2", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 4, GFLAGS),
Expand Down Expand Up @@ -663,7 +664,7 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
RK2928_CLKSEL_CON(30), 0, 2, DFLAGS,
RK2928_CLKGATE_CON(3), 6, GFLAGS),
DIV(0, "sclk_hsicphy_12m", "sclk_hsicphy_480m", 0,
RK2928_CLKGATE_CON(11), 8, 6, DFLAGS),
RK2928_CLKSEL_CON(11), 8, 6, DFLAGS),

MUX(0, "i2s_src", mux_pll_src_gpll_cpll_p, 0,
RK2928_CLKSEL_CON(2), 15, 1, MFLAGS),
Expand All @@ -675,14 +676,6 @@ static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
RK2928_CLKGATE_CON(0), 10, GFLAGS),
MUX(SCLK_I2S0, "sclk_i2s0", mux_sclk_i2s0_p, 0,
RK2928_CLKSEL_CON(3), 8, 2, MFLAGS),
COMPOSITE_NOMUX(0, "spdif_pre", "i2s_src", 0,
RK2928_CLKSEL_CON(5), 0, 7, DFLAGS,
RK2928_CLKGATE_CON(13), 13, GFLAGS),
COMPOSITE_FRAC(0, "spdif_frac", "spdif_pll", 0,
RK2928_CLKSEL_CON(9), 0,
RK2928_CLKGATE_CON(0), 14, GFLAGS),
MUX(SCLK_SPDIF, "sclk_spdif", mux_sclk_spdif_p, 0,
RK2928_CLKSEL_CON(5), 8, 2, MFLAGS),

GATE(0, "hclk_imem0", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS),
GATE(0, "hclk_imem1", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 15, GFLAGS),
Expand Down
Loading

0 comments on commit b572b5f

Please sign in to comment.