Merge branch 'next/drivers' into late/multiplatform

Multiple parts of next/drivers are prerequisites for the final
exynos multiplatform changes, so let's pull in the entire branch.

Signed-off-by: Arnd Bergmann <[email protected]>
This commit is contained in:
Arnd Bergmann 2013-04-19 23:12:42 +02:00
commit 22cf644e03
421 changed files with 20542 additions and 9021 deletions

View file

@ -63,6 +63,14 @@ config CLK_TWL6040
McPDM. McPDM module is using the external bit clock on the McPDM bus
as functional clock.
config COMMON_CLK_AXI_CLKGEN
tristate "AXI clkgen driver"
depends on ARCH_ZYNQ || MICROBLAZE
help
---help---
Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx
FPGAs. It is commonly used in Analog Devices' reference designs.
endmenu
source "drivers/clk/mvebu/Kconfig"

View file

@ -7,6 +7,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o
obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o
obj-$(CONFIG_COMMON_CLK) += clk-gate.o
obj-$(CONFIG_COMMON_CLK) += clk-mux.o
obj-$(CONFIG_COMMON_CLK) += clk-composite.o
# SoCs specific
obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o
@ -23,14 +24,17 @@ ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_ARCH_SUNXI) += sunxi/
obj-$(CONFIG_ARCH_U8500) += ux500/
obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_PLAT_SAMSUNG) += samsung/
obj-$(CONFIG_X86) += x86/
# Chip specific
obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o

View file

@ -0,0 +1,331 @@
/*
* AXI clkgen driver
*
* Copyright 2012-2013 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
*
* Licensed under the GPL-2.
*
*/
#include <linux/platform_device.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/err.h>
#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04
#define AXI_CLKGEN_REG_CLK_OUT1 0x08
#define AXI_CLKGEN_REG_CLK_OUT2 0x0c
#define AXI_CLKGEN_REG_CLK_DIV 0x10
#define AXI_CLKGEN_REG_CLK_FB1 0x14
#define AXI_CLKGEN_REG_CLK_FB2 0x18
#define AXI_CLKGEN_REG_LOCK1 0x1c
#define AXI_CLKGEN_REG_LOCK2 0x20
#define AXI_CLKGEN_REG_LOCK3 0x24
#define AXI_CLKGEN_REG_FILTER1 0x28
#define AXI_CLKGEN_REG_FILTER2 0x2c
struct axi_clkgen {
void __iomem *base;
struct clk_hw clk_hw;
};
static uint32_t axi_clkgen_lookup_filter(unsigned int m)
{
switch (m) {
case 0:
return 0x01001990;
case 1:
return 0x01001190;
case 2:
return 0x01009890;
case 3:
return 0x01001890;
case 4:
return 0x01008890;
case 5 ... 8:
return 0x01009090;
case 9 ... 11:
return 0x01000890;
case 12:
return 0x08009090;
case 13 ... 22:
return 0x01001090;
case 23 ... 36:
return 0x01008090;
case 37 ... 46:
return 0x08001090;
default:
return 0x08008090;
}
}
static const uint32_t axi_clkgen_lock_table[] = {
0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8,
0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8,
0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339,
0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271,
0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4,
0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190,
0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e,
0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c,
0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113,
};
static uint32_t axi_clkgen_lookup_lock(unsigned int m)
{
if (m < ARRAY_SIZE(axi_clkgen_lock_table))
return axi_clkgen_lock_table[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 void axi_clkgen_calc_params(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;
unsigned long m, m_min, m_max;
unsigned long f, dout, best_f, fvco;
fin /= 1000;
fout /= 1000;
best_f = ULONG_MAX;
*best_d = 0;
*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);
m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1);
m_max = min_t(unsigned long, fvco_max * d_max / fin, 64);
for (m = m_min; m <= m_max; m++) {
_d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max));
_d_max = min(d_max, fin * m / fvco_min);
for (d = _d_min; d <= _d_max; d++) {
fvco = fin * m / d;
dout = DIV_ROUND_CLOSEST(fvco, fout);
dout = clamp_t(unsigned long, dout, 1, 128);
f = fvco / dout;
if (abs(f - fout) < abs(best_f - fout)) {
best_f = f;
*best_d = d;
*best_m = m;
*best_dout = dout;
if (best_f == fout)
return;
}
}
}
}
static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low,
unsigned int *high, unsigned int *edge, unsigned int *nocount)
{
if (divider == 1)
*nocount = 1;
else
*nocount = 0;
*high = divider / 2;
*edge = divider % 2;
*low = divider - *high;
}
static void axi_clkgen_write(struct axi_clkgen *axi_clkgen,
unsigned int reg, unsigned int val)
{
writel(val, axi_clkgen->base + reg);
}
static void axi_clkgen_read(struct axi_clkgen *axi_clkgen,
unsigned int reg, unsigned int *val)
{
*val = readl(axi_clkgen->base + reg);
}
static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw)
{
return container_of(clk_hw, struct axi_clkgen, clk_hw);
}
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);
unsigned int d, m, dout;
unsigned int nocount;
unsigned int high;
unsigned int edge;
unsigned int low;
uint32_t filter;
uint32_t lock;
if (parent_rate == 0 || rate == 0)
return -EINVAL;
axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout);
if (d == 0 || dout == 0 || m == 0)
return -EINVAL;
filter = axi_clkgen_lookup_filter(m - 1);
lock = axi_clkgen_lookup_lock(m - 1);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0);
axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1,
(high << 6) | low);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2,
(edge << 7) | (nocount << 6));
axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV,
(edge << 13) | (nocount << 12) | (high << 6) | low);
axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1,
(high << 6) | low);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2,
(edge << 7) | (nocount << 6));
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2,
(((lock >> 16) & 0x1f) << 10) | 0x1);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3,
(((lock >> 24) & 0x1f) << 10) | 0x3e9);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter);
axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1);
return 0;
}
static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
unsigned int d, m, dout;
axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout);
if (d == 0 || dout == 0 || m == 0)
return -EINVAL;
return *parent_rate / d * m / dout;
}
static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw,
unsigned long parent_rate)
{
struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw);
unsigned int d, m, dout;
unsigned int reg;
unsigned long long tmp;
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, &reg);
dout = (reg & 0x3f) + ((reg >> 6) & 0x3f);
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, &reg);
d = (reg & 0x3f) + ((reg >> 6) & 0x3f);
axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, &reg);
m = (reg & 0x3f) + ((reg >> 6) & 0x3f);
if (d == 0 || dout == 0)
return 0;
tmp = (unsigned long long)(parent_rate / d) * m;
do_div(tmp, dout);
if (tmp > ULONG_MAX)
return ULONG_MAX;
return tmp;
}
static const struct clk_ops axi_clkgen_ops = {
.recalc_rate = axi_clkgen_recalc_rate,
.round_rate = axi_clkgen_round_rate,
.set_rate = axi_clkgen_set_rate,
};
static int axi_clkgen_probe(struct platform_device *pdev)
{
struct axi_clkgen *axi_clkgen;
struct clk_init_data init;
const char *parent_name;
const char *clk_name;
struct resource *mem;
struct clk *clk;
axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL);
if (!axi_clkgen)
return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem);
if (IS_ERR(axi_clkgen->base))
return PTR_ERR(axi_clkgen->base);
parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0);
if (!parent_name)
return -EINVAL;
clk_name = pdev->dev.of_node->name;
of_property_read_string(pdev->dev.of_node, "clock-output-names",
&clk_name);
init.name = clk_name;
init.ops = &axi_clkgen_ops;
init.flags = 0;
init.parent_names = &parent_name;
init.num_parents = 1;
axi_clkgen->clk_hw.init = &init;
clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw);
if (IS_ERR(clk))
return PTR_ERR(clk);
return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get,
clk);
}
static int axi_clkgen_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
return 0;
}
static const struct of_device_id axi_clkgen_ids[] = {
{ .compatible = "adi,axi-clkgen-1.00.a" },
{ },
};
MODULE_DEVICE_TABLE(of, axi_clkgen_ids);
static struct platform_driver axi_clkgen_driver = {
.driver = {
.name = "adi-axi-clkgen",
.owner = THIS_MODULE,
.of_match_table = axi_clkgen_ids,
},
.probe = axi_clkgen_probe,
.remove = axi_clkgen_remove,
};
module_platform_driver(axi_clkgen_driver);
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Lars-Peter Clausen <[email protected]>");
MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator");

201
drivers/clk/clk-composite.c Normal file
View file

@ -0,0 +1,201 @@
/*
* Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved.
*
* 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/clk.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
#include <linux/slab.h>
#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw)
static u8 clk_composite_get_parent(struct clk_hw *hw)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *mux_ops = composite->mux_ops;
struct clk_hw *mux_hw = composite->mux_hw;
mux_hw->clk = hw->clk;
return mux_ops->get_parent(mux_hw);
}
static int clk_composite_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *mux_ops = composite->mux_ops;
struct clk_hw *mux_hw = composite->mux_hw;
mux_hw->clk = hw->clk;
return mux_ops->set_parent(mux_hw, index);
}
static unsigned long clk_composite_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *div_ops = composite->div_ops;
struct clk_hw *div_hw = composite->div_hw;
div_hw->clk = hw->clk;
return div_ops->recalc_rate(div_hw, parent_rate);
}
static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *div_ops = composite->div_ops;
struct clk_hw *div_hw = composite->div_hw;
div_hw->clk = hw->clk;
return div_ops->round_rate(div_hw, rate, prate);
}
static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *div_ops = composite->div_ops;
struct clk_hw *div_hw = composite->div_hw;
div_hw->clk = hw->clk;
return div_ops->set_rate(div_hw, rate, parent_rate);
}
static int clk_composite_is_enabled(struct clk_hw *hw)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *gate_ops = composite->gate_ops;
struct clk_hw *gate_hw = composite->gate_hw;
gate_hw->clk = hw->clk;
return gate_ops->is_enabled(gate_hw);
}
static int clk_composite_enable(struct clk_hw *hw)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *gate_ops = composite->gate_ops;
struct clk_hw *gate_hw = composite->gate_hw;
gate_hw->clk = hw->clk;
return gate_ops->enable(gate_hw);
}
static void clk_composite_disable(struct clk_hw *hw)
{
struct clk_composite *composite = to_clk_composite(hw);
const struct clk_ops *gate_ops = composite->gate_ops;
struct clk_hw *gate_hw = composite->gate_hw;
gate_hw->clk = hw->clk;
gate_ops->disable(gate_hw);
}
struct clk *clk_register_composite(struct device *dev, const char *name,
const char **parent_names, int num_parents,
struct clk_hw *mux_hw, const struct clk_ops *mux_ops,
struct clk_hw *div_hw, const struct clk_ops *div_ops,
struct clk_hw *gate_hw, const struct clk_ops *gate_ops,
unsigned long flags)
{
struct clk *clk;
struct clk_init_data init;
struct clk_composite *composite;
struct clk_ops *clk_composite_ops;
composite = kzalloc(sizeof(*composite), GFP_KERNEL);
if (!composite) {
pr_err("%s: could not allocate composite clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.flags = flags | CLK_IS_BASIC;
init.parent_names = parent_names;
init.num_parents = num_parents;
clk_composite_ops = &composite->ops;
if (mux_hw && mux_ops) {
if (!mux_ops->get_parent || !mux_ops->set_parent) {
clk = ERR_PTR(-EINVAL);
goto err;
}
composite->mux_hw = mux_hw;
composite->mux_ops = mux_ops;
clk_composite_ops->get_parent = clk_composite_get_parent;
clk_composite_ops->set_parent = clk_composite_set_parent;
}
if (div_hw && div_ops) {
if (!div_ops->recalc_rate || !div_ops->round_rate ||
!div_ops->set_rate) {
clk = ERR_PTR(-EINVAL);
goto err;
}
composite->div_hw = div_hw;
composite->div_ops = div_ops;
clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
clk_composite_ops->round_rate = clk_composite_round_rate;
clk_composite_ops->set_rate = clk_composite_set_rate;
}
if (gate_hw && gate_ops) {
if (!gate_ops->is_enabled || !gate_ops->enable ||
!gate_ops->disable) {
clk = ERR_PTR(-EINVAL);
goto err;
}
composite->gate_hw = gate_hw;
composite->gate_ops = gate_ops;
clk_composite_ops->is_enabled = clk_composite_is_enabled;
clk_composite_ops->enable = clk_composite_enable;
clk_composite_ops->disable = clk_composite_disable;
}
init.ops = clk_composite_ops;
composite->hw.init = &init;
clk = clk_register(dev, &composite->hw);
if (IS_ERR(clk))
goto err;
if (composite->mux_hw)
composite->mux_hw->clk = clk;
if (composite->div_hw)
composite->div_hw->clk = clk;
if (composite->gate_hw)
composite->gate_hw->clk = clk;
return clk;
err:
kfree(composite);
return clk;
}

View file

@ -32,6 +32,7 @@
static u8 clk_mux_get_parent(struct clk_hw *hw)
{
struct clk_mux *mux = to_clk_mux(hw);
int num_parents = __clk_get_num_parents(hw->clk);
u32 val;
/*
@ -42,7 +43,16 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
* val = 0x4 really means "bit 2, index starts at bit 0"
*/
val = readl(mux->reg) >> mux->shift;
val &= (1 << mux->width) - 1;
val &= mux->mask;
if (mux->table) {
int i;
for (i = 0; i < num_parents; i++)
if (mux->table[i] == val)
return i;
return -EINVAL;
}
if (val && (mux->flags & CLK_MUX_INDEX_BIT))
val = ffs(val) - 1;
@ -50,7 +60,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw)
if (val && (mux->flags & CLK_MUX_INDEX_ONE))
val--;
if (val >= __clk_get_num_parents(hw->clk))
if (val >= num_parents)
return -EINVAL;
return val;
@ -62,17 +72,22 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index)
u32 val;
unsigned long flags = 0;
if (mux->flags & CLK_MUX_INDEX_BIT)
index = (1 << ffs(index));
if (mux->table)
index = mux->table[index];
if (mux->flags & CLK_MUX_INDEX_ONE)
index++;
else {
if (mux->flags & CLK_MUX_INDEX_BIT)
index = (1 << ffs(index));
if (mux->flags & CLK_MUX_INDEX_ONE)
index++;
}
if (mux->lock)
spin_lock_irqsave(mux->lock, flags);
val = readl(mux->reg);
val &= ~(((1 << mux->width) - 1) << mux->shift);
val &= ~(mux->mask << mux->shift);
val |= index << mux->shift;
writel(val, mux->reg);
@ -88,10 +103,10 @@ const struct clk_ops clk_mux_ops = {
};
EXPORT_SYMBOL_GPL(clk_mux_ops);
struct clk *clk_register_mux(struct device *dev, const char *name,
struct clk *clk_register_mux_table(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_mux_flags, spinlock_t *lock)
void __iomem *reg, u8 shift, u32 mask,
u8 clk_mux_flags, u32 *table, spinlock_t *lock)
{
struct clk_mux *mux;
struct clk *clk;
@ -113,9 +128,10 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
/* struct clk_mux assignments */
mux->reg = reg;
mux->shift = shift;
mux->width = width;
mux->mask = mask;
mux->flags = clk_mux_flags;
mux->lock = lock;
mux->table = table;
mux->hw.init = &init;
clk = clk_register(dev, &mux->hw);
@ -125,3 +141,15 @@ struct clk *clk_register_mux(struct device *dev, const char *name,
return clk;
}
struct clk *clk_register_mux(struct device *dev, const char *name,
const char **parent_names, u8 num_parents, unsigned long flags,
void __iomem *reg, u8 shift, u8 width,
u8 clk_mux_flags, spinlock_t *lock)
{
u32 mask = BIT(width) - 1;
return clk_register_mux_table(dev, name, parent_names, num_parents,
flags, reg, shift, mask, clk_mux_flags,
NULL, lock);
}

View file

@ -1113,7 +1113,7 @@ void __init sirfsoc_of_clk_init(void)
for (i = pll1; i < maxclk; i++) {
prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]);
BUG_ON(!prima2_clks[i]);
BUG_ON(IS_ERR(prima2_clks[i]));
}
clk_register_clkdev(prima2_clks[cpu], NULL, "cpu");
clk_register_clkdev(prima2_clks[io], NULL, "io");

View file

@ -20,6 +20,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/clk-provider.h>
#include <linux/clk/zynq.h>
static void __iomem *slcr_base;

View file

