diff options
author | Alex Frid <afrid@nvidia.com> | 2011-01-08 21:00:54 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:42:08 -0800 |
commit | 562dc227863577f491289ed8dfac044cb187a37c (patch) | |
tree | f79049cedc16c86a6c5e0f45fe868b4aa6c3f61c /arch/arm/mach-tegra | |
parent | ef6d01b150a5995d90ac6e1833b2046ba69b42a7 (diff) |
ARM: tegra: clock: Add Tegra3 EMC shared bus
Original-Change-Id: I0c8ed371abb9f2172d42504527d7585e6bef6c94
Reviewed-on: http://git-master/r/15349
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
Tested-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Scott Williams <scwilliams@nvidia.com>
Original-Change-Id: I78576a1ac1bfbb89a59ca428d94a7a99edde6777
Rebase-Id: R3cab0fa7760e2c6eb5d6e84bbc3dca8f6fe3d3fa
Diffstat (limited to 'arch/arm/mach-tegra')
-rw-r--r-- | arch/arm/mach-tegra/Makefile | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/clock.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra2_emc.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_clocks.c | 72 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_emc.c | 76 | ||||
-rw-r--r-- | arch/arm/mach-tegra/tegra3_emc.h | 26 |
6 files changed, 174 insertions, 5 deletions
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 60648d942cdc..0b399475696a 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_fuse.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_speedo.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra3_save.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o +obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra3_emc.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += wakeups-t2.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += wakeups-t3.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pm-t2.o diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h index 6a3551fb57ce..5e1f74fa8d5c 100644 --- a/arch/arm/mach-tegra/clock.h +++ b/arch/arm/mach-tegra/clock.h @@ -191,6 +191,8 @@ void clk_set_cansleep(struct clk *c); unsigned long clk_get_rate_locked(struct clk *c); int clk_set_rate_locked(struct clk *c, unsigned long rate); void tegra2_sdmmc_tap_delay(struct clk *c, int delay); +int tegra_emc_set_rate(unsigned long rate); +long tegra_emc_round_rate(unsigned long rate); static inline bool clk_is_auto_dvfs(struct clk *c) { diff --git a/arch/arm/mach-tegra/tegra2_emc.h b/arch/arm/mach-tegra/tegra2_emc.h index 871387fdeea3..a40937dd7fcf 100644 --- a/arch/arm/mach-tegra/tegra2_emc.h +++ b/arch/arm/mach-tegra/tegra2_emc.h @@ -33,6 +33,4 @@ struct tegra_emc_chip { int table_size; }; -int tegra_emc_set_rate(unsigned long rate); -long tegra_emc_round_rate(unsigned long rate); void tegra_init_emc(const struct tegra_emc_chip *chips, int chips_size); diff --git a/arch/arm/mach-tegra/tegra3_clocks.c b/arch/arm/mach-tegra/tegra3_clocks.c index 384d82ceeaec..9cc42798c6b8 100644 --- a/arch/arm/mach-tegra/tegra3_clocks.c +++ b/arch/arm/mach-tegra/tegra3_clocks.c @@ -1489,6 +1489,55 @@ static struct clk_ops tegra_dtv_clk_ops = { .reset = &tegra3_periph_clk_reset, }; + +/* External memory controller clock ops */ +static void tegra3_emc_clk_init(struct clk *c) +{ + tegra3_periph_clk_init(c); + c->max_rate = clk_get_rate_locked(c->parent); +} + +static long tegra3_emc_clk_round_rate(struct clk *c, unsigned long rate) +{ + long new_rate = rate; + + new_rate = tegra_emc_round_rate(new_rate); + if (new_rate < 0) + new_rate = c->max_rate; + + BUG_ON(new_rate != tegra3_periph_clk_round_rate(c, new_rate)); + + return new_rate; +} + +static int tegra3_emc_clk_set_rate(struct clk *c, unsigned long rate) +{ + int ret; + /* The tegra3 memory controller has an interlock with the clock + * block that allows memory shadowed registers to be updated, + * and then transfer them to the main registers at the same + * time as the clock update without glitches. */ + ret = tegra_emc_set_rate(rate); + if (ret < 0) + return ret; + + /* FIXME: update MC clock control here */ + ret = tegra3_periph_clk_set_rate(c, rate); + udelay(1); + + return ret; +} + +static struct clk_ops tegra_emc_clk_ops = { + .init = &tegra3_emc_clk_init, + .enable = &tegra3_periph_clk_enable, + .disable = &tegra3_periph_clk_disable, + .set_parent = &tegra3_periph_clk_set_parent, + .set_rate = &tegra3_emc_clk_set_rate, + .round_rate = &tegra3_emc_clk_round_rate, + .reset = &tegra3_periph_clk_reset, +}; + /* Clock doubler ops */ static void tegra3_clk_double_init(struct clk *c) { @@ -1635,7 +1684,8 @@ static void tegra_clk_shared_bus_update(struct clk *bus) rate = max(c->u.shared_bus_user.rate, rate); } - clk_set_rate(bus, rate); + if (rate != clk_get_rate(bus)) + clk_set_rate(bus, rate); }; static void tegra_clk_shared_bus_init(struct clk *c) @@ -2393,6 +2443,18 @@ static struct clk_mux_sel mux_plla_clk32_pllp_clkm_plle[] = { { 0, 0}, }; +static struct clk tegra_clk_emc = { + .name = "emc", + .ops = &tegra_emc_clk_ops, + .reg = 0x19c, + .max_rate = 800000000, + .inputs = mux_pllm_pllc_pllp_clkm, + .flags = MUX | DIV_U71 | PERIPH_EMC_ENB, + .u.periph = { + .clk_num = 57, + }, +}; + #define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \ { \ .name = _name, \ @@ -2515,8 +2577,6 @@ struct clk tegra_list_clks[] = { PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, 480000000, mux_clk_m, 0), /* requires min voltage */ PERIPH_CLK("usb2", "tegra-ehci.1", NULL, 58, 0, 480000000, mux_clk_m, 0), /* requires min voltage */ PERIPH_CLK("usb3", "tegra-ehci.2", NULL, 59, 0, 480000000, mux_clk_m, 0), /* requires min voltage */ - /* FIXME: EMC should be separated as shared bus for EMC DVFS */ - PERIPH_CLK("emc", "emc", NULL, 57, 0x19c, 800000000, mux_pllm_pllc_pllp_clkm, MUX | DIV_U71 | PERIPH_EMC_ENB), PERIPH_CLK("dsi", "dsi", NULL, 48, 0, 500000000, mux_plld_out0, 0), /* scales with voltage */ /* FIXME: double-check that DSIB controller is on PLLD clock (not on PLLD2) */ PERIPH_CLK("dsib", "dsib", NULL, 82, 0, 500000000, mux_plld_out0, 0), /* scales with voltage */ @@ -2531,6 +2591,11 @@ struct clk tegra_list_clks[] = { PERIPH_CLK("i2cslow", "i2cslow", NULL, 81, 0x3fc, 26000000, mux_pllp_pllc_clk32_clkm, MUX | DIV_U71), SHARED_CLK("avp.sclk", "tegra-avp", "sclk", &tegra_clk_sclk), + SHARED_CLK("avp.emc", "tegra-avp", "emc", &tegra_clk_emc), + SHARED_CLK("cpu.emc", "cpu", "emc", &tegra_clk_emc), + SHARED_CLK("disp1.emc", "tegradc.0", "emc", &tegra_clk_emc), + SHARED_CLK("disp2.emc", "tegradc.1", "emc", &tegra_clk_emc), + SHARED_CLK("hdmi.emc", "hdmi", "emc", &tegra_clk_emc), }; #define CLK_DUPLICATE(_name, _dev, _con) \ @@ -2600,6 +2665,7 @@ struct clk *tegra_ptr_clks[] = { &tegra_clk_virtual_cpu, &tegra_clk_blink, &tegra_clk_cop, + &tegra_clk_emc, }; static void tegra3_init_one_clock(struct clk *c) diff --git a/arch/arm/mach-tegra/tegra3_emc.c b/arch/arm/mach-tegra/tegra3_emc.c new file mode 100644 index 000000000000..78445ca4d2a1 --- /dev/null +++ b/arch/arm/mach-tegra/tegra3_emc.c @@ -0,0 +1,76 @@ +/* + * arch/arm/mach-tegra/tegra3_emc.c + * + * Copyright (C) 2011 NVIDIA Corporation + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> + +#include <mach/iomap.h> + +#include "clock.h" +#include "tegra3_emc.h" + +#ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE +static bool emc_enable = true; +#else +static bool emc_enable; +#endif +module_param(emc_enable, bool, 0644); + +static void __iomem *emc_base = IO_ADDRESS(TEGRA_EMC_BASE); +static const struct tegra_emc_table *tegra_emc_table; +static int tegra_emc_table_size; + +static inline void emc_writel(u32 val, unsigned long addr) +{ + __raw_writel(val, (u32)emc_base + addr); + +} + +static inline u32 emc_readl(unsigned long addr) +{ + return __raw_readl((u32)emc_base + addr); +} + +/* Select the closest EMC rate that is higher than the requested rate */ +long tegra_emc_round_rate(unsigned long rate) +{ + return -ENOSYS; +} + +/* The EMC registers have shadow registers. When the EMC clock is updated + * in the clock controller, the shadow registers are copied to the active + * registers, allowing glitchless memory bus frequency changes. + * This function updates the shadow registers for a new clock frequency, + * and relies on the clock lock on the emc clock to avoid races between + * multiple frequency changes */ +int tegra_emc_set_rate(unsigned long rate) +{ + return -ENOSYS; +} + +void tegra_init_emc(const struct tegra_emc_table *table, int table_size) +{ + tegra_emc_table = table; + tegra_emc_table_size = table_size; +} diff --git a/arch/arm/mach-tegra/tegra3_emc.h b/arch/arm/mach-tegra/tegra3_emc.h new file mode 100644 index 000000000000..a65e0881cab9 --- /dev/null +++ b/arch/arm/mach-tegra/tegra3_emc.h @@ -0,0 +1,26 @@ +/* + * arch/arm/mach-tegra/tegra3_emc.c + * + * Copyright (C) 2011 NVIDIA Corporation + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +struct tegra_emc_table { + unsigned long rate; +}; + +void tegra_init_emc(const struct tegra_emc_table *table, int table_size); |