diff mbox

ARM: tegra30: clocks: add AHB and APB clocks

Message ID 1350628693-1190-1-git-send-email-josephl@nvidia.com
State Superseded, archived
Headers show

Commit Message

Joseph Lo Oct. 19, 2012, 6:38 a.m. UTC
Adding the AHB and APB bus clock control interface for Tegra30.

Signed-off-by: Joseph Lo <josephl@nvidia.com>
---
 arch/arm/mach-tegra/common.c              |    4 +
 arch/arm/mach-tegra/tegra30_clocks.c      |  106 +++++++++++++++++++++++++++++
 arch/arm/mach-tegra/tegra30_clocks.h      |    1 +
 arch/arm/mach-tegra/tegra30_clocks_data.c |   46 +++++++++++++
 4 files changed, 157 insertions(+), 0 deletions(-)

Comments

Stephen Warren Oct. 19, 2012, 3:53 p.m. UTC | #1
On 10/19/2012 12:38 AM, Joseph Lo wrote:
> Adding the AHB and APB bus clock control interface for Tegra30.

Not really "clock control interface" but just "clocks". I can reword
that when applying the patch though.

The code seems reasonable. I'd like review from Prashant or Peter though.
--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Prashant Gaikwad Oct. 22, 2012, 4:38 a.m. UTC | #2
On Friday 19 October 2012 12:08 PM, Joseph Lo wrote:
> Adding the AHB and APB bus clock control interface for Tegra30.
>
> Signed-off-by: Joseph Lo<josephl@nvidia.com>
> ---
>   arch/arm/mach-tegra/common.c              |    4 +
>   arch/arm/mach-tegra/tegra30_clocks.c      |  106 +++++++++++++++++++++++++++++
>   arch/arm/mach-tegra/tegra30_clocks.h      |    1 +
>   arch/arm/mach-tegra/tegra30_clocks_data.c |   46 +++++++++++++
>   4 files changed, 157 insertions(+), 0 deletions(-)

<snip>

> +
> +static long tegra30_bus_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> +				unsigned long *prate)
> +{
> +	unsigned long parent_rate = *prate;
> +	s64 divider;
> +
> +	if (rate>= parent_rate)
> +		return rate;
> +

return parent_rate?

> +	divider = parent_rate;
> +	divider += rate - 1;
> +	do_div(divider, rate);
> +
> +	if (divider<  0)
> +		return divider;
> +
> +	if (divider>  4)
> +		divider = 4;
> +	do_div(parent_rate, divider);
> +
> +	return parent_rate;
> +}
>

--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Joseph Lo Oct. 22, 2012, 7:17 a.m. UTC | #3
On Mon, 2012-10-22 at 12:38 +0800, Prashant Gaikwad wrote:
> On Friday 19 October 2012 12:08 PM, Joseph Lo wrote:
> > Adding the AHB and APB bus clock control interface for Tegra30.
> >
> > Signed-off-by: Joseph Lo<josephl@nvidia.com>
> > ---
> >   arch/arm/mach-tegra/common.c              |    4 +
> >   arch/arm/mach-tegra/tegra30_clocks.c      |  106 +++++++++++++++++++++++++++++
> >   arch/arm/mach-tegra/tegra30_clocks.h      |    1 +
> >   arch/arm/mach-tegra/tegra30_clocks_data.c |   46 +++++++++++++
> >   4 files changed, 157 insertions(+), 0 deletions(-)
> 
> <snip>
> 
> > +
> > +static long tegra30_bus_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> > +				unsigned long *prate)
> > +{
> > +	unsigned long parent_rate = *prate;
> > +	s64 divider;
> > +
> > +	if (rate>= parent_rate)
> > +		return rate;
> > +
> 
> return parent_rate?

Prashant,

Yes, thanks.

> 
> > +	divider = parent_rate;
> > +	divider += rate - 1;
> > +	do_div(divider, rate);
> > +
> > +	if (divider<  0)
> > +		return divider;
> > +
> > +	if (divider>  4)
> > +		divider = 4;
> > +	do_div(parent_rate, divider);
> > +
> > +	return parent_rate;
> > +}
> >
> 