@ -19,14 +19,77 @@
#include <linux/of.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/sched.h>
static DEFINE_SPINLOCK(enable_lock);
static DEFINE_MUTEX(prepare_lock);
static struct task_struct *prepare_owner;
static struct task_struct *enable_owner;
static int prepare_refcnt;
static int enable_refcnt;
static HLIST_HEAD(clk_root_list);
static HLIST_HEAD(clk_orphan_list);
static LIST_HEAD(clk_notifier_list);
/*** locking ***/
static void clk_prepare_lock(void)
{
if (!mutex_trylock(&prepare_lock)) {
if (prepare_owner == current) {
prepare_refcnt++;
return;
}
mutex_lock(&prepare_lock);
}
WARN_ON_ONCE(prepare_owner != NULL);
WARN_ON_ONCE(prepare_refcnt != 0);
prepare_owner = current;
prepare_refcnt = 1;
}
static void clk_prepare_unlock(void)
{
WARN_ON_ONCE(prepare_owner != current);
WARN_ON_ONCE(prepare_refcnt == 0);
if (--prepare_refcnt)
return;
prepare_owner = NULL;
mutex_unlock(&prepare_lock);
}
static unsigned long clk_enable_lock(void)
{
unsigned long flags;
if (!spin_trylock_irqsave(&enable_lock, flags)) {
if (enable_owner == current) {
enable_refcnt++;
return flags;
}
spin_lock_irqsave(&enable_lock, flags);
}
WARN_ON_ONCE(enable_owner != NULL);
WARN_ON_ONCE(enable_refcnt != 0);
enable_owner = current;
enable_refcnt = 1;
return flags;
}
static void clk_enable_unlock(unsigned long flags)
{
WARN_ON_ONCE(enable_owner != current);
WARN_ON_ONCE(enable_refcnt == 0);
if (--enable_refcnt)
return;
enable_owner = NULL;
spin_unlock_irqrestore(&enable_lock, flags);
}
/*** debugfs support ***/
#ifdef CONFIG_COMMON_CLK_DEBUG
@ -69,7 +132,7 @@ static int clk_summary_show(struct seq_file *s, void *data)
seq_printf(s, " clock enable_cnt prepare_cnt rate\n");
seq_printf(s, "---------------------------------------------------------------------\n");
mutex_lock(&prepare_lock);
clk_prepare_lock();
hlist_for_each_entry(c, &clk_root_list, child_node)
clk_summary_show_subtree(s, c, 0);
@ -77,7 +140,7 @@ static int clk_summary_show(struct seq_file *s, void *data)
hlist_for_each_entry(c, &clk_orphan_list, child_node)
clk_summary_show_subtree(s, c, 0);
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
return 0;
}
@ -130,7 +193,7 @@ static int clk_dump(struct seq_file *s, void *data)
seq_printf(s, "{");
mutex_lock(&prepare_lock);
clk_prepare_lock();
hlist_for_each_entry(c, &clk_root_list, child_node) {
if (!first_node)
@ -144,7 +207,7 @@ static int clk_dump(struct seq_file *s, void *data)
clk_dump_subtree(s, c, 0);
}
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
seq_printf(s, "}");
return 0;
@ -316,7 +379,7 @@ static int __init clk_debug_init(void)
if (!orphandir)
return -ENOMEM;
mutex_lock(&prepare_lock);
clk_prepare_lock();
hlist_for_each_entry(clk, &clk_root_list, child_node)
clk_debug_create_subtree(clk, rootdir);
@ -326,7 +389,7 @@ static int __init clk_debug_init(void)
inited = 1;
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
return 0;
}
@ -335,6 +398,31 @@ late_initcall(clk_debug_init);
static inline int clk_debug_register(struct clk *clk) { return 0; }
#endif
/* caller must hold prepare_lock */
static void clk_unprepare_unused_subtree(struct clk *clk)
{
struct clk *child;
if (!clk)
return;
hlist_for_each_entry(child, &clk->children, child_node)
clk_unprepare_unused_subtree(child);
if (clk->prepare_count)
return;
if (clk->flags & CLK_IGNORE_UNUSED)
return;
if (__clk_is_prepared(clk)) {
if (clk->ops->unprepare_unused)
clk->ops->unprepare_unused(clk->hw);
else if (clk->ops->unprepare)
clk->ops->unprepare(clk->hw);
}
}
/* caller must hold prepare_lock */
static void clk_disable_unused_subtree(struct clk *clk)
{
@ -347,7 +435,7 @@ static void clk_disable_unused_subtree(struct clk *clk)
hlist_for_each_entry(child, &clk->children, child_node)
clk_disable_unused_subtree(child);
spin_lock_irqsave(&enable_lock, flags);
flags = clk_enable_lock();
if (clk->enable_count)
goto unlock_out;
@ -368,7 +456,7 @@ static void clk_disable_unused_subtree(struct clk *clk)
}
unlock_out:
spin_unlock_irqrestore(&enable_lock, flags);
clk_enable_unlock(flags);
out:
return;
@ -378,7 +466,7 @@ static int clk_disable_unused(void)
{
struct clk *clk;
mutex_lock(&prepare_lock);
clk_prepare_lock();
hlist_for_each_entry(clk, &clk_root_list, child_node)
clk_disable_unused_subtree(clk);
@ -386,7 +474,13 @@ static int clk_disable_unused(void)
hlist_for_each_entry(clk, &clk_orphan_list, child_node)
clk_disable_unused_subtree(clk);
mutex_unlock(&prepare_lock);
hlist_for_each_entry(clk, &clk_root_list, child_node)
clk_unprepare_unused_subtree(clk);
hlist_for_each_entry(clk, &clk_orphan_list, child_node)
clk_unprepare_unused_subtree(clk);
clk_prepare_unlock();
return 0;
}
@ -451,6 +545,27 @@ unsigned long __clk_get_flags(struct clk *clk)
return !clk ? 0 : clk->flags;
}
bool __clk_is_prepared(struct clk *clk)
{
int ret;
if (!clk)
return false;
/*
* .is_prepared is optional for clocks that can prepare
* fall back to software usage counter if it is missing
*/
if (!clk->ops->is_prepared) {
ret = clk->prepare_count ? 1 : 0;
goto out;
}
ret = clk->ops->is_prepared(clk->hw);
out:
return !!ret;
}
bool __clk_is_enabled(struct clk *clk)
{
int ret;
@ -548,9 +663,9 @@ void __clk_unprepare(struct clk *clk)
*/
void clk_unprepare(struct clk *clk)
{
mutex_lock(&prepare_lock);
clk_prepare_lock();
__clk_unprepare(clk);
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
}
EXPORT_SYMBOL_GPL(clk_unprepare);
@ -596,9 +711,9 @@ int clk_prepare(struct clk *clk)
{
int ret;
mutex_lock(&prepare_lock);
clk_prepare_lock();
ret = __clk_prepare(clk);
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
return ret;
}
@ -640,9 +755,9 @@ void clk_disable(struct clk *clk)
{
unsigned long flags;
spin_lock_irqsave(&enable_lock, flags);
flags = clk_enable_lock();
__clk_disable(clk);
spin_unlock_irqrestore(&enable_lock, flags);
clk_enable_unlock(flags);
}
EXPORT_SYMBOL_GPL(clk_disable);
@ -693,9 +808,9 @@ int clk_enable(struct clk *clk)
unsigned long flags;
int ret;
spin_lock_irqsave(&enable_lock, flags);
flags = clk_enable_lock();
ret = __clk_enable(clk);
spin_unlock_irqrestore(&enable_lock, flags);
clk_enable_unlock(flags);
return ret;
}
@ -740,9 +855,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
{
unsigned long ret;
mutex_lock(&prepare_lock);
clk_prepare_lock();
ret = __clk_round_rate(clk, rate);
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
return ret;
}
@ -837,13 +952,13 @@ unsigned long clk_get_rate(struct clk *clk)
{
unsigned long rate;
mutex_lock(&prepare_lock);
clk_prepare_lock();
if (clk && (clk->flags & CLK_GET_RATE_NOCACHE))
__clk_recalc_rates(clk, 0);
rate = __clk_get_rate(clk);
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
return rate;
}
@ -974,7 +1089,7 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even
int ret = NOTIFY_DONE;
if (clk->rate == clk->new_rate)
return 0;
return NULL;
if (clk->notifier_count) {
ret = __clk_notify(clk, event, clk->rate, clk->new_rate);
@ -1048,7 +1163,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
int ret = 0;
/* prevent racing with updates to the clock topology */
mutex_lock(&prepare_lock);
clk_prepare_lock();
/* bail early if nothing to do */
if (rate == clk->rate)
@ -1080,7 +1195,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
clk_change_rate(top);
out:
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
return ret;
}
@ -1096,9 +1211,9 @@ struct clk *clk_get_parent(struct clk *clk)
{
struct clk *parent;
mutex_lock(&prepare_lock);
clk_prepare_lock();
parent = __clk_get_parent(clk);
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
return parent;
}
@ -1242,19 +1357,19 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent)
__clk_prepare(parent);
/* FIXME replace with clk_is_enabled(clk) someday */
spin_lock_irqsave(&enable_lock, flags);
flags = clk_enable_lock();
if (clk->enable_count)
__clk_enable(parent);
spin_unlock_irqrestore(&enable_lock, flags);
clk_enable_unlock(flags);
/* change clock input source */
ret = clk->ops->set_parent(clk->hw, i);
/* clean up old prepare and enable */
spin_lock_irqsave(&enable_lock, flags);
flags = clk_enable_lock();
if (clk->enable_count)
__clk_disable(old_parent);
spin_unlock_irqrestore(&enable_lock, flags);
clk_enable_unlock(flags);
if (clk->prepare_count)
__clk_unprepare(old_parent);
@ -1286,7 +1401,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
return -ENOSYS;
/* prevent racing with updates to the clock topology */
mutex_lock(&prepare_lock);
clk_prepare_lock();
if (clk->parent == parent)
goto out;
@ -1315,7 +1430,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
__clk_reparent(clk, parent);
out:
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
return ret;
}
@ -1338,7 +1453,7 @@ int __clk_init(struct device *dev, struct clk *clk)
if (!clk)
return -EINVAL;
mutex_lock(&prepare_lock);
clk_prepare_lock();
/* check to see if a clock with this name is already registered */
if (__clk_lookup(clk->name)) {
@ -1462,7 +1577,7 @@ int __clk_init(struct device *dev, struct clk *clk)
clk_debug_register(clk);
out:
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
return ret;
}
@ -1696,7 +1811,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb)
if (!clk || !nb)
return -EINVAL;
mutex_lock(&prepare_lock);
clk_prepare_lock();
/* search the list of notifiers for this clk */
list_for_each_entry(cn, &clk_notifier_list, node)
@ -1720,7 +1835,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb)
clk->notifier_count++;
out:
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
return ret;
}
@ -1745,7 +1860,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
if (!clk || !nb)
return -EINVAL;
mutex_lock(&prepare_lock);
clk_prepare_lock();
list_for_each_entry(cn, &clk_notifier_list, node)
if (cn->clk == clk)
@ -1766,7 +1881,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
ret = -ENOENT;
}
mutex_unlock(&prepare_lock);
clk_prepare_unlock();
return ret;
}

View file

@ -13,6 +13,7 @@
#include <linux/io.h>
#include <linux/jiffies.h>
#include <linux/spinlock.h>
#include "clk.h"
DEFINE_SPINLOCK(mxs_lock);

View file

