-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
clk: tegra: add Tegra specific clocks
Add Tegra specific clocks, pll, pll_out, peripheral, frac_divider, super. Signed-off-by: Prashant Gaikwad <pgaikwad@nvidia.com> [swarren: alloc sizeof(*foo) not sizeof(struct foo), add comments re: storing pointers to stack variables, make a timeout loop more idiomatic, use _clk_pll_disable() not clk_disable_pll() from _program_pll() to avoid redundant lock operations, unified tegra_clk_periph() and tegra_clk_periph_nodiv(), unified tegra_clk_pll{,e}, rename all clock registration functions so they don't have the same name as the clock structs, return -EINVAL from clk_plle_enable when matching table rate not found, pass ops to _tegra_clk_register_pll rather than a bool.] Acked-by: Mike Turquette <mturquette@linaro.org> Signed-off-by: Stephen Warren <swarren@nvidia.com>
- Loading branch information
Prashant Gaikwad
authored and
Stephen Warren
committed
Jan 28, 2013
1 parent
9598566
commit 8f8f484
Showing
11 changed files
with
2,126 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
obj-y += clk.o | ||
obj-y += clk-audio-sync.o | ||
obj-y += clk-divider.o | ||
obj-y += clk-periph.o | ||
obj-y += clk-periph-gate.o | ||
obj-y += clk-pll.o | ||
obj-y += clk-pll-out.o | ||
obj-y += clk-super.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* | ||
* Copyright (c) 2012, 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-provider.h> | ||
#include <linux/slab.h> | ||
#include <linux/err.h> | ||
|
||
#include "clk.h" | ||
|
||
static unsigned long clk_sync_source_recalc_rate(struct clk_hw *hw, | ||
unsigned long parent_rate) | ||
{ | ||
struct tegra_clk_sync_source *sync = to_clk_sync_source(hw); | ||
|
||
return sync->rate; | ||
} | ||
|
||
static long clk_sync_source_round_rate(struct clk_hw *hw, unsigned long rate, | ||
unsigned long *prate) | ||
{ | ||
struct tegra_clk_sync_source *sync = to_clk_sync_source(hw); | ||
|
||
if (rate > sync->max_rate) | ||
return -EINVAL; | ||
else | ||
return rate; | ||
} | ||
|
||
static int clk_sync_source_set_rate(struct clk_hw *hw, unsigned long rate, | ||
unsigned long parent_rate) | ||
{ | ||
struct tegra_clk_sync_source *sync = to_clk_sync_source(hw); | ||
|
||
sync->rate = rate; | ||
return 0; | ||
} | ||
|
||
const struct clk_ops tegra_clk_sync_source_ops = { | ||
.round_rate = clk_sync_source_round_rate, | ||
.set_rate = clk_sync_source_set_rate, | ||
.recalc_rate = clk_sync_source_recalc_rate, | ||
}; | ||
|
||
struct clk *tegra_clk_register_sync_source(const char *name, | ||
unsigned long rate, unsigned long max_rate) | ||
{ | ||
struct tegra_clk_sync_source *sync; | ||
struct clk_init_data init; | ||
struct clk *clk; | ||
|
||
sync = kzalloc(sizeof(*sync), GFP_KERNEL); | ||
if (!sync) { | ||
pr_err("%s: could not allocate sync source clk\n", __func__); | ||
return ERR_PTR(-ENOMEM); | ||
} | ||
|
||
sync->rate = rate; | ||
sync->max_rate = max_rate; | ||
|
||
init.ops = &tegra_clk_sync_source_ops; | ||
init.name = name; | ||
init.flags = CLK_IS_ROOT; | ||
init.parent_names = NULL; | ||
init.num_parents = 0; | ||
|
||
/* Data in .init is copied by clk_register(), so stack variable OK */ | ||
sync->hw.init = &init; | ||
|
||
clk = clk_register(NULL, &sync->hw); | ||
if (IS_ERR(clk)) | ||
kfree(sync); | ||
|
||
return clk; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
/* | ||
* Copyright (c) 2012, 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/kernel.h> | ||
#include <linux/io.h> | ||
#include <linux/err.h> | ||
#include <linux/slab.h> | ||
#include <linux/clk-provider.h> | ||
#include <linux/clk.h> | ||
|
||
#include "clk.h" | ||
|
||
#define pll_out_override(p) (BIT((p->shift - 6))) | ||
#define div_mask(d) ((1 << (d->width)) - 1) | ||
#define get_mul(d) (1 << d->frac_width) | ||
#define get_max_div(d) div_mask(d) | ||
|
||
#define PERIPH_CLK_UART_DIV_ENB BIT(24) | ||
|
||
static int get_div(struct tegra_clk_frac_div *divider, unsigned long rate, | ||
unsigned long parent_rate) | ||
{ | ||
s64 divider_ux1 = parent_rate; | ||
u8 flags = divider->flags; | ||
int mul; | ||
|
||
if (!rate) | ||
return 0; | ||
|
||
mul = get_mul(divider); | ||
|
||
if (!(flags & TEGRA_DIVIDER_INT)) | ||
divider_ux1 *= mul; | ||
|
||
if (flags & TEGRA_DIVIDER_ROUND_UP) | ||
divider_ux1 += rate - 1; | ||
|
||
do_div(divider_ux1, rate); | ||
|
||
if (flags & TEGRA_DIVIDER_INT) | ||
divider_ux1 *= mul; | ||
|
||
divider_ux1 -= mul; | ||
|
||
if (divider_ux1 < 0) | ||
return 0; | ||
|
||
if (divider_ux1 > get_max_div(divider)) | ||
return -EINVAL; | ||
|
||
return divider_ux1; | ||
} | ||
|
||
static unsigned long clk_frac_div_recalc_rate(struct clk_hw *hw, | ||
unsigned long parent_rate) | ||
{ | ||
struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); | ||
u32 reg; | ||
int div, mul; | ||
u64 rate = parent_rate; | ||
|
||
reg = readl_relaxed(divider->reg) >> divider->shift; | ||
div = reg & div_mask(divider); | ||
|
||
mul = get_mul(divider); | ||
div += mul; | ||
|
||
rate *= mul; | ||
rate += div - 1; | ||
do_div(rate, div); | ||
|
||
return rate; | ||
} | ||
|
||
static long clk_frac_div_round_rate(struct clk_hw *hw, unsigned long rate, | ||
unsigned long *prate) | ||
{ | ||
struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); | ||
int div, mul; | ||
unsigned long output_rate = *prate; | ||
|
||
if (!rate) | ||
return output_rate; | ||
|
||
div = get_div(divider, rate, output_rate); | ||
if (div < 0) | ||
return *prate; | ||
|
||
mul = get_mul(divider); | ||
|
||
return DIV_ROUND_UP(output_rate * mul, div + mul); | ||
} | ||
|
||
static int clk_frac_div_set_rate(struct clk_hw *hw, unsigned long rate, | ||
unsigned long parent_rate) | ||
{ | ||
struct tegra_clk_frac_div *divider = to_clk_frac_div(hw); | ||
int div; | ||
unsigned long flags = 0; | ||
u32 val; | ||
|
||
div = get_div(divider, rate, parent_rate); | ||
if (div < 0) | ||
return div; | ||
|
||
if (divider->lock) | ||
spin_lock_irqsave(divider->lock, flags); | ||
|
||
val = readl_relaxed(divider->reg); | ||
val &= ~(div_mask(divider) << divider->shift); | ||
val |= div << divider->shift; | ||
|
||
if (divider->flags & TEGRA_DIVIDER_UART) { | ||
if (div) | ||
val |= PERIPH_CLK_UART_DIV_ENB; | ||
else | ||
val &= ~PERIPH_CLK_UART_DIV_ENB; | ||
} | ||
|
||
if (divider->flags & TEGRA_DIVIDER_FIXED) | ||
val |= pll_out_override(divider); | ||
|
||
writel_relaxed(val, divider->reg); | ||
|
||
if (divider->lock) | ||
spin_unlock_irqrestore(divider->lock, flags); | ||
|
||
return 0; | ||
} | ||
|
||
const struct clk_ops tegra_clk_frac_div_ops = { | ||
.recalc_rate = clk_frac_div_recalc_rate, | ||
.set_rate = clk_frac_div_set_rate, | ||
.round_rate = clk_frac_div_round_rate, | ||
}; | ||
|
||
struct clk *tegra_clk_register_divider(const char *name, | ||
const char *parent_name, void __iomem *reg, | ||
unsigned long flags, u8 clk_divider_flags, u8 shift, u8 width, | ||
u8 frac_width, spinlock_t *lock) | ||
{ | ||
struct tegra_clk_frac_div *divider; | ||
struct clk *clk; | ||
struct clk_init_data init; | ||
|
||
divider = kzalloc(sizeof(*divider), GFP_KERNEL); | ||
if (!divider) { | ||
pr_err("%s: could not allocate fractional divider clk\n", | ||
__func__); | ||
return ERR_PTR(-ENOMEM); | ||
} | ||
|
||
init.name = name; | ||
init.ops = &tegra_clk_frac_div_ops; | ||
init.flags = flags; | ||
init.parent_names = parent_name ? &parent_name : NULL; | ||
init.num_parents = parent_name ? 1 : 0; | ||
|
||
divider->reg = reg; | ||
divider->shift = shift; | ||
divider->width = width; | ||
divider->frac_width = frac_width; | ||
divider->lock = lock; | ||
divider->flags = clk_divider_flags; | ||
|
||
/* Data in .init is copied by clk_register(), so stack variable OK */ | ||
divider->hw.init = &init; | ||
|
||
clk = clk_register(NULL, ÷r->hw); | ||
if (IS_ERR(clk)) | ||
kfree(divider); | ||
|
||
return clk; | ||
} |
Oops, something went wrong.