--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 0b0a5f5..177f164 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -104,6 +104,10 @@  static __initdata struct tegra_clk_init_table tegra30_clk_init_table[] = {
 	{ "clk_m",	NULL,		0,		true },
 	{ "pll_p",	"clk_m",	408000000,	true },
 	{ "pll_p_out1",	"pll_p",	9600000,	true },
+	{ "pll_p_out4",	"pll_p",	102000000,	true },
+	{ "sclk",	"pll_p_out4",	102000000,	true },
+	{ "hclk",	"sclk",		102000000,	true },
+	{ "pclk",	"hclk",		51000000,	true },
 	{ NULL,		NULL,		0,		0},
 };
 #endif
diff --git a/arch/arm/mach-tegra/tegra30_clocks.c b/arch/arm/mach-tegra/tegra30_clocks.c
index e9de5df..875744f 100644
--- a/arch/arm/mach-tegra/tegra30_clocks.c
+++ b/arch/arm/mach-tegra/tegra30_clocks.c
@@ -792,6 +792,112 @@  struct clk_ops tegra30_twd_ops = {
 	.recalc_rate = tegra30_twd_clk_recalc_rate,
 };
 
+/* bus clock functions */
+static int tegra30_bus_clk_is_enabled(struct clk_hw *hw)
+{
+	struct clk_tegra *c = to_clk_tegra(hw);
+	u32 val = clk_readl(c->reg);
+
+	c->state = ((val >> c->reg_shift) & BUS_CLK_DISABLE) ? OFF : ON;
+	return c->state;
+}
+
+static int tegra30_bus_clk_enable(struct clk_hw *hw)
+{
+	struct clk_tegra *c = to_clk_tegra(hw);
+	u32 val;
+
+	val = clk_readl(c->reg);
+	val &= ~(BUS_CLK_DISABLE << c->reg_shift);
+	clk_writel(val, c->reg);
+
+	return 0;
+}
+
+static void tegra30_bus_clk_disable(struct clk_hw *hw)
+{
+	struct clk_tegra *c = to_clk_tegra(hw);
+	u32 val;
+
+	val = clk_readl(c->reg);
+	val |= BUS_CLK_DISABLE << c->reg_shift;
+	clk_writel(val, c->reg);
+}
+
+static unsigned long tegra30_bus_clk_recalc_rate(struct clk_hw *hw,
+			unsigned long prate)
+{
+	struct clk_tegra *c = to_clk_tegra(hw);
+	u32 val = clk_readl(c->reg);
+	u64 rate = prate;
+
+	c->div = ((val >> c->reg_shift) & BUS_CLK_DIV_MASK) + 1;
+	c->mul = 1;
+
+	if (c->mul != 0 && c->div != 0) {
+		rate *= c->mul;
+		rate += c->div - 1; /* round up */
+		do_div(rate, c->div);
+	}
+	return rate;
+}
+
+static int tegra30_bus_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long parent_rate)
+{
+	struct clk_tegra *c = to_clk_tegra(hw);
+	int ret = -EINVAL;
+	u32 val;
+	int i;
+
+	val = clk_readl(c->reg);
+	for (i = 1; i <= 4; i++) {
+		if (rate == parent_rate / i) {
+			val &= ~(BUS_CLK_DIV_MASK << c->reg_shift);
+			val |= (i - 1) << c->reg_shift;
+			clk_writel(val, c->reg);
+			c->div = i;
+			c->mul = 1;
+			ret = 0;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static long tegra30_bus_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+				unsigned long *prate)
+{
+	unsigned long parent_rate = *prate;
+	s64 divider;
+
+	if (rate >= parent_rate)
+		return rate;
+
+	divider = parent_rate;
+	divider += rate - 1;
+	do_div(divider, rate);
+
+	if (divider < 0)
+		return divider;
+
+	if (divider > 4)
+		divider = 4;
+	do_div(parent_rate, divider);
+
+	return parent_rate;
+}
+
+struct clk_ops tegra30_bus_ops = {
+	.is_enabled = tegra30_bus_clk_is_enabled,
+	.enable = tegra30_bus_clk_enable,
+	.disable = tegra30_bus_clk_disable,
+	.set_rate = tegra30_bus_clk_set_rate,
+	.round_rate = tegra30_bus_clk_round_rate,
+	.recalc_rate = tegra30_bus_clk_recalc_rate,
+};
+
 /* Blink output functions */
 static int tegra30_blink_clk_is_enabled(struct clk_hw *hw)
 {
diff --git a/arch/arm/mach-tegra/tegra30_clocks.h b/arch/arm/mach-tegra/tegra30_clocks.h
index f2f88fe..7a34adb 100644
--- a/arch/arm/mach-tegra/tegra30_clocks.h
+++ b/arch/arm/mach-tegra/tegra30_clocks.h
@@ -34,6 +34,7 @@  extern struct clk_ops tegra_clk_out_ops;
 extern struct clk_ops tegra30_super_ops;
 extern struct clk_ops tegra30_blink_clk_ops;
 extern struct clk_ops tegra30_twd_ops;
+extern struct clk_ops tegra30_bus_ops;
 extern struct clk_ops tegra30_periph_clk_ops;
 extern struct clk_ops tegra30_dsib_clk_ops;
 extern struct clk_ops tegra_nand_clk_ops;
diff --git a/arch/arm/mach-tegra/tegra30_clocks_data.c b/arch/arm/mach-tegra/tegra30_clocks_data.c
index 3d2e553..7bc8b1d 100644
--- a/arch/arm/mach-tegra/tegra30_clocks_data.c
+++ b/arch/arm/mach-tegra/tegra30_clocks_data.c
@@ -711,6 +711,50 @@  static struct clk tegra_clk_sclk = {
 	.num_parents = ARRAY_SIZE(mux_sclk),
 };
 
+static const char *tegra_hclk_parent_names[] = {
+	"tegra_sclk",
+};
+
+static struct clk *tegra_hclk_parents[] = {
+	&tegra_clk_sclk,
+};
+
+static struct clk tegra_hclk;
+static struct clk_tegra tegra_hclk_hw = {
+	.hw = {
+		.clk = &tegra_hclk,
+	},
+	.flags = DIV_BUS,
+	.reg = 0x30,
+	.reg_shift = 4,
+	.max_rate = 378000000,
+	.min_rate = 12000000,
+};
+DEFINE_CLK_TEGRA(hclk, 0, &tegra30_bus_ops, 0, tegra_hclk_parent_names,
+		tegra_hclk_parents, &tegra_clk_sclk);
+
+static const char *tegra_pclk_parent_names[] = {
+	"tegra_hclk",
+};
+
+static struct clk *tegra_pclk_parents[] = {
+	&tegra_hclk,
+};
+
+static struct clk tegra_pclk;
+static struct clk_tegra tegra_pclk_hw = {
+	.hw = {
+		.clk = &tegra_pclk,
+	},
+	.flags = DIV_BUS,
+	.reg = 0x30,
+	.reg_shift = 0,
+	.max_rate = 167000000,
+	.min_rate = 12000000,
+};
+DEFINE_CLK_TEGRA(pclk, 0, &tegra30_bus_ops, 0, tegra_pclk_parent_names,
+		tegra_pclk_parents, &tegra_hclk);
+
 static const char *mux_blink[] = {
 	"clk_32k",
 };
@@ -1325,6 +1369,8 @@  struct clk *tegra_ptr_clks[] = {
 	&tegra_cml1,
 	&tegra_pciex,
 	&tegra_clk_sclk,
+	&tegra_hclk,
+	&tegra_pclk,
 	&tegra_clk_blink,
 	&tegra30_clk_twd,
 };