@ -0,0 +1,8 @@
#
# Samsung Clock specific Makefile
#
obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o
obj-$(CONFIG_ARCH_EXYNOS4) += clk-exynos4.o
obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o
obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,523 @@
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Copyright (c) 2013 Linaro Ltd.
* Author: Thomas Abraham <thomas.ab@samsung.com>
*
* 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.
*
* Common Clock Framework support for Exynos5250 SoC.
*/
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <plat/cpu.h>
#include "clk.h"
#include "clk-pll.h"
#define SRC_CPU 0x200
#define DIV_CPU0 0x500
#define SRC_CORE1 0x4204
#define SRC_TOP0 0x10210
#define SRC_TOP2 0x10218
#define SRC_GSCL 0x10220
#define SRC_DISP1_0 0x1022c
#define SRC_MAU 0x10240
#define SRC_FSYS 0x10244
#define SRC_GEN 0x10248
#define SRC_PERIC0 0x10250
#define SRC_PERIC1 0x10254
#define SRC_MASK_GSCL 0x10320
#define SRC_MASK_DISP1_0 0x1032c
#define SRC_MASK_MAU 0x10334
#define SRC_MASK_FSYS 0x10340
#define SRC_MASK_GEN 0x10344
#define SRC_MASK_PERIC0 0x10350
#define SRC_MASK_PERIC1 0x10354
#define DIV_TOP0 0x10510
#define DIV_TOP1 0x10514
#define DIV_GSCL 0x10520
#define DIV_DISP1_0 0x1052c
#define DIV_GEN 0x1053c
#define DIV_MAU 0x10544
#define DIV_FSYS0 0x10548
#define DIV_FSYS1 0x1054c
#define DIV_FSYS2 0x10550
#define DIV_PERIC0 0x10558
#define DIV_PERIC1 0x1055c
#define DIV_PERIC2 0x10560
#define DIV_PERIC3 0x10564
#define DIV_PERIC4 0x10568
#define DIV_PERIC5 0x1056c
#define GATE_IP_GSCL 0x10920
#define GATE_IP_MFC 0x1092c
#define GATE_IP_GEN 0x10934
#define GATE_IP_FSYS 0x10944
#define GATE_IP_PERIC 0x10950
#define GATE_IP_PERIS 0x10960
#define SRC_CDREX 0x20200
#define PLL_DIV2_SEL 0x20a24
#define GATE_IP_DISP1 0x10928
/*
* Let each supported clock get a unique id. This id is used to lookup the clock
* for device tree based platforms. The clocks are categorized into three
* sections: core, sclk gate and bus interface gate clocks.
*
* When adding a new clock to this list, it is advised to choose a clock
* category and add it to the end of that category. That is because the the
* device tree source file is referring to these ids and any change in the
* sequence number of existing clocks will require corresponding change in the
* device tree files. This limitation would go away when pre-processor support
* for dtc would be available.
*/
enum exynos5250_clks {
none,
/* core clocks */
fin_pll,
/* gate for special clocks (sclk) */
sclk_cam_bayer = 128, sclk_cam0, sclk_cam1, sclk_gscl_wa, sclk_gscl_wb,
sclk_fimd1, sclk_mipi1, sclk_dp, sclk_hdmi, sclk_pixel, sclk_audio0,
sclk_mmc0, sclk_mmc1, sclk_mmc2, sclk_mmc3, sclk_sata, sclk_usb3,
sclk_jpeg, sclk_uart0, sclk_uart1, sclk_uart2, sclk_uart3, sclk_pwm,
sclk_audio1, sclk_audio2, sclk_spdif, sclk_spi0, sclk_spi1, sclk_spi2,
/* gate clocks */
gscl0 = 256, gscl1, gscl2, gscl3, gscl_wa, gscl_wb, smmu_gscl0,
smmu_gscl1, smmu_gscl2, smmu_gscl3, mfc, smmu_mfcl, smmu_mfcr, rotator,
jpeg, mdma1, smmu_rotator, smmu_jpeg, smmu_mdma1, pdma0, pdma1, sata,
usbotg, mipi_hsi, sdmmc0, sdmmc1, sdmmc2, sdmmc3, sromc, usb2, usb3,
sata_phyctrl, sata_phyi2c, uart0, uart1, uart2, uart3, uart4, i2c0,
i2c1, i2c2, i2c3, i2c4, i2c5, i2c6, i2c7, i2c_hdmi, adc, spi0, spi1,
spi2, i2s1, i2s2, pcm1, pcm2, pwm, spdif, ac97, hsi2c0, hsi2c1, hsi2c2,
hsi2c3, chipid, sysreg, pmu, cmu_top, cmu_core, cmu_mem, tzpc0, tzpc1,
tzpc2, tzpc3, tzpc4, tzpc5, tzpc6, tzpc7, tzpc8, tzpc9, hdmi_cec, mct,
wdt, rtc, tmu, fimd1, mie1, dsim0, dp, mixer, hdmi,
nr_clks,
};
/*
* list of controller registers to be saved and restored during a
* suspend/resume cycle.
*/
static __initdata unsigned long exynos5250_clk_regs[] = {
SRC_CPU,
DIV_CPU0,
SRC_CORE1,
SRC_TOP0,
SRC_TOP2,
SRC_GSCL,
SRC_DISP1_0,
SRC_MAU,
SRC_FSYS,
SRC_GEN,
SRC_PERIC0,
SRC_PERIC1,
SRC_MASK_GSCL,
SRC_MASK_DISP1_0,
SRC_MASK_MAU,
SRC_MASK_FSYS,
SRC_MASK_GEN,
SRC_MASK_PERIC0,
SRC_MASK_PERIC1,
DIV_TOP0,
DIV_TOP1,
DIV_GSCL,
DIV_DISP1_0,
DIV_GEN,
DIV_MAU,
DIV_FSYS0,
DIV_FSYS1,
DIV_FSYS2,
DIV_PERIC0,
DIV_PERIC1,
DIV_PERIC2,
DIV_PERIC3,
DIV_PERIC4,
DIV_PERIC5,
GATE_IP_GSCL,
GATE_IP_MFC,
GATE_IP_GEN,
GATE_IP_FSYS,
GATE_IP_PERIC,
GATE_IP_PERIS,
SRC_CDREX,
PLL_DIV2_SEL,
GATE_IP_DISP1,
};
/* list of all parent clock list */
PNAME(mout_apll_p) = { "fin_pll", "fout_apll", };
PNAME(mout_cpu_p) = { "mout_apll", "mout_mpll", };
PNAME(mout_mpll_fout_p) = { "fout_mplldiv2", "fout_mpll" };
PNAME(mout_mpll_p) = { "fin_pll", "mout_mpll_fout" };
PNAME(mout_bpll_fout_p) = { "fout_bplldiv2", "fout_bpll" };
PNAME(mout_bpll_p) = { "fin_pll", "mout_bpll_fout" };
PNAME(mout_vpllsrc_p) = { "fin_pll", "sclk_hdmi27m" };
PNAME(mout_vpll_p) = { "mout_vpllsrc", "fout_vpll" };
PNAME(mout_cpll_p) = { "fin_pll", "fout_cpll" };
PNAME(mout_epll_p) = { "fin_pll", "fout_epll" };
PNAME(mout_mpll_user_p) = { "fin_pll", "sclk_mpll" };
PNAME(mout_bpll_user_p) = { "fin_pll", "sclk_bpll" };
PNAME(mout_aclk166_p) = { "sclk_cpll", "sclk_mpll_user" };
PNAME(mout_aclk200_p) = { "sclk_mpll_user", "sclk_bpll_user" };
PNAME(mout_hdmi_p) = { "div_hdmi_pixel", "sclk_hdmiphy" };
PNAME(mout_usb3_p) = { "sclk_mpll_user", "sclk_cpll" };
PNAME(mout_group1_p) = { "fin_pll", "fin_pll", "sclk_hdmi27m",
"sclk_dptxphy", "sclk_uhostphy", "sclk_hdmiphy",
"sclk_mpll_user", "sclk_epll", "sclk_vpll",
"sclk_cpll" };
PNAME(mout_audio0_p) = { "cdclk0", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy",
"sclk_uhostphy", "sclk_hdmiphy",
"sclk_mpll_user", "sclk_epll", "sclk_vpll",
"sclk_cpll" };
PNAME(mout_audio1_p) = { "cdclk1", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy",
"sclk_uhostphy", "sclk_hdmiphy",
"sclk_mpll_user", "sclk_epll", "sclk_vpll",
"sclk_cpll" };
PNAME(mout_audio2_p) = { "cdclk2", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy",
"sclk_uhostphy", "sclk_hdmiphy",
"sclk_mpll_user", "sclk_epll", "sclk_vpll",
"sclk_cpll" };
PNAME(mout_spdif_p) = { "sclk_audio0", "sclk_audio1", "sclk_audio2",
"spdif_extclk" };
/* fixed rate clocks generated outside the soc */
struct samsung_fixed_rate_clock exynos5250_fixed_rate_ext_clks[] __initdata = {
FRATE(fin_pll, "fin_pll", NULL, CLK_IS_ROOT, 0),
};
/* fixed rate clocks generated inside the soc */
struct samsung_fixed_rate_clock exynos5250_fixed_rate_clks[] __initdata = {
FRATE(none, "sclk_hdmiphy", NULL, CLK_IS_ROOT, 24000000),
FRATE(none, "sclk_hdmi27m", NULL, CLK_IS_ROOT, 27000000),
FRATE(none, "sclk_dptxphy", NULL, CLK_IS_ROOT, 24000000),
FRATE(none, "sclk_uhostphy", NULL, CLK_IS_ROOT, 48000000),
};
struct samsung_fixed_factor_clock exynos5250_fixed_factor_clks[] __initdata = {
FFACTOR(none, "fout_mplldiv2", "fout_mpll", 1, 2, 0),
FFACTOR(none, "fout_bplldiv2", "fout_bpll", 1, 2, 0),
};
struct samsung_mux_clock exynos5250_mux_clks[] __initdata = {
MUX(none, "mout_apll", mout_apll_p, SRC_CPU, 0, 1),
MUX(none, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1),
MUX(none, "mout_mpll_fout", mout_mpll_fout_p, PLL_DIV2_SEL, 4, 1),
MUX(none, "sclk_mpll", mout_mpll_p, SRC_CORE1, 8, 1),
MUX(none, "mout_bpll_fout", mout_bpll_fout_p, PLL_DIV2_SEL, 0, 1),
MUX(none, "sclk_bpll", mout_bpll_p, SRC_CDREX, 0, 1),
MUX(none, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP2, 0, 1),
MUX(none, "sclk_vpll", mout_vpll_p, SRC_TOP2, 16, 1),
MUX(none, "sclk_epll", mout_epll_p, SRC_TOP2, 12, 1),
MUX(none, "sclk_cpll", mout_cpll_p, SRC_TOP2, 8, 1),
MUX(none, "sclk_mpll_user", mout_mpll_user_p, SRC_TOP2, 20, 1),
MUX(none, "sclk_bpll_user", mout_bpll_user_p, SRC_TOP2, 24, 1),
MUX(none, "mout_aclk166", mout_aclk166_p, SRC_TOP0, 8, 1),
MUX(none, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1),
MUX(none, "mout_aclk200", mout_aclk200_p, SRC_TOP0, 12, 1),
MUX(none, "mout_cam_bayer", mout_group1_p, SRC_GSCL, 12, 4),
MUX(none, "mout_cam0", mout_group1_p, SRC_GSCL, 16, 4),
MUX(none, "mout_cam1", mout_group1_p, SRC_GSCL, 20, 4),
MUX(none, "mout_gscl_wa", mout_group1_p, SRC_GSCL, 24, 4),
MUX(none, "mout_gscl_wb", mout_group1_p, SRC_GSCL, 28, 4),
MUX(none, "mout_fimd1", mout_group1_p, SRC_DISP1_0, 0, 4),
MUX(none, "mout_mipi1", mout_group1_p, SRC_DISP1_0, 12, 4),
MUX(none, "mout_dp", mout_group1_p, SRC_DISP1_0, 16, 4),
MUX(none, "mout_hdmi", mout_hdmi_p, SRC_DISP1_0, 20, 1),
MUX(none, "mout_audio0", mout_audio0_p, SRC_MAU, 0, 4),
MUX(none, "mout_mmc0", mout_group1_p, SRC_FSYS, 0, 4),
MUX(none, "mout_mmc1", mout_group1_p, SRC_FSYS, 4, 4),
MUX(none, "mout_mmc2", mout_group1_p, SRC_FSYS, 8, 4),
MUX(none, "mout_mmc3", mout_group1_p, SRC_FSYS, 12, 4),
MUX(none, "mout_sata", mout_aclk200_p, SRC_FSYS, 24, 1),
MUX(none, "mout_usb3", mout_usb3_p, SRC_FSYS, 28, 1),
MUX(none, "mout_jpeg", mout_group1_p, SRC_GEN, 0, 4),
MUX(none, "mout_uart0", mout_group1_p, SRC_PERIC0, 0, 4),
MUX(none, "mout_uart1", mout_group1_p, SRC_PERIC0, 4, 4),
MUX(none, "mout_uart2", mout_group1_p, SRC_PERIC0, 8, 4),
MUX(none, "mout_uart3", mout_group1_p, SRC_PERIC0, 12, 4),
MUX(none, "mout_pwm", mout_group1_p, SRC_PERIC0, 24, 4),
MUX(none, "mout_audio1", mout_audio1_p, SRC_PERIC1, 0, 4),
MUX(none, "mout_audio2", mout_audio2_p, SRC_PERIC1, 4, 4),
MUX(none, "mout_spdif", mout_spdif_p, SRC_PERIC1, 8, 2),
MUX(none, "mout_spi0", mout_group1_p, SRC_PERIC1, 16, 4),
MUX(none, "mout_spi1", mout_group1_p, SRC_PERIC1, 20, 4),
MUX(none, "mout_spi2", mout_group1_p, SRC_PERIC1, 24, 4),
};
struct samsung_div_clock exynos5250_div_clks[] __initdata = {
DIV(none, "div_arm", "mout_cpu", DIV_CPU0, 0, 3),
DIV(none, "sclk_apll", "mout_apll", DIV_CPU0, 24, 3),
DIV(none, "aclk66_pre", "sclk_mpll_user", DIV_TOP1, 24, 3),
DIV(none, "aclk66", "aclk66_pre", DIV_TOP0, 0, 3),
DIV(none, "aclk266", "sclk_mpll_user", DIV_TOP0, 16, 3),
DIV(none, "aclk166", "mout_aclk166", DIV_TOP0, 8, 3),
DIV(none, "aclk333", "mout_aclk333", DIV_TOP0, 20, 3),
DIV(none, "aclk200", "mout_aclk200", DIV_TOP0, 12, 3),
DIV(none, "div_cam_bayer", "mout_cam_bayer", DIV_GSCL, 12, 4),
DIV(none, "div_cam0", "mout_cam0", DIV_GSCL, 16, 4),
DIV(none, "div_cam1", "mout_cam1", DIV_GSCL, 20, 4),
DIV(none, "div_gscl_wa", "mout_gscl_wa", DIV_GSCL, 24, 4),
DIV(none, "div_gscl_wb", "mout_gscl_wb", DIV_GSCL, 28, 4),
DIV(none, "div_fimd1", "mout_fimd1", DIV_DISP1_0, 0, 4),
DIV(none, "div_mipi1", "mout_mipi1", DIV_DISP1_0, 16, 4),
DIV(none, "div_dp", "mout_dp", DIV_DISP1_0, 24, 4),
DIV(none, "div_jpeg", "mout_jpeg", DIV_GEN, 4, 4),
DIV(none, "div_audio0", "mout_audio0", DIV_MAU, 0, 4),
DIV(none, "div_pcm0", "sclk_audio0", DIV_MAU, 4, 8),
DIV(none, "div_sata", "mout_sata", DIV_FSYS0, 20, 4),
DIV(none, "div_usb3", "mout_usb3", DIV_FSYS0, 24, 4),
DIV(none, "div_mmc0", "mout_mmc0", DIV_FSYS1, 8, 8),
DIV(none, "div_mmc1", "mout_mmc1", DIV_FSYS1, 24, 8),
DIV(none, "div_mmc2", "mout_mmc2", DIV_FSYS2, 8, 8),
DIV(none, "div_mmc3", "mout_mmc3", DIV_FSYS2, 24, 8),
DIV(none, "div_uart0", "mout_uart0", DIV_PERIC0, 0, 4),
DIV(none, "div_uart1", "mout_uart1", DIV_PERIC0, 4, 4),
DIV(none, "div_uart2", "mout_uart2", DIV_PERIC0, 8, 4),
DIV(none, "div_uart3", "mout_uart3", DIV_PERIC0, 12, 4),
DIV(none, "div_spi0", "mout_spi0", DIV_PERIC1, 0, 4),
DIV(none, "div_spi1", "mout_spi1", DIV_PERIC1, 16, 4),
DIV(none, "div_spi2", "mout_spi2", DIV_PERIC2, 0, 4),
DIV(none, "div_pwm", "mout_pwm", DIV_PERIC3, 0, 4),
DIV(none, "div_audio1", "mout_audio1", DIV_PERIC4, 0, 4),
DIV(none, "div_pcm1", "sclk_audio1", DIV_PERIC4, 4, 8),
DIV(none, "div_audio2", "mout_audio2", DIV_PERIC4, 16, 4),
DIV(none, "div_pcm2", "sclk_audio2", DIV_PERIC4, 20, 8),
DIV(none, "div_i2s1", "sclk_audio1", DIV_PERIC5, 0, 6),
DIV(none, "div_i2s2", "sclk_audio2", DIV_PERIC5, 8, 6),
DIV(sclk_pixel, "div_hdmi_pixel", "sclk_vpll", DIV_DISP1_0, 28, 4),
DIV_A(none, "armclk", "div_arm", DIV_CPU0, 28, 3, "armclk"),
DIV_F(none, "div_mipi1_pre", "div_mipi1",
DIV_DISP1_0, 20, 4, CLK_SET_RATE_PARENT, 0),
DIV_F(none, "div_mmc_pre0", "div_mmc0",
DIV_FSYS1, 8, 8, CLK_SET_RATE_PARENT, 0),
DIV_F(none, "div_mmc_pre1", "div_mmc1",
DIV_FSYS1, 24, 8, CLK_SET_RATE_PARENT, 0),
DIV_F(none, "div_mmc_pre2", "div_mmc2",
DIV_FSYS2, 8, 8, CLK_SET_RATE_PARENT, 0),
DIV_F(none, "div_mmc_pre3", "div_mmc3",
DIV_FSYS2, 24, 8, CLK_SET_RATE_PARENT, 0),
DIV_F(none, "div_spi_pre0", "div_spi0",
DIV_PERIC1, 8, 8, CLK_SET_RATE_PARENT, 0),
DIV_F(none, "div_spi_pre1", "div_spi1",
DIV_PERIC1, 24, 8, CLK_SET_RATE_PARENT, 0),
DIV_F(none, "div_spi_pre2", "div_spi2",
DIV_PERIC2, 8, 8, CLK_SET_RATE_PARENT, 0),
};
struct samsung_gate_clock exynos5250_gate_clks[] __initdata = {
GATE(gscl0, "gscl0", "none", GATE_IP_GSCL, 0, 0, 0),
GATE(gscl1, "gscl1", "none", GATE_IP_GSCL, 1, 0, 0),
GATE(gscl2, "gscl2", "aclk266", GATE_IP_GSCL, 2, 0, 0),
GATE(gscl3, "gscl3", "aclk266", GATE_IP_GSCL, 3, 0, 0),
GATE(gscl_wa, "gscl_wa", "div_gscl_wa", GATE_IP_GSCL, 5, 0, 0),
GATE(gscl_wb, "gscl_wb", "div_gscl_wb", GATE_IP_GSCL, 6, 0, 0),
GATE(smmu_gscl0, "smmu_gscl0", "aclk266", GATE_IP_GSCL, 7, 0, 0),
GATE(smmu_gscl1, "smmu_gscl1", "aclk266", GATE_IP_GSCL, 8, 0, 0),
GATE(smmu_gscl2, "smmu_gscl2", "aclk266", GATE_IP_GSCL, 9, 0, 0),
GATE(smmu_gscl3, "smmu_gscl3", "aclk266", GATE_IP_GSCL, 10, 0, 0),
GATE(mfc, "mfc", "aclk333", GATE_IP_MFC, 0, 0, 0),
GATE(smmu_mfcl, "smmu_mfcl", "aclk333", GATE_IP_MFC, 1, 0, 0),
GATE(smmu_mfcr, "smmu_mfcr", "aclk333", GATE_IP_MFC, 2, 0, 0),
GATE(rotator, "rotator", "aclk266", GATE_IP_GEN, 1, 0, 0),
GATE(jpeg, "jpeg", "aclk166", GATE_IP_GEN, 2, 0, 0),
GATE(mdma1, "mdma1", "aclk266", GATE_IP_GEN, 4, 0, 0),
GATE(smmu_rotator, "smmu_rotator", "aclk266", GATE_IP_GEN, 6, 0, 0),
GATE(smmu_jpeg, "smmu_jpeg", "aclk166", GATE_IP_GEN, 7, 0, 0),
GATE(smmu_mdma1, "smmu_mdma1", "aclk266", GATE_IP_GEN, 9, 0, 0),
GATE(pdma0, "pdma0", "aclk200", GATE_IP_FSYS, 1, 0, 0),
GATE(pdma1, "pdma1", "aclk200", GATE_IP_FSYS, 2, 0, 0),
GATE(sata, "sata", "aclk200", GATE_IP_FSYS, 6, 0, 0),
GATE(usbotg, "usbotg", "aclk200", GATE_IP_FSYS, 7, 0, 0),
GATE(mipi_hsi, "mipi_hsi", "aclk200", GATE_IP_FSYS, 8, 0, 0),
GATE(sdmmc0, "sdmmc0", "aclk200", GATE_IP_FSYS, 12, 0, 0),
GATE(sdmmc1, "sdmmc1", "aclk200", GATE_IP_FSYS, 13, 0, 0),
GATE(sdmmc2, "sdmmc2", "aclk200", GATE_IP_FSYS, 14, 0, 0),
GATE(sdmmc3, "sdmmc3", "aclk200", GATE_IP_FSYS, 15, 0, 0),
GATE(sromc, "sromc", "aclk200", GATE_IP_FSYS, 17, 0, 0),
GATE(usb2, "usb2", "aclk200", GATE_IP_FSYS, 18, 0, 0),
GATE(usb3, "usb3", "aclk200", GATE_IP_FSYS, 19, 0, 0),
GATE(sata_phyctrl, "sata_phyctrl", "aclk200", GATE_IP_FSYS, 24, 0, 0),
GATE(sata_phyi2c, "sata_phyi2c", "aclk200", GATE_IP_FSYS, 25, 0, 0),
GATE(uart0, "uart0", "aclk66", GATE_IP_PERIC, 0, 0, 0),
GATE(uart1, "uart1", "aclk66", GATE_IP_PERIC, 1, 0, 0),
GATE(uart2, "uart2", "aclk66", GATE_IP_PERIC, 2, 0, 0),
GATE(uart3, "uart3", "aclk66", GATE_IP_PERIC, 3, 0, 0),
GATE(uart4, "uart4", "aclk66", GATE_IP_PERIC, 4, 0, 0),
GATE(i2c0, "i2c0", "aclk66", GATE_IP_PERIC, 6, 0, 0),
GATE(i2c1, "i2c1", "aclk66", GATE_IP_PERIC, 7, 0, 0),
GATE(i2c2, "i2c2", "aclk66", GATE_IP_PERIC, 8, 0, 0),
GATE(i2c3, "i2c3", "aclk66", GATE_IP_PERIC, 9, 0, 0),
GATE(i2c4, "i2c4", "aclk66", GATE_IP_PERIC, 10, 0, 0),
GATE(i2c5, "i2c5", "aclk66", GATE_IP_PERIC, 11, 0, 0),
GATE(i2c6, "i2c6", "aclk66", GATE_IP_PERIC, 12, 0, 0),
GATE(i2c7, "i2c7", "aclk66", GATE_IP_PERIC, 13, 0, 0),
GATE(i2c_hdmi, "i2c_hdmi", "aclk66", GATE_IP_PERIC, 14, 0, 0),
GATE(adc, "adc", "aclk66", GATE_IP_PERIC, 15, 0, 0),
GATE(spi0, "spi0", "aclk66", GATE_IP_PERIC, 16, 0, 0),
GATE(spi1, "spi1", "aclk66", GATE_IP_PERIC, 17, 0, 0),
GATE(spi2, "spi2", "aclk66", GATE_IP_PERIC, 18, 0, 0),
GATE(i2s1, "i2s1", "aclk66", GATE_IP_PERIC, 20, 0, 0),
GATE(i2s2, "i2s2", "aclk66", GATE_IP_PERIC, 21, 0, 0),
GATE(pcm1, "pcm1", "aclk66", GATE_IP_PERIC, 22, 0, 0),
GATE(pcm2, "pcm2", "aclk66", GATE_IP_PERIC, 23, 0, 0),
GATE(pwm, "pwm", "aclk66", GATE_IP_PERIC, 24, 0, 0),
GATE(spdif, "spdif", "aclk66", GATE_IP_PERIC, 26, 0, 0),
GATE(ac97, "ac97", "aclk66", GATE_IP_PERIC, 27, 0, 0),
GATE(hsi2c0, "hsi2c0", "aclk66", GATE_IP_PERIC, 28, 0, 0),
GATE(hsi2c1, "hsi2c1", "aclk66", GATE_IP_PERIC, 29, 0, 0),
GATE(hsi2c2, "hsi2c2", "aclk66", GATE_IP_PERIC, 30, 0, 0),
GATE(hsi2c3, "hsi2c3", "aclk66", GATE_IP_PERIC, 31, 0, 0),
GATE(chipid, "chipid", "aclk66", GATE_IP_PERIS, 0, 0, 0),
GATE(sysreg, "sysreg", "aclk66", GATE_IP_PERIS, 1, 0, 0),
GATE(pmu, "pmu", "aclk66", GATE_IP_PERIS, 2, 0, 0),
GATE(tzpc0, "tzpc0", "aclk66", GATE_IP_PERIS, 6, 0, 0),
GATE(tzpc1, "tzpc1", "aclk66", GATE_IP_PERIS, 7, 0, 0),
GATE(tzpc2, "tzpc2", "aclk66", GATE_IP_PERIS, 8, 0, 0),
GATE(tzpc3, "tzpc3", "aclk66", GATE_IP_PERIS, 9, 0, 0),
GATE(tzpc4, "tzpc4", "aclk66", GATE_IP_PERIS, 10, 0, 0),
GATE(tzpc5, "tzpc5", "aclk66", GATE_IP_PERIS, 11, 0, 0),
GATE(tzpc6, "tzpc6", "aclk66", GATE_IP_PERIS, 12, 0, 0),
GATE(tzpc7, "tzpc7", "aclk66", GATE_IP_PERIS, 13, 0, 0),
GATE(tzpc8, "tzpc8", "aclk66", GATE_IP_PERIS, 14, 0, 0),
GATE(tzpc9, "tzpc9", "aclk66", GATE_IP_PERIS, 15, 0, 0),
GATE(hdmi_cec, "hdmi_cec", "aclk66", GATE_IP_PERIS, 16, 0, 0),
GATE(mct, "mct", "aclk66", GATE_IP_PERIS, 18, 0, 0),
GATE(wdt, "wdt", "aclk66", GATE_IP_PERIS, 19, 0, 0),
GATE(rtc, "rtc", "aclk66", GATE_IP_PERIS, 20, 0, 0),
GATE(tmu, "tmu", "aclk66", GATE_IP_PERIS, 21, 0, 0),
GATE(cmu_top, "cmu_top", "aclk66",
GATE_IP_PERIS, 3, CLK_IGNORE_UNUSED, 0),
GATE(cmu_core, "cmu_core", "aclk66",
GATE_IP_PERIS, 4, CLK_IGNORE_UNUSED, 0),
GATE(cmu_mem, "cmu_mem", "aclk66",
GATE_IP_PERIS, 5, CLK_IGNORE_UNUSED, 0),
GATE(sclk_cam_bayer, "sclk_cam_bayer", "div_cam_bayer",
SRC_MASK_GSCL, 12, CLK_SET_RATE_PARENT, 0),
GATE(sclk_cam0, "sclk_cam0", "div_cam0",
SRC_MASK_GSCL, 16, CLK_SET_RATE_PARENT, 0),
GATE(sclk_cam1, "sclk_cam1", "div_cam1",
SRC_MASK_GSCL, 20, CLK_SET_RATE_PARENT, 0),
GATE(sclk_gscl_wa, "sclk_gscl_wa", "div_gscl_wa",
SRC_MASK_GSCL, 24, CLK_SET_RATE_PARENT, 0),
GATE(sclk_gscl_wb, "sclk_gscl_wb", "div_gscl_wb",
SRC_MASK_GSCL, 28, CLK_SET_RATE_PARENT, 0),
GATE(sclk_fimd1, "sclk_fimd1", "div_fimd1",
SRC_MASK_DISP1_0, 0, CLK_SET_RATE_PARENT, 0),
GATE(sclk_mipi1, "sclk_mipi1", "div_mipi1",
SRC_MASK_DISP1_0, 12, CLK_SET_RATE_PARENT, 0),
GATE(sclk_dp, "sclk_dp", "div_dp",
SRC_MASK_DISP1_0, 16, CLK_SET_RATE_PARENT, 0),
GATE(sclk_hdmi, "sclk_hdmi", "mout_hdmi",
SRC_MASK_DISP1_0, 20, 0, 0),
GATE(sclk_audio0, "sclk_audio0", "div_audio0",
SRC_MASK_MAU, 0, CLK_SET_RATE_PARENT, 0),
GATE(sclk_mmc0, "sclk_mmc0", "div_mmc0",
SRC_MASK_FSYS, 0, CLK_SET_RATE_PARENT, 0),
GATE(sclk_mmc1, "sclk_mmc1", "div_mmc1",
SRC_MASK_FSYS, 4, CLK_SET_RATE_PARENT, 0),
GATE(sclk_mmc2, "sclk_mmc2", "div_mmc2",
SRC_MASK_FSYS, 8, CLK_SET_RATE_PARENT, 0),
GATE(sclk_mmc3, "sclk_mmc3", "div_mmc3",
SRC_MASK_FSYS, 12, CLK_SET_RATE_PARENT, 0),
GATE(sclk_sata, "sclk_sata", "div_sata",
SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0),
GATE(sclk_usb3, "sclk_usb3", "div_usb3",
SRC_MASK_FSYS, 28, CLK_SET_RATE_PARENT, 0),
GATE(sclk_jpeg, "sclk_jpeg", "div_jpeg",
SRC_MASK_GEN, 0, CLK_SET_RATE_PARENT, 0),
GATE(sclk_uart0, "sclk_uart0", "div_uart0",
SRC_MASK_PERIC0, 0, CLK_SET_RATE_PARENT, 0),
GATE(sclk_uart1, "sclk_uart1", "div_uart1",
SRC_MASK_PERIC0, 4, CLK_SET_RATE_PARENT, 0),
GATE(sclk_uart2, "sclk_uart2", "div_uart2",
SRC_MASK_PERIC0, 8, CLK_SET_RATE_PARENT, 0),
GATE(sclk_uart3, "sclk_uart3", "div_uart3",
SRC_MASK_PERIC0, 12, CLK_SET_RATE_PARENT, 0),
GATE(sclk_pwm, "sclk_pwm", "div_pwm",
SRC_MASK_PERIC0, 24, CLK_SET_RATE_PARENT, 0),
GATE(sclk_audio1, "sclk_audio1", "div_audio1",
SRC_MASK_PERIC1, 0, CLK_SET_RATE_PARENT, 0),
GATE(sclk_audio2, "sclk_audio2", "div_audio2",
SRC_MASK_PERIC1, 4, CLK_SET_RATE_PARENT, 0),
GATE(sclk_spdif, "sclk_spdif", "mout_spdif",
SRC_MASK_PERIC1, 4, 0, 0),
GATE(sclk_spi0, "sclk_spi0", "div_spi_pre0",
SRC_MASK_PERIC1, 16, CLK_SET_RATE_PARENT, 0),
GATE(sclk_spi1, "sclk_spi1", "div_spi_pre1",
SRC_MASK_PERIC1, 20, CLK_SET_RATE_PARENT, 0),
GATE(sclk_spi2, "sclk_spi2", "div_spi_pre2",
SRC_MASK_PERIC1, 24, CLK_SET_RATE_PARENT, 0),
GATE(fimd1, "fimd1", "aclk200", GATE_IP_DISP1, 0, 0, 0),
GATE(mie1, "mie1", "aclk200", GATE_IP_DISP1, 1, 0, 0),
GATE(dsim0, "dsim0", "aclk200", GATE_IP_DISP1, 3, 0, 0),
GATE(dp, "dp", "aclk200", GATE_IP_DISP1, 4, 0, 0),
GATE(mixer, "mixer", "aclk200", GATE_IP_DISP1, 5, 0, 0),
GATE(hdmi, "hdmi", "aclk200", GATE_IP_DISP1, 6, 0, 0),
};
static __initdata struct of_device_id ext_clk_match[] = {
{ .compatible = "samsung,clock-xxti", .data = (void *)0, },
{ },
};
/* register exynox5250 clocks */
void __init exynos5250_clk_init(struct device_node *np)
{
void __iomem *reg_base;
struct clk *apll, *mpll, *epll, *vpll, *bpll, *gpll, *cpll;
if (np) {
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
} else {
panic("%s: unable to determine soc\n", __func__);
}
samsung_clk_init(np, reg_base, nr_clks,
exynos5250_clk_regs, ARRAY_SIZE(exynos5250_clk_regs),
NULL, 0);
samsung_clk_of_register_fixed_ext(exynos5250_fixed_rate_ext_clks,
ARRAY_SIZE(exynos5250_fixed_rate_ext_clks),
ext_clk_match);
apll = samsung_clk_register_pll35xx("fout_apll", "fin_pll",
reg_base + 0x100);
mpll = samsung_clk_register_pll35xx("fout_mpll", "fin_pll",
reg_base + 0x4100);
bpll = samsung_clk_register_pll35xx("fout_bpll", "fin_pll",
reg_base + 0x20110);
gpll = samsung_clk_register_pll35xx("fout_gpll", "fin_pll",
reg_base + 0x10150);
cpll = samsung_clk_register_pll35xx("fout_cpll", "fin_pll",
reg_base + 0x10120);
epll = samsung_clk_register_pll36xx("fout_epll", "fin_pll",
reg_base + 0x10130);
vpll = samsung_clk_register_pll36xx("fout_vpll", "mout_vpllsrc",
reg_base + 0x10140);
samsung_clk_register_fixed_rate(exynos5250_fixed_rate_clks,
ARRAY_SIZE(exynos5250_fixed_rate_clks));
samsung_clk_register_fixed_factor(exynos5250_fixed_factor_clks,
ARRAY_SIZE(exynos5250_fixed_factor_clks));
samsung_clk_register_mux(exynos5250_mux_clks,
ARRAY_SIZE(exynos5250_mux_clks));
samsung_clk_register_div(exynos5250_div_clks,
ARRAY_SIZE(exynos5250_div_clks));
samsung_clk_register_gate(exynos5250_gate_clks,
ARRAY_SIZE(exynos5250_gate_clks));
pr_info("Exynos5250: clock setup completed, armclk=%ld\n",
_get_rate("armclk"));
}
CLK_OF_DECLARE(exynos5250_clk, "samsung,exynos5250-clock", exynos5250_clk_init);

View file

@ -0,0 +1,139 @@
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Author: Thomas Abraham <thomas.ab@samsung.com>
*
* 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.
*
* Common Clock Framework support for Exynos5440 SoC.
*/
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <plat/cpu.h>
#include "clk.h"
#include "clk-pll.h"
#define CLKEN_OV_VAL 0xf8
#define CPU_CLK_STATUS 0xfc
#define MISC_DOUT1 0x558
/*
* Let each supported clock get a unique id. This id is used to lookup the clock
* for device tree based platforms.
*/
enum exynos5440_clks {
none, xtal, arm_clk,
spi_baud = 16, pb0_250, pr0_250, pr1_250, b_250, b_125, b_200, sata,
usb, gmac0, cs250, pb0_250_o, pr0_250_o, pr1_250_o, b_250_o, b_125_o,
b_200_o, sata_o, usb_o, gmac0_o, cs250_o,
nr_clks,
};
/* parent clock name list */
PNAME(mout_armclk_p) = { "cplla", "cpllb" };
PNAME(mout_spi_p) = { "div125", "div200" };
/* fixed rate clocks generated outside the soc */
struct samsung_fixed_rate_clock exynos5440_fixed_rate_ext_clks[] __initdata = {
FRATE(none, "xtal", NULL, CLK_IS_ROOT, 0),
};
/* fixed rate clocks */
struct samsung_fixed_rate_clock exynos5440_fixed_rate_clks[] __initdata = {
FRATE(none, "ppll", NULL, CLK_IS_ROOT, 1000000000),
FRATE(none, "usb_phy0", NULL, CLK_IS_ROOT, 60000000),
FRATE(none, "usb_phy1", NULL, CLK_IS_ROOT, 60000000),
FRATE(none, "usb_ohci12", NULL, CLK_IS_ROOT, 12000000),
FRATE(none, "usb_ohci48", NULL, CLK_IS_ROOT, 48000000),
};
/* fixed factor clocks */
struct samsung_fixed_factor_clock exynos5440_fixed_factor_clks[] __initdata = {
FFACTOR(none, "div250", "ppll", 1, 4, 0),
FFACTOR(none, "div200", "ppll", 1, 5, 0),
FFACTOR(none, "div125", "div250", 1, 2, 0),
};
/* mux clocks */
struct samsung_mux_clock exynos5440_mux_clks[] __initdata = {
MUX(none, "mout_spi", mout_spi_p, MISC_DOUT1, 5, 1),
MUX_A(arm_clk, "arm_clk", mout_armclk_p,
CPU_CLK_STATUS, 0, 1, "armclk"),
};
/* divider clocks */
struct samsung_div_clock exynos5440_div_clks[] __initdata = {
DIV(spi_baud, "div_spi", "mout_spi", MISC_DOUT1, 3, 2),
};
/* gate clocks */
struct samsung_gate_clock exynos5440_gate_clks[] __initdata = {
GATE(pb0_250, "pb0_250", "div250", CLKEN_OV_VAL, 3, 0, 0),
GATE(pr0_250, "pr0_250", "div250", CLKEN_OV_VAL, 4, 0, 0),
GATE(pr1_250, "pr1_250", "div250", CLKEN_OV_VAL, 5, 0, 0),
GATE(b_250, "b_250", "div250", CLKEN_OV_VAL, 9, 0, 0),
GATE(b_125, "b_125", "div125", CLKEN_OV_VAL, 10, 0, 0),
GATE(b_200, "b_200", "div200", CLKEN_OV_VAL, 11, 0, 0),
GATE(sata, "sata", "div200", CLKEN_OV_VAL, 12, 0, 0),
GATE(usb, "usb", "div200", CLKEN_OV_VAL, 13, 0, 0),
GATE(gmac0, "gmac0", "div200", CLKEN_OV_VAL, 14, 0, 0),
GATE(cs250, "cs250", "div250", CLKEN_OV_VAL, 19, 0, 0),
GATE(pb0_250_o, "pb0_250_o", "pb0_250", CLKEN_OV_VAL, 3, 0, 0),
GATE(pr0_250_o, "pr0_250_o", "pr0_250", CLKEN_OV_VAL, 4, 0, 0),
GATE(pr1_250_o, "pr1_250_o", "pr1_250", CLKEN_OV_VAL, 5, 0, 0),
GATE(b_250_o, "b_250_o", "b_250", CLKEN_OV_VAL, 9, 0, 0),
GATE(b_125_o, "b_125_o", "b_125", CLKEN_OV_VAL, 10, 0, 0),
GATE(b_200_o, "b_200_o", "b_200", CLKEN_OV_VAL, 11, 0, 0),
GATE(sata_o, "sata_o", "sata", CLKEN_OV_VAL, 12, 0, 0),
GATE(usb_o, "usb_o", "usb", CLKEN_OV_VAL, 13, 0, 0),
GATE(gmac0_o, "gmac0_o", "gmac", CLKEN_OV_VAL, 14, 0, 0),
GATE(cs250_o, "cs250_o", "cs250", CLKEN_OV_VAL, 19, 0, 0),
};
static __initdata struct of_device_id ext_clk_match[] = {
{ .compatible = "samsung,clock-xtal", .data = (void *)0, },
{},
};
/* register exynos5440 clocks */
void __init exynos5440_clk_init(struct device_node *np)
{
void __iomem *reg_base;
reg_base = of_iomap(np, 0);
if (!reg_base) {
pr_err("%s: failed to map clock controller registers,"
" aborting clock initialization\n", __func__);
return;
}
samsung_clk_init(np, reg_base, nr_clks, NULL, 0, NULL, 0);
samsung_clk_of_register_fixed_ext(exynos5440_fixed_rate_ext_clks,
ARRAY_SIZE(exynos5440_fixed_rate_ext_clks), ext_clk_match);
samsung_clk_register_pll2550x("cplla", "xtal", reg_base + 0x1c, 0x10);
samsung_clk_register_pll2550x("cpllb", "xtal", reg_base + 0x20, 0x10);
samsung_clk_register_fixed_rate(exynos5440_fixed_rate_clks,
ARRAY_SIZE(exynos5440_fixed_rate_clks));
samsung_clk_register_fixed_factor(exynos5440_fixed_factor_clks,
ARRAY_SIZE(exynos5440_fixed_factor_clks));
samsung_clk_register_mux(exynos5440_mux_clks,
ARRAY_SIZE(exynos5440_mux_clks));
samsung_clk_register_div(exynos5440_div_clks,
ARRAY_SIZE(exynos5440_div_clks));
samsung_clk_register_gate(exynos5440_gate_clks,
ARRAY_SIZE(exynos5440_gate_clks));
pr_info("Exynos5440: arm_clk = %ldHz\n", _get_rate("armclk"));
pr_info("exynos5440 clock initialization complete\n");
}
CLK_OF_DECLARE(exynos5440_clk, "samsung,exynos5440-clock", exynos5440_clk_init);

View file

@ -0,0 +1,419 @@
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Copyright (c) 2013 Linaro Ltd.
*
* 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.
*
* This file contains the utility functions to register the pll clocks.
*/
#include <linux/errno.h>
#include "clk.h"
#include "clk-pll.h"
/*
* PLL35xx Clock Type
*/
#define PLL35XX_MDIV_MASK (0x3FF)
#define PLL35XX_PDIV_MASK (0x3F)
#define PLL35XX_SDIV_MASK (0x7)
#define PLL35XX_MDIV_SHIFT (16)
#define PLL35XX_PDIV_SHIFT (8)
#define PLL35XX_SDIV_SHIFT (0)
struct samsung_clk_pll35xx {
struct clk_hw hw;
const void __iomem *con_reg;
};
#define to_clk_pll35xx(_hw) container_of(_hw, struct samsung_clk_pll35xx, hw)
static unsigned long samsung_pll35xx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct samsung_clk_pll35xx *pll = to_clk_pll35xx(hw);
u32 mdiv, pdiv, sdiv, pll_con;
u64 fvco = parent_rate;
pll_con = __raw_readl(pll->con_reg);
mdiv = (pll_con >> PLL35XX_MDIV_SHIFT) & PLL35XX_MDIV_MASK;
pdiv = (pll_con >> PLL35XX_PDIV_SHIFT) & PLL35XX_PDIV_MASK;
sdiv = (pll_con >> PLL35XX_SDIV_SHIFT) & PLL35XX_SDIV_MASK;
fvco *= mdiv;
do_div(fvco, (pdiv << sdiv));
return (unsigned long)fvco;
}
static const struct clk_ops samsung_pll35xx_clk_ops = {
.recalc_rate = samsung_pll35xx_recalc_rate,
};
struct clk * __init samsung_clk_register_pll35xx(const char *name,
const char *pname, const void __iomem *con_reg)
{
struct samsung_clk_pll35xx *pll;
struct clk *clk;
struct clk_init_data init;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll) {
pr_err("%s: could not allocate pll clk %s\n", __func__, name);
return NULL;
}
init.name = name;
init.ops = &samsung_pll35xx_clk_ops;
init.flags = CLK_GET_RATE_NOCACHE;
init.parent_names = &pname;
init.num_parents = 1;
pll->hw.init = &init;
pll->con_reg = con_reg;
clk = clk_register(NULL, &pll->hw);
if (IS_ERR(clk)) {
pr_err("%s: failed to register pll clock %s\n", __func__,
name);
kfree(pll);
}
if (clk_register_clkdev(clk, name, NULL))
pr_err("%s: failed to register lookup for %s", __func__, name);
return clk;
}
/*
* PLL36xx Clock Type
*/
#define PLL36XX_KDIV_MASK (0xFFFF)
#define PLL36XX_MDIV_MASK (0x1FF)
#define PLL36XX_PDIV_MASK (0x3F)
#define PLL36XX_SDIV_MASK (0x7)
#define PLL36XX_MDIV_SHIFT (16)
#define PLL36XX_PDIV_SHIFT (8)
#define PLL36XX_SDIV_SHIFT (0)
struct samsung_clk_pll36xx {
struct clk_hw hw;
const void __iomem *con_reg;
};
#define to_clk_pll36xx(_hw) container_of(_hw, struct samsung_clk_pll36xx, hw)
static unsigned long samsung_pll36xx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct samsung_clk_pll36xx *pll = to_clk_pll36xx(hw);
u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1;
u64 fvco = parent_rate;
pll_con0 = __raw_readl(pll->con_reg);
pll_con1 = __raw_readl(pll->con_reg + 4);
mdiv = (pll_con0 >> PLL36XX_MDIV_SHIFT) & PLL36XX_MDIV_MASK;
pdiv = (pll_con0 >> PLL36XX_PDIV_SHIFT) & PLL36XX_PDIV_MASK;
sdiv = (pll_con0 >> PLL36XX_SDIV_SHIFT) & PLL36XX_SDIV_MASK;
kdiv = pll_con1 & PLL36XX_KDIV_MASK;
fvco *= (mdiv << 16) + kdiv;
do_div(fvco, (pdiv << sdiv));
fvco >>= 16;
return (unsigned long)fvco;
}
static const struct clk_ops samsung_pll36xx_clk_ops = {
.recalc_rate = samsung_pll36xx_recalc_rate,
};
struct clk * __init samsung_clk_register_pll36xx(const char *name,
const char *pname, const void __iomem *con_reg)
{
struct samsung_clk_pll36xx *pll;
struct clk *clk;
struct clk_init_data init;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll) {
pr_err("%s: could not allocate pll clk %s\n", __func__, name);
return NULL;
}
init.name = name;
init.ops = &samsung_pll36xx_clk_ops;
init.flags = CLK_GET_RATE_NOCACHE;
init.parent_names = &pname;
init.num_parents = 1;
pll->hw.init = &init;
pll->con_reg = con_reg;
clk = clk_register(NULL, &pll->hw);
if (IS_ERR(clk)) {
pr_err("%s: failed to register pll clock %s\n", __func__,
name);
kfree(pll);
}
if (clk_register_clkdev(clk, name, NULL))
pr_err("%s: failed to register lookup for %s", __func__, name);
return clk;
}
/*
* PLL45xx Clock Type
*/
#define PLL45XX_MDIV_MASK (0x3FF)
#define PLL45XX_PDIV_MASK (0x3F)
#define PLL45XX_SDIV_MASK (0x7)
#define PLL45XX_MDIV_SHIFT (16)
#define PLL45XX_PDIV_SHIFT (8)
#define PLL45XX_SDIV_SHIFT (0)
struct samsung_clk_pll45xx {
struct clk_hw hw;
enum pll45xx_type type;
const void __iomem *con_reg;
};
#define to_clk_pll45xx(_hw) container_of(_hw, struct samsung_clk_pll45xx, hw)
static unsigned long samsung_pll45xx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct samsung_clk_pll45xx *pll = to_clk_pll45xx(hw);
u32 mdiv, pdiv, sdiv, pll_con;
u64 fvco = parent_rate;
pll_con = __raw_readl(pll->con_reg);
mdiv = (pll_con >> PLL45XX_MDIV_SHIFT) & PLL45XX_MDIV_MASK;
pdiv = (pll_con >> PLL45XX_PDIV_SHIFT) & PLL45XX_PDIV_MASK;
sdiv = (pll_con >> PLL45XX_SDIV_SHIFT) & PLL45XX_SDIV_MASK;
if (pll->type == pll_4508)
sdiv = sdiv - 1;
fvco *= mdiv;
do_div(fvco, (pdiv << sdiv));
return (unsigned long)fvco;
}
static const struct clk_ops samsung_pll45xx_clk_ops = {
.recalc_rate = samsung_pll45xx_recalc_rate,
};
struct clk * __init samsung_clk_register_pll45xx(const char *name,
const char *pname, const void __iomem *con_reg,
enum pll45xx_type type)
{
struct samsung_clk_pll45xx *pll;
struct clk *clk;
struct clk_init_data init;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll) {
pr_err("%s: could not allocate pll clk %s\n", __func__, name);
return NULL;
}
init.name = name;
init.ops = &samsung_pll45xx_clk_ops;
init.flags = CLK_GET_RATE_NOCACHE;
init.parent_names = &pname;
init.num_parents = 1;
pll->hw.init = &init;
pll->con_reg = con_reg;
pll->type = type;
clk = clk_register(NULL, &pll->hw);
if (IS_ERR(clk)) {
pr_err("%s: failed to register pll clock %s\n", __func__,
name);
kfree(pll);
}
if (clk_register_clkdev(clk, name, NULL))
pr_err("%s: failed to register lookup for %s", __func__, name);
return clk;
}
/*
* PLL46xx Clock Type
*/
#define PLL46XX_MDIV_MASK (0x1FF)
#define PLL46XX_PDIV_MASK (0x3F)
#define PLL46XX_SDIV_MASK (0x7)
#define PLL46XX_MDIV_SHIFT (16)
#define PLL46XX_PDIV_SHIFT (8)
#define PLL46XX_SDIV_SHIFT (0)
#define PLL46XX_KDIV_MASK (0xFFFF)
#define PLL4650C_KDIV_MASK (0xFFF)
#define PLL46XX_KDIV_SHIFT (0)
struct samsung_clk_pll46xx {
struct clk_hw hw;
enum pll46xx_type type;
const void __iomem *con_reg;
};
#define to_clk_pll46xx(_hw) container_of(_hw, struct samsung_clk_pll46xx, hw)
static unsigned long samsung_pll46xx_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct samsung_clk_pll46xx *pll = to_clk_pll46xx(hw);
u32 mdiv, pdiv, sdiv, kdiv, pll_con0, pll_con1, shift;
u64 fvco = parent_rate;
pll_con0 = __raw_readl(pll->con_reg);
pll_con1 = __raw_readl(pll->con_reg + 4);
mdiv = (pll_con0 >> PLL46XX_MDIV_SHIFT) & PLL46XX_MDIV_MASK;
pdiv = (pll_con0 >> PLL46XX_PDIV_SHIFT) & PLL46XX_PDIV_MASK;
sdiv = (pll_con0 >> PLL46XX_SDIV_SHIFT) & PLL46XX_SDIV_MASK;
kdiv = pll->type == pll_4650c ? pll_con1 & PLL4650C_KDIV_MASK :
pll_con1 & PLL46XX_KDIV_MASK;
shift = pll->type == pll_4600 ? 16 : 10;
fvco *= (mdiv << shift) + kdiv;
do_div(fvco, (pdiv << sdiv));
fvco >>= shift;
return (unsigned long)fvco;
}
static const struct clk_ops samsung_pll46xx_clk_ops = {
.recalc_rate = samsung_pll46xx_recalc_rate,
};
struct clk * __init samsung_clk_register_pll46xx(const char *name,
const char *pname, const void __iomem *con_reg,
enum pll46xx_type type)
{
struct samsung_clk_pll46xx *pll;
struct clk *clk;
struct clk_init_data init;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll) {
pr_err("%s: could not allocate pll clk %s\n", __func__, name);
return NULL;
}
init.name = name;
init.ops = &samsung_pll46xx_clk_ops;
init.flags = CLK_GET_RATE_NOCACHE;
init.parent_names = &pname;
init.num_parents = 1;
pll->hw.init = &init;
pll->con_reg = con_reg;
pll->type = type;
clk = clk_register(NULL, &pll->hw);
if (IS_ERR(clk)) {
pr_err("%s: failed to register pll clock %s\n", __func__,
name);
kfree(pll);
}
if (clk_register_clkdev(clk, name, NULL))
pr_err("%s: failed to register lookup for %s", __func__, name);
return clk;
}
/*
* PLL2550x Clock Type
*/
#define PLL2550X_R_MASK (0x1)
#define PLL2550X_P_MASK (0x3F)
#define PLL2550X_M_MASK (0x3FF)
#define PLL2550X_S_MASK (0x7)
#define PLL2550X_R_SHIFT (20)
#define PLL2550X_P_SHIFT (14)
#define PLL2550X_M_SHIFT (4)
#define PLL2550X_S_SHIFT (0)
struct samsung_clk_pll2550x {
struct clk_hw hw;
const void __iomem *reg_base;
unsigned long offset;
};
#define to_clk_pll2550x(_hw) container_of(_hw, struct samsung_clk_pll2550x, hw)
static unsigned long samsung_pll2550x_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct samsung_clk_pll2550x *pll = to_clk_pll2550x(hw);
u32 r, p, m, s, pll_stat;
u64 fvco = parent_rate;
pll_stat = __raw_readl(pll->reg_base + pll->offset * 3);
r = (pll_stat >> PLL2550X_R_SHIFT) & PLL2550X_R_MASK;
if (!r)
return 0;
p = (pll_stat >> PLL2550X_P_SHIFT) & PLL2550X_P_MASK;
m = (pll_stat >> PLL2550X_M_SHIFT) & PLL2550X_M_MASK;
s = (pll_stat >> PLL2550X_S_SHIFT) & PLL2550X_S_MASK;
fvco *= m;
do_div(fvco, (p << s));
return (unsigned long)fvco;
}
static const struct clk_ops samsung_pll2550x_clk_ops = {
.recalc_rate = samsung_pll2550x_recalc_rate,
};
struct clk * __init samsung_clk_register_pll2550x(const char *name,
const char *pname, const void __iomem *reg_base,
const unsigned long offset)
{
struct samsung_clk_pll2550x *pll;
struct clk *clk;
struct clk_init_data init;
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
if (!pll) {
pr_err("%s: could not allocate pll clk %s\n", __func__, name);
return NULL;
}
init.name = name;
init.ops = &samsung_pll2550x_clk_ops;
init.flags = CLK_GET_RATE_NOCACHE;
init.parent_names = &pname;
init.num_parents = 1;
pll->hw.init = &init;
pll->reg_base = reg_base;
pll->offset = offset;
clk = clk_register(NULL, &pll->hw);
if (IS_ERR(clk)) {
pr_err("%s: failed to register pll clock %s\n", __func__,
name);
kfree(pll);
}
if (clk_register_clkdev(clk, name, NULL))
pr_err("%s: failed to register lookup for %s", __func__, name);
return clk;
}

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Copyright (c) 2013 Linaro Ltd.
*
* 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.
*
* Common Clock Framework support for all PLL's in Samsung platforms
*/
#ifndef __SAMSUNG_CLK_PLL_H
#define __SAMSUNG_CLK_PLL_H
enum pll45xx_type {
pll_4500,
pll_4502,
pll_4508
};
enum pll46xx_type {
pll_4600,
pll_4650,
pll_4650c,
};
extern struct clk * __init samsung_clk_register_pll35xx(const char *name,
const char *pname, const void __iomem *con_reg);
extern struct clk * __init samsung_clk_register_pll36xx(const char *name,
const char *pname, const void __iomem *con_reg);
extern struct clk * __init samsung_clk_register_pll45xx(const char *name,
const char *pname, const void __iomem *con_reg,
enum pll45xx_type type);
extern struct clk * __init samsung_clk_register_pll46xx(const char *name,
const char *pname, const void __iomem *con_reg,
enum pll46xx_type type);
extern struct clk * __init samsung_clk_register_pll2550x(const char *name,
const char *pname, const void __iomem *reg_base,
const unsigned long offset);
#endif /* __SAMSUNG_CLK_PLL_H */

320
drivers/clk/samsung/clk.c Normal file
View file

@ -0,0 +1,320 @@
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Copyright (c) 2013 Linaro Ltd.
* Author: Thomas Abraham <thomas.ab@samsung.com>
*
* 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.
*
* This file includes utility functions to register clocks to common
* clock framework for Samsung platforms.
*/
#include <linux/syscore_ops.h>
#include "clk.h"
static DEFINE_SPINLOCK(lock);
static struct clk **clk_table;
static void __iomem *reg_base;
#ifdef CONFIG_OF
static struct clk_onecell_data clk_data;
#endif
#ifdef CONFIG_PM_SLEEP
static struct samsung_clk_reg_dump *reg_dump;
static unsigned long nr_reg_dump;
static int samsung_clk_suspend(void)
{
struct samsung_clk_reg_dump *rd = reg_dump;
unsigned long i;
for (i = 0; i < nr_reg_dump; i++, rd++)
rd->value = __raw_readl(reg_base + rd->offset);
return 0;
}
static void samsung_clk_resume(void)
{
struct samsung_clk_reg_dump *rd = reg_dump;
unsigned long i;
for (i = 0; i < nr_reg_dump; i++, rd++)
__raw_writel(rd->value, reg_base + rd->offset);
}
static struct syscore_ops samsung_clk_syscore_ops = {
.suspend = samsung_clk_suspend,
.resume = samsung_clk_resume,
};
#endif /* CONFIG_PM_SLEEP */
/* setup the essentials required to support clock lookup using ccf */
void __init samsung_clk_init(struct device_node *np, void __iomem *base,
unsigned long nr_clks, unsigned long *rdump,
unsigned long nr_rdump, unsigned long *soc_rdump,
unsigned long nr_soc_rdump)
{
reg_base = base;
#ifdef CONFIG_PM_SLEEP
if (rdump && nr_rdump) {
unsigned int idx;
reg_dump = kzalloc(sizeof(struct samsung_clk_reg_dump)
* (nr_rdump + nr_soc_rdump), GFP_KERNEL);
if (!reg_dump) {
pr_err("%s: memory alloc for register dump failed\n",
__func__);
return;
}
for (idx = 0; idx < nr_rdump; idx++)
reg_dump[idx].offset = rdump[idx];
for (idx = 0; idx < nr_soc_rdump; idx++)
reg_dump[nr_rdump + idx].offset = soc_rdump[idx];
nr_reg_dump = nr_rdump + nr_soc_rdump;
register_syscore_ops(&samsung_clk_syscore_ops);
}
#endif
clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
if (!clk_table)
panic("could not allocate clock lookup table\n");
if (!np)
return;
#ifdef CONFIG_OF
clk_data.clks = clk_table;
clk_data.clk_num = nr_clks;
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
#endif
}
/* add a clock instance to the clock lookup table used for dt based lookup */
void samsung_clk_add_lookup(struct clk *clk, unsigned int id)
{
if (clk_table && id)
clk_table[id] = clk;
}
/* register a list of aliases */
void __init samsung_clk_register_alias(struct samsung_clock_alias *list,
unsigned int nr_clk)
{
struct clk *clk;
unsigned int idx, ret;
if (!clk_table) {
pr_err("%s: clock table missing\n", __func__);
return;
}
for (idx = 0; idx < nr_clk; idx++, list++) {
if (!list->id) {
pr_err("%s: clock id missing for index %d\n", __func__,
idx);
continue;
}
clk = clk_table[list->id];
if (!clk) {
pr_err("%s: failed to find clock %d\n", __func__,
list->id);
continue;
}
ret = clk_register_clkdev(clk, list->alias, list->dev_name);
if (ret)
pr_err("%s: failed to register lookup %s\n",
__func__, list->alias);
}
}
/* register a list of fixed clocks */
void __init samsung_clk_register_fixed_rate(
struct samsung_fixed_rate_clock *list, unsigned int nr_clk)
{
struct clk *clk;
unsigned int idx, ret;
for (idx = 0; idx < nr_clk; idx++, list++) {
clk = clk_register_fixed_rate(NULL, list->name,
list->parent_name, list->flags, list->fixed_rate);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
list->name);
continue;
}
samsung_clk_add_lookup(clk, list->id);
/*
* Unconditionally add a clock lookup for the fixed rate clocks.
* There are not many of these on any of Samsung platforms.
*/
ret = clk_register_clkdev(clk, list->name, NULL);
if (ret)
pr_err("%s: failed to register clock lookup for %s",
__func__, list->name);
}
}
/* register a list of fixed factor clocks */
void __init samsung_clk_register_fixed_factor(
struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
{
struct clk *clk;
unsigned int idx;
for (idx = 0; idx < nr_clk; idx++, list++) {
clk = clk_register_fixed_factor(NULL, list->name,
list->parent_name, list->flags, list->mult, list->div);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
list->name);
continue;
}
samsung_clk_add_lookup(clk, list->id);
}
}
/* register a list of mux clocks */
void __init samsung_clk_register_mux(struct samsung_mux_clock *list,
unsigned int nr_clk)
{
struct clk *clk;
unsigned int idx, ret;
for (idx = 0; idx < nr_clk; idx++, list++) {
clk = clk_register_mux(NULL, list->name, list->parent_names,
list->num_parents, list->flags, reg_base + list->offset,
list->shift, list->width, list->mux_flags, &lock);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
list->name);
continue;
}
samsung_clk_add_lookup(clk, list->id);
/* register a clock lookup only if a clock alias is specified */
if (list->alias) {
ret = clk_register_clkdev(clk, list->alias,
list->dev_name);
if (ret)
pr_err("%s: failed to register lookup %s\n",
__func__, list->alias);
}
}
}
/* register a list of div clocks */
void __init samsung_clk_register_div(struct samsung_div_clock *list,
unsigned int nr_clk)
{
struct clk *clk;
unsigned int idx, ret;
for (idx = 0; idx < nr_clk; idx++, list++) {
if (list->table)
clk = clk_register_divider_table(NULL, list->name,
list->parent_name, list->flags,
reg_base + list->offset, list->shift,
list->width, list->div_flags,
list->table, &lock);
else
clk = clk_register_divider(NULL, list->name,
list->parent_name, list->flags,
reg_base + list->offset, list->shift,
list->width, list->div_flags, &lock);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
list->name);
continue;
}
samsung_clk_add_lookup(clk, list->id);
/* register a clock lookup only if a clock alias is specified */
if (list->alias) {
ret = clk_register_clkdev(clk, list->alias,
list->dev_name);
if (ret)
pr_err("%s: failed to register lookup %s\n",
__func__, list->alias);
}
}
}
/* register a list of gate clocks */
void __init samsung_clk_register_gate(struct samsung_gate_clock *list,
unsigned int nr_clk)
{
struct clk *clk;
unsigned int idx, ret;
for (idx = 0; idx < nr_clk; idx++, list++) {
clk = clk_register_gate(NULL, list->name, list->parent_name,
list->flags, reg_base + list->offset,
list->bit_idx, list->gate_flags, &lock);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
list->name);
continue;
}
/* register a clock lookup only if a clock alias is specified */
if (list->alias) {
ret = clk_register_clkdev(clk, list->alias,
list->dev_name);
if (ret)
pr_err("%s: failed to register lookup %s\n",
__func__, list->alias);
}
samsung_clk_add_lookup(clk, list->id);
}
}
/*
* obtain the clock speed of all external fixed clock sources from device
* tree and register it
*/
#ifdef CONFIG_OF
void __init samsung_clk_of_register_fixed_ext(
struct samsung_fixed_rate_clock *fixed_rate_clk,
unsigned int nr_fixed_rate_clk,
struct of_device_id *clk_matches)
{
const struct of_device_id *match;
struct device_node *np;
u32 freq;
for_each_matching_node_and_match(np, clk_matches, &match) {
if (of_property_read_u32(np, "clock-frequency", &freq))
continue;
fixed_rate_clk[(u32)match->data].fixed_rate = freq;
}
samsung_clk_register_fixed_rate(fixed_rate_clk, nr_fixed_rate_clk);
}
#endif
/* utility function to get the rate of a specified clock */
unsigned long _get_rate(const char *clk_name)
{
struct clk *clk;
unsigned long rate;
clk = clk_get(NULL, clk_name);
if (IS_ERR(clk)) {
pr_err("%s: could not find clock %s\n", __func__, clk_name);
return 0;
}
rate = clk_get_rate(clk);
clk_put(clk);
return rate;
}

289
drivers/clk/samsung/clk.h Normal file
View file

@ -0,0 +1,289 @@
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Copyright (c) 2013 Linaro Ltd.
* Author: Thomas Abraham <thomas.ab@samsung.com>
*
* 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.
*
* Common Clock Framework support for all Samsung platforms
*/
#ifndef __SAMSUNG_CLK_H
#define __SAMSUNG_CLK_H
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/io.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <mach/map.h>
/**
* struct samsung_clock_alias: information about mux clock
* @id: platform specific id of the clock.
* @dev_name: name of the device to which this clock belongs.
* @alias: optional clock alias name to be assigned to this clock.
*/
struct samsung_clock_alias {
unsigned int id;
const char *dev_name;
const char *alias;
};
#define ALIAS(_id, dname, a) \
{ \
.id = _id, \
.dev_name = dname, \
.alias = a, \
}
/**
* struct samsung_fixed_rate_clock: information about fixed-rate clock
* @id: platform specific id of the clock.
* @name: name of this fixed-rate clock.
* @parent_name: optional parent clock name.
* @flags: optional fixed-rate clock flags.
* @fixed-rate: fixed clock rate of this clock.
*/
struct samsung_fixed_rate_clock {
unsigned int id;
char *name;
const char *parent_name;
unsigned long flags;
unsigned long fixed_rate;
};
#define FRATE(_id, cname, pname, f, frate) \
{ \
.id = _id, \
.name = cname, \
.parent_name = pname, \
.flags = f, \
.fixed_rate = frate, \
}
/*
* struct samsung_fixed_factor_clock: information about fixed-factor clock
* @id: platform specific id of the clock.
* @name: name of this fixed-factor clock.
* @parent_name: parent clock name.
* @mult: fixed multiplication factor.
* @div: fixed division factor.
* @flags: optional fixed-factor clock flags.
*/
struct samsung_fixed_factor_clock {
unsigned int id;
char *name;
const char *parent_name;
unsigned long mult;
unsigned long div;
unsigned long flags;
};
#define FFACTOR(_id, cname, pname, m, d, f) \
{ \
.id = _id, \
.name = cname, \
.parent_name = pname, \
.mult = m, \
.div = d, \
.flags = f, \
}
/**
* struct samsung_mux_clock: information about mux clock
* @id: platform specific id of the clock.
* @dev_name: name of the device to which this clock belongs.
* @name: name of this mux clock.
* @parent_names: array of pointer to parent clock names.
* @num_parents: number of parents listed in @parent_names.
* @flags: optional flags for basic clock.
* @offset: offset of the register for configuring the mux.
* @shift: starting bit location of the mux control bit-field in @reg.
* @width: width of the mux control bit-field in @reg.
* @mux_flags: flags for mux-type clock.
* @alias: optional clock alias name to be assigned to this clock.
*/
struct samsung_mux_clock {
unsigned int id;
const char *dev_name;
const char *name;
const char **parent_names;
u8 num_parents;
unsigned long flags;
unsigned long offset;
u8 shift;
u8 width;
u8 mux_flags;
const char *alias;
};
#define __MUX(_id, dname, cname, pnames, o, s, w, f, mf, a) \
{ \
.id = _id, \
.dev_name = dname, \
.name = cname, \
.parent_names = pnames, \
.num_parents = ARRAY_SIZE(pnames), \
.flags = f, \
.offset = o, \
.shift = s, \
.width = w, \
.mux_flags = mf, \
.alias = a, \
}
#define MUX(_id, cname, pnames, o, s, w) \
__MUX(_id, NULL, cname, pnames, o, s, w, 0, 0, NULL)
#define MUX_A(_id, cname, pnames, o, s, w, a) \
__MUX(_id, NULL, cname, pnames, o, s, w, 0, 0, a)
#define MUX_F(_id, cname, pnames, o, s, w, f, mf) \
__MUX(_id, NULL, cname, pnames, o, s, w, f, mf, NULL)
/**
* @id: platform specific id of the clock.
* struct samsung_div_clock: information about div clock
* @dev_name: name of the device to which this clock belongs.
* @name: name of this div clock.
* @parent_name: name of the parent clock.
* @flags: optional flags for basic clock.
* @offset: offset of the register for configuring the div.
* @shift: starting bit location of the div control bit-field in @reg.
* @div_flags: flags for div-type clock.
* @alias: optional clock alias name to be assigned to this clock.
*/
struct samsung_div_clock {
unsigned int id;
const char *dev_name;
const char *name;
const char *parent_name;
unsigned long flags;
unsigned long offset;
u8 shift;
u8 width;
u8 div_flags;
const char *alias;
struct clk_div_table *table;
};
#define __DIV(_id, dname, cname, pname, o, s, w, f, df, a, t) \
{ \
.id = _id, \
.dev_name = dname, \
.name = cname, \
.parent_name = pname, \
.flags = f, \
.offset = o, \
.shift = s, \
.width = w, \
.div_flags = df, \
.alias = a, \
.table = t, \
}
#define DIV(_id, cname, pname, o, s, w) \
__DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL, NULL)
#define DIV_A(_id, cname, pname, o, s, w, a) \
__DIV(_id, NULL, cname, pname, o, s, w, 0, 0, a, NULL)
#define DIV_F(_id, cname, pname, o, s, w, f, df) \
__DIV(_id, NULL, cname, pname, o, s, w, f, df, NULL, NULL)
#define DIV_T(_id, cname, pname, o, s, w, t) \
__DIV(_id, NULL, cname, pname, o, s, w, 0, 0, NULL, t)
/**
* struct samsung_gate_clock: information about gate clock
* @id: platform specific id of the clock.
* @dev_name: name of the device to which this clock belongs.
* @name: name of this gate clock.
* @parent_name: name of the parent clock.
* @flags: optional flags for basic clock.
* @offset: offset of the register for configuring the gate.
* @bit_idx: bit index of the gate control bit-field in @reg.
* @gate_flags: flags for gate-type clock.
* @alias: optional clock alias name to be assigned to this clock.
*/
struct samsung_gate_clock {
unsigned int id;
const char *dev_name;
const char *name;
const char *parent_name;
unsigned long flags;
unsigned long offset;
u8 bit_idx;
u8 gate_flags;
const char *alias;
};
#define __GATE(_id, dname, cname, pname, o, b, f, gf, a) \
{ \
.id = _id, \
.dev_name = dname, \
.name = cname, \
.parent_name = pname, \
.flags = f, \
.offset = o, \
.bit_idx = b, \
.gate_flags = gf, \
.alias = a, \
}
#define GATE(_id, cname, pname, o, b, f, gf) \
__GATE(_id, NULL, cname, pname, o, b, f, gf, NULL)
#define GATE_A(_id, cname, pname, o, b, f, gf, a) \
__GATE(_id, NULL, cname, pname, o, b, f, gf, a)
#define GATE_D(_id, dname, cname, pname, o, b, f, gf) \
__GATE(_id, dname, cname, pname, o, b, f, gf, NULL)
#define GATE_DA(_id, dname, cname, pname, o, b, f, gf, a) \
__GATE(_id, dname, cname, pname, o, b, f, gf, a)
#define PNAME(x) static const char *x[] __initdata
/**
* struct samsung_clk_reg_dump: register dump of clock controller registers.
* @offset: clock register offset from the controller base address.
* @value: the value to be register at offset.
*/
struct samsung_clk_reg_dump {
u32 offset;
u32 value;
};
extern void __init samsung_clk_init(struct device_node *np, void __iomem *base,
unsigned long nr_clks, unsigned long *rdump,
unsigned long nr_rdump, unsigned long *soc_rdump,
unsigned long nr_soc_rdump);
extern void __init samsung_clk_of_register_fixed_ext(
struct samsung_fixed_rate_clock *fixed_rate_clk,
unsigned int nr_fixed_rate_clk,
struct of_device_id *clk_matches);
extern void samsung_clk_add_lookup(struct clk *clk, unsigned int id);
extern void samsung_clk_register_alias(struct samsung_clock_alias *list,
unsigned int nr_clk);
extern void __init samsung_clk_register_fixed_rate(
struct samsung_fixed_rate_clock *clk_list, unsigned int nr_clk);
extern void __init samsung_clk_register_fixed_factor(
struct samsung_fixed_factor_clock *list, unsigned int nr_clk);
extern void __init samsung_clk_register_mux(struct samsung_mux_clock *clk_list,
unsigned int nr_clk);
extern void __init samsung_clk_register_div(struct samsung_div_clock *clk_list,
unsigned int nr_clk);
extern void __init samsung_clk_register_gate(
struct samsung_gate_clock *clk_list, unsigned int nr_clk);
extern unsigned long _get_rate(const char *clk_name);
#endif /* __SAMSUNG_CLK_H */

View file

@ -960,47 +960,47 @@ void __init spear1340_clk_init(void)
SPEAR1340_SPDIF_IN_CLK_ENB, 0, &_lock);
clk_register_clkdev(clk, NULL, "d0100000.spdif-in");
clk = clk_register_gate(NULL, "acp_clk", "acp_mclk", 0,
clk = clk_register_gate(NULL, "acp_clk", "ahb_clk", 0,
SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_ACP_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "acp_clk");
clk = clk_register_gate(NULL, "plgpio_clk", "plgpio_mclk", 0,
clk = clk_register_gate(NULL, "plgpio_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_PLGPIO_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "e2800000.gpio");
clk = clk_register_gate(NULL, "video_dec_clk", "video_dec_mclk", 0,
clk = clk_register_gate(NULL, "video_dec_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_DEC_CLK_ENB,
0, &_lock);
clk_register_clkdev(clk, NULL, "video_dec");
clk = clk_register_gate(NULL, "video_enc_clk", "video_enc_mclk", 0,
clk = clk_register_gate(NULL, "video_enc_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_ENC_CLK_ENB,
0, &_lock);
clk_register_clkdev(clk, NULL, "video_enc");
clk = clk_register_gate(NULL, "video_in_clk", "video_in_mclk", 0,
clk = clk_register_gate(NULL, "video_in_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_IN_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "spear_vip");
clk = clk_register_gate(NULL, "cam0_clk", "cam0_mclk", 0,
clk = clk_register_gate(NULL, "cam0_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM0_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "d0200000.cam0");
clk = clk_register_gate(NULL, "cam1_clk", "cam1_mclk", 0,
clk = clk_register_gate(NULL, "cam1_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM1_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "d0300000.cam1");
clk = clk_register_gate(NULL, "cam2_clk", "cam2_mclk", 0,
clk = clk_register_gate(NULL, "cam2_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM2_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "d0400000.cam2");
clk = clk_register_gate(NULL, "cam3_clk", "cam3_mclk", 0,
clk = clk_register_gate(NULL, "cam3_clk", "ahb_clk", 0,
SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM3_CLK_ENB, 0,
&_lock);
clk_register_clkdev(clk, NULL, "d0500000.cam3");

View file

@ -0,0 +1,5 @@
#
# Makefile for sunxi specific clk
#
obj-y += clk-sunxi.o clk-factors.o

View file

@ -0,0 +1,180 @@
/*
* Copyright (C) 2013 Emilio López <emilio@elopez.com.ar>
*
* 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.
*
* Adjustable factor-based clock implementation
*/
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/string.h>
#include <linux/delay.h>
#include "clk-factors.h"
/*
* DOC: basic adjustable factor-based clock that cannot gate
*
* Traits of this clock:
* prepare - clk_prepare only ensures that parents are prepared
* enable - clk_enable only ensures that parents are enabled
* rate - rate is adjustable.
* clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1)
* parent - fixed parent. No clk_set_parent support
*/
struct clk_factors {
struct clk_hw hw;
void __iomem *reg;
struct clk_factors_config *config;
void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p);
spinlock_t *lock;
};
#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw)
#define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos))
#define CLRMASK(len, pos) (~(SETMASK(len, pos)))
#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit))
#define FACTOR_SET(bit, len, reg, val) \
(((reg) & CLRMASK(len, bit)) | (val << (bit)))
static unsigned long clk_factors_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
u8 n = 1, k = 0, p = 0, m = 0;
u32 reg;
unsigned long rate;
struct clk_factors *factors = to_clk_factors(hw);
struct clk_factors_config *config = factors->config;
/* Fetch the register value */
reg = readl(factors->reg);
/* Get each individual factor if applicable */
if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE)
n = FACTOR_GET(config->nshift, config->nwidth, reg);
if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE)
k = FACTOR_GET(config->kshift, config->kwidth, reg);
if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE)
m = FACTOR_GET(config->mshift, config->mwidth, reg);
if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE)
p = FACTOR_GET(config->pshift, config->pwidth, reg);
/* Calculate the rate */
rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
return rate;
}
static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_factors *factors = to_clk_factors(hw);
factors->get_factors((u32 *)&rate, (u32)*parent_rate,
NULL, NULL, NULL, NULL);
return rate;
}
static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
u8 n, k, m, p;
u32 reg;
struct clk_factors *factors = to_clk_factors(hw);
struct clk_factors_config *config = factors->config;
unsigned long flags = 0;
factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p);
if (factors->lock)
spin_lock_irqsave(factors->lock, flags);
/* Fetch the register value */
reg = readl(factors->reg);
/* Set up the new factors - macros do not do anything if width is 0 */
reg = FACTOR_SET(config->nshift, config->nwidth, reg, n);
reg = FACTOR_SET(config->kshift, config->kwidth, reg, k);
reg = FACTOR_SET(config->mshift, config->mwidth, reg, m);
reg = FACTOR_SET(config->pshift, config->pwidth, reg, p);
/* Apply them now */
writel(reg, factors->reg);
/* delay 500us so pll stabilizes */
__delay((rate >> 20) * 500 / 2);
if (factors->lock)
spin_unlock_irqrestore(factors->lock, flags);
return 0;
}
static const struct clk_ops clk_factors_ops = {
.recalc_rate = clk_factors_recalc_rate,
.round_rate = clk_factors_round_rate,
.set_rate = clk_factors_set_rate,
};
/**
* clk_register_factors - register a factors clock with
* the clock framework
* @dev: device registering this clock
* @name: name of this clock
* @parent_name: name of clock's parent
* @flags: framework-specific flags
* @reg: register address to adjust factors
* @config: shift and width of factors n, k, m and p
* @get_factors: function to calculate the factors for a given frequency
* @lock: shared register lock for this clock
*/
struct clk *clk_register_factors(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags, void __iomem *reg,
struct clk_factors_config *config,
void (*get_factors)(u32 *rate, u32 parent,
u8 *n, u8 *k, u8 *m, u8 *p),
spinlock_t *lock)
{
struct clk_factors *factors;
struct clk *clk;
struct clk_init_data init;
/* allocate the factors */
factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL);
if (!factors) {
pr_err("%s: could not allocate factors clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &clk_factors_ops;
init.flags = flags;
init.parent_names = (parent_name ? &parent_name : NULL);
init.num_parents = (parent_name ? 1 : 0);
/* struct clk_factors assignments */
factors->reg = reg;
factors->config = config;
factors->lock = lock;
factors->hw.init = &init;
factors->get_factors = get_factors;
/* register the clock */
clk = clk_register(dev, &factors->hw);
if (IS_ERR(clk))
kfree(factors);
return clk;
}

View file

@ -0,0 +1,27 @@
#ifndef __MACH_SUNXI_CLK_FACTORS_H
#define __MACH_SUNXI_CLK_FACTORS_H
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#define SUNXI_FACTORS_NOT_APPLICABLE (0)
struct clk_factors_config {
u8 nshift;
u8 nwidth;
u8 kshift;
u8 kwidth;
u8 mshift;
u8 mwidth;
u8 pshift;
u8 pwidth;
};
struct clk *clk_register_factors(struct device *dev, const char *name,
const char *parent_name,
unsigned long flags, void __iomem *reg,
struct clk_factors_config *config,
void (*get_factors) (u32 *rate, u32 parent_rate,
u8 *n, u8 *k, u8 *m, u8 *p),
spinlock_t *lock);
#endif

View file

@ -0,0 +1,362 @@
/*
* Copyright 2013 Emilio López
*
* Emilio López <emilio@elopez.com.ar>
*
* 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/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/clk/sunxi.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include "clk-factors.h"
static DEFINE_SPINLOCK(clk_lock);
/**
* sunxi_osc_clk_setup() - Setup function for gatable oscillator
*/
#define SUNXI_OSC24M_GATE 0
static void __init sunxi_osc_clk_setup(struct device_node *node)
{
struct clk *clk;
const char *clk_name = node->name;
const char *parent;
void *reg;
reg = of_iomap(node, 0);
parent = of_clk_get_parent_name(node, 0);
clk = clk_register_gate(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
reg, SUNXI_OSC24M_GATE, 0, &clk_lock);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
/**
* sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1
* PLL1 rate is calculated as follows
* rate = (parent_rate * n * (k + 1) >> p) / (m + 1);
* parent_rate is always 24Mhz
*/
static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate,
u8 *n, u8 *k, u8 *m, u8 *p)
{
u8 div;
/* Normalize value to a 6M multiple */
div = *freq / 6000000;
*freq = 6000000 * div;
/* we were called to round the frequency, we can now return */
if (n == NULL)
return;
/* m is always zero for pll1 */
*m = 0;
/* k is 1 only on these cases */
if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000)
*k = 1;
else
*k = 0;
/* p will be 3 for divs under 10 */
if (div < 10)
*p = 3;
/* p will be 2 for divs between 10 - 20 and odd divs under 32 */
else if (div < 20 || (div < 32 && (div & 1)))
*p = 2;
/* p will be 1 for even divs under 32, divs under 40 and odd pairs
* of divs between 40-62 */
else if (div < 40 || (div < 64 && (div & 2)))
*p = 1;
/* any other entries have p = 0 */
else
*p = 0;
/* calculate a suitable n based on k and p */
div <<= *p;
div /= (*k + 1);
*n = div / 4;
}
/**
* sunxi_get_apb1_factors() - calculates m, p factors for APB1
* APB1 rate is calculated as follows
* rate = (parent_rate >> p) / (m + 1);
*/
static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate,
u8 *n, u8 *k, u8 *m, u8 *p)
{
u8 calcm, calcp;
if (parent_rate < *freq)
*freq = parent_rate;
parent_rate = (parent_rate + (*freq - 1)) / *freq;
/* Invalid rate! */
if (parent_rate > 32)
return;
if (parent_rate <= 4)
calcp = 0;
else if (parent_rate <= 8)
calcp = 1;
else if (parent_rate <= 16)
calcp = 2;
else
calcp = 3;
calcm = (parent_rate >> calcp) - 1;
*freq = (parent_rate >> calcp) / (calcm + 1);
/* we were called to round the frequency, we can now return */
if (n == NULL)
return;
*m = calcm;
*p = calcp;
}
/**
* sunxi_factors_clk_setup() - Setup function for factor clocks
*/
struct factors_data {
struct clk_factors_config *table;
void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p);
};
static struct clk_factors_config pll1_config = {
.nshift = 8,
.nwidth = 5,
.kshift = 4,
.kwidth = 2,
.mshift = 0,
.mwidth = 2,
.pshift = 16,
.pwidth = 2,
};
static struct clk_factors_config apb1_config = {
.mshift = 0,
.mwidth = 5,
.pshift = 16,
.pwidth = 2,
};
static const __initconst struct factors_data pll1_data = {
.table = &pll1_config,
.getter = sunxi_get_pll1_factors,
};
static const __initconst struct factors_data apb1_data = {
.table = &apb1_config,
.getter = sunxi_get_apb1_factors,
};
static void __init sunxi_factors_clk_setup(struct device_node *node,
struct factors_data *data)
{
struct clk *clk;
const char *clk_name = node->name;
const char *parent;
void *reg;
reg = of_iomap(node, 0);
parent = of_clk_get_parent_name(node, 0);
clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED,
reg, data->table, data->getter, &clk_lock);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
/**
* sunxi_mux_clk_setup() - Setup function for muxes
*/
#define SUNXI_MUX_GATE_WIDTH 2
struct mux_data {
u8 shift;
};
static const __initconst struct mux_data cpu_data = {
.shift = 16,
};
static const __initconst struct mux_data apb1_mux_data = {
.shift = 24,
};
static void __init sunxi_mux_clk_setup(struct device_node *node,
struct mux_data *data)
{
struct clk *clk;
const char *clk_name = node->name;
const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL);
void *reg;
int i = 0;
reg = of_iomap(node, 0);
while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL)
i++;
clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg,
data->shift, SUNXI_MUX_GATE_WIDTH,
0, &clk_lock);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
/**
* sunxi_divider_clk_setup() - Setup function for simple divider clocks
*/
#define SUNXI_DIVISOR_WIDTH 2
struct div_data {
u8 shift;
u8 pow;
};
static const __initconst struct div_data axi_data = {
.shift = 0,
.pow = 0,
};
static const __initconst struct div_data ahb_data = {
.shift = 4,
.pow = 1,
};
static const __initconst struct div_data apb0_data = {
.shift = 8,
.pow = 1,
};
static void __init sunxi_divider_clk_setup(struct device_node *node,
struct div_data *data)
{
struct clk *clk;
const char *clk_name = node->name;
const char *clk_parent;
void *reg;
reg = of_iomap(node, 0);
clk_parent = of_clk_get_parent_name(node, 0);
clk = clk_register_divider(NULL, clk_name, clk_parent, 0,
reg, data->shift, SUNXI_DIVISOR_WIDTH,
data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0,
&clk_lock);
if (clk) {
of_clk_add_provider(node, of_clk_src_simple_get, clk);
clk_register_clkdev(clk, clk_name, NULL);
}
}
/* Matches for of_clk_init */
static const __initconst struct of_device_id clk_match[] = {
{.compatible = "fixed-clock", .data = of_fixed_clk_setup,},
{.compatible = "allwinner,sun4i-osc-clk", .data = sunxi_osc_clk_setup,},
{}
};
/* Matches for factors clocks */
static const __initconst struct of_device_id clk_factors_match[] = {
{.compatible = "allwinner,sun4i-pll1-clk", .data = &pll1_data,},
{.compatible = "allwinner,sun4i-apb1-clk", .data = &apb1_data,},
{}
};
/* Matches for divider clocks */
static const __initconst struct of_device_id clk_div_match[] = {
{.compatible = "allwinner,sun4i-axi-clk", .data = &axi_data,},
{.compatible = "allwinner,sun4i-ahb-clk", .data = &ahb_data,},
{.compatible = "allwinner,sun4i-apb0-clk", .data = &apb0_data,},
{}
};
/* Matches for mux clocks */
static const __initconst struct of_device_id clk_mux_match[] = {
{.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,},
{.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,},
{}
};
static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match,
void *function)
{
struct device_node *np;
const struct div_data *data;
const struct of_device_id *match;
void (*setup_function)(struct device_node *, const void *) = function;
for_each_matching_node(np, clk_match) {
match = of_match_node(clk_match, np);
data = match->data;
setup_function(np, data);
}
}
void __init sunxi_init_clocks(void)
{
/* Register all the simple sunxi clocks on DT */
of_clk_init(clk_match);
/* Register factor clocks */
of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup);
/* Register divider clocks */
of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup);
/* Register mux clocks */
of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup);
}

View file

@ -9,3 +9,4 @@ obj-y += clk-super.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clk-tegra20.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += clk-tegra30.o
obj-$(CONFIG_ARCH_TEGRA_114_SOC) += clk-tegra114.o

View file

@ -41,7 +41,9 @@ static DEFINE_SPINLOCK(periph_ref_lock);
#define write_rst_clr(val, gate) \
writel_relaxed(val, gate->clk_base + (gate->regs->rst_clr_reg))
#define periph_clk_to_bit(periph) (1 << (gate->clk_num % 32))
#define periph_clk_to_bit(gate) (1 << (gate->clk_num % 32))
#define LVL2_CLK_GATE_OVRE 0x554
/* Peripheral gate clock ops */
static int clk_periph_is_enabled(struct clk_hw *hw)
@ -83,6 +85,13 @@ static int clk_periph_enable(struct clk_hw *hw)
}
}
if (gate->flags & TEGRA_PERIPH_WAR_1005168) {
writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);
writel_relaxed(BIT(22), gate->clk_base + LVL2_CLK_GATE_OVRE);
udelay(1);
writel_relaxed(0, gate->clk_base + LVL2_CLK_GATE_OVRE);
}
spin_unlock_irqrestore(&periph_ref_lock, flags);
return 0;

View file

@ -16,6 +16,7 @@
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/err.h>
@ -128,6 +129,7 @@ void tegra_periph_reset_deassert(struct clk *c)
tegra_periph_reset(gate, 0);
}
EXPORT_SYMBOL(tegra_periph_reset_deassert);
void tegra_periph_reset_assert(struct clk *c)
{
@ -147,6 +149,7 @@ void tegra_periph_reset_assert(struct clk *c)
tegra_periph_reset(gate, 1);
}
EXPORT_SYMBOL(tegra_periph_reset_assert);
const struct clk_ops tegra_clk_periph_ops = {
.get_parent = clk_periph_get_parent,
@ -170,14 +173,15 @@ const struct clk_ops tegra_clk_periph_nodiv_ops = {
static struct clk *_tegra_clk_register_periph(const char *name,
const char **parent_names, int num_parents,
struct tegra_clk_periph *periph,
void __iomem *clk_base, u32 offset, bool div)
void __iomem *clk_base, u32 offset, bool div,
unsigned long flags)
{
struct clk *clk;
struct clk_init_data init;
init.name = name;
init.ops = div ? &tegra_clk_periph_ops : &tegra_clk_periph_nodiv_ops;
init.flags = div ? 0 : CLK_SET_RATE_PARENT;
init.flags = flags;
init.parent_names = parent_names;
init.num_parents = num_parents;
@ -202,10 +206,10 @@ static struct clk *_tegra_clk_register_periph(const char *name,
struct clk *tegra_clk_register_periph(const char *name,
const char **parent_names, int num_parents,
struct tegra_clk_periph *periph, void __iomem *clk_base,
u32 offset)
u32 offset, unsigned long flags)
{
return _tegra_clk_register_periph(name, parent_names, num_parents,
periph, clk_base, offset, true);
periph, clk_base, offset, true, flags);
}
struct clk *tegra_clk_register_periph_nodiv(const char *name,
@ -214,5 +218,5 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name,
u32 offset)
{
return _tegra_clk_register_periph(name, parent_names, num_parents,
periph, clk_base, offset, false);
periph, clk_base, offset, false, CLK_SET_RATE_PARENT);
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -86,8 +86,8 @@
#define PLLE_BASE 0xe8
#define PLLE_MISC 0xec
#define PLL_BASE_LOCK 27
#define PLLE_MISC_LOCK 11
#define PLL_BASE_LOCK BIT(27)
#define PLLE_MISC_LOCK BIT(11)
#define PLL_MISC_LOCK_ENABLE 18
#define PLLDU_MISC_LOCK_ENABLE 22
@ -236,7 +236,7 @@ enum tegra20_clk {
dvc, dsi, mipi = 50, hdmi, csi, tvdac, i2c2, uartc, emc = 57, usb2,
usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3,
pex, owr, afi, csite, pcie_xclk, avpucq = 75, la, irama = 84, iramb,
iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev1, cdev2,
iramc, iramd, cram2, audio_2x, clk_d, csus = 92, cdev2, cdev1,
uartb = 96, vfir, spdif_in, spdif_out, vi, vi_sensor, tvo, cve,
osc, clk_32k, clk_m, sclk, cclk, hclk, pclk, blink, pll_a, pll_a_out0,
pll_c, pll_c_out1, pll_d, pll_d_out0, pll_e, pll_m, pll_m_out1,
@ -248,125 +248,125 @@ static struct clk *clks[clk_max];
static struct clk_onecell_data clk_data;
static struct tegra_clk_pll_freq_table pll_c_freq_table[] = {
{ 12000000, 600000000, 600, 12, 1, 8 },
{ 13000000, 600000000, 600, 13, 1, 8 },
{ 19200000, 600000000, 500, 16, 1, 6 },
{ 26000000, 600000000, 600, 26, 1, 8 },
{ 12000000, 600000000, 600, 12, 0, 8 },
{ 13000000, 600000000, 600, 13, 0, 8 },
{ 19200000, 600000000, 500, 16, 0, 6 },
{ 26000000, 600000000, 600, 26, 0, 8 },
{ 0, 0, 0, 0, 0, 0 },
};
static struct tegra_clk_pll_freq_table pll_m_freq_table[] = {
{ 12000000, 666000000, 666, 12, 1, 8},
{ 13000000, 666000000, 666, 13, 1, 8},
{ 19200000, 666000000, 555, 16, 1, 8},
{ 26000000, 666000000, 666, 26, 1, 8},
{ 12000000, 600000000, 600, 12, 1, 8},
{ 13000000, 600000000, 600, 13, 1, 8},
{ 19200000, 600000000, 375, 12, 1, 6},
{ 26000000, 600000000, 600, 26, 1, 8},
{ 12000000, 666000000, 666, 12, 0, 8},
{ 13000000, 666000000, 666, 13, 0, 8},
{ 19200000, 666000000, 555, 16, 0, 8},
{ 26000000, 666000000, 666, 26, 0, 8},
{ 12000000, 600000000, 600, 12, 0, 8},
{ 13000000, 600000000, 600, 13, 0, 8},
{ 19200000, 600000000, 375, 12, 0, 6},
{ 26000000, 600000000, 600, 26, 0, 8},
{ 0, 0, 0, 0, 0, 0 },
};
static struct tegra_clk_pll_freq_table pll_p_freq_table[] = {
{ 12000000, 216000000, 432, 12, 2, 8},
{ 13000000, 216000000, 432, 13, 2, 8},
{ 19200000, 216000000, 90, 4, 2, 1},
{ 26000000, 216000000, 432, 26, 2, 8},
{ 12000000, 432000000, 432, 12, 1, 8},
{ 13000000, 432000000, 432, 13, 1, 8},
{ 19200000, 432000000, 90, 4, 1, 1},
{ 26000000, 432000000, 432, 26, 1, 8},
{ 12000000, 216000000, 432, 12, 1, 8},
{ 13000000, 216000000, 432, 13, 1, 8},
{ 19200000, 216000000, 90, 4, 1, 1},
{ 26000000, 216000000, 432, 26, 1, 8},
{ 12000000, 432000000, 432, 12, 0, 8},
{ 13000000, 432000000, 432, 13, 0, 8},
{ 19200000, 432000000, 90, 4, 0, 1},
{ 26000000, 432000000, 432, 26, 0, 8},
{ 0, 0, 0, 0, 0, 0 },
};
static struct tegra_clk_pll_freq_table pll_a_freq_table[] = {
{ 28800000, 56448000, 49, 25, 1, 1},
{ 28800000, 73728000, 64, 25, 1, 1},
{ 28800000, 24000000, 5, 6, 1, 1},
{ 28800000, 56448000, 49, 25, 0, 1},
{ 28800000, 73728000, 64, 25, 0, 1},
{ 28800000, 24000000, 5, 6, 0, 1},
{ 0, 0, 0, 0, 0, 0 },
};
static struct tegra_clk_pll_freq_table pll_d_freq_table[] = {
{ 12000000, 216000000, 216, 12, 1, 4},
{ 13000000, 216000000, 216, 13, 1, 4},
{ 19200000, 216000000, 135, 12, 1, 3},
{ 26000000, 216000000, 216, 26, 1, 4},
{ 12000000, 216000000, 216, 12, 0, 4},
{ 13000000, 216000000, 216, 13, 0, 4},
{ 19200000, 216000000, 135, 12, 0, 3},
{ 26000000, 216000000, 216, 26, 0, 4},
{ 12000000, 594000000, 594, 12, 1, 8},
{ 13000000, 594000000, 594, 13, 1, 8},
{ 19200000, 594000000, 495, 16, 1, 8},
{ 26000000, 594000000, 594, 26, 1, 8},
{ 12000000, 594000000, 594, 12, 0, 8},
{ 13000000, 594000000, 594, 13, 0, 8},
{ 19200000, 594000000, 495, 16, 0, 8},
{ 26000000, 594000000, 594, 26, 0, 8},
{ 12000000, 1000000000, 1000, 12, 1, 12},
{ 13000000, 1000000000, 1000, 13, 1, 12},
{ 19200000, 1000000000, 625, 12, 1, 8},
{ 26000000, 1000000000, 1000, 26, 1, 12},
{ 12000000, 1000000000, 1000, 12, 0, 12},
{ 13000000, 1000000000, 1000, 13, 0, 12},
{ 19200000, 1000000000, 625, 12, 0, 8},
{ 26000000, 1000000000, 1000, 26, 0, 12},
{ 0, 0, 0, 0, 0, 0 },
};
static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
{ 12000000, 480000000, 960, 12, 2, 0},
{ 13000000, 480000000, 960, 13, 2, 0},
{ 19200000, 480000000, 200, 4, 2, 0},
{ 26000000, 480000000, 960, 26, 2, 0},
{ 12000000, 480000000, 960, 12, 0, 0},
{ 13000000, 480000000, 960, 13, 0, 0},
{ 19200000, 480000000, 200, 4, 0, 0},
{ 26000000, 480000000, 960, 26, 0, 0},
{ 0, 0, 0, 0, 0, 0 },
};
static struct tegra_clk_pll_freq_table pll_x_freq_table[] = {
/* 1 GHz */
{ 12000000, 1000000000, 1000, 12, 1, 12},
{ 13000000, 1000000000, 1000, 13, 1, 12},
{ 19200000, 1000000000, 625, 12, 1, 8},
{ 26000000, 1000000000, 1000, 26, 1, 12},
{ 12000000, 1000000000, 1000, 12, 0, 12},
{ 13000000, 1000000000, 1000, 13, 0, 12},
{ 19200000, 1000000000, 625, 12, 0, 8},
{ 26000000, 1000000000, 1000, 26, 0, 12},
/* 912 MHz */
{ 12000000, 912000000, 912, 12, 1, 12},
{ 13000000, 912000000, 912, 13, 1, 12},
{ 19200000, 912000000, 760, 16, 1, 8},
{ 26000000, 912000000, 912, 26, 1, 12},
{ 12000000, 912000000, 912, 12, 0, 12},
{ 13000000, 912000000, 912, 13, 0, 12},
{ 19200000, 912000000, 760, 16, 0, 8},
{ 26000000, 912000000, 912, 26, 0, 12},
/* 816 MHz */
{ 12000000, 816000000, 816, 12, 1, 12},
{ 13000000, 816000000, 816, 13, 1, 12},
{ 19200000, 816000000, 680, 16, 1, 8},
{ 26000000, 816000000, 816, 26, 1, 12},
{ 12000000, 816000000, 816, 12, 0, 12},
{ 13000000, 816000000, 816, 13, 0, 12},
{ 19200000, 816000000, 680, 16, 0, 8},
{ 26000000, 816000000, 816, 26, 0, 12},
/* 760 MHz */
{ 12000000, 760000000, 760, 12, 1, 12},
{ 13000000, 760000000, 760, 13, 1, 12},
{ 19200000, 760000000, 950, 24, 1, 8},
{ 26000000, 760000000, 760, 26, 1, 12},
{ 12000000, 760000000, 760, 12, 0, 12},
{ 13000000, 760000000, 760, 13, 0, 12},
{ 19200000, 760000000, 950, 24, 0, 8},
{ 26000000, 760000000, 760, 26, 0, 12},
/* 750 MHz */
{ 12000000, 750000000, 750, 12, 1, 12},
{ 13000000, 750000000, 750, 13, 1, 12},
{ 19200000, 750000000, 625, 16, 1, 8},
{ 26000000, 750000000, 750, 26, 1, 12},
{ 12000000, 750000000, 750, 12, 0, 12},
{ 13000000, 750000000, 750, 13, 0, 12},
{ 19200000, 750000000, 625, 16, 0, 8},
{ 26000000, 750000000, 750, 26, 0, 12},
/* 608 MHz */
{ 12000000, 608000000, 608, 12, 1, 12},
{ 13000000, 608000000, 608, 13, 1, 12},
{ 19200000, 608000000, 380, 12, 1, 8},
{ 26000000, 608000000, 608, 26, 1, 12},
{ 12000000, 608000000, 608, 12, 0, 12},
{ 13000000, 608000000, 608, 13, 0, 12},
{ 19200000, 608000000, 380, 12, 0, 8},
{ 26000000, 608000000, 608, 26, 0, 12},
/* 456 MHz */
{ 12000000, 456000000, 456, 12, 1, 12},
{ 13000000, 456000000, 456, 13, 1, 12},
{ 19200000, 456000000, 380, 16, 1, 8},
{ 26000000, 456000000, 456, 26, 1, 12},
{ 12000000, 456000000, 456, 12, 0, 12},
{ 13000000, 456000000, 456, 13, 0, 12},
{ 19200000, 456000000, 380, 16, 0, 8},
{ 26000000, 456000000, 456, 26, 0, 12},
/* 312 MHz */
{ 12000000, 312000000, 312, 12, 1, 12},
{ 13000000, 312000000, 312, 13, 1, 12},
{ 19200000, 312000000, 260, 16, 1, 8},
{ 26000000, 312000000, 312, 26, 1, 12},
{ 12000000, 312000000, 312, 12, 0, 12},
{ 13000000, 312000000, 312, 13, 0, 12},
{ 19200000, 312000000, 260, 16, 0, 8},
{ 26000000, 312000000, 312, 26, 0, 12},
{ 0, 0, 0, 0, 0, 0 },
};
static struct tegra_clk_pll_freq_table pll_e_freq_table[] = {
{ 12000000, 100000000, 200, 24, 1, 0 },
{ 12000000, 100000000, 200, 24, 0, 0 },
{ 0, 0, 0, 0, 0, 0 },
};
@ -380,7 +380,7 @@ static struct tegra_clk_pll_params pll_c_params = {
.vco_max = 1400000000,
.base_reg = PLLC_BASE,
.misc_reg = PLLC_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
.lock_delay = 300,
};
@ -394,7 +394,7 @@ static struct tegra_clk_pll_params pll_m_params = {
.vco_max = 1200000000,
.base_reg = PLLM_BASE,
.misc_reg = PLLM_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
.lock_delay = 300,
};
@ -408,7 +408,7 @@ static struct tegra_clk_pll_params pll_p_params = {
.vco_max = 1400000000,
.base_reg = PLLP_BASE,
.misc_reg = PLLP_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
.lock_delay = 300,
};
@ -422,7 +422,7 @@ static struct tegra_clk_pll_params pll_a_params = {
.vco_max = 1400000000,
.base_reg = PLLA_BASE,
.misc_reg = PLLA_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
.lock_delay = 300,
};
@ -436,11 +436,17 @@ static struct tegra_clk_pll_params pll_d_params = {
.vco_max = 1000000000,
.base_reg = PLLD_BASE,
.misc_reg = PLLD_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
.lock_delay = 1000,
};
static struct pdiv_map pllu_p[] = {
{ .pdiv = 1, .hw_val = 1 },
{ .pdiv = 2, .hw_val = 0 },
{ .pdiv = 0, .hw_val = 0 },
};
static struct tegra_clk_pll_params pll_u_params = {
.input_min = 2000000,
.input_max = 40000000,
@ -450,9 +456,10 @@ static struct tegra_clk_pll_params pll_u_params = {
.vco_max = 960000000,
.base_reg = PLLU_BASE,
.misc_reg = PLLU_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
.lock_delay = 1000,
.pdiv_tohw = pllu_p,
};
static struct tegra_clk_pll_params pll_x_params = {
@ -464,7 +471,7 @@ static struct tegra_clk_pll_params pll_x_params = {
.vco_max = 1200000000,
.base_reg = PLLX_BASE,
.misc_reg = PLLX_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
.lock_delay = 300,
};
@ -478,7 +485,7 @@ static struct tegra_clk_pll_params pll_e_params = {
.vco_max = 0,
.base_reg = PLLE_BASE,
.misc_reg = PLLE_MISC,
.lock_bit_idx = PLLE_MISC_LOCK,
.lock_mask = PLLE_MISC_LOCK,
.lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE,
.lock_delay = 0,
};
@ -711,8 +718,8 @@ static void tegra20_pll_init(void)
}
static const char *cclk_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m",
"pll_p_cclk", "pll_p_out4_cclk",
"pll_p_out3_cclk", "clk_d", "pll_x" };
"pll_p", "pll_p_out4",
"pll_p_out3", "clk_d", "pll_x" };
static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4",
"pll_p_out3", "pll_p_out2", "clk_d",
"clk_32k", "pll_m_out1" };
@ -721,38 +728,6 @@ static void tegra20_super_clk_init(void)
{
struct clk *clk;
/*
* DIV_U71 dividers for CCLK, these dividers are used only
* if parent clock is fixed rate.
*/
/*
* Clock input to cclk divided from pll_p using
* U71 divider of cclk.
*/
clk = tegra_clk_register_divider("pll_p_cclk", "pll_p",
clk_base + SUPER_CCLK_DIVIDER, 0,
TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
clk_register_clkdev(clk, "pll_p_cclk", NULL);
/*
* Clock input to cclk divided from pll_p_out3 using
* U71 divider of cclk.
*/
clk = tegra_clk_register_divider("pll_p_out3_cclk", "pll_p_out3",
clk_base + SUPER_CCLK_DIVIDER, 0,
TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
clk_register_clkdev(clk, "pll_p_out3_cclk", NULL);
/*
* Clock input to cclk divided from pll_p_out4 using
* U71 divider of cclk.
*/
clk = tegra_clk_register_divider("pll_p_out4_cclk", "pll_p_out4",
clk_base + SUPER_CCLK_DIVIDER, 0,
TEGRA_DIVIDER_INT, 16, 8, 1, NULL);
clk_register_clkdev(clk, "pll_p_out4_cclk", NULL);
/* CCLK */
clk = tegra_clk_register_super_mux("cclk", cclk_parents,
ARRAY_SIZE(cclk_parents), CLK_SET_RATE_PARENT,
@ -1044,7 +1019,7 @@ static void __init tegra20_periph_clk_init(void)
data = &tegra_periph_clk_list[i];
clk = tegra_clk_register_periph(data->name, data->parent_names,
data->num_parents, &data->periph,
clk_base, data->offset);
clk_base, data->offset, data->flags);
clk_register_clkdev(clk, data->con_id, data->dev_id);
clks[data->clk_id] = clk;
}
@ -1279,9 +1254,16 @@ static __initdata struct tegra_clk_init_table init_table[] = {
{host1x, pll_c, 150000000, 0},
{disp1, pll_p, 600000000, 0},
{disp2, pll_p, 600000000, 0},
{gr2d, pll_c, 300000000, 0},
{gr3d, pll_c, 300000000, 0},
{clk_max, clk_max, 0, 0}, /* This MUST be the last entry */
};
static void __init tegra20_clock_apply_init_table(void)
{
tegra_init_from_table(init_table, clks, clk_max);
}
/*
* Some clocks may be used by different drivers depending on the board
* configuration. List those here to register them twice in the clock lookup
@ -1348,7 +1330,7 @@ void __init tegra20_clock_init(struct device_node *np)
clk_data.clk_num = ARRAY_SIZE(clks);
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
tegra_init_from_table(init_table, clks, clk_max);
tegra_clk_apply_init_table = tegra20_clock_apply_init_table;
tegra_cpu_car_ops = &tegra20_cpu_car_ops;
}

View file

@ -116,8 +116,8 @@
#define PLLDU_MISC_LOCK_ENABLE 22
#define PLLE_MISC_LOCK_ENABLE 9
#define PLL_BASE_LOCK 27
#define PLLE_MISC_LOCK 11
#define PLL_BASE_LOCK BIT(27)
#define PLLE_MISC_LOCK BIT(11)
#define PLLE_AUX 0x48c
#define PLLC_OUT 0x84
@ -330,7 +330,7 @@ enum tegra30_clk {
usb3, mpe, vde, bsea, bsev, speedo, uartd, uarte, i2c3, sbc4, sdmmc3,
pcie, owr, afi, csite, pciex, avpucq, la, dtv = 79, ndspeed, i2cslow,
dsib, irama = 84, iramb, iramc, iramd, cram2, audio_2x = 90, csus = 92,
cdev1, cdev2, cpu_g = 96, cpu_lp, gr3d2, mselect, tsensor, i2s3, i2s4,
cdev2, cdev1, cpu_g = 96, cpu_lp, gr3d2, mselect, tsensor, i2s3, i2s4,
i2c4, sbc5, sbc6, d_audio, apbif, dam0, dam1, dam2, hda2codec_2x,
atomics, audio0_2x, audio1_2x, audio2_2x, audio3_2x, audio4_2x,
spdif_2x, actmon, extern1, extern2, extern3, sata_oob, sata, hda,
@ -374,164 +374,170 @@ static const struct utmi_clk_param utmi_parameters[] = {
};
static struct tegra_clk_pll_freq_table pll_c_freq_table[] = {
{ 12000000, 1040000000, 520, 6, 1, 8},
{ 13000000, 1040000000, 480, 6, 1, 8},
{ 16800000, 1040000000, 495, 8, 1, 8}, /* actual: 1039.5 MHz */
{ 19200000, 1040000000, 325, 6, 1, 6},
{ 26000000, 1040000000, 520, 13, 1, 8},
{ 12000000, 1040000000, 520, 6, 0, 8},
{ 13000000, 1040000000, 480, 6, 0, 8},
{ 16800000, 1040000000, 495, 8, 0, 8}, /* actual: 1039.5 MHz */
{ 19200000, 1040000000, 325, 6, 0, 6},
{ 26000000, 1040000000, 520, 13, 0, 8},
{ 12000000, 832000000, 416, 6, 1, 8},
{ 13000000, 832000000, 832, 13, 1, 8},
{ 16800000, 832000000, 396, 8, 1, 8}, /* actual: 831.6 MHz */
{ 19200000, 832000000, 260, 6, 1, 8},
{ 26000000, 832000000, 416, 13, 1, 8},
{ 12000000, 832000000, 416, 6, 0, 8},
{ 13000000, 832000000, 832, 13, 0, 8},
{ 16800000, 832000000, 396, 8, 0, 8}, /* actual: 831.6 MHz */
{ 19200000, 832000000, 260, 6, 0, 8},
{ 26000000, 832000000, 416, 13, 0, 8},
{ 12000000, 624000000, 624, 12, 1, 8},
{ 13000000, 624000000, 624, 13, 1, 8},
{ 16800000, 600000000, 520, 14, 1, 8},
{ 19200000, 624000000, 520, 16, 1, 8},
{ 26000000, 624000000, 624, 26, 1, 8},
{ 12000000, 624000000, 624, 12, 0, 8},
{ 13000000, 624000000, 624, 13, 0, 8},
{ 16800000, 600000000, 520, 14, 0, 8},
{ 19200000, 624000000, 520, 16, 0, 8},
{ 26000000, 624000000, 624, 26, 0, 8},
{ 12000000, 600000000, 600, 12, 1, 8},
{ 13000000, 600000000, 600, 13, 1, 8},
{ 16800000, 600000000, 500, 14, 1, 8},
{ 19200000, 600000000, 375, 12, 1, 6},
{ 26000000, 600000000, 600, 26, 1, 8},
{ 12000000, 600000000, 600, 12, 0, 8},
{ 13000000, 600000000, 600, 13, 0, 8},
{ 16800000, 600000000, 500, 14, 0, 8},
{ 19200000, 600000000, 375, 12, 0, 6},
{ 26000000, 600000000, 600, 26, 0, 8},
{ 12000000, 520000000, 520, 12, 1, 8},
{ 13000000, 520000000, 520, 13, 1, 8},
{ 16800000, 520000000, 495, 16, 1, 8}, /* actual: 519.75 MHz */
{ 19200000, 520000000, 325, 12, 1, 6},
{ 26000000, 520000000, 520, 26, 1, 8},
{ 12000000, 520000000, 520, 12, 0, 8},
{ 13000000, 520000000, 520, 13, 0, 8},
{ 16800000, 520000000, 495, 16, 0, 8}, /* actual: 519.75 MHz */
{ 19200000, 520000000, 325, 12, 0, 6},
{ 26000000, 520000000, 520, 26, 0, 8},
{ 12000000, 416000000, 416, 12, 1, 8},
{ 13000000, 416000000, 416, 13, 1, 8},
{ 16800000, 416000000, 396, 16, 1, 8}, /* actual: 415.8 MHz */
{ 19200000, 416000000, 260, 12, 1, 6},
{ 26000000, 416000000, 416, 26, 1, 8},
{ 12000000, 416000000, 416, 12, 0, 8},
{ 13000000, 416000000, 416, 13, 0, 8},
{ 16800000, 416000000, 396, 16, 0, 8}, /* actual: 415.8 MHz */
{ 19200000, 416000000, 260, 12, 0, 6},
{ 26000000, 416000000, 416, 26, 0, 8},
{ 0, 0, 0, 0, 0, 0 },
};
static struct tegra_clk_pll_freq_table pll_m_freq_table[] = {
{ 12000000, 666000000, 666, 12, 1, 8},
{ 13000000, 666000000, 666, 13, 1, 8},
{ 16800000, 666000000, 555, 14, 1, 8},
{ 19200000, 666000000, 555, 16, 1, 8},
{ 26000000, 666000000, 666, 26, 1, 8},
{ 12000000, 600000000, 600, 12, 1, 8},
{ 13000000, 600000000, 600, 13, 1, 8},
{ 16800000, 600000000, 500, 14, 1, 8},
{ 19200000, 600000000, 375, 12, 1, 6},
{ 26000000, 600000000, 600, 26, 1, 8},
{ 12000000, 666000000, 666, 12, 0, 8},
{ 13000000, 666000000, 666, 13, 0, 8},
{ 16800000, 666000000, 555, 14, 0, 8},
{ 19200000, 666000000, 555, 16, 0, 8},
{ 26000000, 666000000, 666, 26, 0, 8},
{ 12000000, 600000000, 600, 12, 0, 8},
{ 13000000, 600000000, 600, 13, 0, 8},
{ 16800000, 600000000, 500, 14, 0, 8},
{ 19200000, 600000000, 375, 12, 0, 6},
{ 26000000, 600000000, 600, 26, 0, 8},
{ 0, 0, 0, 0, 0, 0 },
};
static struct tegra_clk_pll_freq_table pll_p_freq_table[] = {
{ 12000000, 216000000, 432, 12, 2, 8},
{ 13000000, 216000000, 432, 13, 2, 8},
{ 16800000, 216000000, 360, 14, 2, 8},
{ 19200000, 216000000, 360, 16, 2, 8},
{ 26000000, 216000000, 432, 26, 2, 8},
{ 12000000, 216000000, 432, 12, 1, 8},
{ 13000000, 216000000, 432, 13, 1, 8},
{ 16800000, 216000000, 360, 14, 1, 8},
{ 19200000, 216000000, 360, 16, 1, 8},
{ 26000000, 216000000, 432, 26, 1, 8},
{ 0, 0, 0, 0, 0, 0 },
};
static struct tegra_clk_pll_freq_table pll_a_freq_table[] = {
{ 9600000, 564480000, 294, 5, 1, 4},
{ 9600000, 552960000, 288, 5, 1, 4},
{ 9600000, 24000000, 5, 2, 1, 1},
{ 9600000, 564480000, 294, 5, 0, 4},
{ 9600000, 552960000, 288, 5, 0, 4},
{ 9600000, 24000000, 5, 2, 0, 1},
{ 28800000, 56448000, 49, 25, 1, 1},
{ 28800000, 73728000, 64, 25, 1, 1},
{ 28800000, 24000000, 5, 6, 1, 1},
{ 28800000, 56448000, 49, 25, 0, 1},
{ 28800000, 73728000, 64, 25, 0, 1},
{ 28800000, 24000000, 5, 6, 0, 1},
{ 0, 0, 0, 0, 0, 0 },
};
static struct tegra_clk_pll_freq_table pll_d_freq_table[] = {
{ 12000000, 216000000, 216, 12, 1, 4},
{ 13000000, 216000000, 216, 13, 1, 4},
{ 16800000, 216000000, 180, 14, 1, 4},
{ 19200000, 216000000, 180, 16, 1, 4},
{ 26000000, 216000000, 216, 26, 1, 4},
{ 12000000, 216000000, 216, 12, 0, 4},
{ 13000000, 216000000, 216, 13, 0, 4},
{ 16800000, 216000000, 180, 14, 0, 4},
{ 19200000, 216000000, 180, 16, 0, 4},
{ 26000000, 216000000, 216, 26, 0, 4},
{ 12000000, 594000000, 594, 12, 1, 8},
{ 13000000, 594000000, 594, 13, 1, 8},
{ 16800000, 594000000, 495, 14, 1, 8},
{ 19200000, 594000000, 495, 16, 1, 8},
{ 26000000, 594000000, 594, 26, 1, 8},
{ 12000000, 594000000, 594, 12, 0, 8},
{ 13000000, 594000000, 594, 13, 0, 8},
{ 16800000, 594000000, 495, 14, 0, 8},
{ 19200000, 594000000, 495, 16, 0, 8},
{ 26000000, 594000000, 594, 26, 0, 8},
{ 12000000, 1000000000, 1000, 12, 1, 12},
{ 13000000, 1000000000, 1000, 13, 1, 12},
{ 19200000, 1000000000, 625, 12, 1, 8},
{ 26000000, 1000000000, 1000, 26, 1, 12},
{ 12000000, 1000000000, 1000, 12, 0, 12},
{ 13000000, 1000000000, 1000, 13, 0, 12},
{ 19200000, 1000000000, 625, 12, 0, 8},
{ 26000000, 1000000000, 1000, 26, 0, 12},
{ 0, 0, 0, 0, 0, 0 },
};
static struct pdiv_map pllu_p[] = {
{ .pdiv = 1, .hw_val = 1 },
{ .pdiv = 2, .hw_val = 0 },
{ .pdiv = 0, .hw_val = 0 },
};
static struct tegra_clk_pll_freq_table pll_u_freq_table[] = {
{ 12000000, 480000000, 960, 12, 2, 12},
{ 13000000, 480000000, 960, 13, 2, 12},
{ 16800000, 480000000, 400, 7, 2, 5},
{ 19200000, 480000000, 200, 4, 2, 3},
{ 26000000, 480000000, 960, 26, 2, 12},
{ 12000000, 480000000, 960, 12, 0, 12},
{ 13000000, 480000000, 960, 13, 0, 12},
{ 16800000, 480000000, 400, 7, 0, 5},
{ 19200000, 480000000, 200, 4, 0, 3},
{ 26000000, 480000000, 960, 26, 0, 12},
{ 0, 0, 0, 0, 0, 0 },
};
static struct tegra_clk_pll_freq_table pll_x_freq_table[] = {
/* 1.7 GHz */
{ 12000000, 1700000000, 850, 6, 1, 8},
{ 13000000, 1700000000, 915, 7, 1, 8}, /* actual: 1699.2 MHz */
{ 16800000, 1700000000, 708, 7, 1, 8}, /* actual: 1699.2 MHz */
{ 19200000, 1700000000, 885, 10, 1, 8}, /* actual: 1699.2 MHz */
{ 26000000, 1700000000, 850, 13, 1, 8},
{ 12000000, 1700000000, 850, 6, 0, 8},
{ 13000000, 1700000000, 915, 7, 0, 8}, /* actual: 1699.2 MHz */
{ 16800000, 1700000000, 708, 7, 0, 8}, /* actual: 1699.2 MHz */
{ 19200000, 1700000000, 885, 10, 0, 8}, /* actual: 1699.2 MHz */
{ 26000000, 1700000000, 850, 13, 0, 8},
/* 1.6 GHz */
{ 12000000, 1600000000, 800, 6, 1, 8},
{ 13000000, 1600000000, 738, 6, 1, 8}, /* actual: 1599.0 MHz */
{ 16800000, 1600000000, 857, 9, 1, 8}, /* actual: 1599.7 MHz */
{ 19200000, 1600000000, 500, 6, 1, 8},
{ 26000000, 1600000000, 800, 13, 1, 8},
{ 12000000, 1600000000, 800, 6, 0, 8},
{ 13000000, 1600000000, 738, 6, 0, 8}, /* actual: 1599.0 MHz */
{ 16800000, 1600000000, 857, 9, 0, 8}, /* actual: 1599.7 MHz */
{ 19200000, 1600000000, 500, 6, 0, 8},
{ 26000000, 1600000000, 800, 13, 0, 8},
/* 1.5 GHz */
{ 12000000, 1500000000, 750, 6, 1, 8},
{ 13000000, 1500000000, 923, 8, 1, 8}, /* actual: 1499.8 MHz */
{ 16800000, 1500000000, 625, 7, 1, 8},
{ 19200000, 1500000000, 625, 8, 1, 8},
{ 26000000, 1500000000, 750, 13, 1, 8},
{ 12000000, 1500000000, 750, 6, 0, 8},
{ 13000000, 1500000000, 923, 8, 0, 8}, /* actual: 1499.8 MHz */
{ 16800000, 1500000000, 625, 7, 0, 8},
{ 19200000, 1500000000, 625, 8, 0, 8},
{ 26000000, 1500000000, 750, 13, 0, 8},
/* 1.4 GHz */
{ 12000000, 1400000000, 700, 6, 1, 8},
{ 13000000, 1400000000, 969, 9, 1, 8}, /* actual: 1399.7 MHz */
{ 16800000, 1400000000, 1000, 12, 1, 8},
{ 19200000, 1400000000, 875, 12, 1, 8},
{ 26000000, 1400000000, 700, 13, 1, 8},
{ 12000000, 1400000000, 700, 6, 0, 8},
{ 13000000, 1400000000, 969, 9, 0, 8}, /* actual: 1399.7 MHz */
{ 16800000, 1400000000, 1000, 12, 0, 8},
{ 19200000, 1400000000, 875, 12, 0, 8},
{ 26000000, 1400000000, 700, 13, 0, 8},
/* 1.3 GHz */
{ 12000000, 1300000000, 975, 9, 1, 8},
{ 13000000, 1300000000, 1000, 10, 1, 8},
{ 16800000, 1300000000, 928, 12, 1, 8}, /* actual: 1299.2 MHz */
{ 19200000, 1300000000, 812, 12, 1, 8}, /* actual: 1299.2 MHz */
{ 26000000, 1300000000, 650, 13, 1, 8},
{ 12000000, 1300000000, 975, 9, 0, 8},
{ 13000000, 1300000000, 1000, 10, 0, 8},
{ 16800000, 1300000000, 928, 12, 0, 8}, /* actual: 1299.2 MHz */
{ 19200000, 1300000000, 812, 12, 0, 8}, /* actual: 1299.2 MHz */
{ 26000000, 1300000000, 650, 13, 0, 8},
/* 1.2 GHz */
{ 12000000, 1200000000, 1000, 10, 1, 8},
{ 13000000, 1200000000, 923, 10, 1, 8}, /* actual: 1199.9 MHz */
{ 16800000, 1200000000, 1000, 14, 1, 8},
{ 19200000, 1200000000, 1000, 16, 1, 8},
{ 26000000, 1200000000, 600, 13, 1, 8},
{ 12000000, 1200000000, 1000, 10, 0, 8},
{ 13000000, 1200000000, 923, 10, 0, 8}, /* actual: 1199.9 MHz */
{ 16800000, 1200000000, 1000, 14, 0, 8},
{ 19200000, 1200000000, 1000, 16, 0, 8},
{ 26000000, 1200000000, 600, 13, 0, 8},
/* 1.1 GHz */
{ 12000000, 1100000000, 825, 9, 1, 8},
{ 13000000, 1100000000, 846, 10, 1, 8}, /* actual: 1099.8 MHz */
{ 16800000, 1100000000, 982, 15, 1, 8}, /* actual: 1099.8 MHz */
{ 19200000, 1100000000, 859, 15, 1, 8}, /* actual: 1099.5 MHz */
{ 26000000, 1100000000, 550, 13, 1, 8},
{ 12000000, 1100000000, 825, 9, 0, 8},
{ 13000000, 1100000000, 846, 10, 0, 8}, /* actual: 1099.8 MHz */
{ 16800000, 1100000000, 982, 15, 0, 8}, /* actual: 1099.8 MHz */
{ 19200000, 1100000000, 859, 15, 0, 8}, /* actual: 1099.5 MHz */
{ 26000000, 1100000000, 550, 13, 0, 8},
/* 1 GHz */
{ 12000000, 1000000000, 1000, 12, 1, 8},
{ 13000000, 1000000000, 1000, 13, 1, 8},
{ 16800000, 1000000000, 833, 14, 1, 8}, /* actual: 999.6 MHz */
{ 19200000, 1000000000, 625, 12, 1, 8},
{ 26000000, 1000000000, 1000, 26, 1, 8},
{ 12000000, 1000000000, 1000, 12, 0, 8},
{ 13000000, 1000000000, 1000, 13, 0, 8},
{ 16800000, 1000000000, 833, 14, 0, 8}, /* actual: 999.6 MHz */
{ 19200000, 1000000000, 625, 12, 0, 8},
{ 26000000, 1000000000, 1000, 26, 0, 8},
{ 0, 0, 0, 0, 0, 0 },
};
@ -553,7 +559,7 @@ static struct tegra_clk_pll_params pll_c_params = {
.vco_max = 1400000000,
.base_reg = PLLC_BASE,
.misc_reg = PLLC_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
.lock_delay = 300,
};
@ -567,7 +573,7 @@ static struct tegra_clk_pll_params pll_m_params = {
.vco_max = 1200000000,
.base_reg = PLLM_BASE,
.misc_reg = PLLM_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
.lock_delay = 300,
};
@ -581,7 +587,7 @@ static struct tegra_clk_pll_params pll_p_params = {
.vco_max = 1400000000,
.base_reg = PLLP_BASE,
.misc_reg = PLLP_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
.lock_delay = 300,
};
@ -595,7 +601,7 @@ static struct tegra_clk_pll_params pll_a_params = {
.vco_max = 1400000000,
.base_reg = PLLA_BASE,
.misc_reg = PLLA_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
.lock_delay = 300,
};
@ -609,7 +615,7 @@ static struct tegra_clk_pll_params pll_d_params = {
.vco_max = 1000000000,
.base_reg = PLLD_BASE,
.misc_reg = PLLD_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
.lock_delay = 1000,
};
@ -623,7 +629,7 @@ static struct tegra_clk_pll_params pll_d2_params = {
.vco_max = 1000000000,
.base_reg = PLLD2_BASE,
.misc_reg = PLLD2_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
.lock_delay = 1000,
};
@ -637,9 +643,10 @@ static struct tegra_clk_pll_params pll_u_params = {
.vco_max = 960000000,
.base_reg = PLLU_BASE,
.misc_reg = PLLU_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLLDU_MISC_LOCK_ENABLE,
.lock_delay = 1000,
.pdiv_tohw = pllu_p,
};
static struct tegra_clk_pll_params pll_x_params = {
@ -651,7 +658,7 @@ static struct tegra_clk_pll_params pll_x_params = {
.vco_max = 1700000000,
.base_reg = PLLX_BASE,
.misc_reg = PLLX_MISC,
.lock_bit_idx = PLL_BASE_LOCK,
.lock_mask = PLL_BASE_LOCK,
.lock_enable_bit_idx = PLL_MISC_LOCK_ENABLE,
.lock_delay = 300,
};
@ -665,7 +672,7 @@ static struct tegra_clk_pll_params pll_e_params = {
.vco_max = 2400000000U,
.base_reg = PLLE_BASE,
.misc_reg = PLLE_MISC,
.lock_bit_idx = PLLE_MISC_LOCK,
.lock_mask = PLLE_MISC_LOCK,
.lock_enable_bit_idx = PLLE_MISC_LOCK_ENABLE,
.lock_delay = 300,
};
@ -1661,7 +1668,7 @@ static void __init tegra30_periph_clk_init(void)
data = &tegra_periph_clk_list[i];
clk = tegra_clk_register_periph(data->name, data->parent_names,
data->num_parents, &data->periph,
clk_base, data->offset);
clk_base, data->offset, data->flags);
clk_register_clkdev(clk, data->con_id, data->dev_id);
clks[data->clk_id] = clk;
}
@ -1911,9 +1918,16 @@ static __initdata struct tegra_clk_init_table init_table[] = {
{disp1, pll_p, 600000000, 0},
{disp2, pll_p, 600000000, 0},
{twd, clk_max, 0, 1},
{gr2d, pll_c, 300000000, 0},
{gr3d, pll_c, 300000000, 0},
{clk_max, clk_max, 0, 0}, /* This MUST be the last entry. */
};
static void __init tegra30_clock_apply_init_table(void)
{
tegra_init_from_table(init_table, clks, clk_max);
}
/*
* Some clocks may be used by different drivers depending on the board
* configuration. List those here to register them twice in the clock lookup
@ -1987,7 +2001,7 @@ void __init tegra30_clock_init(struct device_node *np)
clk_data.clk_num = ARRAY_SIZE(clks);
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
tegra_init_from_table(init_table, clks, clk_max);
tegra_clk_apply_init_table = tegra30_clock_apply_init_table;
tegra_cpu_car_ops = &tegra30_cpu_car_ops;
}

View file

@ -22,7 +22,8 @@
#include "clk.h"
/* Global data of Tegra CPU CAR ops */
struct tegra_cpu_car_ops *tegra_cpu_car_ops;
static struct tegra_cpu_car_ops dummy_car_ops;
struct tegra_cpu_car_ops *tegra_cpu_car_ops = &dummy_car_ops;
void __init tegra_init_dup_clks(struct tegra_clk_duplicate *dup_list,
struct clk *clks[], int clk_max)
@ -76,6 +77,7 @@ void __init tegra_init_from_table(struct tegra_clk_init_table *tbl,
static const struct of_device_id tegra_dt_clk_match[] = {
{ .compatible = "nvidia,tegra20-car", .data = tegra20_clock_init },
{ .compatible = "nvidia,tegra30-car", .data = tegra30_clock_init },
{ .compatible = "nvidia,tegra114-car", .data = tegra114_clock_init },
{ }
};
@ -83,3 +85,13 @@ void __init tegra_clocks_init(void)
{
of_clk_init(tegra_dt_clk_match);
}
tegra_clk_apply_init_table_func tegra_clk_apply_init_table;
void __init tegra_clocks_apply_init_table(void)
{
if (!tegra_clk_apply_init_table)
return;
tegra_clk_apply_init_table();
}

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify it
@ -116,6 +116,17 @@ struct tegra_clk_pll_freq_table {
u8 cpcon;
};
/**
* struct pdiv_map - map post divider to hw value
*
* @pdiv: post divider
* @hw_val: value to be written to the PLL hw
*/
struct pdiv_map {
u8 pdiv;
u8 hw_val;
};
/**
* struct clk_pll_params - PLL parameters
*
@ -143,9 +154,18 @@ struct tegra_clk_pll_params {
u32 base_reg;
u32 misc_reg;
u32 lock_reg;
u32 lock_bit_idx;
u32 lock_mask;
u32 lock_enable_bit_idx;
u32 iddq_reg;
u32 iddq_bit_idx;
u32 aux_reg;
u32 dyn_ramp_reg;
u32 ext_misc_reg[3];
int stepa_shift;
int stepb_shift;
int lock_delay;
int max_p;
struct pdiv_map *pdiv_tohw;
};
/**
@ -182,12 +202,16 @@ struct tegra_clk_pll_params {
* TEGRA_PLL_FIXED - We are not supposed to change output frequency
* of some plls.
* TEGRA_PLLE_CONFIGURE - Configure PLLE when enabling.
* TEGRA_PLL_LOCK_MISC - Lock bit is in the misc register instead of the
* base register.
* TEGRA_PLL_BYPASS - PLL has bypass bit
* TEGRA_PLL_HAS_LOCK_ENABLE - PLL has bit to enable lock monitoring
*/
struct tegra_clk_pll {
struct clk_hw hw;
void __iomem *clk_base;
void __iomem *pmc;
u8 flags;
u32 flags;
unsigned long fixed_rate;
spinlock_t *lock;
u8 divn_shift;
@ -210,20 +234,64 @@ struct tegra_clk_pll {
#define TEGRA_PLLM BIT(5)
#define TEGRA_PLL_FIXED BIT(6)
#define TEGRA_PLLE_CONFIGURE BIT(7)
#define TEGRA_PLL_LOCK_MISC BIT(8)
#define TEGRA_PLL_BYPASS BIT(9)
#define TEGRA_PLL_HAS_LOCK_ENABLE BIT(10)
extern const struct clk_ops tegra_clk_pll_ops;
extern const struct clk_ops tegra_clk_plle_ops;
struct clk *tegra_clk_register_pll(const char *name, const char *parent_name,
void __iomem *clk_base, void __iomem *pmc,
unsigned long flags, unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params, u8 pll_flags,
struct tegra_clk_pll_params *pll_params, u32 pll_flags,
struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock);
struct clk *tegra_clk_register_plle(const char *name, const char *parent_name,
void __iomem *clk_base, void __iomem *pmc,
unsigned long flags, unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params, u8 pll_flags,
struct tegra_clk_pll_params *pll_params, u32 pll_flags,
struct tegra_clk_pll_freq_table *freq_table, spinlock_t *lock);
struct clk *tegra_clk_register_pllxc(const char *name, const char *parent_name,
void __iomem *clk_base, void __iomem *pmc,
unsigned long flags, unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params,
u32 pll_flags,
struct tegra_clk_pll_freq_table *freq_table,
spinlock_t *lock);
struct clk *tegra_clk_register_pllm(const char *name, const char *parent_name,
void __iomem *clk_base, void __iomem *pmc,
unsigned long flags, unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params,
u32 pll_flags,
struct tegra_clk_pll_freq_table *freq_table,
spinlock_t *lock);
struct clk *tegra_clk_register_pllc(const char *name, const char *parent_name,
void __iomem *clk_base, void __iomem *pmc,
unsigned long flags, unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params,
u32 pll_flags,
struct tegra_clk_pll_freq_table *freq_table,
spinlock_t *lock);
struct clk *tegra_clk_register_pllre(const char *name, const char *parent_name,
void __iomem *clk_base, void __iomem *pmc,
unsigned long flags, unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params,
u32 pll_flags,
struct tegra_clk_pll_freq_table *freq_table,
spinlock_t *lock, unsigned long parent_rate);
struct clk *tegra_clk_register_plle_tegra114(const char *name,
const char *parent_name,
void __iomem *clk_base, unsigned long flags,
unsigned long fixed_rate,
struct tegra_clk_pll_params *pll_params,
struct tegra_clk_pll_freq_table *freq_table,
spinlock_t *lock);
/**
* struct tegra_clk_pll_out - PLL divider down clock
*
@ -290,6 +358,7 @@ struct tegra_clk_periph_regs {
* TEGRA_PERIPH_ON_APB - If peripheral is in the APB bus then read the
* bus to flush the write operation in apb bus. This flag indicates
* that this peripheral is in apb bus.
* TEGRA_PERIPH_WAR_1005168 - Apply workaround for Tegra114 MSENC bug
*/
struct tegra_clk_periph_gate {
u32 magic;
@ -309,6 +378,7 @@ struct tegra_clk_periph_gate {
#define TEGRA_PERIPH_NO_RESET BIT(0)
#define TEGRA_PERIPH_MANUAL_RESET BIT(1)
#define TEGRA_PERIPH_ON_APB BIT(2)
#define TEGRA_PERIPH_WAR_1005168 BIT(3)
void tegra_periph_reset(struct tegra_clk_periph_gate *gate, bool assert);
extern const struct clk_ops tegra_clk_periph_gate_ops;
@ -349,21 +419,22 @@ extern const struct clk_ops tegra_clk_periph_ops;
struct clk *tegra_clk_register_periph(const char *name,
const char **parent_names, int num_parents,
struct tegra_clk_periph *periph, void __iomem *clk_base,
u32 offset);
u32 offset, unsigned long flags);
struct clk *tegra_clk_register_periph_nodiv(const char *name,
const char **parent_names, int num_parents,
struct tegra_clk_periph *periph, void __iomem *clk_base,
u32 offset);
#define TEGRA_CLK_PERIPH(_mux_shift, _mux_width, _mux_flags, \
#define TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, _mux_flags, \
_div_shift, _div_width, _div_frac_width, \
_div_flags, _clk_num, _enb_refcnt, _regs, \
_gate_flags) \
_gate_flags, _table) \
{ \
.mux = { \
.flags = _mux_flags, \
.shift = _mux_shift, \
.width = _mux_width, \
.mask = _mux_mask, \
.table = _table, \
}, \
.divider = { \
.flags = _div_flags, \
@ -391,28 +462,41 @@ struct tegra_periph_init_data {
u32 offset;
const char *con_id;
const char *dev_id;
unsigned long flags;
};
#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset, \
_mux_shift, _mux_width, _mux_flags, _div_shift, \
#define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\
_mux_shift, _mux_mask, _mux_flags, _div_shift, \
_div_width, _div_frac_width, _div_flags, _regs, \
_clk_num, _enb_refcnt, _gate_flags, _clk_id) \
_clk_num, _enb_refcnt, _gate_flags, _clk_id, _table,\
_flags) \
{ \
.name = _name, \
.clk_id = _clk_id, \
.parent_names = _parent_names, \
.num_parents = ARRAY_SIZE(_parent_names), \
.periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_width, \
.periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, \
_mux_flags, _div_shift, \
_div_width, _div_frac_width, \
_div_flags, _clk_num, \
_enb_refcnt, _regs, \
_gate_flags), \
_gate_flags, _table), \
.offset = _offset, \
.con_id = _con_id, \
.dev_id = _dev_id, \
.flags = _flags \
}
#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\
_mux_shift, _mux_width, _mux_flags, _div_shift, \
_div_width, _div_frac_width, _div_flags, _regs, \
_clk_num, _enb_refcnt, _gate_flags, _clk_id) \
TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\
_mux_shift, BIT(_mux_width) - 1, _mux_flags, \
_div_shift, _div_width, _div_frac_width, _div_flags, \
_regs, _clk_num, _enb_refcnt, _gate_flags, _clk_id,\
NULL, 0)
/**
* struct clk_super_mux - super clock
*
@ -499,4 +583,13 @@ void tegra30_clock_init(struct device_node *np);
static inline void tegra30_clock_init(struct device_node *np) {}
#endif /* CONFIG_ARCH_TEGRA_3x_SOC */
#ifdef CONFIG_ARCH_TEGRA_114_SOC
void tegra114_clock_init(struct device_node *np);
#else
static inline void tegra114_clock_init(struct device_node *np) {}
#endif /* CONFIG_ARCH_TEGRA114_SOC */
typedef void (*tegra_clk_apply_init_table_func)(void);
extern tegra_clk_apply_init_table_func tegra_clk_apply_init_table;
#endif /* TEGRA_CLK_H */

View file

@ -20,15 +20,23 @@
struct clk_prcmu {
struct clk_hw hw;
u8 cg_sel;
int is_prepared;
int is_enabled;
int opp_requested;
};
/* PRCMU clock operations. */
static int clk_prcmu_prepare(struct clk_hw *hw)
{
int ret;
struct clk_prcmu *clk = to_clk_prcmu(hw);
return prcmu_request_clock(clk->cg_sel, true);
ret = prcmu_request_clock(clk->cg_sel, true);
if (!ret)
clk->is_prepared = 1;
return ret;;
}
static void clk_prcmu_unprepare(struct clk_hw *hw)
@ -36,7 +44,15 @@ static void clk_prcmu_unprepare(struct clk_hw *hw)
struct clk_prcmu *clk = to_clk_prcmu(hw);
if (prcmu_request_clock(clk->cg_sel, false))
pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
hw->init->name);
__clk_get_name(hw->clk));
else
clk->is_prepared = 0;
}
static int clk_prcmu_is_prepared(struct clk_hw *hw)
{
struct clk_prcmu *clk = to_clk_prcmu(hw);
return clk->is_prepared;
}
static int clk_prcmu_enable(struct clk_hw *hw)
@ -79,58 +95,52 @@ static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate,
return prcmu_set_clock_rate(clk->cg_sel, rate);
}
static int request_ape_opp100(bool enable)
{
static int reqs;
int err = 0;
if (enable) {
if (!reqs)
err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
"clock", 100);
if (!err)
reqs++;
} else {
reqs--;
if (!reqs)
prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
"clock");
}
return err;
}
static int clk_prcmu_opp_prepare(struct clk_hw *hw)
{
int err;
struct clk_prcmu *clk = to_clk_prcmu(hw);
err = request_ape_opp100(true);
if (err) {
pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n",
__func__, hw->init->name);
return err;
if (!clk->opp_requested) {
err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP,
(char *)__clk_get_name(hw->clk),
100);
if (err) {
pr_err("clk_prcmu: %s fail req APE OPP for %s.\n",
__func__, __clk_get_name(hw->clk));
return err;
}
clk->opp_requested = 1;
}
err = prcmu_request_clock(clk->cg_sel, true);
if (err)
request_ape_opp100(false);
if (err) {
prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
(char *)__clk_get_name(hw->clk));
clk->opp_requested = 0;
return err;
}
return err;
clk->is_prepared = 1;
return 0;
}
static void clk_prcmu_opp_unprepare(struct clk_hw *hw)
{
struct clk_prcmu *clk = to_clk_prcmu(hw);
if (prcmu_request_clock(clk->cg_sel, false))
goto out_error;
if (request_ape_opp100(false))
goto out_error;
return;
if (prcmu_request_clock(clk->cg_sel, false)) {
pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
__clk_get_name(hw->clk));
return;
}
out_error:
pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
hw->init->name);
if (clk->opp_requested) {
prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP,
(char *)__clk_get_name(hw->clk));
clk->opp_requested = 0;
}
clk->is_prepared = 0;
}
static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
@ -138,38 +148,49 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
int err;
struct clk_prcmu *clk = to_clk_prcmu(hw);
err = prcmu_request_ape_opp_100_voltage(true);
if (err) {
pr_err("clk_prcmu: %s failed to request APE OPP VOLT for %s.\n",
__func__, hw->init->name);
return err;
if (!clk->opp_requested) {
err = prcmu_request_ape_opp_100_voltage(true);
if (err) {
pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n",
__func__, __clk_get_name(hw->clk));
return err;
}
clk->opp_requested = 1;
}
err = prcmu_request_clock(clk->cg_sel, true);
if (err)
if (err) {
prcmu_request_ape_opp_100_voltage(false);
clk->opp_requested = 0;
return err;
}
return err;
clk->is_prepared = 1;
return 0;
}
static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw)
{
struct clk_prcmu *clk = to_clk_prcmu(hw);
if (prcmu_request_clock(clk->cg_sel, false))
goto out_error;
if (prcmu_request_ape_opp_100_voltage(false))
goto out_error;
return;
if (prcmu_request_clock(clk->cg_sel, false)) {
pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
__clk_get_name(hw->clk));
return;
}
out_error:
pr_err("clk_prcmu: %s failed to disable %s.\n", __func__,
hw->init->name);
if (clk->opp_requested) {
prcmu_request_ape_opp_100_voltage(false);
clk->opp_requested = 0;
}
clk->is_prepared = 0;
}
static struct clk_ops clk_prcmu_scalable_ops = {
.prepare = clk_prcmu_prepare,
.unprepare = clk_prcmu_unprepare,
.is_prepared = clk_prcmu_is_prepared,
.enable = clk_prcmu_enable,
.disable = clk_prcmu_disable,
.is_enabled = clk_prcmu_is_enabled,
@ -181,6 +202,7 @@ static struct clk_ops clk_prcmu_scalable_ops = {
static struct clk_ops clk_prcmu_gate_ops = {
.prepare = clk_prcmu_prepare,
.unprepare = clk_prcmu_unprepare,
.is_prepared = clk_prcmu_is_prepared,
.enable = clk_prcmu_enable,
.disable = clk_prcmu_disable,
.is_enabled = clk_prcmu_is_enabled,
@ -202,6 +224,7 @@ static struct clk_ops clk_prcmu_rate_ops = {
static struct clk_ops clk_prcmu_opp_gate_ops = {
.prepare = clk_prcmu_opp_prepare,
.unprepare = clk_prcmu_opp_unprepare,
.is_prepared = clk_prcmu_is_prepared,
.enable = clk_prcmu_enable,
.disable = clk_prcmu_disable,
.is_enabled = clk_prcmu_is_enabled,
@ -211,6 +234,7 @@ static struct clk_ops clk_prcmu_opp_gate_ops = {
static struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
.prepare = clk_prcmu_opp_volt_prepare,
.unprepare = clk_prcmu_opp_volt_unprepare,
.is_prepared = clk_prcmu_is_prepared,
.enable = clk_prcmu_enable,
.disable = clk_prcmu_disable,
.is_enabled = clk_prcmu_is_enabled,
@ -242,7 +266,9 @@ static struct clk *clk_reg_prcmu(const char *name,
}
clk->cg_sel = cg_sel;
clk->is_prepared = 1;
clk->is_enabled = 1;
clk->opp_requested = 0;
/* "rate" can be used for changing the initial frequency */
if (rate)
prcmu_set_clock_rate(cg_sel, rate);