diff options
35 files changed, 4917 insertions, 629 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index f401b92a99f..bf7273f3dc6 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -573,6 +573,7 @@ config ARCH_TEGRA select HAVE_CLK select COMMON_CLKDEV select ARCH_HAS_BARRIERS if CACHE_L2X0 + select ARCH_HAS_CPUFREQ help This enables support for NVIDIA Tegra based systems (Tegra APX, Tegra 6xx and Tegra 2 series). diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index a57713c1954..acd9552f8ad 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -16,6 +16,10 @@ config ARCH_TEGRA_2x_SOC endchoice +config TEGRA_PCI + bool "PCI Express support" + select PCI + comment "Tegra board type" config MACH_HARMONY @@ -47,4 +51,11 @@ config TEGRA_DEBUG_UARTE endchoice +config TEGRA_SYSTEM_DMA + bool "Enable system DMA driver for NVIDIA Tegra SoCs" + default y + help + Adds system DMA functionality for NVIDIA Tegra SoCs, used by + several Tegra device drivers + endif diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 51e9370eed9..cdbc68e4c0c 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -1,14 +1,21 @@ obj-y += common.o obj-y += io.o -obj-y += irq.o +obj-y += irq.o legacy_irq.o obj-y += clock.o obj-y += timer.o obj-y += gpio.o obj-y += pinmux.o +obj-y += fuse.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += clock.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_clocks.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_dvfs.o +obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o obj-$(CONFIG_SMP) += platsmp.o localtimer.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o +obj-$(CONFIG_TEGRA_SYSTEM_DMA) += dma.o +obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o +obj-$(CONFIG_TEGRA_PCI) += pcie.o obj-${CONFIG_MACH_HARMONY} += board-harmony.o obj-${CONFIG_MACH_HARMONY} += board-harmony-pinmux.o +obj-${CONFIG_MACH_HARMONY} += board-harmony-pcie.o diff --git a/arch/arm/mach-tegra/board-harmony-pcie.c b/arch/arm/mach-tegra/board-harmony-pcie.c new file mode 100644 index 00000000000..f7e7d4514b6 --- /dev/null +++ b/arch/arm/mach-tegra/board-harmony-pcie.c @@ -0,0 +1,57 @@ +/* + * arch/arm/mach-tegra/board-harmony-pcie.c + * + * Copyright (C) 2010 CompuLab, Ltd. + * Mike Rapoport <mike@compulab.co.il> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/kernel.h> +#include <linux/gpio.h> +#include <linux/err.h> +#include <linux/regulator/consumer.h> + +#include <asm/mach-types.h> + +#include <mach/pinmux.h> +#include "board.h" + +#ifdef CONFIG_TEGRA_PCI + +static int __init harmony_pcie_init(void) +{ + int err; + + if (!machine_is_harmony()) + return 0; + + tegra_pinmux_set_tristate(TEGRA_PINGROUP_GPV, TEGRA_TRI_NORMAL); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXA, TEGRA_TRI_NORMAL); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXK, TEGRA_TRI_NORMAL); + + err = tegra_pcie_init(true, true); + if (err) + goto err_pcie; + + return 0; + +err_pcie: + tegra_pinmux_set_tristate(TEGRA_PINGROUP_GPV, TEGRA_TRI_TRISTATE); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXA, TEGRA_TRI_TRISTATE); + tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXK, TEGRA_TRI_TRISTATE); + + return err; +} + +subsys_initcall(harmony_pcie_init); + +#endif diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index 3d06354136f..0de565ca37c 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -27,6 +27,7 @@ void __init tegra_common_init(void); void __init tegra_map_common_io(void); void __init tegra_init_irq(void); void __init tegra_init_clock(void); +int __init tegra_pcie_init(bool init_port0, bool init_port1); extern struct sys_timer tegra_timer; #endif diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index 03ad578349b..ae19f95585b 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -24,13 +24,80 @@ #include <linux/debugfs.h> #include <linux/slab.h> #include <linux/seq_file.h> +#include <linux/regulator/consumer.h> #include <asm/clkdev.h> #include "clock.h" +#include "board.h" +#include "fuse.h" static LIST_HEAD(clocks); static DEFINE_SPINLOCK(clock_lock); +static DEFINE_MUTEX(dvfs_lock); + +static int clk_is_dvfs(struct clk *c) +{ + return (c->dvfs != NULL); +}; + +static int dvfs_set_rate(struct dvfs *d, unsigned long rate) +{ + struct dvfs_table *t; + + if (d->table == NULL) + return -ENODEV; + + for (t = d->table; t->rate != 0; t++) { + if (rate <= t->rate) { + if (!d->reg) + return 0; + + return regulator_set_voltage(d->reg, + t->millivolts * 1000, + d->max_millivolts * 1000); + } + } + + return -EINVAL; +} + +static void dvfs_init(struct clk *c) +{ + int process_id; + int i; + struct dvfs_table *table; + + process_id = c->dvfs->cpu ? tegra_core_process_id() : + tegra_cpu_process_id(); + + for (i = 0; i < c->dvfs->process_id_table_length; i++) + if (process_id == c->dvfs->process_id_table[i].process_id) + c->dvfs->table = c->dvfs->process_id_table[i].table; + + if (c->dvfs->table == NULL) { + pr_err("Failed to find dvfs table for clock %s process %d\n", + c->name, process_id); + return; + } + + c->dvfs->max_millivolts = 0; + for (table = c->dvfs->table; table->rate != 0; table++) + if (c->dvfs->max_millivolts < table->millivolts) + c->dvfs->max_millivolts = table->millivolts; + + c->dvfs->reg = regulator_get(NULL, c->dvfs->reg_id); + + if (IS_ERR(c->dvfs->reg)) { + pr_err("Failed to get regulator %s for clock %s\n", + c->dvfs->reg_id, c->name); + c->dvfs->reg = NULL; + return; + } + + if (c->refcnt > 0) + dvfs_set_rate(c->dvfs, c->rate); +} struct clk *tegra_get_clock_by_name(const char *name) { @@ -48,14 +115,31 @@ struct clk *tegra_get_clock_by_name(const char *name) return ret; } +static void clk_recalculate_rate(struct clk *c) +{ + u64 rate; + + if (!c->parent) + return; + + rate = c->parent->rate; + + if (c->mul != 0 && c->div != 0) { + rate = rate * c->mul; + do_div(rate, c->div); + } + + if (rate > c->max_rate) + pr_warn("clocks: Set clock %s to rate %llu, max is %lu\n", + c->name, rate, c->max_rate); + + c->rate = rate; +} + int clk_reparent(struct clk *c, struct clk *parent) { pr_debug("%s: %s\n", __func__, c->name); - if (c->refcnt && c->parent) - clk_disable_locked(c->parent); c->parent = parent; - if (c->refcnt && c->parent) - clk_enable_locked(c->parent); list_del(&c->sibling); list_add_tail(&c->sibling, &parent->children); return 0; @@ -67,8 +151,7 @@ static void propagate_rate(struct clk *c) pr_debug("%s: %s\n", __func__, c->name); list_for_each_entry(clkp, &c->children, sibling) { pr_debug(" %s\n", clkp->name); - if (clkp->ops->recalculate_rate) - clkp->ops->recalculate_rate(clkp); + clk_recalculate_rate(clkp); propagate_rate(clkp); } } @@ -77,6 +160,8 @@ void clk_init(struct clk *c) { unsigned long flags; + pr_debug("%s: %s\n", __func__, c->name); + spin_lock_irqsave(&clock_lock, flags); INIT_LIST_HEAD(&c->children); @@ -85,6 +170,8 @@ void clk_init(struct clk *c) if (c->ops && c->ops->init) c->ops->init(c); + clk_recalculate_rate(c); + list_add(&c->node, &clocks); if (c->parent) @@ -122,13 +209,38 @@ int clk_enable_locked(struct clk *c) return 0; } +int clk_enable_cansleep(struct clk *c) +{ + int ret; + unsigned long flags; + + mutex_lock(&dvfs_lock); + + if (clk_is_dvfs(c) && c->refcnt > 0) + dvfs_set_rate(c->dvfs, c->rate); + + spin_lock_irqsave(&clock_lock, flags); + ret = clk_enable_locked(c); + spin_unlock_irqrestore(&clock_lock, flags); + + mutex_unlock(&dvfs_lock); + + return ret; +} +EXPORT_SYMBOL(clk_enable_cansleep); + int clk_enable(struct clk *c) { int ret; unsigned long flags; + + if (clk_is_dvfs(c)) + BUG(); + spin_lock_irqsave(&clock_lock, flags); ret = clk_enable_locked(c); spin_unlock_irqrestore(&clock_lock, flags); + return ret; } EXPORT_SYMBOL(clk_enable); @@ -152,9 +264,30 @@ void clk_disable_locked(struct clk *c) c->refcnt--; } +void clk_disable_cansleep(struct clk *c) +{ + unsigned long flags; + + mutex_lock(&dvfs_lock); + + spin_lock_irqsave(&clock_lock, flags); + clk_disable_locked(c); + spin_unlock_irqrestore(&clock_lock, flags); + + if (clk_is_dvfs(c) && c->refcnt == 0) + dvfs_set_rate(c->dvfs, c->rate); + + mutex_unlock(&dvfs_lock); +} +EXPORT_SYMBOL(clk_disable_cansleep); + void clk_disable(struct clk *c) { unsigned long flags; + + if (clk_is_dvfs(c)) + BUG(); + spin_lock_irqsave(&clock_lock, flags); clk_disable_locked(c); spin_unlock_irqrestore(&clock_lock, flags); @@ -175,6 +308,8 @@ int clk_set_parent_locked(struct clk *c, struct clk *parent) if (ret) return ret; + clk_recalculate_rate(c); + propagate_rate(c); return 0; @@ -197,22 +332,69 @@ struct clk *clk_get_parent(struct clk *c) } EXPORT_SYMBOL(clk_get_parent); -int clk_set_rate(struct clk *c, unsigned long rate) +int clk_set_rate_locked(struct clk *c, unsigned long rate) +{ + int ret; + + if (rate > c->max_rate) + rate = c->max_rate; + + if (!c->ops || !c->ops->set_rate) + return -ENOSYS; + + ret = c->ops->set_rate(c, rate); + + if (ret) + return ret; + + clk_recalculate_rate(c); + + propagate_rate(c); + + return 0; +} + +int clk_set_rate_cansleep(struct clk *c, unsigned long rate) { int ret = 0; unsigned long flags; + pr_debug("%s: %s\n", __func__, c->name); + + mutex_lock(&dvfs_lock); + + if (rate > c->rate) + ret = dvfs_set_rate(c->dvfs, rate); + if (ret) + goto out; + spin_lock_irqsave(&clock_lock, flags); + ret = clk_set_rate_locked(c, rate); + spin_unlock_irqrestore(&clock_lock, flags); - pr_debug("%s: %s\n", __func__, c->name); + if (ret) + goto out; - if (c->ops && c->ops->set_rate) - ret = c->ops->set_rate(c, rate); - else - ret = -ENOSYS; + ret = dvfs_set_rate(c->dvfs, rate); - propagate_rate(c); +out: + mutex_unlock(&dvfs_lock); + return ret; +} +EXPORT_SYMBOL(clk_set_rate_cansleep); + +int clk_set_rate(struct clk *c, unsigned long rate) +{ + int ret = 0; + unsigned long flags; + + pr_debug("%s: %s\n", __func__, c->name); + + if (clk_is_dvfs(c)) + BUG(); + spin_lock_irqsave(&clock_lock, flags); + ret = clk_set_rate_locked(c, rate); spin_unlock_irqrestore(&clock_lock, flags); return ret; @@ -235,6 +417,20 @@ unsigned long clk_get_rate(struct clk *c) } EXPORT_SYMBOL(clk_get_rate); +long clk_round_rate(struct clk *c, unsigned long rate) +{ + pr_debug("%s: %s\n", __func__, c->name); + + if (!c->ops || !c->ops->round_rate) + return -ENOSYS; + + if (rate > c->max_rate) + rate = c->max_rate; + + return c->ops->round_rate(c, rate); +} +EXPORT_SYMBOL(clk_round_rate); + static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table) { struct clk *c; @@ -308,13 +504,28 @@ void tegra_periph_reset_assert(struct clk *c) } EXPORT_SYMBOL(tegra_periph_reset_assert); -int __init tegra_init_clock(void) +void __init tegra_init_clock(void) { tegra2_init_clocks(); +} + +int __init tegra_init_dvfs(void) +{ + struct clk *c, *safe; + + mutex_lock(&dvfs_lock); + + list_for_each_entry_safe(c, safe, &clocks, node) + if (c->dvfs) + dvfs_init(c); + + mutex_unlock(&dvfs_lock); return 0; } +late_initcall(tegra_init_dvfs); + #ifdef CONFIG_DEBUG_FS static struct dentry *clk_debugfs_root; @@ -324,7 +535,7 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level) struct clk *child; struct clk *safe; const char *state = "uninit"; - char div[5] = {0}; + char div[8] = {0}; if (c->state == ON) state = "on"; @@ -332,16 +543,26 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level) state = "off"; if (c->mul != 0 && c->div != 0) { - BUG_ON(c->mul > 2); - if (c->mul > c->div) - snprintf(div, sizeof(div), "x%d", c->mul / c->div); - else + if (c->mul > c->div) { + int mul = c->mul / c->div; + int mul2 = (c->mul * 10 / c->div) % 10; + int mul3 = (c->mul * 10) % c->div; + if (mul2 == 0 && mul3 == 0) + snprintf(div, sizeof(div), "x%d", mul); + else if (mul3 == 0) + snprintf(div, sizeof(div), "x%d.%d", mul, mul2); + else + snprintf(div, sizeof(div), "x%d.%d..", mul, mul2); + } else { snprintf(div, sizeof(div), "%d%s", c->div / c->mul, (c->div % c->mul) ? ".5" : ""); + } } - seq_printf(s, "%*s%-*s %-6s %-3d %-5s %-10lu\n", - level * 3 + 1, c->set ? "" : "*", + seq_printf(s, "%*s%c%c%-*s %-6s %-3d %-8s %-10lu\n", + level * 3 + 1, "", + c->rate > c->max_rate ? '!' : ' ', + !c->set ? '*' : ' ', 30 - level * 3, c->name, state, c->refcnt, div, c->rate); list_for_each_entry_safe(child, safe, &c->children, sibling) { @@ -353,8 +574,8 @@ static int clock_tree_show(struct seq_file *s, void *data) { struct clk *c; unsigned long flags; - seq_printf(s, " clock state ref div rate \n"); - seq_printf(s, "-----------------------------------------------------------\n"); + seq_printf(s, " clock state ref div rate\n"); + seq_printf(s, "--------------------------------------------------------------\n"); spin_lock_irqsave(&clock_lock, flags); list_for_each_entry(c, &clocks, node) if (c->parent == NULL) diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h index af7c70e2a3b..94fd859770f 100644 --- a/arch/arm/mach-tegra/clock.h +++ b/arch/arm/mach-tegra/clock.h @@ -27,18 +27,43 @@ #define DIV_U71 (1 << 1) #define DIV_U71_FIXED (1 << 2) #define DIV_2 (1 << 3) -#define PLL_FIXED (1 << 4) -#define PLL_HAS_CPCON (1 << 5) -#define MUX (1 << 6) -#define PLLD (1 << 7) -#define PERIPH_NO_RESET (1 << 8) -#define PERIPH_NO_ENB (1 << 9) -#define PERIPH_EMC_ENB (1 << 10) -#define PERIPH_MANUAL_RESET (1 << 11) -#define PLL_ALT_MISC_REG (1 << 12) +#define DIV_U16 (1 << 4) +#define PLL_FIXED (1 << 5) +#define PLL_HAS_CPCON (1 << 6) +#define MUX (1 << 7) +#define PLLD (1 << 8) +#define PERIPH_NO_RESET (1 << 9) +#define PERIPH_NO_ENB (1 << 10) +#define PERIPH_EMC_ENB (1 << 11) +#define PERIPH_MANUAL_RESET (1 << 12) +#define PLL_ALT_MISC_REG (1 << 13) +#define PLLU (1 << 14) #define ENABLE_ON_INIT (1 << 28) struct clk; +struct regulator; + +struct dvfs_table { + unsigned long rate; + int millivolts; +}; + +struct dvfs_process_id_table { + int process_id; + struct dvfs_table *table; +}; + + +struct dvfs { + struct regulator *reg; + struct dvfs_table *table; + int max_millivolts; + + int process_id_table_length; + const char *reg_id; + bool cpu; + struct dvfs_process_id_table process_id_table[]; +}; struct clk_mux_sel { struct clk *input; @@ -58,12 +83,9 @@ struct clk_ops { void (*init)(struct clk *); int (*enable)(struct clk *); void (*disable)(struct clk *); - void (*recalc)(struct clk *); int (*set_parent)(struct clk *, struct clk *); int (*set_rate)(struct clk *, unsigned long); - unsigned long (*get_rate)(struct clk *); long (*round_rate)(struct clk *, unsigned long); - unsigned long (*recalculate_rate)(struct clk *); }; enum clk_state { @@ -85,6 +107,7 @@ struct clk { struct clk *parent; struct clk_lookup lookup; unsigned long rate; + unsigned long max_rate; u32 flags; u32 refcnt; const char *name; @@ -103,10 +126,6 @@ struct clk { unsigned long cf_max; unsigned long vco_min; unsigned long vco_max; - u32 m; - u32 n; - u32 p; - u32 cpcon; const struct clk_pll_table *pll_table; /* DIV */ @@ -117,6 +136,12 @@ struct clk { const struct clk_mux_sel *inputs; u32 sel; u32 reg_mask; + + /* Virtual cpu clock */ + struct clk *main; + struct clk *backup; + + struct dvfs *dvfs; }; @@ -141,6 +166,7 @@ unsigned long clk_measure_input_freq(void); void clk_disable_locked(struct clk *c); int clk_enable_locked(struct clk *c); int clk_set_parent_locked(struct clk *c, struct clk *parent); +int clk_set_rate_locked(struct clk *c, unsigned long rate); int clk_reparent(struct clk *c, struct clk *parent); void tegra_clk_init_from_table(struct tegra_clk_init_table *table); diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index 039a514b61e..7c91e2b9d64 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -19,13 +19,17 @@ #include <linux/init.h> #include <linux/io.h> +#include <linux/clk.h> +#include <linux/delay.h> #include <asm/hardware/cache-l2x0.h> #include <mach/iomap.h> +#include <mach/dma.h> #include "board.h" #include "clock.h" +#include "fuse.h" static __initdata struct tegra_clk_init_table common_clk_init_table[] = { /* name parent rate enabled */ @@ -35,8 +39,8 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = { { "pll_p_out2", "pll_p", 48000000, true }, { "pll_p_out3", "pll_p", 72000000, true }, { "pll_p_out4", "pll_p", 108000000, true }, - { "sys", "pll_p_out4", 108000000, true }, - { "hclk", "sys", 108000000, true }, + { "sclk", "pll_p_out4", 108000000, true }, + { "hclk", "sclk", 108000000, true }, { "pclk", "hclk", 54000000, true }, { NULL, NULL, 0, 0}, }; @@ -51,11 +55,16 @@ void __init tegra_init_cache(void) l2x0_init(p, 0x6C080001, 0x8200c3fe); #endif + } void __init tegra_common_init(void) { + tegra_init_fuse(); tegra_init_clock(); tegra_clk_init_from_table(common_clk_init_table); tegra_init_cache(); +#ifdef CONFIG_TEGRA_SYSTEM_DMA + tegra_dma_init(); +#endif } diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c new file mode 100644 index 00000000000..fea5719c707 --- /dev/null +++ b/arch/arm/mach-tegra/cpu-tegra.c @@ -0,0 +1,185 @@ +/* + * arch/arm/mach-tegra/cpu-tegra.c + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross <ccross@google.com> + * Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/cpufreq.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <asm/system.h> + +#include <mach/hardware.h> +#include <mach/clk.h> + +/* Frequency table index must be sequential starting at 0 */ +static struct cpufreq_frequency_table freq_table[] = { + { 0, 312000 }, + { 1, 456000 }, + { 2, 608000 }, + { 3, 760000 }, + { 4, 816000 }, + { 5, 912000 }, + { 6, 1000000 }, + { 7, CPUFREQ_TABLE_END }, +}; + +#define NUM_CPUS 2 + +static struct clk *cpu_clk; + +static unsigned long target_cpu_speed[NUM_CPUS]; + +int tegra_verify_speed(struct cpufreq_policy *policy) +{ + return cpufreq_frequency_table_verify(policy, freq_table); +} + +unsigned int tegra_getspeed(unsigned int cpu) +{ + unsigned long rate; + + if (cpu >= NUM_CPUS) + return 0; + + rate = clk_get_rate(cpu_clk) / 1000; + return rate; +} + +static int tegra_update_cpu_speed(void) +{ + int i; + unsigned long rate = 0; + int ret = 0; + struct cpufreq_freqs freqs; + + for_each_online_cpu(i) + rate = max(rate, target_cpu_speed[i]); + + freqs.old = tegra_getspeed(0); + freqs.new = rate; + + if (freqs.old == freqs.new) + return ret; + + for_each_online_cpu(freqs.cpu) + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + +#ifdef CONFIG_CPU_FREQ_DEBUG + printk(KERN_DEBUG "cpufreq-tegra: transition: %u --> %u\n", + freqs.old, freqs.new); +#endif + + ret = clk_set_rate_cansleep(cpu_clk, freqs.new * 1000); + if (ret) { + pr_err("cpu-tegra: Failed to set cpu frequency to %d kHz\n", + freqs.new); + return ret; + } + + for_each_online_cpu(freqs.cpu) + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static int tegra_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + int idx; + unsigned int freq; + + cpufreq_frequency_table_target(policy, freq_table, target_freq, + relation, &idx); + + freq = freq_table[idx].frequency; + + target_cpu_speed[policy->cpu] = freq; + + return tegra_update_cpu_speed(); +} + +static int tegra_cpu_init(struct cpufreq_policy *policy) +{ + if (policy->cpu >= NUM_CPUS) + return -EINVAL; + + cpu_clk = clk_get_sys(NULL, "cpu"); + if (IS_ERR(cpu_clk)) + return PTR_ERR(cpu_clk); + + cpufreq_frequency_table_cpuinfo(policy, freq_table); + cpufreq_frequency_table_get_attr(freq_table, policy->cpu); + policy->cur = tegra_getspeed(policy->cpu); + target_cpu_speed[policy->cpu] = policy->cur; + + /* FIXME: what's the actual transition time? */ + policy->cpuinfo.transition_latency = 300 * 1000; + + policy->shared_type = CPUFREQ_SHARED_TYPE_ALL; + cpumask_copy(policy->related_cpus, cpu_possible_mask); + + return 0; +} + +static int tegra_cpu_exit(struct cpufreq_policy *policy) +{ + cpufreq_frequency_table_cpuinfo(policy, freq_table); + clk_put(cpu_clk); + return 0; +} + +static struct freq_attr *tegra_cpufreq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + NULL, +}; + +static struct cpufreq_driver tegra_cpufreq_driver = { + .verify = tegra_verify_speed, + .target = tegra_target, + .get = tegra_getspeed, + .init = tegra_cpu_init, + .exit = tegra_cpu_exit, + .name = "tegra", + .attr = tegra_cpufreq_attr, +}; + +static int __init tegra_cpufreq_init(void) +{ + return cpufreq_register_driver(&tegra_cpufreq_driver); +} + +static void __exit tegra_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&tegra_cpufreq_driver); +} + + +MODULE_AUTHOR("Colin Cross <ccross@android.com>"); +MODULE_DESCRIPTION("cpufreq driver for Nvidia Tegra2"); +MODULE_LICENSE("GPL"); +module_init(tegra_cpufreq_init); +module_exit(tegra_cpufreq_exit); diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c new file mode 100644 index 00000000000..edda6ec5e92 --- /dev/null +++ b/arch/arm/mach-tegra/dma.c @@ -0,0 +1,752 @@ +/* + * arch/arm/mach-tegra/dma.c + * + * System DMA driver for NVIDIA Tegra SoCs + * + * Copyright (c) 2008-2009, 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/io.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/err.h> +#include <linux/irq.h> +#include <linux/delay.h> +#include <mach/dma.h> +#include <mach/irqs.h> +#include <mach/iomap.h> + +#define APB_DMA_GEN 0x000 +#define GEN_ENABLE (1<<31) + +#define APB_DMA_CNTRL 0x010 + +#define APB_DMA_IRQ_MASK 0x01c + +#define APB_DMA_IRQ_MASK_SET 0x020 + +#define APB_DMA_CHAN_CSR 0x000 +#define CSR_ENB (1<<31) +#define CSR_IE_EOC (1<<30) +#define CSR_HOLD (1<<29) +#define CSR_DIR (1<<28) +#define CSR_ONCE (1<<27) +#define CSR_FLOW (1<<21) +#define CSR_REQ_SEL_SHIFT 16 +#define CSR_REQ_SEL_MASK (0x1F<<CSR_REQ_SEL_SHIFT) +#define CSR_REQ_SEL_INVALID (31<<CSR_REQ_SEL_SHIFT) +#define CSR_WCOUNT_SHIFT 2 +#define CSR_WCOUNT_MASK 0xFFFC + +#define APB_DMA_CHAN_STA 0x004 +#define STA_BUSY (1<<31) +#define STA_ISE_EOC (1<<30) +#define STA_HALT (1<<29) +#define STA_PING_PONG (1<<28) +#define STA_COUNT_SHIFT 2 +#define STA_COUNT_MASK 0xFFFC + +#define APB_DMA_CHAN_AHB_PTR 0x010 + +#define APB_DMA_CHAN_AHB_SEQ 0x014 +#define AHB_SEQ_INTR_ENB (1<<31) +#define AHB_SEQ_BUS_WIDTH_SHIFT 28 +#define AHB_SEQ_BUS_WIDTH_MASK (0x7<<AHB_SEQ_BUS_WIDTH_SHIFT) +#define AHB_SEQ_BUS_WIDTH_8 (0<<AHB_SEQ_BUS_WIDTH_SHIFT) +#define AHB_SEQ_BUS_WIDTH_16 (1<<AHB_SEQ_BUS_WIDTH_SHIFT) +#define AHB_SEQ_BUS_WIDTH_32 (2<<AHB_SEQ_BUS_WIDTH_SHIFT) +#define AHB_SEQ_BUS_WIDTH_64 (3<<AHB_SEQ_BUS_WIDTH_SHIFT) +#define AHB_SEQ_BUS_WIDTH_128 (4<<AHB_SEQ_BUS_WIDTH_SHIFT) +#define AHB_SEQ_DATA_SWAP (1<<27) +#define AHB_SEQ_BURST_MASK (0x7<<24) +#define AHB_SEQ_BURST_1 (4<<24) +#define AHB_SEQ_BURST_4 (5<<24) +#define AHB_SEQ_BURST_8 (6<<24) +#define AHB_SEQ_DBL_BUF (1<<19) +#define AHB_SEQ_WRAP_SHIFT 16 +#define AHB_SEQ_WRAP_MASK (0x7<<AHB_SEQ_WRAP_SHIFT) + +#define APB_DMA_CHAN_APB_PTR 0x018 + +#define APB_DMA_CHAN_APB_SEQ 0x01c +#define APB_SEQ_BUS_WIDTH_SHIFT 28 +#define APB_SEQ_BUS_WIDTH_MASK (0x7<<APB_SEQ_BUS_WIDTH_SHIFT) +#define APB_SEQ_BUS_WIDTH_8 (0<<APB_SEQ_BUS_WIDTH_SHIFT) +#define APB_SEQ_BUS_WIDTH_16 (1<<APB_SEQ_BUS_WIDTH_SHIFT) +#define APB_SEQ_BUS_WIDTH_32 (2<<APB_SEQ_BUS_WIDTH_SHIFT) +#define APB_SEQ_BUS_WIDTH_64 (3<<APB_SEQ_BUS_WIDTH_SHIFT) +#define APB_SEQ_BUS_WIDTH_128 (4<<APB_SEQ_BUS_WIDTH_SHIFT) +#define APB_SEQ_DATA_SWAP (1<<27) +#define APB_SEQ_WRAP_SHIFT 16 +#define APB_SEQ_WRAP_MASK (0x7<<APB_SEQ_WRAP_SHIFT) + +#define TEGRA_SYSTEM_DMA_CH_NR 16 +#define TEGRA_SYSTEM_DMA_AVP_CH_NUM 4 +#define TEGRA_SYSTEM_DMA_CH_MIN 0 +#define TEGRA_SYSTEM_DMA_CH_MAX \ + (TEGRA_SYSTEM_DMA_CH_NR - TEGRA_SYSTEM_DMA_AVP_CH_NUM - 1) + +#define NV_DMA_MAX_TRASFER_SIZE 0x10000 + +const unsigned int ahb_addr_wrap_table[8] = { + 0, 32, 64, 128, 256, 512, 1024, 2048 +}; + +const unsigned int apb_addr_wrap_table[8] = {0, 1, 2, 4, 8, 16, 32, 64}; + +const unsigned int bus_width_table[5] = {8, 16, 32, 64, 128}; + +#define TEGRA_DMA_NAME_SIZE 16 +struct tegra_dma_channel { + struct list_head list; + int id; + spinlock_t lock; + char name[TEGRA_DMA_NAME_SIZE]; + void __iomem *addr; + int mode; + int irq; + + /* Register shadow */ + u32 csr; + u32 ahb_seq; + u32 ahb_ptr; + u32 apb_seq; + u32 apb_ptr; +}; + +#define NV_DMA_MAX_CHANNELS 32 + +static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS); +static struct tegra_dma_channel dma_channels[NV_DMA_MAX_CHANNELS]; + +static void tegra_dma_update_hw(struct tegra_dma_channel *ch, + struct tegra_dma_req *req); +static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch, + struct tegra_dma_req *req); +static void tegra_dma_init_hw(struct tegra_dma_channel *ch); +static void tegra_dma_stop(struct tegra_dma_channel *ch); + +void tegra_dma_flush(struct tegra_dma_channel *ch) +{ +} +EXPORT_SYMBOL(tegra_dma_flush); + +void tegra_dma_dequeue(struct tegra_dma_channel *ch) +{ + struct tegra_dma_req *req; + + req = list_entry(ch->list.next, typeof(*req), node); + + tegra_dma_dequeue_req(ch, req); + return; +} + +void tegra_dma_stop(struct tegra_dma_channel *ch) +{ + unsigned int csr; + unsigned int status; + + csr = ch->csr; + csr &= ~CSR_IE_EOC; + writel(csr, ch->addr + APB_DMA_CHAN_CSR); + + csr &= ~CSR_ENB; + writel(csr, ch->addr + APB_DMA_CHAN_CSR); + + status = readl(ch->addr + APB_DMA_CHAN_STA); + if (status & STA_ISE_EOC) + writel(status, ch->addr + APB_DMA_CHAN_STA); +} + +int tegra_dma_cancel(struct tegra_dma_channel *ch) +{ + unsigned int csr; + unsigned long irq_flags; + + spin_lock_irqsave(&ch->lock, irq_flags); + while (!list_empty(&ch->list)) + list_del(ch->list.next); + + csr = ch->csr; + csr &= ~CSR_REQ_SEL_MASK; + csr |= CSR_REQ_SEL_INVALID; + + /* Set the enable as that is not shadowed */ + csr |= CSR_ENB; + writel(csr, ch->addr + APB_DMA_CHAN_CSR); + + tegra_dma_stop(ch); + + spin_unlock_irqrestore(&ch->lock, irq_flags); + return 0; +} + +int tegra_dma_dequeue_req(struct tegra_dma_channel *ch, + struct tegra_dma_req *_req) +{ + unsigned int csr; + unsigned int status; + struct tegra_dma_req *req = NULL; + int found = 0; + unsigned long irq_flags; + int to_transfer; + int req_transfer_count; + + spin_lock_irqsave(&ch->lock, irq_flags); + list_for_each_entry(req, &ch->list, node) { + if (req == _req) { + list_del(&req->node); + found = 1; + break; + } + } + if (!found) { + spin_unlock_irqrestore(&ch->lock, irq_flags); + return 0; + } + + /* STOP the DMA and get the transfer count. + * Getting the transfer count is tricky. + * - Change the source selector to invalid to stop the DMA from + * FIFO to memory. + * - Read the status register to know the number of pending + * bytes to be transfered. + * - Finally stop or program the DMA to the next buffer in the + * list. + */ + csr = ch->csr; + csr &= ~CSR_REQ_SEL_MASK; + csr |= CSR_REQ_SEL_INVALID; + + /* Set the enable as that is not shadowed */ + csr |= CSR_ENB; + writel(csr, ch->addr + APB_DMA_CHAN_CSR); + + /* Get the transfer count */ + status = readl(ch->addr + APB_DMA_CHAN_STA); + to_transfer = (status & STA_COUNT_MASK) >> STA_COUNT_SHIFT; + req_transfer_count = (ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT; + req_transfer_count += 1; + to_transfer += 1; + + req->bytes_transferred = req_transfer_count; + + if (status & STA_BUSY) + req->bytes_transferred -= to_transfer; + + /* In continous transfer mode, DMA only tracks the count of the + * half DMA buffer. So, if the DMA already finished half the DMA + * then add the half buffer to the completed count. + * + * FIXME: There can be a race here. What if the req to + * dequue happens at the same time as the DMA just moved to + * the new buffer and SW didn't yet received the interrupt? + */ + if (ch->mode & TEGRA_DMA_MODE_CONTINOUS) + if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) + req->bytes_transferred += req_transfer_count; + + req->bytes_transferred *= 4; + + tegra_dma_stop(ch); + if (!list_empty(&ch->list)) { + /* if the list is not empty, queue the next request */ + struct tegra_dma_req *next_req; + next_req = list_entry(ch->list.next, + typeof(*next_req), node); + tegra_dma_update_hw(ch, next_req); + } + req->status = -TEGRA_DMA_REQ_ERROR_ABORTED; + + spin_unlock_irqrestore(&ch->lock, irq_flags); + + /* Callback should be called without any lock */ + req->complete(req); + return 0; +} +EXPORT_SYMBOL(tegra_dma_dequeue_req); + +bool tegra_dma_is_empty(struct tegra_dma_channel *ch) +{ + unsigned long irq_flags; + bool is_empty; + + spin_lock_irqsave(&ch->lock, irq_flags); + if (list_empty(&ch->list)) + is_empty = true; + else + is_empty = false; + spin_unlock_irqrestore(&ch->lock, irq_flags); + return is_empty; +} +EXPORT_SYMBOL(tegra_dma_is_empty); + +bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch, + struct tegra_dma_req *_req) +{ + unsigned long irq_flags; + struct tegra_dma_req *req; + + spin_lock_irqsave(&ch->lock, irq_flags); + list_for_each_entry(req, &ch->list, node) { + if (req == _req) { + spin_unlock_irqrestore(&ch->lock, irq_flags); + return true; + } + } + spin_unlock_irqrestore(&ch->lock, irq_flags); + return false; +} +EXPORT_SYMBOL(tegra_dma_is_req_inflight); + +int tegra_dma_enqueue_req(struct tegra_dma_channel *ch, + struct tegra_dma_req *req) +{ + unsigned long irq_flags; + int start_dma = 0; + + if (req->size > NV_DMA_MAX_TRASFER_SIZE || + req->source_addr & 0x3 || req->dest_addr & 0x3) { + pr_err("Invalid DMA request for channel %d\n", ch->id); + return -EINVAL; + } + + spin_lock_irqsave(&ch->lock, irq_flags); + + req->bytes_transferred = 0; + req->status = 0; + req->buffer_status = 0; + if (list_empty(&ch->list)) + start_dma = 1; + + list_add_tail(&req->node, &ch->list); + + if (start_dma) + tegra_dma_update_hw(ch, req); + + spin_unlock_irqrestore(&ch->lock, irq_flags); + + return 0; +} +EXPORT_SYMBOL(tegra_dma_enqueue_req); + +struct tegra_dma_channel *tegra_dma_allocate_channel(int mode) +{ + int channel; + struct tegra_dma_channel *ch; + + /* first channel is the shared channel */ + if (mode & TEGRA_DMA_SHARED) { + channel = TEGRA_SYSTEM_DMA_CH_MIN; + } else { + channel = find_first_zero_bit(channel_usage, + ARRAY_SIZE(dma_channels)); + if (channel >= ARRAY_SIZE(dma_channels)) + return NULL; + } + __set_bit(channel, channel_usage); + ch = &dma_channels[channel]; + ch->mode = mode; + return ch; +} +EXPORT_SYMBOL(tegra_dma_allocate_channel); + +void tegra_dma_free_channel(struct tegra_dma_channel *ch) +{ + if (ch->mode & TEGRA_DMA_SHARED) + return; + tegra_dma_cancel(ch); + __clear_bit(ch->id, channel_usage); +} +EXPORT_SYMBOL(tegra_dma_free_channel); + +static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch, + struct tegra_dma_req *req) +{ + if (req->to_memory) { + ch->apb_ptr = req->source_addr; + ch->ahb_ptr = req->dest_addr; + } else { + ch->apb_ptr = req->dest_addr; + ch->ahb_ptr = req->source_addr; + } + writel(ch->apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR); + writel(ch->ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR); + + req->status = TEGRA_DMA_REQ_INFLIGHT; + return; +} + +static void tegra_dma_update_hw(struct tegra_dma_channel *ch, + struct tegra_dma_req *req) +{ + int ahb_addr_wrap; + int apb_addr_wrap; + int ahb_bus_width; + int apb_bus_width; + int index; + unsigned long csr; + + + ch->csr |= CSR_FLOW; + ch->csr &= ~CSR_REQ_SEL_MASK; + ch->csr |= req->req_sel << CSR_REQ_SEL_SHIFT; + ch->ahb_seq &= ~AHB_SEQ_BURST_MASK; + ch->ahb_seq |= AHB_SEQ_BURST_1; + + /* One shot mode is always single buffered, + * continuous mode is always double buffered + * */ + if (ch->mode & TEGRA_DMA_MODE_ONESHOT) { + ch->csr |= CSR_ONCE; + ch->ahb_seq &= ~AHB_SEQ_DBL_BUF; + ch->csr &= ~CSR_WCOUNT_MASK; + ch->csr |= ((req->size>>2) - 1) << CSR_WCOUNT_SHIFT; + } else { + ch->csr &= ~CSR_ONCE; + ch->ahb_seq |= AHB_SEQ_DBL_BUF; + + /* In double buffered mode, we set the size to half the + * requested size and interrupt when half the buffer + * is full */ + ch->csr &= ~CSR_WCOUNT_MASK; + ch->csr |= ((req->size>>3) - 1) << CSR_WCOUNT_SHIFT; + } + + if (req->to_memory) { + ch->csr &= ~CSR_DIR; + ch->apb_ptr = req->source_addr; + ch->ahb_ptr = req->dest_addr; + + apb_addr_wrap = req->source_wrap; + ahb_addr_wrap = req->dest_wrap; + apb_bus_width = req->source_bus_width; + ahb_bus_width = req->dest_bus_width; + + } else { + ch->csr |= CSR_DIR; + ch->apb_ptr = req->dest_addr; + ch->ahb_ptr = req->source_addr; + + apb_addr_wrap = req->dest_wrap; + ahb_addr_wrap = req->source_wrap; + apb_bus_width = req->dest_bus_width; + ahb_bus_width = req->source_bus_width; + } + + apb_addr_wrap >>= 2; + ahb_addr_wrap >>= 2; + + /* set address wrap for APB size */ + index = 0; + do { + if (apb_addr_wrap_table[index] == apb_addr_wrap) + break; + index++; + } while (index < ARRAY_SIZE(apb_addr_wrap_table)); + BUG_ON(index == ARRAY_SIZE(apb_addr_wrap_table)); + ch->apb_seq &= ~APB_SEQ_WRAP_MASK; + ch->apb_seq |= index << APB_SEQ_WRAP_SHIFT; + + /* set address wrap for AHB size */ + index = 0; + do { + if (ahb_addr_wrap_table[index] == ahb_addr_wrap) + break; + index++; + } while (index < ARRAY_SIZE(ahb_addr_wrap_table)); + BUG_ON(index == ARRAY_SIZE(ahb_addr_wrap_table)); + ch->ahb_seq &= ~AHB_SEQ_WRAP_MASK; + ch->ahb_seq |= index << AHB_SEQ_WRAP_SHIFT; + + for (index = 0; index < ARRAY_SIZE(bus_width_table); index++) { + if (bus_width_table[index] == ahb_bus_width) + break; + } + BUG_ON(index == ARRAY_SIZE(bus_width_table)); + ch->ahb_seq &= ~AHB_SEQ_BUS_WIDTH_MASK; + ch->ahb_seq |= index << AHB_SEQ_BUS_WIDTH_SHIFT; + + for (index = 0; index < ARRAY_SIZE(bus_width_table); index++) { + if (bus_width_table[index] == apb_bus_width) + break; + } + BUG_ON(index == ARRAY_SIZE(bus_width_table)); + ch->apb_seq &= ~APB_SEQ_BUS_WIDTH_MASK; + ch->apb_seq |= index << APB_SEQ_BUS_WIDTH_SHIFT; + + ch->csr |= CSR_IE_EOC; + + /* update hw registers with the shadow */ + writel(ch->csr, ch->addr + APB_DMA_CHAN_CSR); + writel(ch->apb_seq, ch->addr + APB_DMA_CHAN_APB_SEQ); + writel(ch->apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR); + writel(ch->ahb_seq, ch->addr + APB_DMA_CHAN_AHB_SEQ); + writel(ch->ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR); + + csr = ch->csr | CSR_ENB; + writel(csr, ch->addr + APB_DMA_CHAN_CSR); + + req->status = TEGRA_DMA_REQ_INFLIGHT; +} + +static void tegra_dma_init_hw(struct tegra_dma_channel *ch) +{ + /* One shot with an interrupt to CPU after transfer */ + ch->csr = CSR_ONCE | CSR_IE_EOC; + ch->ahb_seq = AHB_SEQ_BUS_WIDTH_32 | AHB_SEQ_INTR_ENB; + ch->apb_seq = APB_SEQ_BUS_WIDTH_32 | 1 << APB_SEQ_WRAP_SHIFT; +} + +static void handle_oneshot_dma(struct tegra_dma_channel *ch) +{ + struct tegra_dma_req *req; + + spin_lock(&ch->lock); + if (list_empty(&ch->list)) { + spin_unlock(&ch->lock); + return; + } + + req = list_entry(ch->list.next, typeof(*req), node); + if (req) { + int bytes_transferred; + + bytes_transferred = + (ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT; + bytes_transferred += 1; + bytes_transferred <<= 2; + + list_del(&req->node); + req->bytes_transferred = bytes_transferred; + req->status = TEGRA_DMA_REQ_SUCCESS; + + spin_unlock(&ch->lock); + /* Callback should be called without any lock */ + pr_debug("%s: transferred %d bytes\n", __func__, + req->bytes_transferred); + req->complete(req); + spin_lock(&ch->lock); + } + + if (!list_empty(&ch->list)) { + req = list_entry(ch->list.next, typeof(*req), node); + /* the complete function we just called may have enqueued + another req, in which case dma has already started */ + if (req->status != TEGRA_DMA_REQ_INFLIGHT) + tegra_dma_update_hw(ch, req); + } + spin_unlock(&ch->lock); +} + +static void handle_continuous_dma(struct tegra_dma_channel *ch) +{ + struct tegra_dma_req *req; + + spin_lock(&ch->lock); + if (list_empty(&ch->list)) { + spin_unlock(&ch->lock); + return; + } + + req = list_entry(ch->list.next, typeof(*req), node); + if (req) { + if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_EMPTY) { + /* Load the next request into the hardware, if available + * */ + if (!list_is_last(&req->node, &ch->list)) { + struct tegra_dma_req *next_req; + + next_req = list_entry(req->node.next, + typeof(*next_req), node); + tegra_dma_update_hw_partial(ch, next_req); + } + req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL; + req->status = TEGRA_DMA_REQ_SUCCESS; + /* DMA lock is NOT held when callback is called */ + spin_unlock(&ch->lock); + if (likely(req->threshold)) + req->threshold(req); + return; + + } else if (req->buffer_status == + TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) { + /* Callback when the buffer is completely full (i.e on + * the second interrupt */ + int bytes_transferred; + + bytes_transferred = + (ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT; + bytes_transferred += 1; + bytes_transferred <<= 3; + + req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL; + req->bytes_transferred = bytes_transferred; + req->status = TEGRA_DMA_REQ_SUCCESS; + list_del(&req->node); + + /* DMA lock is NOT held when callbak is called */ + spin_unlock(&ch->lock); + req->complete(req); + return; + + } else { + BUG(); + } + } + spin_unlock(&ch->lock); +} + +static irqreturn_t dma_isr(int irq, void *data) +{ + struct tegra_dma_channel *ch = data; + unsigned long status; + + status = readl(ch->addr + APB_DMA_CHAN_STA); + if (status & STA_ISE_EOC) + writel(status, ch->addr + APB_DMA_CHAN_STA); + else { + pr_warning("Got a spurious ISR for DMA channel %d\n", ch->id); + return IRQ_HANDLED; + } + return IRQ_WAKE_THREAD; +} + +static irqreturn_t dma_thread_fn(int irq, void *data) +{ + struct tegra_dma_channel *ch = data; + + if (ch->mode & TEGRA_DMA_MODE_ONESHOT) + handle_oneshot_dma(ch); + else + handle_continuous_dma(ch); + + + return IRQ_HANDLED; +} + +int __init tegra_dma_init(void) +{ + int ret = 0; + int i; + unsigned int irq; + void __iomem *addr; + + addr = IO_ADDRESS(TEGRA_APB_DMA_BASE); + writel(GEN_ENABLE, addr + APB_DMA_GEN); + writel(0, addr + APB_DMA_CNTRL); + writel(0xFFFFFFFFul >> (31 - TEGRA_SYSTEM_DMA_CH_MAX), + addr + APB_DMA_IRQ_MASK_SET); + + memset(channel_usage, 0, sizeof(channel_usage)); + memset(dma_channels, 0, sizeof(dma_channels)); + + /* Reserve all the channels we are not supposed to touch */ + for (i = 0; i < TEGRA_SYSTEM_DMA_CH_MIN; i++) + __set_bit(i, channel_usage); + + for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) { + struct tegra_dma_channel *ch = &dma_channels[i]; + + __clear_bit(i, channel_usage); + + ch->id = i; + snprintf(ch->name, TEGRA_DMA_NAME_SIZE, "dma_channel_%d", i); + + ch->addr = IO_ADDRESS(TEGRA_APB_DMA_CH0_BASE + + TEGRA_APB_DMA_CH0_SIZE * i); + + spin_lock_init(&ch->lock); + INIT_LIST_HEAD(&ch->list); + tegra_dma_init_hw(ch); + + irq = INT_APB_DMA_CH0 + i; + ret = request_threaded_irq(irq, dma_isr, dma_thread_fn, 0, + dma_channels[i].name, ch); + if (ret) { + pr_err("Failed to register IRQ %d for DMA %d\n", + irq, i); + goto fail; + } + ch->irq = irq; + } + /* mark the shared channel allocated */ + __set_bit(TEGRA_SYSTEM_DMA_CH_MIN, channel_usage); + + for (i = TEGRA_SYSTEM_DMA_CH_MAX+1; i < NV_DMA_MAX_CHANNELS; i++) + __set_bit(i, channel_usage); + + return ret; +fail: + writel(0, addr + APB_DMA_GEN); + for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) { + struct tegra_dma_channel *ch = &dma_channels[i]; + if (ch->irq) + free_irq(ch->irq, ch); + } + return ret; +} + +#ifdef CONFIG_PM +static u32 apb_dma[5*TEGRA_SYSTEM_DMA_CH_NR + 3]; + +void tegra_dma_suspend(void) +{ + void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE); + u32 *ctx = apb_dma; + int i; + + *ctx++ = readl(addr + APB_DMA_GEN); + *ctx++ = readl(addr + APB_DMA_CNTRL); + *ctx++ = readl(addr + APB_DMA_IRQ_MASK); + + for (i = 0; i < TEGRA_SYSTEM_DMA_CH_NR; i++) { + addr = IO_ADDRESS(TEGRA_APB_DMA_CH0_BASE + + TEGRA_APB_DMA_CH0_SIZE * i); + + *ctx++ = readl(addr + APB_DMA_CHAN_CSR); + *ctx++ = readl(addr + APB_DMA_CHAN_AHB_PTR); + *ctx++ = readl(addr + APB_DMA_CHAN_AHB_SEQ); + *ctx++ = readl(addr + APB_DMA_CHAN_APB_PTR); + *ctx++ = readl(addr + APB_DMA_CHAN_APB_SEQ); + } +} + +void tegra_dma_resume(void) +{ + void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE); + u32 *ctx = apb_dma; + int i; + + writel(*ctx++, addr + APB_DMA_GEN); + writel(*ctx++, addr + APB_DMA_CNTRL); + writel(*ctx++, addr + APB_DMA_IRQ_MASK); + + for (i = 0; i < TEGRA_SYSTEM_DMA_CH_NR; i++) { + addr = IO_ADDRESS(TEGRA_APB_DMA_CH0_BASE + + TEGRA_APB_DMA_CH0_SIZE * i); + + writel(*ctx++, addr + APB_DMA_CHAN_CSR); + writel(*ctx++, addr + APB_DMA_CHAN_AHB_PTR); + writel(*ctx++, addr + APB_DMA_CHAN_AHB_SEQ); + writel(*ctx++, addr + APB_DMA_CHAN_APB_PTR); + writel(*ctx++, addr + APB_DMA_CHAN_APB_SEQ); + } +} + +#endif diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c new file mode 100644 index 00000000000..1fa26d9a1a6 --- /dev/null +++ b/arch/arm/mach-tegra/fuse.c @@ -0,0 +1,84 @@ +/* + * arch/arm/mach-tegra/fuse.c + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross <ccross@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/kernel.h> +#include <linux/io.h> + +#include <mach/iomap.h> + +#include "fuse.h" + +#define FUSE_UID_LOW 0x108 +#define FUSE_UID_HIGH 0x10c +#define FUSE_SKU_INFO 0x110 +#define FUSE_SPARE_BIT 0x200 + +static inline u32 fuse_readl(unsigned long offset) +{ + return readl(IO_TO_VIRT(TEGRA_FUSE_BASE + offset)); +} + +static inline void fuse_writel(u32 value, unsigned long offset) +{ + writel(value, IO_TO_VIRT(TEGRA_FUSE_BASE + offset)); +} + +void tegra_init_fuse(void) +{ + u32 reg = readl(IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48)); + reg |= 1 << 28; + writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48)); + + pr_info("Tegra SKU: %d CPU Process: %d Core Process: %d\n", + tegra_sku_id(), tegra_cpu_process_id(), + tegra_core_process_id()); +} + +unsigned long long tegra_chip_uid(void) +{ + unsigned long long lo, hi; + + lo = fuse_readl(FUSE_UID_LOW); + hi = fuse_readl(FUSE_UID_HIGH); + return (hi << 32ull) | lo; +} + +int tegra_sku_id(void) +{ + int sku_id; + u32 reg = fuse_readl(FUSE_SKU_INFO); + sku_id = reg & 0xFF; + return sku_id; +} + +int tegra_cpu_process_id(void) +{ + int cpu_process_id; + u32 reg = fuse_readl(FUSE_SPARE_BIT); + cpu_process_id = (reg >> 6) & 3; + return cpu_process_id; +} + +int tegra_core_process_id(void) +{ + int core_process_id; + u32 reg = fuse_readl(FUSE_SPARE_BIT); + core_process_id = (reg >> 12) & 3; + return core_process_id; +} diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h new file mode 100644 index 00000000000..584b2e27dbd --- /dev/null +++ b/arch/arm/mach-tegra/fuse.h @@ -0,0 +1,24 @@ +/* + * arch/arm/mach-tegra/fuse.c + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross <ccross@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +unsigned long long tegra_chip_uid(void); +int tegra_sku_id(void); +int tegra_cpu_process_id(void); +int tegra_core_process_id(void); +void tegra_init_fuse(void); diff --git a/arch/arm/mach-tegra/gpio.c b/arch/arm/mach-tegra/gpio.c index fe78fba25f3..0775265e69f 100644 --- a/arch/arm/mach-tegra/gpio.c +++ b/arch/arm/mach-tegra/gpio.c @@ -19,6 +19,7 @@ #include <linux/init.h> #include <linux/irq.h> +#include <linux/interrupt.h> #include <linux/io.h> #include <linux/gpio.h> @@ -60,6 +61,13 @@ struct tegra_gpio_bank { int bank; int irq; spinlock_t lvl_lock[4]; +#ifdef CONFIG_PM + u32 cnf[4]; + u32 out[4]; + u32 oe[4]; + u32 int_enb[4]; + u32 int_lvl[4]; +#endif }; @@ -131,7 +139,7 @@ static struct gpio_chip tegra_gpio_chip = { .direction_output = tegra_gpio_direction_output, .set = tegra_gpio_set, .base = 0, - .ngpio = ARCH_NR_GPIOS, + .ngpio = TEGRA_NR_GPIOS, }; static void tegra_gpio_irq_ack(unsigned int irq) @@ -244,6 +252,76 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) } +#ifdef CONFIG_PM +void tegra_gpio_resume(void) +{ + unsigned long flags; + int b, p, i; + + local_irq_save(flags); + + for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) { + struct tegra_gpio_bank *bank = &tegra_gpio_banks[b]; + + for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { + unsigned int gpio = (b<<5) | (p<<3); + __raw_writel(bank->cnf[p], GPIO_CNF(gpio)); + __raw_writel(bank->out[p], GPIO_OUT(gpio)); + __raw_writel(bank->oe[p], GPIO_OE(gpio)); + __raw_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio)); + __raw_writel(bank->int_enb[p], GPIO_INT_ENB(gpio)); + } + } + + local_irq_restore(flags); + + for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) { + struct irq_desc *desc = irq_to_desc(i); + if (!desc || (desc->status & IRQ_WAKEUP)) + continue; + enable_irq(i); + } +} + +void tegra_gpio_suspend(void) +{ + unsigned long flags; + int b, p, i; + + for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) { + struct irq_desc *desc = irq_to_desc(i); + if (!desc) + continue; + if (desc->status & IRQ_WAKEUP) { + int gpio = i - INT_GPIO_BASE; + pr_debug("gpio %d.%d is wakeup\n", gpio/8, gpio&7); + continue; + } + disable_irq(i); + } + + local_irq_save(flags); + for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) { + struct tegra_gpio_bank *bank = &tegra_gpio_banks[b]; + + for (p = 0; p < ARRAY_SIZE(bank->oe); p++) { + unsigned int gpio = (b<<5) | (p<<3); + bank->cnf[p] = __raw_readl(GPIO_CNF(gpio)); + bank->out[p] = __raw_readl(GPIO_OUT(gpio)); + bank->oe[p] = __raw_readl(GPIO_OE(gpio)); + bank->int_enb[p] = __raw_readl(GPIO_INT_ENB(gpio)); + bank->int_lvl[p] = __raw_readl(GPIO_INT_LVL(gpio)); + } + } + local_irq_restore(flags); +} + +static int tegra_gpio_wake_enable(unsigned int irq, unsigned int enable) +{ + struct tegra_gpio_bank *bank = get_irq_chip_data(irq); + return set_irq_wake(bank->irq, enable); +} +#endif static struct irq_chip tegra_gpio_irq_chip = { .name = "GPIO", @@ -251,6 +329,9 @@ static struct irq_chip tegra_gpio_irq_chip = { .mask = tegra_gpio_irq_mask, .unmask = tegra_gpio_irq_unmask, .set_type = tegra_gpio_irq_set_type, +#ifdef CONFIG_PM + .set_wake = tegra_gpio_wake_enable, +#endif }; @@ -274,7 +355,7 @@ static int __init tegra_gpio_init(void) gpiochip_add(&tegra_gpio_chip); - for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + ARCH_NR_GPIOS); i++) { + for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) { bank = &tegra_gpio_banks[GPIO_BANK(irq_to_gpio(i))]; lockdep_set_class(&irq_desc[i].lock, &gpio_lock_class); @@ -312,15 +393,16 @@ static int dbg_gpio_show(struct seq_file *s, void *unused) for (i = 0; i < 7; i++) { for (j = 0; j < 4; j++) { int gpio = tegra_gpio_compose(i, j, 0); - seq_printf(s, "%d:%d %02x %02x %02x %02x %02x %02x %06x\n", - i, j, - __raw_readl(GPIO_CNF(gpio)), - __raw_readl(GPIO_OE(gpio)), - __raw_readl(GPIO_OUT(gpio)), - __raw_readl(GPIO_IN(gpio)), - __raw_readl(GPIO_INT_STA(gpio)), - __raw_readl(GPIO_INT_ENB(gpio)), - __raw_readl(GPIO_INT_LVL(gpio))); + seq_printf(s, + "%d:%d %02x %02x %02x %02x %02x %02x %06x\n", + i, j, + __raw_readl(GPIO_CNF(gpio)), + __raw_readl(GPIO_OE(gpio)), + __raw_readl(GPIO_OUT(gpio)), + __raw_readl(GPIO_IN(gpio)), + __raw_readl(GPIO_INT_STA(gpio)), + __raw_readl(GPIO_INT_ENB(gpio)), + __raw_readl(GPIO_INT_LVL(gpio))); } } return 0; diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h index 2896f25ebfb..d7723955dac 100644 --- a/arch/arm/mach-tegra/include/mach/clk.h +++ b/arch/arm/mach-tegra/include/mach/clk.h @@ -23,4 +23,9 @@ void tegra_periph_reset_deassert(struct clk *c); void tegra_periph_reset_assert(struct clk *c); +int clk_enable_cansleep(struct clk *clk); +void clk_disable_cansleep(struct clk *clk); +int clk_set_rate_cansleep(struct clk *clk, unsigned long rate); +int clk_set_parent_cansleep(struct clk *clk, struct clk *parent); + #endif diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h new file mode 100644 index 00000000000..39011bd9a92 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/dma.h @@ -0,0 +1,155 @@ +/* + * arch/arm/mach-tegra/include/mach/dma.h + * + * Copyright (c) 2008-2009, 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. + */ + +#ifndef __MACH_TEGRA_DMA_H +#define __MACH_TEGRA_DMA_H + +#include <linux/list.h> + +#if defined(CONFIG_TEGRA_SYSTEM_DMA) + +struct tegra_dma_req; +struct tegra_dma_channel; + +#define TEGRA_DMA_REQ_SEL_CNTR 0 +#define TEGRA_DMA_REQ_SEL_I2S_2 1 +#define TEGRA_DMA_REQ_SEL_I2S_1 2 +#define TEGRA_DMA_REQ_SEL_SPD_I 3 +#define TEGRA_DMA_REQ_SEL_UI_I 4 +#define TEGRA_DMA_REQ_SEL_MIPI 5 +#define TEGRA_DMA_REQ_SEL_I2S2_2 6 +#define TEGRA_DMA_REQ_SEL_I2S2_1 7 +#define TEGRA_DMA_REQ_SEL_UARTA 8 +#define TEGRA_DMA_REQ_SEL_UARTB 9 +#define TEGRA_DMA_REQ_SEL_UARTC 10 +#define TEGRA_DMA_REQ_SEL_SPI 11 +#define TEGRA_DMA_REQ_SEL_AC97 12 +#define TEGRA_DMA_REQ_SEL_ACMODEM 13 +#define TEGRA_DMA_REQ_SEL_SL4B 14 +#define TEGRA_DMA_REQ_SEL_SL2B1 15 +#define TEGRA_DMA_REQ_SEL_SL2B2 16 +#define TEGRA_DMA_REQ_SEL_SL2B3 17 +#define TEGRA_DMA_REQ_SEL_SL2B4 18 +#define TEGRA_DMA_REQ_SEL_UARTD 19 +#define TEGRA_DMA_REQ_SEL_UARTE 20 +#define TEGRA_DMA_REQ_SEL_I2C 21 +#define TEGRA_DMA_REQ_SEL_I2C2 22 +#define TEGRA_DMA_REQ_SEL_I2C3 23 +#define TEGRA_DMA_REQ_SEL_DVC_I2C 24 +#define TEGRA_DMA_REQ_SEL_OWR 25 +#define TEGRA_DMA_REQ_SEL_INVALID 31 + +enum tegra_dma_mode { + TEGRA_DMA_SHARED = 1, + TEGRA_DMA_MODE_CONTINOUS = 2, + TEGRA_DMA_MODE_ONESHOT = 4, +}; + +enum tegra_dma_req_error { + TEGRA_DMA_REQ_SUCCESS = 0, + TEGRA_DMA_REQ_ERROR_ABORTED, + TEGRA_DMA_REQ_INFLIGHT, +}; + +enum tegra_dma_req_buff_status { + TEGRA_DMA_REQ_BUF_STATUS_EMPTY = 0, + TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL, + TEGRA_DMA_REQ_BUF_STATUS_FULL, +}; + +struct tegra_dma_req { + struct list_head node; + unsigned int modid; + int instance; + + /* Called when the req is complete and from the DMA ISR context. + * When this is called the req structure is no longer queued by + * the DMA channel. + * + * State of the DMA depends on the number of req it has. If there are + * no DMA requests queued up, then it will STOP the DMA. It there are + * more requests in the DMA, then it will queue the next request. + */ + void (*complete)(struct tegra_dma_req *req); + + /* This is a called from the DMA ISR context when the DMA is still in + * progress and is actively filling same buffer. + * + * In case of continous mode receive, this threshold is 1/2 the buffer + * size. In other cases, this will not even be called as there is no + * hardware support for it. + * + * In the case of continous mode receive, if there is next req already + * queued, DMA programs the HW to use that req when this req is + * completed. If there is no "next req" queued, then DMA ISR doesn't do + * anything before calling this callback. + * + * This is mainly used by the cases, where the clients has queued + * only one req and want to get some sort of DMA threshold + * callback to program the next buffer. + * + */ + void (*threshold)(struct tegra_dma_req *req); + + /* 1 to copy to memory. + * 0 to copy from the memory to device FIFO */ + int to_memory; + + void *virt_addr; + + unsigned long source_addr; + unsigned long dest_addr; + unsigned long dest_wrap; + unsigned long source_wrap; + unsigned long source_bus_width; + unsigned long dest_bus_width; + unsigned long req_sel; + unsigned int size; + + /* Updated by the DMA driver on the conpletion of the request. */ + int bytes_transferred; + int status; + + /* DMA completion tracking information */ + int buffer_status; + + /* Client specific data */ + void *dev; +}; + +int tegra_dma_enqueue_req(struct tegra_dma_channel *ch, + struct tegra_dma_req *req); +int tegra_dma_dequeue_req(struct tegra_dma_channel *ch, + struct tegra_dma_req *req); +void tegra_dma_dequeue(struct tegra_dma_channel *ch); +void tegra_dma_flush(struct tegra_dma_channel *ch); + +bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch, + struct tegra_dma_req *req); +bool tegra_dma_is_empty(struct tegra_dma_channel *ch); + +struct tegra_dma_channel *tegra_dma_allocate_channel(int mode); +void tegra_dma_free_channel(struct tegra_dma_channel *ch); + +int __init tegra_dma_init(void); + +#endif + +#endif diff --git a/arch/arm/mach-tegra/include/mach/gpio.h b/arch/arm/mach-tegra/include/mach/gpio.h index 540e822e50f..e31f486d69a 100644 --- a/arch/arm/mach-tegra/include/mach/gpio.h +++ b/arch/arm/mach-tegra/include/mach/gpio.h @@ -22,7 +22,7 @@ #include <mach/irqs.h> -#define ARCH_NR_GPIOS INT_GPIO_NR +#define TEGRA_NR_GPIOS INT_GPIO_NR #include <asm-generic/gpio.h> @@ -35,7 +35,7 @@ static inline int gpio_to_irq(unsigned int gpio) { - if (gpio < ARCH_NR_GPIOS) + if (gpio < TEGRA_NR_GPIOS) return INT_GPIO_BASE + gpio; return -EINVAL; } diff --git a/arch/arm/mach-tegra/include/mach/hardware.h b/arch/arm/mach-tegra/include/mach/hardware.h index 6014edf60d9..56e43b3a5b9 100644 --- a/arch/arm/mach-tegra/include/mach/hardware.h +++ b/arch/arm/mach-tegra/include/mach/hardware.h @@ -21,4 +21,8 @@ #ifndef __MACH_TEGRA_HARDWARE_H #define __MACH_TEGRA_HARDWARE_H +#define PCIBIOS_MIN_IO 0x1000 +#define PCIBIOS_MIN_MEM 0 +#define pcibios_assign_all_busses() 1 + #endif diff --git a/arch/arm/mach-tegra/include/mach/io.h b/arch/arm/mach-tegra/include/mach/io.h index 35edfc32ffc..f0981b1ac59 100644 --- a/arch/arm/mach-tegra/include/mach/io.h +++ b/arch/arm/mach-tegra/include/mach/io.h @@ -21,7 +21,7 @@ #ifndef __MACH_TEGRA_IO_H #define __MACH_TEGRA_IO_H -#define IO_SPACE_LIMIT 0xffffffff +#define IO_SPACE_LIMIT 0xffff /* On TEGRA, many peripherals are very closely packed in * two 256MB io windows (that actually only use about 64KB @@ -33,6 +33,10 @@ * */ +#define IO_IRAM_PHYS 0x40000000 +#define IO_IRAM_VIRT 0xFE400000 +#define IO_IRAM_SIZE SZ_256K + #define IO_CPU_PHYS 0x50040000 #define IO_CPU_VIRT 0xFE000000 #define IO_CPU_SIZE SZ_16K @@ -55,6 +59,8 @@ IO_TO_VIRT_XLATE((n), IO_APB_PHYS, IO_APB_VIRT) : \ IO_TO_VIRT_BETWEEN((n), IO_CPU_PHYS, IO_CPU_SIZE) ? \ IO_TO_VIRT_XLATE((n), IO_CPU_PHYS, IO_CPU_VIRT) : \ + IO_TO_VIRT_BETWEEN((n), IO_IRAM_PHYS, IO_IRAM_SIZE) ? \ + IO_TO_VIRT_XLATE((n), IO_IRAM_PHYS, IO_IRAM_VIRT) : \ 0) #ifndef __ASSEMBLER__ @@ -67,10 +73,20 @@ void tegra_iounmap(volatile void __iomem *addr); #define IO_ADDRESS(n) ((void __iomem *) IO_TO_VIRT(n)) +#ifdef CONFIG_TEGRA_PCI +extern void __iomem *tegra_pcie_io_base; + +static inline void __iomem *__io(unsigned long addr) +{ + return tegra_pcie_io_base + (addr & IO_SPACE_LIMIT); +} +#else static inline void __iomem *__io(unsigned long addr) { return (void __iomem *)addr; } +#endif + #define __io(a) __io(a) #define __mem_pci(a) (a) diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h index 1741f7dd7a9..44a4f4bcf91 100644 --- a/arch/arm/mach-tegra/include/mach/iomap.h +++ b/arch/arm/mach-tegra/include/mach/iomap.h @@ -23,9 +23,15 @@ #include <asm/sizes.h> +#define TEGRA_IRAM_BASE 0x40000000 +#define TEGRA_IRAM_SIZE SZ_256K + #define TEGRA_ARM_PERIF_BASE 0x50040000 #define TEGRA_ARM_PERIF_SIZE SZ_8K +#define TEGRA_ARM_PL310_BASE 0x50043000 +#define TEGRA_ARM_PL310_SIZE SZ_4K + #define TEGRA_ARM_INT_DIST_BASE 0x50041000 #define TEGRA_ARM_INT_DIST_SIZE SZ_4K @@ -68,7 +74,22 @@ #define TEGRA_FLOW_CTRL_BASE 0x60007000 #define TEGRA_FLOW_CTRL_SIZE 20 -#define TEGRA_STATMON_BASE 0x6000C4000 +#define TEGRA_AHB_DMA_BASE 0x60008000 +#define TEGRA_AHB_DMA_SIZE SZ_4K + +#define TEGRA_AHB_DMA_CH0_BASE 0x60009000 +#define TEGRA_AHB_DMA_CH0_SIZE 32 + +#define TEGRA_APB_DMA_BASE 0x6000A000 +#define TEGRA_APB_DMA_SIZE SZ_4K + +#define TEGRA_APB_DMA_CH0_BASE 0x6000B000 +#define TEGRA_APB_DMA_CH0_SIZE 32 + +#define TEGRA_AHB_GIZMO_BASE 0x6000C004 +#define TEGRA_AHB_GIZMO_SIZE 0x10C + +#define TEGRA_STATMON_BASE 0x6000C400 #define TEGRA_STATMON_SIZE SZ_1K #define TEGRA_GPIO_BASE 0x6000D000 @@ -137,7 +158,7 @@ #define TEGRA_I2C3_BASE 0x7000C500 #define TEGRA_I2C3_SIZE SZ_256 -#define TEGRA_OWR_BASE 0x7000D000 +#define TEGRA_OWR_BASE 0x7000C600 #define TEGRA_OWR_SIZE 80 #define TEGRA_DVC_BASE 0x7000D000 @@ -182,12 +203,12 @@ #define TEGRA_USB_BASE 0xC5000000 #define TEGRA_USB_SIZE SZ_16K -#define TEGRA_USB1_BASE 0xC5004000 -#define TEGRA_USB1_SIZE SZ_16K - -#define TEGRA_USB2_BASE 0xC5008000 +#define TEGRA_USB2_BASE 0xC5004000 #define TEGRA_USB2_SIZE SZ_16K +#define TEGRA_USB3_BASE 0xC5008000 +#define TEGRA_USB3_SIZE SZ_16K + #define TEGRA_SDMMC1_BASE 0xC8000000 #define TEGRA_SDMMC1_SIZE SZ_512 diff --git a/arch/arm/mach-tegra/include/mach/irqs.h b/arch/arm/mach-tegra/include/mach/irqs.h index 20f640edaa0..71bbf342295 100644 --- a/arch/arm/mach-tegra/include/mach/irqs.h +++ b/arch/arm/mach-tegra/include/mach/irqs.h @@ -25,6 +25,7 @@ #define IRQ_LOCALTIMER 29 +#ifdef CONFIG_ARCH_TEGRA_2x_SOC /* Primary Interrupt Controller */ #define INT_PRI_BASE (INT_GIC_BASE + 32) #define INT_TMR1 (INT_PRI_BASE + 0) @@ -169,5 +170,6 @@ #define INT_GPIO_NR (28 * 8) #define NR_IRQS (INT_GPIO_BASE + INT_GPIO_NR) +#endif #endif diff --git a/arch/arm/mach-tegra/include/mach/legacy_irq.h b/arch/arm/mach-tegra/include/mach/legacy_irq.h new file mode 100644 index 00000000000..db1eb3dd04c --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/legacy_irq.h @@ -0,0 +1,31 @@ +/* + * arch/arm/mach-tegra/include/mach/legacy_irq.h + * + * Copyright (C) 2010 Google, Inc. + * Author: Colin Cross <ccross@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef _ARCH_ARM_MACH_TEGRA_LEGARY_IRQ_H +#define _ARCH_ARM_MACH_TEGRA_LEGARY_IRQ_H + +void tegra_legacy_mask_irq(unsigned int irq); +void tegra_legacy_unmask_irq(unsigned int irq); +void tegra_legacy_select_fiq(unsigned int irq, bool fiq); +void tegra_legacy_force_irq_set(unsigned int irq); +void tegra_legacy_force_irq_clr(unsigned int irq); +int tegra_legacy_force_irq_status(unsigned int irq); +void tegra_legacy_select_fiq(unsigned int irq, bool fiq); +unsigned long tegra_legacy_vfiq(int nr); +unsigned long tegra_legacy_class(int nr); + +#endif diff --git a/arch/arm/mach-tegra/include/mach/pinmux-t2.h b/arch/arm/mach-tegra/include/mach/pinmux-t2.h new file mode 100644 index 00000000000..e5b9d740f97 --- /dev/null +++ b/arch/arm/mach-tegra/include/mach/pinmux-t2.h @@ -0,0 +1,174 @@ +/* + * linux/arch/arm/mach-tegra/include/mach/pinmux-t2.h + * + * Copyright (C) 2010 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#ifndef __MACH_TEGRA_PINMUX_T2_H +#define __MACH_TEGRA_PINMUX_T2_H + +enum tegra_pingroup { + TEGRA_PINGROUP_ATA = 0, + TEGRA_PINGROUP_ATB, + TEGRA_PINGROUP_ATC, + TEGRA_PINGROUP_ATD, + TEGRA_PINGROUP_ATE, + TEGRA_PINGROUP_CDEV1, + TEGRA_PINGROUP_CDEV2, + TEGRA_PINGROUP_CRTP, + TEGRA_PINGROUP_CSUS, + TEGRA_PINGROUP_DAP1, + TEGRA_PINGROUP_DAP2, + TEGRA_PINGROUP_DAP3, + TEGRA_PINGROUP_DAP4, + TEGRA_PINGROUP_DDC, + TEGRA_PINGROUP_DTA, + TEGRA_PINGROUP_DTB, + TEGRA_PINGROUP_DTC, + TEGRA_PINGROUP_DTD, + TEGRA_PINGROUP_DTE, + TEGRA_PINGROUP_DTF, + TEGRA_PINGROUP_GMA, + TEGRA_PINGROUP_GMB, + TEGRA_PINGROUP_GMC, + TEGRA_PINGROUP_GMD, + TEGRA_PINGROUP_GME, + TEGRA_PINGROUP_GPU, + TEGRA_PINGROUP_GPU7, + TEGRA_PINGROUP_GPV, + TEGRA_PINGROUP_HDINT, + TEGRA_PINGROUP_I2CP, + TEGRA_PINGROUP_IRRX, + TEGRA_PINGROUP_IRTX, + TEGRA_PINGROUP_KBCA, + TEGRA_PINGROUP_KBCB, + TEGRA_PINGROUP_KBCC, + TEGRA_PINGROUP_KBCD, + TEGRA_PINGROUP_KBCE, + TEGRA_PINGROUP_KBCF, + TEGRA_PINGROUP_LCSN, + TEGRA_PINGROUP_LD0, + TEGRA_PINGROUP_LD1, + TEGRA_PINGROUP_LD10, + TEGRA_PINGROUP_LD11, + TEGRA_PINGROUP_LD12, + TEGRA_PINGROUP_LD13, + TEGRA_PINGROUP_LD14, + TEGRA_PINGROUP_LD15, + TEGRA_PINGROUP_LD16, + TEGRA_PINGROUP_LD17, + TEGRA_PINGROUP_LD2, + TEGRA_PINGROUP_LD3, + TEGRA_PINGROUP_LD4, + TEGRA_PINGROUP_LD5, + TEGRA_PINGROUP_LD6, + TEGRA_PINGROUP_LD7, + TEGRA_PINGROUP_LD8, + TEGRA_PINGROUP_LD9, + TEGRA_PINGROUP_LDC, + TEGRA_PINGROUP_LDI, + TEGRA_PINGROUP_LHP0, + TEGRA_PINGROUP_LHP1, + TEGRA_PINGROUP_LHP2, + TEGRA_PINGROUP_LHS, + TEGRA_PINGROUP_LM0, + TEGRA_PINGROUP_LM1, + TEGRA_PINGROUP_LPP, + TEGRA_PINGROUP_LPW0, + TEGRA_PINGROUP_LPW1, + TEGRA_PINGROUP_LPW2, + TEGRA_PINGROUP_LSC0, + TEGRA_PINGROUP_LSC1, + TEGRA_PINGROUP_LSCK, + TEGRA_PINGROUP_LSDA, + TEGRA_PINGROUP_LSDI, + TEGRA_PINGROUP_LSPI, + TEGRA_PINGROUP_LVP0, + TEGRA_PINGROUP_LVP1, + TEGRA_PINGROUP_LVS, + TEGRA_PINGROUP_OWC, + TEGRA_PINGROUP_PMC, + TEGRA_PINGROUP_PTA, + TEGRA_PINGROUP_RM, + TEGRA_PINGROUP_SDB, + TEGRA_PINGROUP_SDC, + TEGRA_PINGROUP_SDD, + TEGRA_PINGROUP_SDIO1, + TEGRA_PINGROUP_SLXA, + TEGRA_PINGROUP_SLXC, + TEGRA_PINGROUP_SLXD, + TEGRA_PINGROUP_SLXK, + TEGRA_PINGROUP_SPDI, + TEGRA_PINGROUP_SPDO, + TEGRA_PINGROUP_SPIA, + TEGRA_PINGROUP_SPIB, + TEGRA_PINGROUP_SPIC, + TEGRA_PINGROUP_SPID, + TEGRA_PINGROUP_SPIE, + TEGRA_PINGROUP_SPIF, + TEGRA_PINGROUP_SPIG, + TEGRA_PINGROUP_SPIH, + TEGRA_PINGROUP_UAA, + TEGRA_PINGROUP_UAB, + TEGRA_PINGROUP_UAC, + TEGRA_PINGROUP_UAD, + TEGRA_PINGROUP_UCA, + TEGRA_PINGROUP_UCB, + TEGRA_PINGROUP_UDA, + /* these pin groups only have pullup and pull down control */ + TEGRA_PINGROUP_CK32, + TEGRA_PINGROUP_DDRC, + TEGRA_PINGROUP_PMCA, + TEGRA_PINGROUP_PMCB, + TEGRA_PINGROUP_PMCC, + TEGRA_PINGROUP_PMCD, + TEGRA_PINGROUP_PMCE, + TEGRA_PINGROUP_XM2C, + TEGRA_PINGROUP_XM2D, + TEGRA_MAX_PINGROUP, +}; + +enum tegra_drive_pingroup { + TEGRA_DRIVE_PINGROUP_AO1 = 0, + TEGRA_DRIVE_PINGROUP_AO2, + TEGRA_DRIVE_PINGROUP_AT1, + TEGRA_DRIVE_PINGROUP_AT2, + TEGRA_DRIVE_PINGROUP_CDEV1, + TEGRA_DRIVE_PINGROUP_CDEV2, + TEGRA_DRIVE_PINGROUP_CSUS, + TEGRA_DRIVE_PINGROUP_DAP1, + TEGRA_DRIVE_PINGROUP_DAP2, + TEGRA_DRIVE_PINGROUP_DAP3, + TEGRA_DRIVE_PINGROUP_DAP4, + TEGRA_DRIVE_PINGROUP_DBG, + TEGRA_DRIVE_PINGROUP_LCD1, + TEGRA_DRIVE_PINGROUP_LCD2, + TEGRA_DRIVE_PINGROUP_SDMMC2, + TEGRA_DRIVE_PINGROUP_SDMMC3, + TEGRA_DRIVE_PINGROUP_SPI, + TEGRA_DRIVE_PINGROUP_UAA, + TEGRA_DRIVE_PINGROUP_UAB, + TEGRA_DRIVE_PINGROUP_UART2, + TEGRA_DRIVE_PINGROUP_UART3, + TEGRA_DRIVE_PINGROUP_VI1, + TEGRA_DRIVE_PINGROUP_VI2, + TEGRA_DRIVE_PINGROUP_XM2A, + TEGRA_DRIVE_PINGROUP_XM2C, + TEGRA_DRIVE_PINGROUP_XM2D, + TEGRA_DRIVE_PINGROUP_XM2CLK, + TEGRA_DRIVE_PINGROUP_MEMCOMP, + TEGRA_MAX_DRIVE_PINGROUP, +}; + +#endif + diff --git a/arch/arm/mach-tegra/include/mach/pinmux.h b/arch/arm/mach-tegra/include/mach/pinmux.h index 41c8ce5b7c2..defd8775def 100644 --- a/arch/arm/mach-tegra/include/mach/pinmux.h +++ b/arch/arm/mach-tegra/include/mach/pinmux.h @@ -17,126 +17,11 @@ #ifndef __MACH_TEGRA_PINMUX_H #define __MACH_TEGRA_PINMUX_H -enum tegra_pingroup { - TEGRA_PINGROUP_ATA = 0, - TEGRA_PINGROUP_ATB, - TEGRA_PINGROUP_ATC, - TEGRA_PINGROUP_ATD, - TEGRA_PINGROUP_ATE, - TEGRA_PINGROUP_CDEV1, - TEGRA_PINGROUP_CDEV2, - TEGRA_PINGROUP_CRTP, - TEGRA_PINGROUP_CSUS, - TEGRA_PINGROUP_DAP1, - TEGRA_PINGROUP_DAP2, - TEGRA_PINGROUP_DAP3, - TEGRA_PINGROUP_DAP4, - TEGRA_PINGROUP_DDC, - TEGRA_PINGROUP_DTA, - TEGRA_PINGROUP_DTB, - TEGRA_PINGROUP_DTC, - TEGRA_PINGROUP_DTD, - TEGRA_PINGROUP_DTE, - TEGRA_PINGROUP_DTF, - TEGRA_PINGROUP_GMA, - TEGRA_PINGROUP_GMB, - TEGRA_PINGROUP_GMC, - TEGRA_PINGROUP_GMD, - TEGRA_PINGROUP_GME, - TEGRA_PINGROUP_GPU, - TEGRA_PINGROUP_GPU7, - TEGRA_PINGROUP_GPV, - TEGRA_PINGROUP_HDINT, - TEGRA_PINGROUP_I2CP, - TEGRA_PINGROUP_IRRX, - TEGRA_PINGROUP_IRTX, - TEGRA_PINGROUP_KBCA, - TEGRA_PINGROUP_KBCB, - TEGRA_PINGROUP_KBCC, - TEGRA_PINGROUP_KBCD, - TEGRA_PINGROUP_KBCE, - TEGRA_PINGROUP_KBCF, - TEGRA_PINGROUP_LCSN, - TEGRA_PINGROUP_LD0, - TEGRA_PINGROUP_LD1, - TEGRA_PINGROUP_LD10, - TEGRA_PINGROUP_LD11, - TEGRA_PINGROUP_LD12, - TEGRA_PINGROUP_LD13, - TEGRA_PINGROUP_LD14, - TEGRA_PINGROUP_LD15, - TEGRA_PINGROUP_LD16, - TEGRA_PINGROUP_LD17, - TEGRA_PINGROUP_LD2, - TEGRA_PINGROUP_LD3, - TEGRA_PINGROUP_LD4, - TEGRA_PINGROUP_LD5, - TEGRA_PINGROUP_LD6, - TEGRA_PINGROUP_LD7, - TEGRA_PINGROUP_LD8, - TEGRA_PINGROUP_LD9, - TEGRA_PINGROUP_LDC, - TEGRA_PINGROUP_LDI, - TEGRA_PINGROUP_LHP0, - TEGRA_PINGROUP_LHP1, - TEGRA_PINGROUP_LHP2, - TEGRA_PINGROUP_LHS, - TEGRA_PINGROUP_LM0, - TEGRA_PINGROUP_LM1, - TEGRA_PINGROUP_LPP, - TEGRA_PINGROUP_LPW0, - TEGRA_PINGROUP_LPW1, - TEGRA_PINGROUP_LPW2, - TEGRA_PINGROUP_LSC0, - TEGRA_PINGROUP_LSC1, - TEGRA_PINGROUP_LSCK, - TEGRA_PINGROUP_LSDA, - TEGRA_PINGROUP_LSDI, - TEGRA_PINGROUP_LSPI, - TEGRA_PINGROUP_LVP0, - TEGRA_PINGROUP_LVP1, - TEGRA_PINGROUP_LVS, - TEGRA_PINGROUP_OWC, - TEGRA_PINGROUP_PMC, - TEGRA_PINGROUP_PTA, - TEGRA_PINGROUP_RM, - TEGRA_PINGROUP_SDB, - TEGRA_PINGROUP_SDC, - TEGRA_PINGROUP_SDD, - TEGRA_PINGROUP_SDIO1, - TEGRA_PINGROUP_SLXA, - TEGRA_PINGROUP_SLXC, - TEGRA_PINGROUP_SLXD, - TEGRA_PINGROUP_SLXK, - TEGRA_PINGROUP_SPDI, - TEGRA_PINGROUP_SPDO, - TEGRA_PINGROUP_SPIA, - TEGRA_PINGROUP_SPIB, - TEGRA_PINGROUP_SPIC, - TEGRA_PINGROUP_SPID, - TEGRA_PINGROUP_SPIE, - TEGRA_PINGROUP_SPIF, - TEGRA_PINGROUP_SPIG, - TEGRA_PINGROUP_SPIH, - TEGRA_PINGROUP_UAA, - TEGRA_PINGROUP_UAB, - TEGRA_PINGROUP_UAC, - TEGRA_PINGROUP_UAD, - TEGRA_PINGROUP_UCA, - TEGRA_PINGROUP_UCB, - TEGRA_PINGROUP_UDA, - /* these pin groups only have pullup and pull down control */ - TEGRA_PINGROUP_CK32, - TEGRA_PINGROUP_DDRC, - TEGRA_PINGROUP_PMCA, - TEGRA_PINGROUP_PMCB, - TEGRA_PINGROUP_PMCC, - TEGRA_PINGROUP_PMCD, - TEGRA_PINGROUP_PMCE, - TEGRA_PINGROUP_XM2C, - TEGRA_PINGROUP_XM2D, - TEGRA_MAX_PINGROUP, -}; +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) +#include "pinmux-t2.h" +#else +#error "Undefined Tegra architecture" +#endif enum tegra_mux_func { TEGRA_MUX_RSVD = 0x8000, @@ -205,6 +90,7 @@ enum tegra_mux_func { TEGRA_MUX_VI, TEGRA_MUX_VI_SENSOR_CLK, TEGRA_MUX_XIO, + TEGRA_MUX_SAFE, TEGRA_MAX_MUX, }; @@ -219,6 +105,18 @@ enum tegra_tristate { TEGRA_TRI_TRISTATE = 1, }; +enum tegra_vddio { + TEGRA_VDDIO_BB = 0, + TEGRA_VDDIO_LCD, + TEGRA_VDDIO_VI, + TEGRA_VDDIO_UART, + TEGRA_VDDIO_DDR, + TEGRA_VDDIO_NAND, + TEGRA_VDDIO_SYS, + TEGRA_VDDIO_AUDIO, + TEGRA_VDDIO_SD, +}; + struct tegra_pingroup_config { enum tegra_pingroup pingroup; enum tegra_mux_func func; @@ -270,38 +168,6 @@ enum tegra_pull_strength { TEGRA_MAX_PULL, }; -enum tegra_drive_pingroup { - TEGRA_DRIVE_PINGROUP_AO1 = 0, - TEGRA_DRIVE_PINGROUP_AO2, - TEGRA_DRIVE_PINGROUP_AT1, - TEGRA_DRIVE_PINGROUP_AT2, - TEGRA_DRIVE_PINGROUP_CDEV1, - TEGRA_DRIVE_PINGROUP_CDEV2, - TEGRA_DRIVE_PINGROUP_CSUS, - TEGRA_DRIVE_PINGROUP_DAP1, - TEGRA_DRIVE_PINGROUP_DAP2, - TEGRA_DRIVE_PINGROUP_DAP3, - TEGRA_DRIVE_PINGROUP_DAP4, - TEGRA_DRIVE_PINGROUP_DBG, - TEGRA_DRIVE_PINGROUP_LCD1, - TEGRA_DRIVE_PINGROUP_LCD2, - TEGRA_DRIVE_PINGROUP_SDMMC2, - TEGRA_DRIVE_PINGROUP_SDMMC3, - TEGRA_DRIVE_PINGROUP_SPI, - TEGRA_DRIVE_PINGROUP_UAA, - TEGRA_DRIVE_PINGROUP_UAB, - TEGRA_DRIVE_PINGROUP_UART2, - TEGRA_DRIVE_PINGROUP_UART3, - TEGRA_DRIVE_PINGROUP_VI1, - TEGRA_DRIVE_PINGROUP_VI2, - TEGRA_DRIVE_PINGROUP_XM2A, - TEGRA_DRIVE_PINGROUP_XM2C, - TEGRA_DRIVE_PINGROUP_XM2D, - TEGRA_DRIVE_PINGROUP_XM2CLK, - TEGRA_DRIVE_PINGROUP_MEMCOMP, - TEGRA_MAX_DRIVE_PINGROUP, -}; - enum tegra_drive { TEGRA_DRIVE_DIV_8 = 0, TEGRA_DRIVE_DIV_4, @@ -331,18 +197,44 @@ struct tegra_drive_pingroup_config { enum tegra_slew slew_falling; }; -int tegra_pinmux_set_func(enum tegra_pingroup pg, enum tegra_mux_func func); -int tegra_pinmux_set_tristate(enum tegra_pingroup pg, enum tegra_tristate tristate); -int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg, enum tegra_pullupdown pupd); +struct tegra_drive_pingroup_desc { + const char *name; + s16 reg; +}; + +struct tegra_pingroup_desc { + const char *name; + int funcs[4]; + int func_safe; + int vddio; + s16 tri_reg; /* offset into the TRISTATE_REG_* register bank */ + s16 mux_reg; /* offset into the PIN_MUX_CTL_* register bank */ + s16 pupd_reg; /* offset into the PULL_UPDOWN_REG_* register bank */ + s8 tri_bit; /* offset into the TRISTATE_REG_* register bit */ + s8 mux_bit; /* offset into the PIN_MUX_CTL_* register bit */ + s8 pupd_bit; /* offset into the PULL_UPDOWN_REG_* register bit */ +}; + +extern const struct tegra_pingroup_desc tegra_soc_pingroups[]; +extern const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[]; -void tegra_pinmux_config_pingroup(enum tegra_pingroup pingroup, - enum tegra_mux_func func, enum tegra_pullupdown pupd, +int tegra_pinmux_set_tristate(enum tegra_pingroup pg, enum tegra_tristate tristate); +int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg, + enum tegra_pullupdown pupd); -void tegra_pinmux_config_table(struct tegra_pingroup_config *config, int len); +void tegra_pinmux_config_table(const struct tegra_pingroup_config *config, + int len); void tegra_drive_pinmux_config_table(struct tegra_drive_pingroup_config *config, int len); - +void tegra_pinmux_set_safe_pinmux_table(const struct tegra_pingroup_config *config, + int len); +void tegra_pinmux_config_pinmux_table(const struct tegra_pingroup_config *config, + int len); +void tegra_pinmux_config_tristate_table(const struct tegra_pingroup_config *config, + int len, enum tegra_tristate tristate); +void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *config, + int len, enum tegra_pullupdown pupd); #endif diff --git a/arch/arm/mach-tegra/io.c b/arch/arm/mach-tegra/io.c index 9fe2c5c683d..31848a9592f 100644 --- a/arch/arm/mach-tegra/io.c +++ b/arch/arm/mach-tegra/io.c @@ -49,6 +49,12 @@ static struct map_desc tegra_io_desc[] __initdata = { .length = IO_CPU_SIZE, .type = MT_DEVICE, }, + { + .virtual = IO_IRAM_VIRT, + .pfn = __phys_to_pfn(IO_IRAM_PHYS), + .length = IO_IRAM_SIZE, + .type = MT_DEVICE, + }, }; void __init tegra_map_common_io(void) diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c index 1fdbe708d43..50a8dfb9a0c 100644 --- a/arch/arm/mach-tegra/irq.c +++ b/arch/arm/mach-tegra/irq.c @@ -4,6 +4,8 @@ * Author: * Colin Cross <ccross@google.com> * + * Copyright (C) 2010, NVIDIA Corporation + * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. @@ -27,8 +29,143 @@ #include "board.h" +#define INT_SYS_NR (INT_GPIO_BASE - INT_PRI_BASE) +#define INT_SYS_SZ (INT_SEC_BASE - INT_PRI_BASE) +#define PPI_NR ((INT_SYS_NR+INT_SYS_SZ-1)/INT_SYS_SZ) + +#define APBDMA_IRQ_STA_CPU 0x14 +#define APBDMA_IRQ_MASK_SET 0x20 +#define APBDMA_IRQ_MASK_CLR 0x24 + +#define ICTLR_CPU_IER 0x20 +#define ICTLR_CPU_IER_SET 0x24 +#define ICTLR_CPU_IER_CLR 0x28 +#define ICTLR_CPU_IEP_CLASS 0x2c +#define ICTLR_COP_IER 0x30 +#define ICTLR_COP_IER_SET 0x34 +#define ICTLR_COP_IER_CLR 0x38 +#define ICTLR_COP_IEP_CLASS 0x3c + +static void (*gic_mask_irq)(unsigned int irq); +static void (*gic_unmask_irq)(unsigned int irq); + +#define irq_to_ictlr(irq) (((irq)-32) >> 5) +static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE); +#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr)*0x100) + +static void tegra_mask(unsigned int irq) +{ + void __iomem *addr = ictlr_to_virt(irq_to_ictlr(irq)); + gic_mask_irq(irq); + writel(1<<(irq&31), addr+ICTLR_CPU_IER_CLR); +} + +static void tegra_unmask(unsigned int irq) +{ + void __iomem *addr = ictlr_to_virt(irq_to_ictlr(irq)); + gic_unmask_irq(irq); + writel(1<<(irq&31), addr+ICTLR_CPU_IER_SET); +} + +#ifdef CONFIG_PM + +static int tegra_set_wake(unsigned int irq, unsigned int on) +{ + return 0; +} +#endif + +static struct irq_chip tegra_irq = { + .name = "PPI", + .mask = tegra_mask, + .unmask = tegra_unmask, +#ifdef CONFIG_PM + .set_wake = tegra_set_wake, +#endif +}; + void __init tegra_init_irq(void) { + struct irq_chip *gic; + unsigned int i; + + for (i = 0; i < PPI_NR; i++) { + writel(~0, ictlr_to_virt(i) + ICTLR_CPU_IER_CLR); + writel(0, ictlr_to_virt(i) + ICTLR_CPU_IEP_CLASS); + } + gic_dist_init(0, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE), 29); gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100)); + + gic = get_irq_chip(29); + gic_unmask_irq = gic->unmask; + gic_mask_irq = gic->mask; + tegra_irq.ack = gic->ack; +#ifdef CONFIG_SMP + tegra_irq.set_affinity = gic->set_affinity; +#endif + + for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) { + set_irq_chip(i, &tegra_irq); + set_irq_handler(i, handle_level_irq); + set_irq_flags(i, IRQF_VALID); + } +} + +#ifdef CONFIG_PM +static u32 cop_ier[PPI_NR]; +static u32 cpu_ier[PPI_NR]; +static u32 cpu_iep[PPI_NR]; + +void tegra_irq_suspend(void) +{ + unsigned long flags; + int i; + + for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) { + struct irq_desc *desc = irq_to_desc(i); + if (!desc) + continue; + if (desc->status & IRQ_WAKEUP) { + pr_debug("irq %d is wakeup\n", i); + continue; + } + disable_irq(i); + } + + local_irq_save(flags); + for (i = 0; i < PPI_NR; i++) { + void __iomem *ictlr = ictlr_to_virt(i); + cpu_ier[i] = readl(ictlr + ICTLR_CPU_IER); + cpu_iep[i] = readl(ictlr + ICTLR_CPU_IEP_CLASS); + cop_ier[i] = readl(ictlr + ICTLR_COP_IER); + writel(~0, ictlr + ICTLR_COP_IER_CLR); + } + local_irq_restore(flags); +} + +void tegra_irq_resume(void) +{ + unsigned long flags; + int i; + + local_irq_save(flags); + for (i = 0; i < PPI_NR; i++) { + void __iomem *ictlr = ictlr_to_virt(i); + writel(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS); + writel(~0ul, ictlr + ICTLR_CPU_IER_CLR); + writel(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET); + writel(0, ictlr + ICTLR_COP_IEP_CLASS); + writel(~0ul, ictlr + ICTLR_COP_IER_CLR); + writel(cop_ier[i], ictlr + ICTLR_COP_IER_SET); + } + local_irq_restore(flags); + + for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) { + struct irq_desc *desc = irq_to_desc(i); + if (!desc || (desc->status & IRQ_WAKEUP)) + continue; + enable_irq(i); + } } +#endif diff --git a/arch/arm/mach-tegra/legacy_irq.c b/arch/arm/mach-tegra/legacy_irq.c new file mode 100644 index 00000000000..7cc8601c19f --- /dev/null +++ b/arch/arm/mach-tegra/legacy_irq.c @@ -0,0 +1,114 @@ +/* + * arch/arm/mach-tegra/legacy_irq.c + * + * Copyright (C) 2010 Google, Inc. + * Author: Colin Cross <ccross@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/io.h> +#include <linux/kernel.h> +#include <mach/iomap.h> +#include <mach/legacy_irq.h> + +#define ICTLR_CPU_IER 0x20 +#define ICTLR_CPU_IER_SET 0x24 +#define ICTLR_CPU_IER_CLR 0x28 +#define ICTLR_CPU_IEP_CLASS 0x2C +#define ICTLR_CPU_IEP_VFIQ 0x08 +#define ICTLR_CPU_IEP_FIR 0x14 +#define ICTLR_CPU_IEP_FIR_SET 0x18 +#define ICTLR_CPU_IEP_FIR_CLR 0x1c + +static void __iomem *ictlr_reg_base[] = { + IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE), + IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE), + IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE), + IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE), +}; + +/* When going into deep sleep, the CPU is powered down, taking the GIC with it + In order to wake, the wake interrupts need to be enabled in the legacy + interrupt controller. */ +void tegra_legacy_unmask_irq(unsigned int irq) +{ + void __iomem *base; + pr_debug("%s: %d\n", __func__, irq); + + irq -= 32; + base = ictlr_reg_base[irq>>5]; + writel(1 << (irq & 31), base + ICTLR_CPU_IER_SET); +} + +void tegra_legacy_mask_irq(unsigned int irq) +{ + void __iomem *base; + pr_debug("%s: %d\n", __func__, irq); + + irq -= 32; + base = ictlr_reg_base[irq>>5]; + writel(1 << (irq & 31), base + ICTLR_CPU_IER_CLR); +} + +void tegra_legacy_force_irq_set(unsigned int irq) +{ + void __iomem *base; + pr_debug("%s: %d\n", __func__, irq); + + irq -= 32; + base = ictlr_reg_base[irq>>5]; + writel(1 << (irq & 31), base + ICTLR_CPU_IEP_FIR_SET); +} + +void tegra_legacy_force_irq_clr(unsigned int irq) +{ + void __iomem *base; + pr_debug("%s: %d\n", __func__, irq); + + irq -= 32; + base = ictlr_reg_base[irq>>5]; + writel(1 << (irq & 31), base + ICTLR_CPU_IEP_FIR_CLR); +} + +int tegra_legacy_force_irq_status(unsigned int irq) +{ + void __iomem *base; + pr_debug("%s: %d\n", __func__, irq); + + irq -= 32; + base = ictlr_reg_base[irq>>5]; + return !!(readl(base + ICTLR_CPU_IEP_FIR) & (1 << (irq & 31))); +} + +void tegra_legacy_select_fiq(unsigned int irq, bool fiq) +{ + void __iomem *base; + pr_debug("%s: %d\n", __func__, irq); + + irq -= 32; + base = ictlr_reg_base[irq>>5]; + writel(fiq << (irq & 31), base + ICTLR_CPU_IEP_CLASS); +} + +unsigned long tegra_legacy_vfiq(int nr) +{ + void __iomem *base; + base = ictlr_reg_base[nr]; + return readl(base + ICTLR_CPU_IEP_VFIQ); +} + +unsigned long tegra_legacy_class(int nr) +{ + void __iomem *base; + base = ictlr_reg_base[nr]; + return readl(base + ICTLR_CPU_IEP_CLASS); +} diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c new file mode 100644 index 00000000000..53f5fa37014 --- /dev/null +++ b/arch/arm/mach-tegra/pcie.c @@ -0,0 +1,915 @@ +/* + * arch/arm/mach-tegra/pci.c + * + * PCIe host controller driver for TEGRA(2) SOCs + * + * Copyright (c) 2010, CompuLab, Ltd. + * Author: Mike Rapoport <mike@compulab.co.il> + * + * Based on NVIDIA PCIe driver + * Copyright (c) 2008-2009, NVIDIA Corporation. + * + * Bits taken from arch/arm/mach-dove/pcie.c + * + * 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/pci.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/clk.h> +#include <linux/delay.h> + +#include <asm/sizes.h> +#include <asm/mach/pci.h> + +#include <mach/pinmux.h> +#include <mach/iomap.h> +#include <mach/clk.h> + +/* register definitions */ +#define AFI_OFFSET 0x3800 +#define PADS_OFFSET 0x3000 +#define RP0_OFFSET 0x0000 +#define RP1_OFFSET 0x1000 + +#define AFI_AXI_BAR0_SZ 0x00 +#define AFI_AXI_BAR1_SZ 0x04 +#define AFI_AXI_BAR2_SZ 0x08 +#define AFI_AXI_BAR3_SZ 0x0c +#define AFI_AXI_BAR4_SZ 0x10 +#define AFI_AXI_BAR5_SZ 0x14 + +#define AFI_AXI_BAR0_START 0x18 +#define AFI_AXI_BAR1_START 0x1c +#define AFI_AXI_BAR2_START 0x20 +#define AFI_AXI_BAR3_START 0x24 +#define AFI_AXI_BAR4_START 0x28 +#define AFI_AXI_BAR5_START 0x2c + +#define AFI_FPCI_BAR0 0x30 +#define AFI_FPCI_BAR1 0x34 +#define AFI_FPCI_BAR2 0x38 +#define AFI_FPCI_BAR3 0x3c +#define AFI_FPCI_BAR4 0x40 +#define AFI_FPCI_BAR5 0x44 + +#define AFI_CACHE_BAR0_SZ 0x48 +#define AFI_CACHE_BAR0_ST 0x4c +#define AFI_CACHE_BAR1_SZ 0x50 +#define AFI_CACHE_BAR1_ST 0x54 + +#define AFI_MSI_BAR_SZ 0x60 +#define AFI_MSI_FPCI_BAR_ST 0x64 +#define AFI_MSI_AXI_BAR_ST 0x68 + +#define AFI_CONFIGURATION 0xac +#define AFI_CONFIGURATION_EN_FPCI (1 << 0) + +#define AFI_FPCI_ERROR_MASKS 0xb0 + +#define AFI_INTR_MASK 0xb4 +#define AFI_INTR_MASK_INT_MASK (1 << 0) +#define AFI_INTR_MASK_MSI_MASK (1 << 8) + +#define AFI_INTR_CODE 0xb8 +#define AFI_INTR_CODE_MASK 0xf +#define AFI_INTR_MASTER_ABORT 4 +#define AFI_INTR_LEGACY 6 + +#define AFI_INTR_SIGNATURE 0xbc +#define AFI_SM_INTR_ENABLE 0xc4 + +#define AFI_AFI_INTR_ENABLE 0xc8 +#define AFI_INTR_EN_INI_SLVERR (1 << 0) +#define AFI_INTR_EN_INI_DECERR (1 << 1) +#define AFI_INTR_EN_TGT_SLVERR (1 << 2) +#define AFI_INTR_EN_TGT_DECERR (1 << 3) +#define AFI_INTR_EN_TGT_WRERR (1 << 4) +#define AFI_INTR_EN_DFPCI_DECERR (1 << 5) +#define AFI_INTR_EN_AXI_DECERR (1 << 6) +#define AFI_INTR_EN_FPCI_TIMEOUT (1 << 7) + +#define AFI_PCIE_CONFIG 0x0f8 +#define AFI_PCIE_CONFIG_PCIEC0_DISABLE_DEVICE (1 << 1) +#define AFI_PCIE_CONFIG_PCIEC1_DISABLE_DEVICE (1 << 2) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK (0xf << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE (0x0 << 20) +#define AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL (0x1 << 20) + +#define AFI_FUSE 0x104 +#define AFI_FUSE_PCIE_T0_GEN2_DIS (1 << 2) + +#define AFI_PEX0_CTRL 0x110 +#define AFI_PEX1_CTRL 0x118 +#define AFI_PEX_CTRL_RST (1 << 0) +#define AFI_PEX_CTRL_REFCLK_EN (1 << 3) + +#define RP_VEND_XP 0x00000F00 +#define RP_VEND_XP_DL_UP (1 << 30) + +#define RP_LINK_CONTROL_STATUS 0x00000090 +#define RP_LINK_CONTROL_STATUS_LINKSTAT_MASK 0x3fff0000 + +#define PADS_CTL_SEL 0x0000009C + +#define PADS_CTL 0x000000A0 +#define PADS_CTL_IDDQ_1L (1 << 0) +#define PADS_CTL_TX_DATA_EN_1L (1 << 6) +#define PADS_CTL_RX_DATA_EN_1L (1 << 10) + +#define PADS_PLL_CTL 0x000000B8 +#define PADS_PLL_CTL_RST_B4SM (1 << 1) +#define PADS_PLL_CTL_LOCKDET (1 << 8) +#define PADS_PLL_CTL_REFCLK_MASK (0x3 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CML (0 << 16) +#define PADS_PLL_CTL_REFCLK_INTERNAL_CMOS (1 << 16) +#define PADS_PLL_CTL_REFCLK_EXTERNAL (2 << 16) +#define PADS_PLL_CTL_TXCLKREF_MASK (0x1 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV10 (0 << 20) +#define PADS_PLL_CTL_TXCLKREF_DIV5 (1 << 20) + +/* PMC access is required for PCIE xclk (un)clamping */ +#define PMC_SCRATCH42 0x144 +#define PMC_SCRATCH42_PCX_CLAMP (1 << 0) + +static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE); + +#define pmc_writel(value, reg) \ + __raw_writel(value, (u32)reg_pmc_base + (reg)) +#define pmc_readl(reg) \ + __raw_readl((u32)reg_pmc_base + (reg)) + +/* + * Tegra2 defines 1GB in the AXI address map for PCIe. + * + * That address space is split into different regions, with sizes and + * offsets as follows: + * + * 0x80000000 - 0x80003fff - PCI controller registers + * 0x80004000 - 0x80103fff - PCI configuration space + * 0x80104000 - 0x80203fff - PCI extended configuration space + * 0x80203fff - 0x803fffff - unused + * 0x80400000 - 0x8040ffff - downstream IO + * 0x80410000 - 0x8fffffff - unused + * 0x90000000 - 0x9fffffff - non-prefetchable memory + * 0xa0000000 - 0xbfffffff - prefetchable memory + */ +#define TEGRA_PCIE_BASE 0x80000000 + +#define PCIE_REGS_SZ SZ_16K +#define PCIE_CFG_OFF PCIE_REGS_SZ +#define PCIE_CFG_SZ SZ_1M +#define PCIE_EXT_CFG_OFF (PCIE_CFG_SZ + PCIE_CFG_OFF) +#define PCIE_EXT_CFG_SZ SZ_1M +#define PCIE_IOMAP_SZ (PCIE_REGS_SZ + PCIE_CFG_SZ + PCIE_EXT_CFG_SZ) + +#define MMIO_BASE (TEGRA_PCIE_BASE + SZ_4M) +#define MMIO_SIZE SZ_64K +#define MEM_BASE_0 (TEGRA_PCIE_BASE + SZ_256M) +#define MEM_SIZE_0 SZ_128M +#define MEM_BASE_1 (MEM_BASE_0 + MEM_SIZE_0) +#define MEM_SIZE_1 SZ_128M +#define PREFETCH_MEM_BASE_0 (MEM_BASE_1 + MEM_SIZE_1) +#define PREFETCH_MEM_SIZE_0 SZ_128M +#define PREFETCH_MEM_BASE_1 (PREFETCH_MEM_BASE_0 + PREFETCH_MEM_SIZE_0) +#define PREFETCH_MEM_SIZE_1 SZ_128M + +#define PCIE_CONF_BUS(b) ((b) << 16) +#define PCIE_CONF_DEV(d) ((d) << 11) +#define PCIE_CONF_FUNC(f) ((f) << 8) +#define PCIE_CONF_REG(r) \ + (((r) & ~0x3) | (((r) < 256) ? PCIE_CFG_OFF : PCIE_EXT_CFG_OFF)) + +struct tegra_pcie_port { + int index; + u8 root_bus_nr; + void __iomem *base; + + bool link_up; + + char io_space_name[16]; + char mem_space_name[16]; + char prefetch_space_name[20]; + struct resource res[3]; +}; + +struct tegra_pcie_info { + struct tegra_pcie_port port[2]; + int num_ports; + + void __iomem *regs; + struct resource res_mmio; + + struct clk *pex_clk; + struct clk *afi_clk; + struct clk *pcie_xclk; + struct clk *pll_e; +}; + +static struct tegra_pcie_info tegra_pcie = { + .res_mmio = { + .name = "PCI IO", + .start = MMIO_BASE, + .end = MMIO_BASE + MMIO_SIZE - 1, + .flags = IORESOURCE_MEM, + }, +}; + +void __iomem *tegra_pcie_io_base; +EXPORT_SYMBOL(tegra_pcie_io_base); + +static inline void afi_writel(u32 value, unsigned long offset) +{ + writel(value, offset + AFI_OFFSET + tegra_pcie.regs); +} + +static inline u32 afi_readl(unsigned long offset) +{ + return readl(offset + AFI_OFFSET + tegra_pcie.regs); +} + +static inline void pads_writel(u32 value, unsigned long offset) +{ + writel(value, offset + PADS_OFFSET + tegra_pcie.regs); +} + +static inline u32 pads_readl(unsigned long offset) +{ + return readl(offset + PADS_OFFSET + tegra_pcie.regs); +} + +static struct tegra_pcie_port *bus_to_port(int bus) +{ + int i; + + for (i = tegra_pcie.num_ports - 1; i >= 0; i--) { + int rbus = tegra_pcie.port[i].root_bus_nr; + if (rbus != -1 && rbus == bus) + break; + } + + return i >= 0 ? tegra_pcie.port + i : NULL; +} + +static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct tegra_pcie_port *pp = bus_to_port(bus->number); + void __iomem *addr; + + if (pp) { + if (devfn != 0) { + *val = 0xffffffff; + return PCIBIOS_DEVICE_NOT_FOUND; + } + + addr = pp->base + (where & ~0x3); + } else { + addr = tegra_pcie.regs + (PCIE_CONF_BUS(bus->number) + + PCIE_CONF_DEV(PCI_SLOT(devfn)) + + PCIE_CONF_FUNC(PCI_FUNC(devfn)) + + PCIE_CONF_REG(where)); + } + + *val = readl(addr); + + if (size == 1) + *val = (*val >> (8 * (where & 3))) & 0xff; + else if (size == 2) + *val = (*val >> (8 * (where & 3))) & 0xffff; + + return PCIBIOS_SUCCESSFUL; +} + +static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct tegra_pcie_port *pp = bus_to_port(bus->number); + void __iomem *addr; + + u32 mask; + u32 tmp; + + if (pp) { + if (devfn != 0) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr = pp->base + (where & ~0x3); + } else { + addr = tegra_pcie.regs + (PCIE_CONF_BUS(bus->number) + + PCIE_CONF_DEV(PCI_SLOT(devfn)) + + PCIE_CONF_FUNC(PCI_FUNC(devfn)) + + PCIE_CONF_REG(where)); + } + + if (size == 4) { + writel(val, addr); + return PCIBIOS_SUCCESSFUL; + } + + if (size == 2) + mask = ~(0xffff << ((where & 0x3) * 8)); + else if (size == 1) + mask = ~(0xff << ((where & 0x3) * 8)); + else + return PCIBIOS_BAD_REGISTER_NUMBER; + + tmp = readl(addr) & mask; + tmp |= val << ((where & 0x3) * 8); + writel(tmp, addr); + + return PCIBIOS_SUCCESSFUL; +} + +static struct pci_ops tegra_pcie_ops = { + .read = tegra_pcie_read_conf, + .write = tegra_pcie_write_conf, +}; + +static void __devinit tegra_pcie_fixup_bridge(struct pci_dev *dev) +{ + u16 reg; + + if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) { + pci_read_config_word(dev, PCI_COMMAND, ®); + reg |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_SERR); + pci_write_config_word(dev, PCI_COMMAND, reg); + } +} +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_fixup_bridge); + +/* Tegra PCIE root complex wrongly reports device class */ +static void __devinit tegra_pcie_fixup_class(struct pci_dev *dev) +{ + dev->class = PCI_CLASS_BRIDGE_PCI << 8; +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_fixup_class); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class); + +/* Tegra PCIE requires relaxed ordering */ +static void __devinit tegra_pcie_relax_enable(struct pci_dev *dev) +{ + u16 val16; + int pos = pci_find_capability(dev, PCI_CAP_ID_EXP); + + if (pos <= 0) { + dev_err(&dev->dev, "skipping relaxed ordering fixup\n"); + return; + } + + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &val16); + val16 |= PCI_EXP_DEVCTL_RELAX_EN; + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, val16); +} +DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable); + +static int tegra_pcie_setup(int nr, struct pci_sys_data *sys) +{ + struct tegra_pcie_port *pp; + + if (nr >= tegra_pcie.num_ports) + return 0; + + pp = tegra_pcie.port + nr; + pp->root_bus_nr = sys->busnr; + + /* + * IORESOURCE_IO + */ + snprintf(pp->io_space_name, sizeof(pp->io_space_name), + "PCIe %d I/O", pp->index); + pp->io_space_name[sizeof(pp->io_space_name) - 1] = 0; + pp->res[0].name = pp->io_space_name; + if (pp->index == 0) { + pp->res[0].start = PCIBIOS_MIN_IO; + pp->res[0].end = pp->res[0].start + SZ_32K - 1; + } else { + pp->res[0].start = PCIBIOS_MIN_IO + SZ_32K; + pp->res[0].end = IO_SPACE_LIMIT; + } + pp->res[0].flags = IORESOURCE_IO; + if (request_resource(&ioport_resource, &pp->res[0])) + panic("Request PCIe IO resource failed\n"); + sys->resource[0] = &pp->res[0]; + + /* + * IORESOURCE_MEM + */ + snprintf(pp->mem_space_name, sizeof(pp->mem_space_name), + "PCIe %d MEM", pp->index); + pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0; + pp->res[1].name = pp->mem_space_name; + if (pp->index == 0) { + pp->res[1].start = MEM_BASE_0; + pp->res[1].end = pp->res[1].start + MEM_SIZE_0 - 1; + } else { + pp->res[1].start = MEM_BASE_1; + pp->res[1].end = pp->res[1].start + MEM_SIZE_1 - 1; + } + pp->res[1].flags = IORESOURCE_MEM; + if (request_resource(&iomem_resource, &pp->res[1])) + panic("Request PCIe Memory resource failed\n"); + sys->resource[1] = &pp->res[1]; + + /* + * IORESOURCE_MEM | IORESOURCE_PREFETCH + */ + snprintf(pp->prefetch_space_name, sizeof(pp->prefetch_space_name), + "PCIe %d PREFETCH MEM", pp->index); + pp->prefetch_space_name[sizeof(pp->prefetch_space_name) - 1] = 0; + pp->res[2].name = pp->prefetch_space_name; + if (pp->index == 0) { + pp->res[2].start = PREFETCH_MEM_BASE_0; + pp->res[2].end = pp->res[2].start + PREFETCH_MEM_SIZE_0 - 1; + } else { + pp->res[2].start = PREFETCH_MEM_BASE_1; + pp->res[2].end = pp->res[2].start + PREFETCH_MEM_SIZE_1 - 1; + } + pp->res[2].flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; + if (request_resource(&iomem_resource, &pp->res[2])) + panic("Request PCIe Prefetch Memory resource failed\n"); + sys->resource[2] = &pp->res[2]; + + return 1; +} + +static int tegra_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return INT_PCIE_INTR; +} + +static struct pci_bus __init *tegra_pcie_scan_bus(int nr, + struct pci_sys_data *sys) +{ + struct tegra_pcie_port *pp; + + if (nr >= tegra_pcie.num_ports) + return 0; + + pp = tegra_pcie.port + nr; + pp->root_bus_nr = sys->busnr; + + return pci_scan_bus(sys->busnr, &tegra_pcie_ops, sys); +} + +static struct hw_pci tegra_pcie_hw __initdata = { + .nr_controllers = 2, + .setup = tegra_pcie_setup, + .scan = tegra_pcie_scan_bus, + .swizzle = pci_std_swizzle, + .map_irq = tegra_pcie_map_irq, +}; + + +static irqreturn_t tegra_pcie_isr(int irq, void *arg) +{ + const char *err_msg[] = { + "Unknown", + "AXI slave error", + "AXI decode error", + "Target abort", + "Master abort", + "Invalid write", + "Response decoding error", + "AXI response decoding error", + "Transcation timeout", + }; + + u32 code, signature; + + code = afi_readl(AFI_INTR_CODE) & AFI_INTR_CODE_MASK; + signature = afi_readl(AFI_INTR_SIGNATURE); + afi_writel(0, AFI_INTR_CODE); + + if (code == AFI_INTR_LEGACY) + return IRQ_NONE; + + if (code >= ARRAY_SIZE(err_msg)) + code = 0; + + /* + * do not pollute kernel log with master abort reports since they + * happen a lot during enumeration + */ + if (code == AFI_INTR_MASTER_ABORT) + pr_debug("PCIE: %s, signature: %08x\n", err_msg[code], signature); + else + pr_err("PCIE: %s, signature: %08x\n", err_msg[code], signature); + + return IRQ_HANDLED; +} + +static void tegra_pcie_setup_translations(void) +{ + u32 fpci_bar; + u32 size; + u32 axi_address; + + /* Bar 0: config Bar */ + fpci_bar = ((u32)0xfdff << 16); + size = PCIE_CFG_SZ; + axi_address = TEGRA_PCIE_BASE + PCIE_CFG_OFF; + afi_writel(axi_address, AFI_AXI_BAR0_START); + afi_writel(size >> 12, AFI_AXI_BAR0_SZ); + afi_writel(fpci_bar, AFI_FPCI_BAR0); + + /* Bar 1: extended config Bar */ + fpci_bar = ((u32)0xfe1 << 20); + size = PCIE_EXT_CFG_SZ; + axi_address = TEGRA_PCIE_BASE + PCIE_EXT_CFG_OFF; + afi_writel(axi_address, AFI_AXI_BAR1_START); + afi_writel(size >> 12, AFI_AXI_BAR1_SZ); + afi_writel(fpci_bar, AFI_FPCI_BAR1); + + /* Bar 2: downstream IO bar */ + fpci_bar = ((__u32)0xfdfc << 16); + size = MMIO_SIZE; + axi_address = MMIO_BASE; + afi_writel(axi_address, AFI_AXI_BAR2_START); + afi_writel(size >> 12, AFI_AXI_BAR2_SZ); + afi_writel(fpci_bar, AFI_FPCI_BAR2); + + /* Bar 3: prefetchable memory BAR */ + fpci_bar = (((PREFETCH_MEM_BASE_0 >> 12) & 0x0fffffff) << 4) | 0x1; + size = PREFETCH_MEM_SIZE_0 + PREFETCH_MEM_SIZE_1; + axi_address = PREFETCH_MEM_BASE_0; + afi_writel(axi_address, AFI_AXI_BAR3_START); + afi_writel(size >> 12, AFI_AXI_BAR3_SZ); + afi_writel(fpci_bar, AFI_FPCI_BAR3); + + /* Bar 4: non prefetchable memory BAR */ + fpci_bar = (((MEM_BASE_0 >> 12) & 0x0FFFFFFF) << 4) | 0x1; + size = MEM_SIZE_0 + MEM_SIZE_1; + axi_address = MEM_BASE_0; + afi_writel(axi_address, AFI_AXI_BAR4_START); + afi_writel(size >> 12, AFI_AXI_BAR4_SZ); + afi_writel(fpci_bar, AFI_FPCI_BAR4); + + /* Bar 5: NULL out the remaining BAR as it is not used */ + fpci_bar = 0; + size = 0; + axi_address = 0; + afi_writel(axi_address, AFI_AXI_BAR5_START); + afi_writel(size >> 12, AFI_AXI_BAR5_SZ); + afi_writel(fpci_bar, AFI_FPCI_BAR5); + + /* map all upstream transactions as uncached */ + afi_writel(PHYS_OFFSET, AFI_CACHE_BAR0_ST); + afi_writel(0, AFI_CACHE_BAR0_SZ); + afi_writel(0, AFI_CACHE_BAR1_ST); + afi_writel(0, AFI_CACHE_BAR1_SZ); + + /* No MSI */ + afi_writel(0, AFI_MSI_FPCI_BAR_ST); + afi_writel(0, AFI_MSI_BAR_SZ); + afi_writel(0, AFI_MSI_AXI_BAR_ST); + afi_writel(0, AFI_MSI_BAR_SZ); +} + +static void tegra_pcie_enable_controller(void) +{ + u32 val, reg; + int i; + + /* Enable slot clock and pulse the reset signals */ + for (i = 0, reg = AFI_PEX0_CTRL; i < 2; i++, reg += 0x8) { + val = afi_readl(reg) | AFI_PEX_CTRL_REFCLK_EN; + afi_writel(val, reg); + val &= ~AFI_PEX_CTRL_RST; + afi_writel(val, reg); + + val = afi_readl(reg) | AFI_PEX_CTRL_RST; + afi_writel(val, reg); + } + + /* Enable dual controller and both ports */ + val = afi_readl(AFI_PCIE_CONFIG); + val &= ~(AFI_PCIE_CONFIG_PCIEC0_DISABLE_DEVICE | + AFI_PCIE_CONFIG_PCIEC1_DISABLE_DEVICE | + AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK); + val |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL; + afi_writel(val, AFI_PCIE_CONFIG); + + val = afi_readl(AFI_FUSE) & ~AFI_FUSE_PCIE_T0_GEN2_DIS; + afi_writel(val, AFI_FUSE); + + /* Initialze internal PHY, enable up to 16 PCIE lanes */ + pads_writel(0x0, PADS_CTL_SEL); + + /* override IDDQ to 1 on all 4 lanes */ + val = pads_readl(PADS_CTL) | PADS_CTL_IDDQ_1L; + pads_writel(val, PADS_CTL); + + /* + * set up PHY PLL inputs select PLLE output as refclock, + * set TX ref sel to div10 (not div5) + */ + val = pads_readl(PADS_PLL_CTL); + val &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK); + val |= (PADS_PLL_CTL_REFCLK_INTERNAL_CML | PADS_PLL_CTL_TXCLKREF_DIV10); + pads_writel(val, PADS_PLL_CTL); + + /* take PLL out of reset */ + val = pads_readl(PADS_PLL_CTL) | PADS_PLL_CTL_RST_B4SM; + pads_writel(val, PADS_PLL_CTL); + + /* + * Hack, set the clock voltage to the DEFAULT provided by hw folks. + * This doesn't exist in the documentation + */ + pads_writel(0xfa5cfa5c, 0xc8); + + /* Wait for the PLL to lock */ + do { + val = pads_readl(PADS_PLL_CTL); + } while (!(val & PADS_PLL_CTL_LOCKDET)); + + /* turn off IDDQ override */ + val = pads_readl(PADS_CTL) & ~PADS_CTL_IDDQ_1L; + pads_writel(val, PADS_CTL); + + /* enable TX/RX data */ + val = pads_readl(PADS_CTL); + val |= (PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L); + pads_writel(val, PADS_CTL); + + /* Take the PCIe interface module out of reset */ + tegra_periph_reset_deassert(tegra_pcie.pcie_xclk); + + /* Finally enable PCIe */ + val = afi_readl(AFI_CONFIGURATION) | AFI_CONFIGURATION_EN_FPCI; + afi_writel(val, AFI_CONFIGURATION); + + val = (AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR | + AFI_INTR_EN_TGT_SLVERR | AFI_INTR_EN_TGT_DECERR | + AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR); + afi_writel(val, AFI_AFI_INTR_ENABLE); + afi_writel(0xffffffff, AFI_SM_INTR_ENABLE); + + /* FIXME: No MSI for now, only INT */ + afi_writel(AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK); + + /* Disable all execptions */ + afi_writel(0, AFI_FPCI_ERROR_MASKS); + + return; +} + +static void tegra_pcie_xclk_clamp(bool clamp) +{ + u32 reg; + + reg = pmc_readl(PMC_SCRATCH42) & ~PMC_SCRATCH42_PCX_CLAMP; + + if (clamp) + reg |= PMC_SCRATCH42_PCX_CLAMP; + + pmc_writel(reg, PMC_SCRATCH42); +} + +static int tegra_pcie_power_on(void) +{ + tegra_pcie_xclk_clamp(true); + tegra_periph_reset_assert(tegra_pcie.pcie_xclk); + tegra_pcie_xclk_clamp(false); + + clk_enable(tegra_pcie.afi_clk); + clk_enable(tegra_pcie.pex_clk); + return clk_enable(tegra_pcie.pll_e); +} + +static void tegra_pcie_power_off(void) +{ + tegra_periph_reset_assert(tegra_pcie.pcie_xclk); + tegra_periph_reset_assert(tegra_pcie.afi_clk); + tegra_periph_reset_assert(tegra_pcie.pex_clk); + + tegra_pcie_xclk_clamp(true); +} + +static int tegra_pcie_clocks_get(void) +{ + int err; + + tegra_pcie.pex_clk = clk_get(NULL, "pex"); + if (IS_ERR(tegra_pcie.pex_clk)) + return PTR_ERR(tegra_pcie.pex_clk); + + tegra_pcie.afi_clk = clk_get(NULL, "afi"); + if (IS_ERR(tegra_pcie.afi_clk)) { + err = PTR_ERR(tegra_pcie.afi_clk); + goto err_afi_clk; + } + + tegra_pcie.pcie_xclk = clk_get(NULL, "pcie_xclk"); + if (IS_ERR(tegra_pcie.pcie_xclk)) { + err = PTR_ERR(tegra_pcie.pcie_xclk); + goto err_pcie_xclk; + } + + tegra_pcie.pll_e = clk_get_sys(NULL, "pll_e"); + if (IS_ERR(tegra_pcie.pll_e)) { + err = PTR_ERR(tegra_pcie.pll_e); + goto err_pll_e; + } + + return 0; + +err_pll_e: + clk_put(tegra_pcie.pcie_xclk); +err_pcie_xclk: + clk_put(tegra_pcie.afi_clk); +err_afi_clk: + clk_put(tegra_pcie.pex_clk); + + return err; +} + +static void tegra_pcie_clocks_put(void) +{ + clk_put(tegra_pcie.pll_e); + clk_put(tegra_pcie.pcie_xclk); + clk_put(tegra_pcie.afi_clk); + clk_put(tegra_pcie.pex_clk); +} + +static int __init tegra_pcie_get_resources(void) +{ + struct resource *res_mmio = &tegra_pcie.res_mmio; + int err; + + err = tegra_pcie_clocks_get(); + if (err) { + pr_err("PCIE: failed to get clocks: %d\n", err); + return err; + } + + err = tegra_pcie_power_on(); + if (err) { + pr_err("PCIE: failed to power up: %d\n", err); + goto err_pwr_on; + } + + tegra_pcie.regs = ioremap_nocache(TEGRA_PCIE_BASE, PCIE_IOMAP_SZ); + if (tegra_pcie.regs == NULL) { + pr_err("PCIE: Failed to map PCI/AFI registers\n"); + err = -ENOMEM; + goto err_map_reg; + } + + err = request_resource(&iomem_resource, res_mmio); + if (err) { + pr_err("PCIE: Failed to request resources: %d\n", err); + goto err_req_io; + } + + tegra_pcie_io_base = ioremap_nocache(res_mmio->start, + resource_size(res_mmio)); + if (tegra_pcie_io_base == NULL) { + pr_err("PCIE: Failed to map IO\n"); + err = -ENOMEM; + goto err_map_io; + } + + err = request_irq(INT_PCIE_INTR, tegra_pcie_isr, + IRQF_SHARED, "PCIE", &tegra_pcie); + if (err) { + pr_err("PCIE: Failed to register IRQ: %d\n", err); + goto err_irq; + } + set_irq_flags(INT_PCIE_INTR, IRQF_VALID); + + return 0; + +err_irq: + iounmap(tegra_pcie_io_base); +err_map_io: + release_resource(&tegra_pcie.res_mmio); +err_req_io: + iounmap(tegra_pcie.regs); +err_map_reg: + tegra_pcie_power_off(); +err_pwr_on: + tegra_pcie_clocks_put(); + + return err; +} + +/* + * FIXME: If there are no PCIe cards attached, then calling this function + * can result in the increase of the bootup time as there are big timeout + * loops. + */ +#define TEGRA_PCIE_LINKUP_TIMEOUT 200 /* up to 1.2 seconds */ +static bool tegra_pcie_check_link(struct tegra_pcie_port *pp, int idx, + u32 reset_reg) +{ + u32 reg; + int retries = 3; + int timeout; + + do { + timeout = TEGRA_PCIE_LINKUP_TIMEOUT; + while (timeout) { + reg = readl(pp->base + RP_VEND_XP); + + if (reg & RP_VEND_XP_DL_UP) + break; + + mdelay(1); + timeout--; + } + + if (!timeout) { + pr_err("PCIE: port %d: link down, retrying\n", idx); + goto retry; + } + + timeout = TEGRA_PCIE_LINKUP_TIMEOUT; + while (timeout) { + reg = readl(pp->base + RP_LINK_CONTROL_STATUS); + + if (reg & 0x20000000) + return true; + + mdelay(1); + timeout--; + } + +retry: + /* Pulse the PEX reset */ + reg = afi_readl(reset_reg) | AFI_PEX_CTRL_RST; + afi_writel(reg, reset_reg); + mdelay(1); + reg = afi_readl(reset_reg) & ~AFI_PEX_CTRL_RST; + afi_writel(reg, reset_reg); + + retries--; + } while (retries); + + return false; +} + +static void __init tegra_pcie_add_port(int index, u32 offset, u32 reset_reg) +{ + struct tegra_pcie_port *pp; + + pp = tegra_pcie.port + tegra_pcie.num_ports; + + pp->index = -1; + pp->base = tegra_pcie.regs + offset; + pp->link_up = tegra_pcie_check_link(pp, index, reset_reg); + + if (!pp->link_up) { + pp->base = NULL; + printk(KERN_INFO "PCIE: port %d: link down, ignoring\n", index); + return; + } + + tegra_pcie.num_ports++; + pp->index = index; + pp->root_bus_nr = -1; + memset(pp->res, 0, sizeof(pp->res)); +} + +int __init tegra_pcie_init(bool init_port0, bool init_port1) +{ + int err; + + if (!(init_port0 || init_port1)) + return -ENODEV; + + err = tegra_pcie_get_resources(); + if (err) + return err; + + tegra_pcie_enable_controller(); + + /* setup the AFI address translations */ + tegra_pcie_setup_translations(); + + if (init_port0) + tegra_pcie_add_port(0, RP0_OFFSET, AFI_PEX0_CTRL); + + if (init_port1) + tegra_pcie_add_port(1, RP1_OFFSET, AFI_PEX1_CTRL); + + pci_common_init(&tegra_pcie_hw); + + return 0; +} diff --git a/arch/arm/mach-tegra/pinmux-t2-tables.c b/arch/arm/mach-tegra/pinmux-t2-tables.c new file mode 100644 index 00000000000..a6ea34e782d --- /dev/null +++ b/arch/arm/mach-tegra/pinmux-t2-tables.c @@ -0,0 +1,260 @@ +/* + * linux/arch/arm/mach-tegra/pinmux-t2-tables.c + * + * Common pinmux configurations for Tegra 2 SoCs + * + * Copyright (C) 2010 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/errno.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/string.h> + +#include <mach/iomap.h> +#include <mach/pinmux.h> + +#define DRIVE_PINGROUP(pg_name, r) \ + [TEGRA_DRIVE_PINGROUP_ ## pg_name] = { \ + .name = #pg_name, \ + .reg = r \ + } + +const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[TEGRA_MAX_DRIVE_PINGROUP] = { + DRIVE_PINGROUP(AO1, 0x868), + DRIVE_PINGROUP(AO2, 0x86c), + DRIVE_PINGROUP(AT1, 0x870), + DRIVE_PINGROUP(AT2, 0x874), + DRIVE_PINGROUP(CDEV1, 0x878), + DRIVE_PINGROUP(CDEV2, 0x87c), + DRIVE_PINGROUP(CSUS, 0x880), + DRIVE_PINGROUP(DAP1, 0x884), + DRIVE_PINGROUP(DAP2, 0x888), + DRIVE_PINGROUP(DAP3, 0x88c), + DRIVE_PINGROUP(DAP4, 0x890), + DRIVE_PINGROUP(DBG, 0x894), + DRIVE_PINGROUP(LCD1, 0x898), + DRIVE_PINGROUP(LCD2, 0x89c), + DRIVE_PINGROUP(SDMMC2, 0x8a0), + DRIVE_PINGROUP(SDMMC3, 0x8a4), + DRIVE_PINGROUP(SPI, 0x8a8), + DRIVE_PINGROUP(UAA, 0x8ac), + DRIVE_PINGROUP(UAB, 0x8b0), + DRIVE_PINGROUP(UART2, 0x8b4), + DRIVE_PINGROUP(UART3, 0x8b8), + DRIVE_PINGROUP(VI1, 0x8bc), + DRIVE_PINGROUP(VI2, 0x8c0), + DRIVE_PINGROUP(XM2A, 0x8c4), + DRIVE_PINGROUP(XM2C, 0x8c8), + DRIVE_PINGROUP(XM2D, 0x8cc), + DRIVE_PINGROUP(XM2CLK, 0x8d0), + DRIVE_PINGROUP(MEMCOMP, 0x8d4), +}; + +#define PINGROUP(pg_name, vdd, f0, f1, f2, f3, f_safe, \ + tri_r, tri_b, mux_r, mux_b, pupd_r, pupd_b) \ + [TEGRA_PINGROUP_ ## pg_name] = { \ + .name = #pg_name, \ + .vddio = TEGRA_VDDIO_ ## vdd, \ + .funcs = { \ + TEGRA_MUX_ ## f0, \ + TEGRA_MUX_ ## f1, \ + TEGRA_MUX_ ## f2, \ + TEGRA_MUX_ ## f3, \ + }, \ + .func_safe = TEGRA_MUX_ ## f_safe, \ + .tri_reg = tri_r, \ + .tri_bit = tri_b, \ + .mux_reg = mux_r, \ + .mux_bit = mux_b, \ + .pupd_reg = pupd_r, \ + .pupd_bit = pupd_b, \ + } + +const struct tegra_pingroup_desc tegra_soc_pingroups[TEGRA_MAX_PINGROUP] = { + PINGROUP(ATA, NAND, IDE, NAND, GMI, RSVD, IDE, 0x14, 0, 0x80, 24, 0xA0, 0), + PINGROUP(ATB, NAND, IDE, NAND, GMI, SDIO4, IDE, 0x14, 1, 0x80, 16, 0xA0, 2), + PINGROUP(ATC, NAND, IDE, NAND, GMI, SDIO4, IDE, 0x14, 2, 0x80, 22, 0xA0, 4), + PINGROUP(ATD, NAND, IDE, NAND, GMI, SDIO4, IDE, 0x14, 3, 0x80, 20, 0xA0, 6), + PINGROUP(ATE, NAND, IDE, NAND, GMI, RSVD, IDE, 0x18, 25, 0x80, 12, 0xA0, 8), + PINGROUP(CDEV1, AUDIO, OSC, PLLA_OUT, PLLM_OUT1, AUDIO_SYNC, OSC, 0x14, 4, 0x88, 2, 0xA8, 0), + PINGROUP(CDEV2, AUDIO, OSC, AHB_CLK, APB_CLK, PLLP_OUT4, OSC, 0x14, 5, 0x88, 4, 0xA8, 2), + PINGROUP(CRTP, LCD, CRT, RSVD, RSVD, RSVD, RSVD, 0x20, 14, 0x98, 20, 0xA4, 24), + PINGROUP(CSUS, VI, PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK, PLLC_OUT1, 0x14, 6, 0x88, 6, 0xAC, 24), + PINGROUP(DAP1, AUDIO, DAP1, RSVD, GMI, SDIO2, DAP1, 0x14, 7, 0x88, 20, 0xA0, 10), + PINGROUP(DAP2, AUDIO, DAP2, TWC, RSVD, GMI, DAP2, 0x14, 8, 0x88, 22, 0xA0, 12), + PINGROUP(DAP3, BB, DAP3, RSVD, RSVD, RSVD, DAP3, 0x14, 9, 0x88, 24, 0xA0, 14), + PINGROUP(DAP4, UART, DAP4, RSVD, GMI, RSVD, DAP4, 0x14, 10, 0x88, 26, 0xA0, 16), + PINGROUP(DDC, LCD, I2C2, RSVD, RSVD, RSVD, RSVD4, 0x18, 31, 0x88, 0, 0xB0, 28), + PINGROUP(DTA, VI, RSVD, SDIO2, VI, RSVD, RSVD4, 0x14, 11, 0x84, 20, 0xA0, 18), + PINGROUP(DTB, VI, RSVD, RSVD, VI, SPI1, RSVD1, 0x14, 12, 0x84, 22, 0xA0, 20), + PINGROUP(DTC, VI, RSVD, RSVD, VI, RSVD, RSVD1, 0x14, 13, 0x84, 26, 0xA0, 22), + PINGROUP(DTD, VI, RSVD, SDIO2, VI, RSVD, RSVD1, 0x14, 14, 0x84, 28, 0xA0, 24), + PINGROUP(DTE, VI, RSVD, RSVD, VI, SPI1, RSVD1, 0x14, 15, 0x84, 30, 0xA0, 26), + PINGROUP(DTF, VI, I2C3, RSVD, VI, RSVD, RSVD4, 0x20, 12, 0x98, 30, 0xA0, 28), + PINGROUP(GMA, NAND, UARTE, SPI3, GMI, SDIO4, SPI3, 0x14, 28, 0x84, 0, 0xB0, 20), + PINGROUP(GMB, NAND, IDE, NAND, GMI, GMI_INT, GMI, 0x18, 29, 0x88, 28, 0xB0, 22), + PINGROUP(GMC, NAND, UARTD, SPI4, GMI, SFLASH, SPI4, 0x14, 29, 0x84, 2, 0xB0, 24), + PINGROUP(GMD, NAND, RSVD, NAND, GMI, SFLASH, GMI, 0x18, 30, 0x88, 30, 0xB0, 26), + PINGROUP(GME, NAND, RSVD, DAP5, GMI, SDIO4, GMI, 0x18, 0, 0x8C, 0, 0xA8, 24), + PINGROUP(GPU, UART, PWM, UARTA, GMI, RSVD, RSVD4, 0x14, 16, 0x8C, 4, 0xA4, 20), + PINGROUP(GPU7, SYS, RTCK, RSVD, RSVD, RSVD, RTCK, 0x20, 11, 0x98, 28, 0xA4, 6), + PINGROUP(GPV, SD, PCIE, RSVD, RSVD, RSVD, PCIE, 0x14, 17, 0x8C, 2, 0xA0, 30), + PINGROUP(HDINT, LCD, HDMI, RSVD, RSVD, RSVD, HDMI, 0x1C, 23, 0x84, 4, 0xAC, 22), + PINGROUP(I2CP, SYS, I2C, RSVD, RSVD, RSVD, RSVD4, 0x14, 18, 0x88, 8, 0xA4, 2), + PINGROUP(IRRX, UART, UARTA, UARTB, GMI, SPI4, UARTB, 0x14, 20, 0x88, 18, 0xA8, 22), + PINGROUP(IRTX, UART, UARTA, UARTB, GMI, SPI4, UARTB, 0x14, 19, 0x88, 16, 0xA8, 20), + PINGROUP(KBCA, SYS, KBC, NAND, SDIO2, EMC_TEST0_DLL, KBC, 0x14, 22, 0x88, 10, 0xA4, 8), + PINGROUP(KBCB, SYS, KBC, NAND, SDIO2, MIO, KBC, 0x14, 21, 0x88, 12, 0xA4, 10), + PINGROUP(KBCC, SYS, KBC, NAND, TRACE, EMC_TEST1_DLL, KBC, 0x18, 26, 0x88, 14, 0xA4, 12), + PINGROUP(KBCD, SYS, KBC, NAND, SDIO2, MIO, KBC, 0x20, 10, 0x98, 26, 0xA4, 14), + PINGROUP(KBCE, SYS, KBC, NAND, OWR, RSVD, KBC, 0x14, 26, 0x80, 28, 0xB0, 2), + PINGROUP(KBCF, SYS, KBC, NAND, TRACE, MIO, KBC, 0x14, 27, 0x80, 26, 0xB0, 0), + PINGROUP(LCSN, LCD, DISPLAYA, DISPLAYB, SPI3, RSVD, RSVD4, 0x1C, 31, 0x90, 12, 0xAC, 20), + PINGROUP(LD0, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 0, 0x94, 0, 0xAC, 12), + PINGROUP(LD1, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 1, 0x94, 2, 0xAC, 12), + PINGROUP(LD10, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 10, 0x94, 20, 0xAC, 12), + PINGROUP(LD11, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 11, 0x94, 22, 0xAC, 12), + PINGROUP(LD12, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 12, 0x94, 24, 0xAC, 12), + PINGROUP(LD13, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 13, 0x94, 26, 0xAC, 12), + PINGROUP(LD14, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 14, 0x94, 28, 0xAC, 12), + PINGROUP(LD15, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 15, 0x94, 30, 0xAC, 12), + PINGROUP(LD16, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 16, 0x98, 0, 0xAC, 12), + PINGROUP(LD17, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 17, 0x98, 2, 0xAC, 12), + PINGROUP(LD2, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 2, 0x94, 4, 0xAC, 12), + PINGROUP(LD3, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 3, 0x94, 6, 0xAC, 12), + PINGROUP(LD4, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 4, 0x94, 8, 0xAC, 12), + PINGROUP(LD5, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 5, 0x94, 10, 0xAC, 12), + PINGROUP(LD6, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 6, 0x94, 12, 0xAC, 12), + PINGROUP(LD7, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 7, 0x94, 14, 0xAC, 12), + PINGROUP(LD8, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 8, 0x94, 16, 0xAC, 12), + PINGROUP(LD9, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 9, 0x94, 18, 0xAC, 12), + PINGROUP(LDC, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 30, 0x90, 14, 0xAC, 20), + PINGROUP(LDI, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x20, 6, 0x98, 16, 0xAC, 18), + PINGROUP(LHP0, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 18, 0x98, 10, 0xAC, 16), + PINGROUP(LHP1, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 19, 0x98, 4, 0xAC, 14), + PINGROUP(LHP2, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 20, 0x98, 6, 0xAC, 14), + PINGROUP(LHS, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x20, 7, 0x90, 22, 0xAC, 22), + PINGROUP(LM0, LCD, DISPLAYA, DISPLAYB, SPI3, RSVD, RSVD4, 0x1C, 24, 0x90, 26, 0xAC, 22), + PINGROUP(LM1, LCD, DISPLAYA, DISPLAYB, RSVD, CRT, RSVD3, 0x1C, 25, 0x90, 28, 0xAC, 22), + PINGROUP(LPP, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x20, 8, 0x98, 14, 0xAC, 18), + PINGROUP(LPW0, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 3, 0x90, 0, 0xAC, 20), + PINGROUP(LPW1, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x20, 4, 0x90, 2, 0xAC, 20), + PINGROUP(LPW2, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 5, 0x90, 4, 0xAC, 20), + PINGROUP(LSC0, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 27, 0x90, 18, 0xAC, 22), + PINGROUP(LSC1, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x1C, 28, 0x90, 20, 0xAC, 20), + PINGROUP(LSCK, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x1C, 29, 0x90, 16, 0xAC, 20), + PINGROUP(LSDA, LCD, DISPLAYA, DISPLAYB, SPI3, HDMI, DISPLAYA, 0x20, 1, 0x90, 8, 0xAC, 20), + PINGROUP(LSDI, LCD, DISPLAYA, DISPLAYB, SPI3, RSVD, DISPLAYA, 0x20, 2, 0x90, 6, 0xAC, 20), + PINGROUP(LSPI, LCD, DISPLAYA, DISPLAYB, XIO, HDMI, DISPLAYA, 0x20, 0, 0x90, 10, 0xAC, 22), + PINGROUP(LVP0, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 21, 0x90, 30, 0xAC, 22), + PINGROUP(LVP1, LCD, DISPLAYA, DISPLAYB, RSVD, RSVD, RSVD4, 0x1C, 22, 0x98, 8, 0xAC, 16), + PINGROUP(LVS, LCD, DISPLAYA, DISPLAYB, XIO, RSVD, RSVD4, 0x1C, 26, 0x90, 24, 0xAC, 22), + PINGROUP(OWC, SYS, OWR, RSVD, RSVD, RSVD, OWR, 0x14, 31, 0x84, 8, 0xB0, 30), + PINGROUP(PMC, SYS, PWR_ON, PWR_INTR, RSVD, RSVD, PWR_ON, 0x14, 23, 0x98, 18, -1, -1), + PINGROUP(PTA, NAND, I2C2, HDMI, GMI, RSVD, RSVD4, 0x14, 24, 0x98, 22, 0xA4, 4), + PINGROUP(RM, UART, I2C, RSVD, RSVD, RSVD, RSVD4, 0x14, 25, 0x80, 14, 0xA4, 0), + PINGROUP(SDB, SD, UARTA, PWM, SDIO3, SPI2, PWM, 0x20, 15, 0x8C, 10, -1, -1), + PINGROUP(SDC, SD, PWM, TWC, SDIO3, SPI3, TWC, 0x18, 1, 0x8C, 12, 0xAC, 28), + PINGROUP(SDD, SD, UARTA, PWM, SDIO3, SPI3, PWM, 0x18, 2, 0x8C, 14, 0xAC, 30), + PINGROUP(SDIO1, BB, SDIO1, RSVD, UARTE, UARTA, RSVD2, 0x14, 30, 0x80, 30, 0xB0, 18), + PINGROUP(SLXA, SD, PCIE, SPI4, SDIO3, SPI2, PCIE, 0x18, 3, 0x84, 6, 0xA4, 22), + PINGROUP(SLXC, SD, SPDIF, SPI4, SDIO3, SPI2, SPI4, 0x18, 5, 0x84, 10, 0xA4, 26), + PINGROUP(SLXD, SD, SPDIF, SPI4, SDIO3, SPI2, SPI4, 0x18, 6, 0x84, 12, 0xA4, 28), + PINGROUP(SLXK, SD, PCIE, SPI4, SDIO3, SPI2, PCIE, 0x18, 7, 0x84, 14, 0xA4, 30), + PINGROUP(SPDI, AUDIO, SPDIF, RSVD, I2C, SDIO2, RSVD2, 0x18, 8, 0x8C, 8, 0xA4, 16), + PINGROUP(SPDO, AUDIO, SPDIF, RSVD, I2C, SDIO2, RSVD2, 0x18, 9, 0x8C, 6, 0xA4, 18), + PINGROUP(SPIA, AUDIO, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 10, 0x8C, 30, 0xA8, 4), + PINGROUP(SPIB, AUDIO, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 11, 0x8C, 28, 0xA8, 6), + PINGROUP(SPIC, AUDIO, SPI1, SPI2, SPI3, GMI, GMI, 0x18, 12, 0x8C, 26, 0xA8, 8), + PINGROUP(SPID, AUDIO, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x18, 13, 0x8C, 24, 0xA8, 10), + PINGROUP(SPIE, AUDIO, SPI2, SPI1, SPI2_ALT, GMI, GMI, 0x18, 14, 0x8C, 22, 0xA8, 12), + PINGROUP(SPIF, AUDIO, SPI3, SPI1, SPI2, RSVD, RSVD4, 0x18, 15, 0x8C, 20, 0xA8, 14), + PINGROUP(SPIG, AUDIO, SPI3, SPI2, SPI2_ALT, I2C, SPI2_ALT, 0x18, 16, 0x8C, 18, 0xA8, 16), + PINGROUP(SPIH, AUDIO, SPI3, SPI2, SPI2_ALT, I2C, SPI2_ALT, 0x18, 17, 0x8C, 16, 0xA8, 18), + PINGROUP(UAA, BB, SPI3, MIPI_HS, UARTA, ULPI, MIPI_HS, 0x18, 18, 0x80, 0, 0xAC, 0), + PINGROUP(UAB, BB, SPI2, MIPI_HS, UARTA, ULPI, MIPI_HS, 0x18, 19, 0x80, 2, 0xAC, 2), + PINGROUP(UAC, BB, OWR, RSVD, RSVD, RSVD, RSVD4, 0x18, 20, 0x80, 4, 0xAC, 4), + PINGROUP(UAD, UART, IRDA, SPDIF, UARTA, SPI4, SPDIF, 0x18, 21, 0x80, 6, 0xAC, 6), + PINGROUP(UCA, UART, UARTC, RSVD, GMI, RSVD, RSVD4, 0x18, 22, 0x84, 16, 0xAC, 8), + PINGROUP(UCB, UART, UARTC, PWM, GMI, RSVD, RSVD4, 0x18, 23, 0x84, 18, 0xAC, 10), + PINGROUP(UDA, BB, SPI1, RSVD, UARTD, ULPI, RSVD2, 0x20, 13, 0x80, 8, 0xB0, 16), + /* these pin groups only have pullup and pull down control */ + PINGROUP(CK32, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 14), + PINGROUP(DDRC, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xAC, 26), + PINGROUP(PMCA, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 4), + PINGROUP(PMCB, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 6), + PINGROUP(PMCC, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 8), + PINGROUP(PMCD, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 10), + PINGROUP(PMCE, SYS, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xB0, 12), + PINGROUP(XM2C, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xA8, 30), + PINGROUP(XM2D, DDR, RSVD, RSVD, RSVD, RSVD, RSVD, -1, -1, -1, -1, 0xA8, 28), +}; + +#ifdef CONFIG_PM +#define TRISTATE_REG_A 0x14 +#define TRISTATE_REG_NUM 4 +#define PIN_MUX_CTL_REG_A 0x80 +#define PIN_MUX_CTL_REG_NUM 8 +#define PULLUPDOWN_REG_A 0xa0 +#define PULLUPDOWN_REG_NUM 5 + +static u32 pinmux_reg[TRISTATE_REG_NUM + PIN_MUX_CTL_REG_NUM + + PULLUPDOWN_REG_NUM]; + +static inline unsigned long pg_readl(unsigned long offset) +{ + return readl(IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset)); +} + +static inline void pg_writel(unsigned long value, unsigned long offset) +{ + writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset)); +} + +void tegra_pinmux_suspend(void) +{ + unsigned int i; + u32 *ctx = pinmux_reg; + + for (i = 0; i < TRISTATE_REG_NUM; i++) + *ctx++ = pg_readl(TRISTATE_REG_A + i*4); + + for (i = 0; i < PIN_MUX_CTL_REG_NUM; i++) + *ctx++ = pg_readl(PIN_MUX_CTL_REG_A + i*4); + + for (i = 0; i < PULLUPDOWN_REG_NUM; i++) + *ctx++ = pg_readl(PULLUPDOWN_REG_A + i*4); +} + +void tegra_pinmux_resume(void) +{ + unsigned int i; + u32 *ctx = pinmux_reg; + + for (i = 0; i < PIN_MUX_CTL_REG_NUM; i++) + pg_writel(*ctx++, PIN_MUX_CTL_REG_A + i*4); + + for (i = 0; i < PULLUPDOWN_REG_NUM; i++) + pg_writel(*ctx++, PULLUPDOWN_REG_A + i*4); + + for (i = 0; i < TRISTATE_REG_NUM; i++) + pg_writel(*ctx++, TRISTATE_REG_A + i*4); +} +#endif diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c index 13ae10237e8..f80d507671b 100644 --- a/arch/arm/mach-tegra/pinmux.c +++ b/arch/arm/mach-tegra/pinmux.c @@ -14,7 +14,8 @@ * */ - +#include <linux/init.h> +#include <linux/module.h> #include <linux/kernel.h> #include <linux/errno.h> #include <linux/spinlock.h> @@ -23,21 +24,6 @@ #include <mach/iomap.h> #include <mach/pinmux.h> - -#define TEGRA_TRI_STATE(x) (0x14 + (4 * (x))) -#define TEGRA_PP_MUX_CTL(x) (0x80 + (4 * (x))) -#define TEGRA_PP_PU_PD(x) (0xa0 + (4 * (x))) - -#define REG_A 0 -#define REG_B 1 -#define REG_C 2 -#define REG_D 3 -#define REG_E 4 -#define REG_F 5 -#define REG_G 6 - -#define REG_N -1 - #define HSM_EN(reg) (((reg) >> 2) & 0x1) #define SCHMT_EN(reg) (((reg) >> 3) & 0x1) #define LPMD(reg) (((reg) >> 4) & 0x3) @@ -46,154 +32,8 @@ #define SLWR(reg) (((reg) >> 28) & 0x3) #define SLWF(reg) (((reg) >> 30) & 0x3) -struct tegra_pingroup_desc { - const char *name; - int funcs[4]; - s8 tri_reg; /* offset into the TRISTATE_REG_* register bank */ - s8 tri_bit; /* offset into the TRISTATE_REG_* register bit */ - s8 mux_reg; /* offset into the PIN_MUX_CTL_* register bank */ - s8 mux_bit; /* offset into the PIN_MUX_CTL_* register bit */ - s8 pupd_reg; /* offset into the PULL_UPDOWN_REG_* register bank */ - s8 pupd_bit; /* offset into the PULL_UPDOWN_REG_* register bit */ -}; - -#define PINGROUP(pg_name, f0, f1, f2, f3, \ - tri_r, tri_b, mux_r, mux_b, pupd_r, pupd_b) \ - [TEGRA_PINGROUP_ ## pg_name] = { \ - .name = #pg_name, \ - .funcs = { \ - TEGRA_MUX_ ## f0, \ - TEGRA_MUX_ ## f1, \ - TEGRA_MUX_ ## f2, \ - TEGRA_MUX_ ## f3, \ - }, \ - .tri_reg = REG_ ## tri_r, \ - .tri_bit = tri_b, \ - .mux_reg = REG_ ## mux_r, \ - .mux_bit = mux_b, \ - .pupd_reg = REG_ ## pupd_r, \ - .pupd_bit = pupd_b, \ - } - -static const struct tegra_pingroup_desc pingroups[TEGRA_MAX_PINGROUP] = { - PINGROUP(ATA, IDE, NAND, GMI, RSVD, A, 0, A, 24, A, 0), - PINGROUP(ATB, IDE, NAND, GMI, SDIO4, A, 1, A, 16, A, 2), - PINGROUP(ATC, IDE, NAND, GMI, SDIO4, A, 2, A, 22, A, 4), - PINGROUP(ATD, IDE, NAND, GMI, SDIO4, A, 3, A, 20, A, 6), - PINGROUP(ATE, IDE, NAND, GMI, RSVD, B, 25, A, 12, A, 8), - PINGROUP(CDEV1, OSC, PLLA_OUT, PLLM_OUT1, AUDIO_SYNC, A, 4, C, 2, C, 0), - PINGROUP(CDEV2, OSC, AHB_CLK, APB_CLK, PLLP_OUT4, A, 5, C, 4, C, 2), - PINGROUP(CRTP, CRT, RSVD, RSVD, RSVD, D, 14, G, 20, B, 24), - PINGROUP(CSUS, PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK, A, 6, C, 6, D, 24), - PINGROUP(DAP1, DAP1, RSVD, GMI, SDIO2, A, 7, C, 20, A, 10), - PINGROUP(DAP2, DAP2, TWC, RSVD, GMI, A, 8, C, 22, A, 12), - PINGROUP(DAP3, DAP3, RSVD, RSVD, RSVD, A, 9, C, 24, A, 14), - PINGROUP(DAP4, DAP4, RSVD, GMI, RSVD, A, 10, C, 26, A, 16), - PINGROUP(DDC, I2C2, RSVD, RSVD, RSVD, B, 31, C, 0, E, 28), - PINGROUP(DTA, RSVD, SDIO2, VI, RSVD, A, 11, B, 20, A, 18), - PINGROUP(DTB, RSVD, RSVD, VI, SPI1, A, 12, B, 22, A, 20), - PINGROUP(DTC, RSVD, RSVD, VI, RSVD, A, 13, B, 26, A, 22), - PINGROUP(DTD, RSVD, SDIO2, VI, RSVD, A, 14, B, 28, A, 24), - PINGROUP(DTE, RSVD, RSVD, VI, SPI1, A, 15, B, 30, A, 26), - PINGROUP(DTF, I2C3, RSVD, VI, RSVD, D, 12, G, 30, A, 28), - PINGROUP(GMA, UARTE, SPI3, GMI, SDIO4, A, 28, B, 0, E, 20), - PINGROUP(GMB, IDE, NAND, GMI, GMI_INT, B, 29, C, 28, E, 22), - PINGROUP(GMC, UARTD, SPI4, GMI, SFLASH, A, 29, B, 2, E, 24), - PINGROUP(GMD, RSVD, NAND, GMI, SFLASH, B, 30, C, 30, E, 26), - PINGROUP(GME, RSVD, DAP5, GMI, SDIO4, B, 0, D, 0, C, 24), - PINGROUP(GPU, PWM, UARTA, GMI, RSVD, A, 16, D, 4, B, 20), - PINGROUP(GPU7, RTCK, RSVD, RSVD, RSVD, D, 11, G, 28, B, 6), - PINGROUP(GPV, PCIE, RSVD, RSVD, RSVD, A, 17, D, 2, A, 30), - PINGROUP(HDINT, HDMI, RSVD, RSVD, RSVD, C, 23, B, 4, D, 22), - PINGROUP(I2CP, I2C, RSVD, RSVD, RSVD, A, 18, C, 8, B, 2), - PINGROUP(IRRX, UARTA, UARTB, GMI, SPI4, A, 20, C, 18, C, 22), - PINGROUP(IRTX, UARTA, UARTB, GMI, SPI4, A, 19, C, 16, C, 20), - PINGROUP(KBCA, KBC, NAND, SDIO2, EMC_TEST0_DLL, A, 22, C, 10, B, 8), - PINGROUP(KBCB, KBC, NAND, SDIO2, MIO, A, 21, C, 12, B, 10), - PINGROUP(KBCC, KBC, NAND, TRACE, EMC_TEST1_DLL, B, 26, C, 14, B, 12), - PINGROUP(KBCD, KBC, NAND, SDIO2, MIO, D, 10, G, 26, B, 14), - PINGROUP(KBCE, KBC, NAND, OWR, RSVD, A, 26, A, 28, E, 2), - PINGROUP(KBCF, KBC, NAND, TRACE, MIO, A, 27, A, 26, E, 0), - PINGROUP(LCSN, DISPLAYA, DISPLAYB, SPI3, RSVD, C, 31, E, 12, D, 20), - PINGROUP(LD0, DISPLAYA, DISPLAYB, XIO, RSVD, C, 0, F, 0, D, 12), - PINGROUP(LD1, DISPLAYA, DISPLAYB, XIO, RSVD, C, 1, F, 2, D, 12), - PINGROUP(LD10, DISPLAYA, DISPLAYB, XIO, RSVD, C, 10, F, 20, D, 12), - PINGROUP(LD11, DISPLAYA, DISPLAYB, XIO, RSVD, C, 11, F, 22, D, 12), - PINGROUP(LD12, DISPLAYA, DISPLAYB, XIO, RSVD, C, 12, F, 24, D, 12), - PINGROUP(LD13, DISPLAYA, DISPLAYB, XIO, RSVD, C, 13, F, 26, D, 12), - PINGROUP(LD14, DISPLAYA, DISPLAYB, XIO, RSVD, C, 14, F, 28, D, 12), - PINGROUP(LD15, DISPLAYA, DISPLAYB, XIO, RSVD, C, 15, F, 30, D, 12), - PINGROUP(LD16, DISPLAYA, DISPLAYB, XIO, RSVD, C, 16, G, 0, D, 12), - PINGROUP(LD17, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 17, G, 2, D, 12), - PINGROUP(LD2, DISPLAYA, DISPLAYB, XIO, RSVD, C, 2, F, 4, D, 12), - PINGROUP(LD3, DISPLAYA, DISPLAYB, XIO, RSVD, C, 3, F, 6, D, 12), - PINGROUP(LD4, DISPLAYA, DISPLAYB, XIO, RSVD, C, 4, F, 8, D, 12), - PINGROUP(LD5, DISPLAYA, DISPLAYB, XIO, RSVD, C, 5, F, 10, D, 12), - PINGROUP(LD6, DISPLAYA, DISPLAYB, XIO, RSVD, C, 6, F, 12, D, 12), - PINGROUP(LD7, DISPLAYA, DISPLAYB, XIO, RSVD, C, 7, F, 14, D, 12), - PINGROUP(LD8, DISPLAYA, DISPLAYB, XIO, RSVD, C, 8, F, 16, D, 12), - PINGROUP(LD9, DISPLAYA, DISPLAYB, XIO, RSVD, C, 9, F, 18, D, 12), - PINGROUP(LDC, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 30, E, 14, D, 20), - PINGROUP(LDI, DISPLAYA, DISPLAYB, RSVD, RSVD, D, 6, G, 16, D, 18), - PINGROUP(LHP0, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 18, G, 10, D, 16), - PINGROUP(LHP1, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 19, G, 4, D, 14), - PINGROUP(LHP2, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 20, G, 6, D, 14), - PINGROUP(LHS, DISPLAYA, DISPLAYB, XIO, RSVD, D, 7, E, 22, D, 22), - PINGROUP(LM0, DISPLAYA, DISPLAYB, SPI3, RSVD, C, 24, E, 26, D, 22), - PINGROUP(LM1, DISPLAYA, DISPLAYB, RSVD, CRT, C, 25, E, 28, D, 22), - PINGROUP(LPP, DISPLAYA, DISPLAYB, RSVD, RSVD, D, 8, G, 14, D, 18), - PINGROUP(LPW0, DISPLAYA, DISPLAYB, SPI3, HDMI, D, 3, E, 0, D, 20), - PINGROUP(LPW1, DISPLAYA, DISPLAYB, RSVD, RSVD, D, 4, E, 2, D, 20), - PINGROUP(LPW2, DISPLAYA, DISPLAYB, SPI3, HDMI, D, 5, E, 4, D, 20), - PINGROUP(LSC0, DISPLAYA, DISPLAYB, XIO, RSVD, C, 27, E, 18, D, 22), - PINGROUP(LSC1, DISPLAYA, DISPLAYB, SPI3, HDMI, C, 28, E, 20, D, 20), - PINGROUP(LSCK, DISPLAYA, DISPLAYB, SPI3, HDMI, C, 29, E, 16, D, 20), - PINGROUP(LSDA, DISPLAYA, DISPLAYB, SPI3, HDMI, D, 1, E, 8, D, 20), - PINGROUP(LSDI, DISPLAYA, DISPLAYB, SPI3, RSVD, D, 2, E, 6, D, 20), - PINGROUP(LSPI, DISPLAYA, DISPLAYB, XIO, HDMI, D, 0, E, 10, D, 22), - PINGROUP(LVP0, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 21, E, 30, D, 22), - PINGROUP(LVP1, DISPLAYA, DISPLAYB, RSVD, RSVD, C, 22, G, 8, D, 16), - PINGROUP(LVS, DISPLAYA, DISPLAYB, XIO, RSVD, C, 26, E, 24, D, 22), - PINGROUP(OWC, OWR, RSVD, RSVD, RSVD, A, 31, B, 8, E, 30), - PINGROUP(PMC, PWR_ON, PWR_INTR, RSVD, RSVD, A, 23, G, 18, N, -1), - PINGROUP(PTA, I2C2, HDMI, GMI, RSVD, A, 24, G, 22, B, 4), - PINGROUP(RM, I2C, RSVD, RSVD, RSVD, A, 25, A, 14, B, 0), - PINGROUP(SDB, UARTA, PWM, SDIO3, SPI2, D, 15, D, 10, N, -1), - PINGROUP(SDC, PWM, TWC, SDIO3, SPI3, B, 1, D, 12, D, 28), - PINGROUP(SDD, UARTA, PWM, SDIO3, SPI3, B, 2, D, 14, D, 30), - PINGROUP(SDIO1, SDIO1, RSVD, UARTE, UARTA, A, 30, A, 30, E, 18), - PINGROUP(SLXA, PCIE, SPI4, SDIO3, SPI2, B, 3, B, 6, B, 22), - PINGROUP(SLXC, SPDIF, SPI4, SDIO3, SPI2, B, 5, B, 10, B, 26), - PINGROUP(SLXD, SPDIF, SPI4, SDIO3, SPI2, B, 6, B, 12, B, 28), - PINGROUP(SLXK, PCIE, SPI4, SDIO3, SPI2, B, 7, B, 14, B, 30), - PINGROUP(SPDI, SPDIF, RSVD, I2C, SDIO2, B, 8, D, 8, B, 16), - PINGROUP(SPDO, SPDIF, RSVD, I2C, SDIO2, B, 9, D, 6, B, 18), - PINGROUP(SPIA, SPI1, SPI2, SPI3, GMI, B, 10, D, 30, C, 4), - PINGROUP(SPIB, SPI1, SPI2, SPI3, GMI, B, 11, D, 28, C, 6), - PINGROUP(SPIC, SPI1, SPI2, SPI3, GMI, B, 12, D, 26, C, 8), - PINGROUP(SPID, SPI2, SPI1, SPI2_ALT, GMI, B, 13, D, 24, C, 10), - PINGROUP(SPIE, SPI2, SPI1, SPI2_ALT, GMI, B, 14, D, 22, C, 12), - PINGROUP(SPIF, SPI3, SPI1, SPI2, RSVD, B, 15, D, 20, C, 14), - PINGROUP(SPIG, SPI3, SPI2, SPI2_ALT, I2C, B, 16, D, 18, C, 16), - PINGROUP(SPIH, SPI3, SPI2, SPI2_ALT, I2C, B, 17, D, 16, C, 18), - PINGROUP(UAA, SPI3, MIPI_HS, UARTA, ULPI, B, 18, A, 0, D, 0), - PINGROUP(UAB, SPI2, MIPI_HS, UARTA, ULPI, B, 19, A, 2, D, 2), - PINGROUP(UAC, OWR, RSVD, RSVD, RSVD, B, 20, A, 4, D, 4), - PINGROUP(UAD, IRDA, SPDIF, UARTA, SPI4, B, 21, A, 6, D, 6), - PINGROUP(UCA, UARTC, RSVD, GMI, RSVD, B, 22, B, 16, D, 8), - PINGROUP(UCB, UARTC, PWM, GMI, RSVD, B, 23, B, 18, D, 10), - PINGROUP(UDA, SPI1, RSVD, UARTD, ULPI, D, 13, A, 8, E, 16), - /* these pin groups only have pullup and pull down control */ - PINGROUP(CK32, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, E, 14), - PINGROUP(DDRC, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, D, 26), - PINGROUP(PMCA, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, E, 4), - PINGROUP(PMCB, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, E, 6), - PINGROUP(PMCC, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, E, 8), - PINGROUP(PMCD, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, E, 10), - PINGROUP(PMCE, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, E, 12), - PINGROUP(XM2C, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, C, 30), - PINGROUP(XM2D, RSVD, RSVD, RSVD, RSVD, N, -1, N, -1, C, 28), -}; +static const struct tegra_pingroup_desc *const pingroups = tegra_soc_pingroups; +static const struct tegra_drive_pingroup_desc *const drive_pingroups = tegra_soc_drive_pingroups; static char *tegra_mux_names[TEGRA_MAX_MUX] = { [TEGRA_MUX_AHB_CLK] = "AHB_CLK", @@ -256,48 +96,7 @@ static char *tegra_mux_names[TEGRA_MAX_MUX] = { [TEGRA_MUX_VI] = "VI", [TEGRA_MUX_VI_SENSOR_CLK] = "VI_SENSOR_CLK", [TEGRA_MUX_XIO] = "XIO", -}; - -struct tegra_drive_pingroup_desc { - const char *name; - s16 reg; -}; - -#define DRIVE_PINGROUP(pg_name, r) \ - [TEGRA_DRIVE_PINGROUP_ ## pg_name] = { \ - .name = #pg_name, \ - .reg = r \ - } - -static const struct tegra_drive_pingroup_desc drive_pingroups[TEGRA_MAX_PINGROUP] = { - DRIVE_PINGROUP(AO1, 0x868), - DRIVE_PINGROUP(AO2, 0x86c), - DRIVE_PINGROUP(AT1, 0x870), - DRIVE_PINGROUP(AT2, 0x874), - DRIVE_PINGROUP(CDEV1, 0x878), - DRIVE_PINGROUP(CDEV2, 0x87c), - DRIVE_PINGROUP(CSUS, 0x880), - DRIVE_PINGROUP(DAP1, 0x884), - DRIVE_PINGROUP(DAP2, 0x888), - DRIVE_PINGROUP(DAP3, 0x88c), - DRIVE_PINGROUP(DAP4, 0x890), - DRIVE_PINGROUP(DBG, 0x894), - DRIVE_PINGROUP(LCD1, 0x898), - DRIVE_PINGROUP(LCD2, 0x89c), - DRIVE_PINGROUP(SDMMC2, 0x8a0), - DRIVE_PINGROUP(SDMMC3, 0x8a4), - DRIVE_PINGROUP(SPI, 0x8a8), - DRIVE_PINGROUP(UAA, 0x8ac), - DRIVE_PINGROUP(UAB, 0x8b0), - DRIVE_PINGROUP(UART2, 0x8b4), - DRIVE_PINGROUP(UART3, 0x8b8), - DRIVE_PINGROUP(VI1, 0x8bc), - DRIVE_PINGROUP(VI2, 0x8c0), - DRIVE_PINGROUP(XM2A, 0x8c4), - DRIVE_PINGROUP(XM2C, 0x8c8), - DRIVE_PINGROUP(XM2D, 0x8cc), - DRIVE_PINGROUP(XM2CLK, 0x8d0), - DRIVE_PINGROUP(MEMCOMP, 0x8d4), + [TEGRA_MUX_SAFE] = "<safe>", }; static const char *tegra_drive_names[TEGRA_MAX_DRIVE] = { @@ -381,22 +180,27 @@ static inline void pg_writel(unsigned long value, unsigned long offset) writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset)); } -int tegra_pinmux_set_func(enum tegra_pingroup pg, enum tegra_mux_func func) +static int tegra_pinmux_set_func(const struct tegra_pingroup_config *config) { int mux = -1; int i; unsigned long reg; unsigned long flags; + enum tegra_pingroup pg = config->pingroup; + enum tegra_mux_func func = config->func; if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) return -ERANGE; - if (pingroups[pg].mux_reg == REG_N) + if (pingroups[pg].mux_reg < 0) return -EINVAL; if (func < 0) return -ERANGE; + if (func == TEGRA_MUX_SAFE) + func = pingroups[pg].func_safe; + if (func & TEGRA_MUX_RSVD) { mux = func & 0x3; } else { @@ -413,10 +217,10 @@ int tegra_pinmux_set_func(enum tegra_pingroup pg, enum tegra_mux_func func) spin_lock_irqsave(&mux_lock, flags); - reg = pg_readl(TEGRA_PP_MUX_CTL(pingroups[pg].mux_reg)); + reg = pg_readl(pingroups[pg].mux_reg); reg &= ~(0x3 << pingroups[pg].mux_bit); reg |= mux << pingroups[pg].mux_bit; - pg_writel(reg, TEGRA_PP_MUX_CTL(pingroups[pg].mux_reg)); + pg_writel(reg, pingroups[pg].mux_reg); spin_unlock_irqrestore(&mux_lock, flags); @@ -432,16 +236,16 @@ int tegra_pinmux_set_tristate(enum tegra_pingroup pg, if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) return -ERANGE; - if (pingroups[pg].tri_reg == REG_N) + if (pingroups[pg].tri_reg < 0) return -EINVAL; spin_lock_irqsave(&mux_lock, flags); - reg = pg_readl(TEGRA_TRI_STATE(pingroups[pg].tri_reg)); + reg = pg_readl(pingroups[pg].tri_reg); reg &= ~(0x1 << pingroups[pg].tri_bit); if (tristate) reg |= 1 << pingroups[pg].tri_bit; - pg_writel(reg, TEGRA_TRI_STATE(pingroups[pg].tri_reg)); + pg_writel(reg, pingroups[pg].tri_reg); spin_unlock_irqrestore(&mux_lock, flags); @@ -457,7 +261,7 @@ int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg, if (pg < 0 || pg >= TEGRA_MAX_PINGROUP) return -ERANGE; - if (pingroups[pg].pupd_reg == REG_N) + if (pingroups[pg].pupd_reg < 0) return -EINVAL; if (pupd != TEGRA_PUPD_NORMAL && @@ -468,38 +272,39 @@ int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg, spin_lock_irqsave(&mux_lock, flags); - reg = pg_readl(TEGRA_PP_PU_PD(pingroups[pg].pupd_reg)); + reg = pg_readl(pingroups[pg].pupd_reg); reg &= ~(0x3 << pingroups[pg].pupd_bit); reg |= pupd << pingroups[pg].pupd_bit; - pg_writel(reg, TEGRA_PP_PU_PD(pingroups[pg].pupd_reg)); + pg_writel(reg, pingroups[pg].pupd_reg); spin_unlock_irqrestore(&mux_lock, flags); return 0; } -void tegra_pinmux_config_pingroup(enum tegra_pingroup pingroup, - enum tegra_mux_func func, - enum tegra_pullupdown pupd, - enum tegra_tristate tristate) +static void tegra_pinmux_config_pingroup(const struct tegra_pingroup_config *config) { + enum tegra_pingroup pingroup = config->pingroup; + enum tegra_mux_func func = config->func; + enum tegra_pullupdown pupd = config->pupd; + enum tegra_tristate tristate = config->tristate; int err; - if (pingroups[pingroup].mux_reg != REG_N) { - err = tegra_pinmux_set_func(pingroup, func); + if (pingroups[pingroup].mux_reg >= 0) { + err = tegra_pinmux_set_func(config); if (err < 0) pr_err("pinmux: can't set pingroup %s func to %s: %d\n", pingroup_name(pingroup), func_name(func), err); } - if (pingroups[pingroup].pupd_reg != REG_N) { + if (pingroups[pingroup].pupd_reg >= 0) { err = tegra_pinmux_set_pullupdown(pingroup, pupd); if (err < 0) pr_err("pinmux: can't set pingroup %s pullupdown to %s: %d\n", pingroup_name(pingroup), pupd_name(pupd), err); } - if (pingroups[pingroup].tri_reg != REG_N) { + if (pingroups[pingroup].tri_reg >= 0) { err = tegra_pinmux_set_tristate(pingroup, tristate); if (err < 0) pr_err("pinmux: can't set pingroup %s tristate to %s: %d\n", @@ -507,17 +312,12 @@ void tegra_pinmux_config_pingroup(enum tegra_pingroup pingroup, } } - - -void tegra_pinmux_config_table(struct tegra_pingroup_config *config, int len) +void tegra_pinmux_config_table(const struct tegra_pingroup_config *config, int len) { int i; for (i = 0; i < len; i++) - tegra_pinmux_config_pingroup(config[i].pingroup, - config[i].func, - config[i].pupd, - config[i].tristate); + tegra_pinmux_config_pingroup(&config[i]); } static const char *drive_pinmux_name(enum tegra_drive_pingroup pg) @@ -784,6 +584,86 @@ void tegra_drive_pinmux_config_table(struct tegra_drive_pingroup_config *config, config[i].slew_falling); } +void tegra_pinmux_set_safe_pinmux_table(const struct tegra_pingroup_config *config, + int len) +{ + int i; + struct tegra_pingroup_config c; + + for (i = 0; i < len; i++) { + int err; + c = config[i]; + if (c.pingroup < 0 || c.pingroup >= TEGRA_MAX_PINGROUP) { + WARN_ON(1); + continue; + } + c.func = pingroups[c.pingroup].func_safe; + err = tegra_pinmux_set_func(&c); + if (err < 0) + pr_err("%s: tegra_pinmux_set_func returned %d setting " + "%s to %s\n", __func__, err, + pingroup_name(c.pingroup), func_name(c.func)); + } +} + +void tegra_pinmux_config_pinmux_table(const struct tegra_pingroup_config *config, + int len) +{ + int i; + + for (i = 0; i < len; i++) { + int err; + if (config[i].pingroup < 0 || + config[i].pingroup >= TEGRA_MAX_PINGROUP) { + WARN_ON(1); + continue; + } + err = tegra_pinmux_set_func(&config[i]); + if (err < 0) + pr_err("%s: tegra_pinmux_set_func returned %d setting " + "%s to %s\n", __func__, err, + pingroup_name(config[i].pingroup), + func_name(config[i].func)); + } +} + +void tegra_pinmux_config_tristate_table(const struct tegra_pingroup_config *config, + int len, enum tegra_tristate tristate) +{ + int i; + int err; + enum tegra_pingroup pingroup; + + for (i = 0; i < len; i++) { + pingroup = config[i].pingroup; + if (pingroups[pingroup].tri_reg >= 0) { + err = tegra_pinmux_set_tristate(pingroup, tristate); + if (err < 0) + pr_err("pinmux: can't set pingroup %s tristate" + " to %s: %d\n", pingroup_name(pingroup), + tri_name(tristate), err); + } + } +} + +void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *config, + int len, enum tegra_pullupdown pupd) +{ + int i; + int err; + enum tegra_pingroup pingroup; + + for (i = 0; i < len; i++) { + pingroup = config[i].pingroup; + if (pingroups[pingroup].pupd_reg >= 0) { + err = tegra_pinmux_set_pullupdown(pingroup, pupd); + if (err < 0) + pr_err("pinmux: can't set pingroup %s pullupdown" + " to %s: %d\n", pingroup_name(pingroup), + pupd_name(pupd), err); + } + } +} #ifdef CONFIG_DEBUG_FS @@ -812,11 +692,11 @@ static int dbg_pinmux_show(struct seq_file *s, void *unused) len = strlen(pingroups[i].name); dbg_pad_field(s, 5 - len); - if (pingroups[i].mux_reg == REG_N) { + if (pingroups[i].mux_reg < 0) { seq_printf(s, "TEGRA_MUX_NONE"); len = strlen("NONE"); } else { - mux = (pg_readl(TEGRA_PP_MUX_CTL(pingroups[i].mux_reg)) >> + mux = (pg_readl(pingroups[i].mux_reg) >> pingroups[i].mux_bit) & 0x3; if (pingroups[i].funcs[mux] == TEGRA_MUX_RSVD) { seq_printf(s, "TEGRA_MUX_RSVD%1lu", mux+1); @@ -829,21 +709,21 @@ static int dbg_pinmux_show(struct seq_file *s, void *unused) } dbg_pad_field(s, 13-len); - if (pingroups[i].mux_reg == REG_N) { + if (pingroups[i].pupd_reg < 0) { seq_printf(s, "TEGRA_PUPD_NORMAL"); len = strlen("NORMAL"); } else { - pupd = (pg_readl(TEGRA_PP_PU_PD(pingroups[i].pupd_reg)) >> + pupd = (pg_readl(pingroups[i].pupd_reg) >> pingroups[i].pupd_bit) & 0x3; seq_printf(s, "TEGRA_PUPD_%s", pupd_name(pupd)); len = strlen(pupd_name(pupd)); } dbg_pad_field(s, 9 - len); - if (pingroups[i].tri_reg == REG_N) { + if (pingroups[i].tri_reg < 0) { seq_printf(s, "TEGRA_TRI_NORMAL"); } else { - tri = (pg_readl(TEGRA_TRI_STATE(pingroups[i].tri_reg)) >> + tri = (pg_readl(pingroups[i].tri_reg) >> pingroups[i].tri_bit) & 0x1; seq_printf(s, "TEGRA_TRI_%s", tri_name(tri)); diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c index 426163231ff..ae3b308e22a 100644 --- a/arch/arm/mach-tegra/tegra2_clocks.c +++ b/arch/arm/mach-tegra/tegra2_clocks.c @@ -30,14 +30,21 @@ #include <mach/iomap.h> #include "clock.h" +#include "fuse.h" +#include "tegra2_dvfs.h" #define RST_DEVICES 0x004 #define RST_DEVICES_SET 0x300 #define RST_DEVICES_CLR 0x304 +#define RST_DEVICES_NUM 3 #define CLK_OUT_ENB 0x010 #define CLK_OUT_ENB_SET 0x320 #define CLK_OUT_ENB_CLR 0x324 +#define CLK_OUT_ENB_NUM 3 + +#define CLK_MASK_ARM 0x44 +#define MISC_CLK_ENB 0x48 #define OSC_CTRL 0x50 #define OSC_CTRL_OSC_FREQ_MASK (3<<30) @@ -45,6 +52,7 @@ #define OSC_CTRL_OSC_FREQ_19_2MHZ (1<<30) #define OSC_CTRL_OSC_FREQ_12MHZ (2<<30) #define OSC_CTRL_OSC_FREQ_26MHZ (3<<30) +#define OSC_CTRL_MASK 0x3f2 #define OSC_FREQ_DET 0x58 #define OSC_FREQ_DET_TRIG (1<<31) @@ -53,10 +61,17 @@ #define OSC_FREQ_DET_BUSY (1<<31) #define OSC_FREQ_DET_CNT_MASK 0xFFFF +#define PERIPH_CLK_SOURCE_I2S1 0x100 +#define PERIPH_CLK_SOURCE_EMC 0x19c +#define PERIPH_CLK_SOURCE_OSC 0x1fc +#define PERIPH_CLK_SOURCE_NUM \ + ((PERIPH_CLK_SOURCE_OSC - PERIPH_CLK_SOURCE_I2S1) / 4) + #define PERIPH_CLK_SOURCE_MASK (3<<30) #define PERIPH_CLK_SOURCE_SHIFT 30 #define PERIPH_CLK_SOURCE_ENABLE (1<<28) -#define PERIPH_CLK_SOURCE_DIV_MASK 0xFF +#define PERIPH_CLK_SOURCE_DIVU71_MASK 0xFF +#define PERIPH_CLK_SOURCE_DIVU16_MASK 0xFFFF #define PERIPH_CLK_SOURCE_DIV_SHIFT 0 #define PLL_BASE 0x0 @@ -79,8 +94,9 @@ #define PLL_OUT_RESET_DISABLE (1<<0) #define PLL_MISC(c) (((c)->flags & PLL_ALT_MISC_REG) ? 0x4 : 0xc) +#define PLL_MISC_LOCK_ENABLE(c) (((c)->flags & PLLU) ? (1<<22) : (1<<18)) + #define PLL_MISC_DCCON_SHIFT 20 -#define PLL_MISC_LOCK_ENABLE (1<<18) #define PLL_MISC_CPCON_SHIFT 8 #define PLL_MISC_CPCON_MASK (0xF<<PLL_MISC_CPCON_SHIFT) #define PLL_MISC_LFCON_SHIFT 4 @@ -88,10 +104,14 @@ #define PLL_MISC_VCOCON_SHIFT 0 #define PLL_MISC_VCOCON_MASK (0xF<<PLL_MISC_VCOCON_SHIFT) +#define PLLU_BASE_POST_DIV (1<<20) + #define PLLD_MISC_CLKENABLE (1<<30) #define PLLD_MISC_DIV_RST (1<<23) #define PLLD_MISC_DCCON_SHIFT 12 +#define PLLE_MISC_READY (1 << 15) + #define PERIPH_CLK_TO_ENB_REG(c) ((c->clk_num / 32) * 4) #define PERIPH_CLK_TO_ENB_SET_REG(c) ((c->clk_num / 32) * 8) #define PERIPH_CLK_TO_ENB_BIT(c) (1 << (c->clk_num % 32)) @@ -143,30 +163,37 @@ unsigned long clk_measure_input_freq(void) } } -static int clk_div71_get_divider(struct clk *c, unsigned long rate) +static int clk_div71_get_divider(unsigned long parent_rate, unsigned long rate) { - unsigned long divider_u71; + s64 divider_u71 = parent_rate * 2; + divider_u71 += rate - 1; + do_div(divider_u71, rate); - divider_u71 = DIV_ROUND_UP(c->rate * 2, rate); + if (divider_u71 - 2 < 0) + return 0; - if (divider_u71 - 2 > 255 || divider_u71 - 2 < 0) + if (divider_u71 - 2 > 255) return -EINVAL; return divider_u71 - 2; } -static unsigned long tegra2_clk_recalculate_rate(struct clk *c) +static int clk_div16_get_divider(unsigned long parent_rate, unsigned long rate) { - unsigned long rate; - rate = c->parent->rate; + s64 divider_u16; - if (c->mul != 0 && c->div != 0) - c->rate = rate * c->mul / c->div; - else - c->rate = rate; - return c->rate; -} + divider_u16 = parent_rate; + divider_u16 += rate - 1; + do_div(divider_u16, rate); + + if (divider_u16 - 1 < 0) + return 0; + if (divider_u16 - 1 > 255) + return -EINVAL; + + return divider_u16 - 1; +} /* clk_m functions */ static unsigned long tegra2_clk_m_autodetect_rate(struct clk *c) @@ -244,7 +271,6 @@ static void tegra2_super_clk_init(struct clk *c) } BUG_ON(sel->input == NULL); c->parent = sel->input; - tegra2_clk_recalculate_rate(c); } static int tegra2_super_clk_enable(struct clk *c) @@ -266,6 +292,7 @@ static int tegra2_super_clk_set_parent(struct clk *c, struct clk *p) u32 val; const struct clk_mux_sel *sel; int shift; + val = clk_readl(c->reg + SUPER_CLK_MUX);; BUG_ON(((val & SUPER_STATE_MASK) != SUPER_STATE_RUN) && ((val & SUPER_STATE_MASK) != SUPER_STATE_IDLE)); @@ -273,11 +300,18 @@ static int tegra2_super_clk_set_parent(struct clk *c, struct clk *p) SUPER_IDLE_SOURCE_SHIFT : SUPER_RUN_SOURCE_SHIFT; for (sel = c->inputs; sel->input != NULL; sel++) { if (sel->input == p) { - clk_reparent(c, p); val &= ~(SUPER_SOURCE_MASK << shift); val |= sel->value << shift; + + if (c->refcnt) + clk_enable_locked(p); + clk_writel(val, c->reg); - c->rate = c->parent->rate; + + if (c->refcnt && c->parent) + clk_disable_locked(c->parent); + + clk_reparent(c, p); return 0; } } @@ -289,7 +323,61 @@ static struct clk_ops tegra_super_ops = { .enable = tegra2_super_clk_enable, .disable = tegra2_super_clk_disable, .set_parent = tegra2_super_clk_set_parent, - .recalculate_rate = tegra2_clk_recalculate_rate, +}; + +/* virtual cpu clock functions */ +/* some clocks can not be stopped (cpu, memory bus) while the SoC is running. + To change the frequency of these clocks, the parent pll may need to be + reprogrammed, so the clock must be moved off the pll, the pll reprogrammed, + and then the clock moved back to the pll. To hide this sequence, a virtual + clock handles it. + */ +static void tegra2_cpu_clk_init(struct clk *c) +{ +} + +static int tegra2_cpu_clk_enable(struct clk *c) +{ + return 0; +} + +static void tegra2_cpu_clk_disable(struct clk *c) +{ + pr_debug("%s on clock %s\n", __func__, c->name); + + /* oops - don't disable the CPU clock! */ + BUG(); +} + +static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate) +{ + int ret; + ret = clk_set_parent_locked(c->parent, c->backup); + if (ret) { + pr_err("Failed to switch cpu to clock %s\n", c->backup->name); + return ret; + } + + ret = clk_set_rate_locked(c->main, rate); + if (ret) { + pr_err("Failed to change cpu pll to %lu\n", rate); + return ret; + } + + ret = clk_set_parent_locked(c->parent, c->main); + if (ret) { + pr_err("Failed to switch cpu to clock %s\n", c->main->name); + return ret; + } + + return 0; +} + +static struct clk_ops tegra_cpu_ops = { + .init = tegra2_cpu_clk_init, + .enable = tegra2_cpu_clk_enable, + .disable = tegra2_cpu_clk_disable, + .set_rate = tegra2_cpu_clk_set_rate, }; /* bus clock functions */ @@ -299,7 +387,6 @@ static void tegra2_bus_clk_init(struct clk *c) c->state = ((val >> c->reg_shift) & BUS_CLK_DISABLE) ? OFF : ON; c->div = ((val >> c->reg_shift) & BUS_CLK_DIV_MASK) + 1; c->mul = 1; - tegra2_clk_recalculate_rate(c); } static int tegra2_bus_clk_enable(struct clk *c) @@ -340,27 +427,15 @@ static struct clk_ops tegra_bus_ops = { .enable = tegra2_bus_clk_enable, .disable = tegra2_bus_clk_disable, .set_rate = tegra2_bus_clk_set_rate, - .recalculate_rate = tegra2_clk_recalculate_rate, }; /* PLL Functions */ -static unsigned long tegra2_pll_clk_recalculate_rate(struct clk *c) -{ - u64 rate; - rate = c->parent->rate; - rate *= c->n; - do_div(rate, c->m); - if (c->p == 2) - rate >>= 1; - c->rate = rate; - return c->rate; -} - static int tegra2_pll_clk_wait_for_lock(struct clk *c) { ktime_t before; before = ktime_get(); + while (!(clk_readl(c->reg + PLL_BASE) & PLL_BASE_LOCK)) { if (ktime_us_delta(ktime_get(), before) > 5000) { pr_err("Timed out waiting for lock bit on pll %s", @@ -380,24 +455,19 @@ static void tegra2_pll_clk_init(struct clk *c) if (c->flags & PLL_FIXED && !(val & PLL_BASE_OVERRIDE)) { pr_warning("Clock %s has unknown fixed frequency\n", c->name); - c->n = 1; - c->m = 0; - c->p = 1; + c->mul = 1; + c->div = 1; } else if (val & PLL_BASE_BYPASS) { - c->n = 1; - c->m = 1; - c->p = 1; + c->mul = 1; + c->div = 1; } else { - c->n = (val & PLL_BASE_DIVN_MASK) >> PLL_BASE_DIVN_SHIFT; - c->m = (val & PLL_BASE_DIVM_MASK) >> PLL_BASE_DIVM_SHIFT; - c->p = (val & PLL_BASE_DIVP_MASK) ? 2 : 1; + c->mul = (val & PLL_BASE_DIVN_MASK) >> PLL_BASE_DIVN_SHIFT; + c->div = (val & PLL_BASE_DIVM_MASK) >> PLL_BASE_DIVM_SHIFT; + if (c->flags & PLLU) + c->div *= (val & PLLU_BASE_POST_DIV) ? 1 : 2; + else + c->div *= (val & PLL_BASE_DIVP_MASK) ? 2 : 1; } - - val = clk_readl(c->reg + PLL_MISC(c)); - if (c->flags & PLL_HAS_CPCON) - c->cpcon = (val & PLL_MISC_CPCON_MASK) >> PLL_MISC_CPCON_SHIFT; - - tegra2_pll_clk_recalculate_rate(c); } static int tegra2_pll_clk_enable(struct clk *c) @@ -411,7 +481,7 @@ static int tegra2_pll_clk_enable(struct clk *c) clk_writel(val, c->reg + PLL_BASE); val = clk_readl(c->reg + PLL_MISC(c)); - val |= PLL_MISC_LOCK_ENABLE; + val |= PLL_MISC_LOCK_ENABLE(c); clk_writel(val, c->reg + PLL_MISC(c)); tegra2_pll_clk_wait_for_lock(c); @@ -441,33 +511,36 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate) input_rate = c->parent->rate; for (sel = c->pll_table; sel->input_rate != 0; sel++) { if (sel->input_rate == input_rate && sel->output_rate == rate) { - c->n = sel->n; - c->m = sel->m; - c->p = sel->p; - c->cpcon = sel->cpcon; + c->mul = sel->n; + c->div = sel->m * sel->p; val = clk_readl(c->reg + PLL_BASE); if (c->flags & PLL_FIXED) val |= PLL_BASE_OVERRIDE; val &= ~(PLL_BASE_DIVP_MASK | PLL_BASE_DIVN_MASK | PLL_BASE_DIVM_MASK); - val |= (c->m << PLL_BASE_DIVM_SHIFT) | - (c->n << PLL_BASE_DIVN_SHIFT); - BUG_ON(c->p > 2); - if (c->p == 2) - val |= 1 << PLL_BASE_DIVP_SHIFT; + val |= (sel->m << PLL_BASE_DIVM_SHIFT) | + (sel->n << PLL_BASE_DIVN_SHIFT); + BUG_ON(sel->p < 1 || sel->p > 2); + if (c->flags & PLLU) { + if (sel->p == 1) + val |= PLLU_BASE_POST_DIV; + } else { + if (sel->p == 2) + val |= 1 << PLL_BASE_DIVP_SHIFT; + } clk_writel(val, c->reg + PLL_BASE); if (c->flags & PLL_HAS_CPCON) { - val = c->cpcon << PLL_MISC_CPCON_SHIFT; - val |= PLL_MISC_LOCK_ENABLE; + val = clk_readl(c->reg + PLL_MISC(c)); + val &= ~PLL_MISC_CPCON_MASK; + val |= sel->cpcon << PLL_MISC_CPCON_SHIFT; clk_writel(val, c->reg + PLL_MISC(c)); } if (c->state == ON) tegra2_pll_clk_enable(c); - c->rate = rate; return 0; } } @@ -479,7 +552,46 @@ static struct clk_ops tegra_pll_ops = { .enable = tegra2_pll_clk_enable, .disable = tegra2_pll_clk_disable, .set_rate = tegra2_pll_clk_set_rate, - .recalculate_rate = tegra2_pll_clk_recalculate_rate, +}; + +static void tegra2_pllx_clk_init(struct clk *c) +{ + tegra2_pll_clk_init(c); + + if (tegra_sku_id() == 7) + c->max_rate = 750000000; +} + +static struct clk_ops tegra_pllx_ops = { + .init = tegra2_pllx_clk_init, + .enable = tegra2_pll_clk_enable, + .disable = tegra2_pll_clk_disable, + .set_rate = tegra2_pll_clk_set_rate, +}; + +static int tegra2_plle_clk_enable(struct clk *c) +{ + u32 val; + + pr_debug("%s on clock %s\n", __func__, c->name); + + mdelay(1); + + val = clk_readl(c->reg + PLL_BASE); + if (!(val & PLLE_MISC_READY)) + return -EBUSY; + + val = clk_readl(c->reg + PLL_BASE); + val |= PLL_BASE_ENABLE | PLL_BASE_BYPASS; + clk_writel(val, c->reg + PLL_BASE); + + return 0; +} + +static struct clk_ops tegra_plle_ops = { + .init = tegra2_pll_clk_init, + .enable = tegra2_plle_clk_enable, + .set_rate = tegra2_pll_clk_set_rate, }; /* Clock divider ops */ @@ -503,8 +615,6 @@ static void tegra2_pll_div_clk_init(struct clk *c) c->div = 1; c->mul = 1; } - - tegra2_clk_recalculate_rate(c); } static int tegra2_pll_div_clk_enable(struct clk *c) @@ -565,7 +675,7 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate) int divider_u71; pr_debug("%s: %s %lu\n", __func__, c->name, rate); if (c->flags & DIV_U71) { - divider_u71 = clk_div71_get_divider(c->parent, rate); + divider_u71 = clk_div71_get_divider(c->parent->rate, rate); if (divider_u71 >= 0) { val = clk_readl(c->reg); new_val = val >> c->reg_shift; @@ -580,25 +690,37 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate) clk_writel(val, c->reg); c->div = divider_u71 + 2; c->mul = 2; - tegra2_clk_recalculate_rate(c); return 0; } } else if (c->flags & DIV_2) { - if (c->parent->rate == rate * 2) { - c->rate = rate; + if (c->parent->rate == rate * 2) return 0; - } } return -EINVAL; } +static long tegra2_pll_div_clk_round_rate(struct clk *c, unsigned long rate) +{ + int divider; + pr_debug("%s: %s %lu\n", __func__, c->name, rate); + + if (c->flags & DIV_U71) { + divider = clk_div71_get_divider(c->parent->rate, rate); + if (divider < 0) + return divider; + return c->parent->rate * 2 / (divider + 2); + } else if (c->flags & DIV_2) { + return c->parent->rate / 2; + } + return -EINVAL; +} static struct clk_ops tegra_pll_div_ops = { .init = tegra2_pll_div_clk_init, .enable = tegra2_pll_div_clk_enable, .disable = tegra2_pll_div_clk_disable, .set_rate = tegra2_pll_div_clk_set_rate, - .recalculate_rate = tegra2_clk_recalculate_rate, + .round_rate = tegra2_pll_div_clk_round_rate, }; /* Periph clk ops */ @@ -621,9 +743,13 @@ static void tegra2_periph_clk_init(struct clk *c) } if (c->flags & DIV_U71) { - u32 divu71 = val & PERIPH_CLK_SOURCE_DIV_MASK; + u32 divu71 = val & PERIPH_CLK_SOURCE_DIVU71_MASK; c->div = divu71 + 2; c->mul = 2; + } else if (c->flags & DIV_U16) { + u32 divu16 = val & PERIPH_CLK_SOURCE_DIVU16_MASK; + c->div = divu16 + 1; + c->mul = 1; } else { c->div = 1; c->mul = 1; @@ -637,7 +763,6 @@ static void tegra2_periph_clk_init(struct clk *c) if (clk_readl(RST_DEVICES + PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_ENB_BIT(c)) c->state = OFF; - tegra2_clk_recalculate_rate(c); } static int tegra2_periph_clk_enable(struct clk *c) @@ -692,12 +817,19 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p) pr_debug("%s: %s %s\n", __func__, c->name, p->name); for (sel = c->inputs; sel->input != NULL; sel++) { if (sel->input == p) { - clk_reparent(c, p); val = clk_readl(c->reg); val &= ~PERIPH_CLK_SOURCE_MASK; val |= (sel->value) << PERIPH_CLK_SOURCE_SHIFT; + + if (c->refcnt) + clk_enable_locked(p); + clk_writel(val, c->reg); - c->rate = c->parent->rate; + + if (c->refcnt && c->parent) + clk_disable_locked(c->parent); + + clk_reparent(c, p); return 0; } } @@ -708,20 +840,55 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p) static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate) { u32 val; - int divider_u71; + int divider; pr_debug("%s: %lu\n", __func__, rate); if (c->flags & DIV_U71) { - divider_u71 = clk_div71_get_divider(c->parent, rate); - if (divider_u71 >= 0) { + divider = clk_div71_get_divider(c->parent->rate, rate); + if (divider >= 0) { val = clk_readl(c->reg); - val &= ~PERIPH_CLK_SOURCE_DIV_MASK; - val |= divider_u71; + val &= ~PERIPH_CLK_SOURCE_DIVU71_MASK; + val |= divider; clk_writel(val, c->reg); - c->div = divider_u71 + 2; + c->div = divider + 2; c->mul = 2; - tegra2_clk_recalculate_rate(c); return 0; } + } else if (c->flags & DIV_U16) { + divider = clk_div16_get_divider(c->parent->rate, rate); + if (divider >= 0) { + val = clk_readl(c->reg); + val &= ~PERIPH_CLK_SOURCE_DIVU16_MASK; + val |= divider; + clk_writel(val, c->reg); + c->div = divider + 1; + c->mul = 1; + return 0; + } + } else if (c->parent->rate <= rate) { + c->div = 1; + c->mul = 1; + return 0; + } + return -EINVAL; +} + +static long tegra2_periph_clk_round_rate(struct clk *c, + unsigned long rate) +{ + int divider; + pr_debug("%s: %s %lu\n", __func__, c->name, rate); + + if (c->flags & DIV_U71) { + divider = clk_div71_get_divider(c->parent->rate, rate); + if (divider < 0) + return divider; + + return c->parent->rate * 2 / (divider + 2); + } else if (c->flags & DIV_U16) { + divider = clk_div16_get_divider(c->parent->rate, rate); + if (divider < 0) + return divider; + return c->parent->rate / (divider + 1); } return -EINVAL; } @@ -732,7 +899,7 @@ static struct clk_ops tegra_periph_clk_ops = { .disable = &tegra2_periph_clk_disable, .set_parent = &tegra2_periph_clk_set_parent, .set_rate = &tegra2_periph_clk_set_rate, - .recalculate_rate = &tegra2_clk_recalculate_rate, + .round_rate = &tegra2_periph_clk_round_rate, }; /* Clock doubler ops */ @@ -744,21 +911,108 @@ static void tegra2_clk_double_init(struct clk *c) if (!(clk_readl(CLK_OUT_ENB + PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_ENB_BIT(c))) c->state = OFF; - tegra2_clk_recalculate_rate(c); }; +static int tegra2_clk_double_set_rate(struct clk *c, unsigned long rate) +{ + if (rate != 2 * c->parent->rate) + return -EINVAL; + c->mul = 2; + c->div = 1; + return 0; +} + static struct clk_ops tegra_clk_double_ops = { .init = &tegra2_clk_double_init, .enable = &tegra2_periph_clk_enable, .disable = &tegra2_periph_clk_disable, - .recalculate_rate = &tegra2_clk_recalculate_rate, + .set_rate = &tegra2_clk_double_set_rate, +}; + +static void tegra2_audio_sync_clk_init(struct clk *c) +{ + int source; + const struct clk_mux_sel *sel; + u32 val = clk_readl(c->reg); + c->state = (val & (1<<4)) ? OFF : ON; + source = val & 0xf; + for (sel = c->inputs; sel->input != NULL; sel++) + if (sel->value == source) + break; + BUG_ON(sel->input == NULL); + c->parent = sel->input; +} + +static int tegra2_audio_sync_clk_enable(struct clk *c) +{ + clk_writel(0, c->reg); + return 0; +} + +static void tegra2_audio_sync_clk_disable(struct clk *c) +{ + clk_writel(1, c->reg); +} + +static int tegra2_audio_sync_clk_set_parent(struct clk *c, struct clk *p) +{ + u32 val; + const struct clk_mux_sel *sel; + for (sel = c->inputs; sel->input != NULL; sel++) { + if (sel->input == p) { + val = clk_readl(c->reg); + val &= ~0xf; + val |= sel->value; + + if (c->refcnt) + clk_enable_locked(p); + + clk_writel(val, c->reg); + + if (c->refcnt && c->parent) + clk_disable_locked(c->parent); + + clk_reparent(c, p); + return 0; + } + } + + return -EINVAL; +} + +static int tegra2_audio_sync_clk_set_rate(struct clk *c, unsigned long rate) +{ + unsigned long parent_rate; + if (!c->parent) { + pr_err("%s: clock has no parent\n", __func__); + return -EINVAL; + } + parent_rate = c->parent->rate; + if (rate != parent_rate) { + pr_err("%s: %s/%ld differs from parent %s/%ld\n", + __func__, + c->name, rate, + c->parent->name, parent_rate); + return -EINVAL; + } + c->rate = parent_rate; + return 0; +} + +static struct clk_ops tegra_audio_sync_clk_ops = { + .init = tegra2_audio_sync_clk_init, + .enable = tegra2_audio_sync_clk_enable, + .disable = tegra2_audio_sync_clk_disable, + .set_rate = tegra2_audio_sync_clk_set_rate, + .set_parent = tegra2_audio_sync_clk_set_parent, }; /* Clock definitions */ static struct clk tegra_clk_32k = { .name = "clk_32k", - .rate = 32678, + .rate = 32768, .ops = NULL, + .max_rate = 32768, }; static struct clk_pll_table tegra_pll_s_table[] = { @@ -782,6 +1036,7 @@ static struct clk tegra_pll_s = { .vco_min = 12000000, .vco_max = 26000000, .pll_table = tegra_pll_s_table, + .max_rate = 26000000, }; static struct clk_mux_sel tegra_clk_m_sel[] = { @@ -797,6 +1052,7 @@ static struct clk tegra_clk_m = { .reg = 0x1fc, .reg_mask = (1<<28), .reg_shift = 28, + .max_rate = 26000000, }; static struct clk_pll_table tegra_pll_c_table[] = { @@ -816,6 +1072,7 @@ static struct clk tegra_pll_c = { .vco_min = 20000000, .vco_max = 1400000000, .pll_table = tegra_pll_c_table, + .max_rate = 600000000, }; static struct clk tegra_pll_c_out1 = { @@ -825,9 +1082,18 @@ static struct clk tegra_pll_c_out1 = { .parent = &tegra_pll_c, .reg = 0x84, .reg_shift = 0, + .max_rate = 600000000, }; static struct clk_pll_table tegra_pll_m_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}, { 0, 0, 0, 0, 0, 0 }, }; @@ -844,6 +1110,7 @@ static struct clk tegra_pll_m = { .vco_min = 20000000, .vco_max = 1200000000, .pll_table = tegra_pll_m_table, + .max_rate = 800000000, }; static struct clk tegra_pll_m_out1 = { @@ -853,6 +1120,7 @@ static struct clk tegra_pll_m_out1 = { .parent = &tegra_pll_m, .reg = 0x94, .reg_shift = 0, + .max_rate = 600000000, }; static struct clk_pll_table tegra_pll_p_table[] = { @@ -880,6 +1148,7 @@ static struct clk tegra_pll_p = { .vco_min = 20000000, .vco_max = 1400000000, .pll_table = tegra_pll_p_table, + .max_rate = 432000000, }; static struct clk tegra_pll_p_out1 = { @@ -889,6 +1158,7 @@ static struct clk tegra_pll_p_out1 = { .parent = &tegra_pll_p, .reg = 0xa4, .reg_shift = 0, + .max_rate = 432000000, }; static struct clk tegra_pll_p_out2 = { @@ -898,6 +1168,7 @@ static struct clk tegra_pll_p_out2 = { .parent = &tegra_pll_p, .reg = 0xa4, .reg_shift = 16, + .max_rate = 432000000, }; static struct clk tegra_pll_p_out3 = { @@ -907,6 +1178,7 @@ static struct clk tegra_pll_p_out3 = { .parent = &tegra_pll_p, .reg = 0xa8, .reg_shift = 0, + .max_rate = 432000000, }; static struct clk tegra_pll_p_out4 = { @@ -916,6 +1188,7 @@ static struct clk tegra_pll_p_out4 = { .parent = &tegra_pll_p, .reg = 0xa8, .reg_shift = 16, + .max_rate = 432000000, }; static struct clk_pll_table tegra_pll_a_table[] = { @@ -923,6 +1196,7 @@ static struct clk_pll_table tegra_pll_a_table[] = { { 28800000, 73728000, 64, 25, 1, 1}, { 28800000, 11289600, 49, 25, 1, 1}, { 28800000, 12288000, 64, 25, 1, 1}, + { 28800000, 24000000, 5, 6, 1, 1}, { 0, 0, 0, 0, 0, 0 }, }; @@ -939,6 +1213,7 @@ static struct clk tegra_pll_a = { .vco_min = 20000000, .vco_max = 1400000000, .pll_table = tegra_pll_a_table, + .max_rate = 56448000, }; static struct clk tegra_pll_a_out0 = { @@ -948,6 +1223,7 @@ static struct clk tegra_pll_a_out0 = { .parent = &tegra_pll_a, .reg = 0xb4, .reg_shift = 0, + .max_rate = 56448000, }; static struct clk_pll_table tegra_pll_d_table[] = { @@ -971,6 +1247,7 @@ static struct clk tegra_pll_d = { .vco_min = 40000000, .vco_max = 1000000000, .pll_table = tegra_pll_d_table, + .max_rate = 1000000000, }; static struct clk tegra_pll_d_out0 = { @@ -978,19 +1255,20 @@ static struct clk tegra_pll_d_out0 = { .ops = &tegra_pll_div_ops, .flags = DIV_2 | PLLD, .parent = &tegra_pll_d, + .max_rate = 500000000, }; static struct clk_pll_table tegra_pll_u_table[] = { - { 12000000, 480000000, 960, 12, 1, 0}, - { 13000000, 480000000, 960, 13, 1, 0}, - { 19200000, 480000000, 200, 4, 1, 0}, - { 26000000, 480000000, 960, 26, 1, 0}, + { 12000000, 480000000, 960, 12, 2, 0}, + { 13000000, 480000000, 960, 13, 2, 0}, + { 19200000, 480000000, 200, 4, 2, 0}, + { 26000000, 480000000, 960, 26, 2, 0}, { 0, 0, 0, 0, 0, 0 }, }; static struct clk tegra_pll_u = { .name = "pll_u", - .flags = 0, + .flags = PLLU, .ops = &tegra_pll_ops, .reg = 0xc0, .input_min = 2000000, @@ -1001,24 +1279,59 @@ static struct clk tegra_pll_u = { .vco_min = 480000000, .vco_max = 960000000, .pll_table = tegra_pll_u_table, + .max_rate = 480000000, }; static struct clk_pll_table tegra_pll_x_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, 750000000, 750, 12, 1, 12}, - { 13000000, 750000000, 750, 13, 1, 12}, - { 19200000, 750000000, 625, 16, 1, 8}, - { 26000000, 750000000, 750, 26, 1, 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}, + + /* 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}, + + /* 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}, + + /* 608 MHz */ + { 12000000, 608000000, 760, 12, 1, 12}, + { 13000000, 608000000, 760, 13, 1, 12}, + { 19200000, 608000000, 380, 12, 1, 8}, + { 26000000, 608000000, 760, 26, 1, 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}, + + /* 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}, + { 0, 0, 0, 0, 0, 0 }, }; static struct clk tegra_pll_x = { .name = "pll_x", .flags = PLL_HAS_CPCON | PLL_ALT_MISC_REG, - .ops = &tegra_pll_ops, + .ops = &tegra_pllx_ops, .reg = 0xe0, .input_min = 2000000, .input_max = 31000000, @@ -1028,6 +1341,24 @@ static struct clk tegra_pll_x = { .vco_min = 20000000, .vco_max = 1200000000, .pll_table = tegra_pll_x_table, + .max_rate = 1000000000, +}; + +static struct clk_pll_table tegra_pll_e_table[] = { + { 12000000, 100000000, 200, 24, 1, 0 }, + { 0, 0, 0, 0, 0, 0 }, +}; + +static struct clk tegra_pll_e = { + .name = "pll_e", + .flags = PLL_ALT_MISC_REG, + .ops = &tegra_plle_ops, + .input_min = 12000000, + .input_max = 12000000, + .max_rate = 100000000, + .parent = &tegra_clk_m, + .reg = 0xe8, + .pll_table = tegra_pll_e_table, }; static struct clk tegra_clk_d = { @@ -1038,19 +1369,77 @@ static struct clk tegra_clk_d = { .reg = 0x34, .reg_shift = 12, .parent = &tegra_clk_m, + .max_rate = 52000000, +}; + +/* initialized before peripheral clocks */ +static struct clk_mux_sel mux_audio_sync_clk[8+1]; +static const struct audio_sources { + const char *name; + int value; +} mux_audio_sync_clk_sources[] = { + { .name = "spdif_in", .value = 0 }, + { .name = "i2s1", .value = 1 }, + { .name = "i2s2", .value = 2 }, + { .name = "pll_a_out0", .value = 4 }, +#if 0 /* FIXME: not implemented */ + { .name = "ac97", .value = 3 }, + { .name = "ext_audio_clk2", .value = 5 }, + { .name = "ext_audio_clk1", .value = 6 }, + { .name = "ext_vimclk", .value = 7 }, +#endif + { 0, 0 } +}; + +static struct clk tegra_clk_audio = { + .name = "audio", + .inputs = mux_audio_sync_clk, + .reg = 0x38, + .max_rate = 24000000, + .ops = &tegra_audio_sync_clk_ops }; -/* FIXME: need tegra_audio static struct clk tegra_clk_audio_2x = { - .name = "clk_d", + .name = "audio_2x", .flags = PERIPH_NO_RESET, + .max_rate = 48000000, .ops = &tegra_clk_double_ops, .clk_num = 89, .reg = 0x34, .reg_shift = 8, - .parent = &tegra_audio, + .parent = &tegra_clk_audio, +}; + +struct clk_lookup tegra_audio_clk_lookups[] = { + { .con_id = "audio", .clk = &tegra_clk_audio }, + { .con_id = "audio_2x", .clk = &tegra_clk_audio_2x } +}; + +/* This is called after peripheral clocks are initialized, as the + * audio_sync clock depends on some of the peripheral clocks. + */ + +static void init_audio_sync_clock_mux(void) +{ + int i; + struct clk_mux_sel *sel = mux_audio_sync_clk; + const struct audio_sources *src = mux_audio_sync_clk_sources; + struct clk_lookup *lookup; + + for (i = 0; src->name; i++, sel++, src++) { + sel->input = tegra_get_clock_by_name(src->name); + if (!sel->input) + pr_err("%s: could not find clk %s\n", __func__, + src->name); + sel->value = src->value; + } + + lookup = tegra_audio_clk_lookups; + for (i = 0; i < ARRAY_SIZE(tegra_audio_clk_lookups); i++, lookup++) { + clk_init(lookup->clk); + clkdev_add(lookup); + } } -*/ static struct clk_mux_sel mux_cclk[] = { { .input = &tegra_clk_m, .value = 0}, @@ -1077,27 +1466,40 @@ static struct clk_mux_sel mux_sclk[] = { { 0, 0}, }; -static struct clk tegra_clk_cpu = { - .name = "cpu", +static struct clk tegra_clk_cclk = { + .name = "cclk", .inputs = mux_cclk, .reg = 0x20, .ops = &tegra_super_ops, + .max_rate = 1000000000, }; -static struct clk tegra_clk_sys = { - .name = "sys", +static struct clk tegra_clk_sclk = { + .name = "sclk", .inputs = mux_sclk, .reg = 0x28, .ops = &tegra_super_ops, + .max_rate = 600000000, +}; + +static struct clk tegra_clk_virtual_cpu = { + .name = "cpu", + .parent = &tegra_clk_cclk, + .main = &tegra_pll_x, + .backup = &tegra_clk_m, + .ops = &tegra_cpu_ops, + .max_rate = 1000000000, + .dvfs = &tegra_dvfs_virtual_cpu_dvfs, }; static struct clk tegra_clk_hclk = { .name = "hclk", .flags = DIV_BUS, - .parent = &tegra_clk_sys, + .parent = &tegra_clk_sclk, .reg = 0x30, .reg_shift = 4, .ops = &tegra_bus_ops, + .max_rate = 240000000, }; static struct clk tegra_clk_pclk = { @@ -1107,6 +1509,7 @@ static struct clk tegra_clk_pclk = { .reg = 0x30, .reg_shift = 0, .ops = &tegra_bus_ops, + .max_rate = 108000000, }; static struct clk_mux_sel mux_pllm_pllc_pllp_plla[] = { @@ -1133,10 +1536,9 @@ static struct clk_mux_sel mux_pllp_pllc_pllm_clkm[] = { { 0, 0}, }; -static struct clk_mux_sel mux_plla_audio_pllp_clkm[] = { - {.input = &tegra_pll_a, .value = 0}, - /* FIXME: no mux defined for tegra_audio - {.input = &tegra_audio, .value = 1},*/ +static struct clk_mux_sel mux_pllaout0_audio2x_pllp_clkm[] = { + {.input = &tegra_pll_a_out0, .value = 0}, + {.input = &tegra_clk_audio_2x, .value = 1}, {.input = &tegra_pll_p, .value = 2}, {.input = &tegra_clk_m, .value = 3}, { 0, 0}, @@ -1153,8 +1555,7 @@ static struct clk_mux_sel mux_pllp_plld_pllc_clkm[] = { static struct clk_mux_sel mux_pllp_pllc_audio_clkm_clk32[] = { {.input = &tegra_pll_p, .value = 0}, {.input = &tegra_pll_c, .value = 1}, - /* FIXME: no mux defined for tegra_audio - {.input = &tegra_audio, .value = 2},*/ + {.input = &tegra_clk_audio, .value = 2}, {.input = &tegra_clk_m, .value = 3}, {.input = &tegra_clk_32k, .value = 4}, { 0, 0}, @@ -1187,7 +1588,7 @@ static struct clk_mux_sel mux_clk_32k[] = { { 0, 0}, }; -#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _inputs, _flags) \ +#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \ { \ .name = _name, \ .lookup = { \ @@ -1199,72 +1600,79 @@ static struct clk_mux_sel mux_clk_32k[] = { .reg = _reg, \ .inputs = _inputs, \ .flags = _flags, \ + .max_rate = _max, \ } struct clk tegra_periph_clks[] = { - PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, mux_clk_32k, PERIPH_NO_RESET), - PERIPH_CLK("timer", "timer", NULL, 5, 0, mux_clk_m, 0), - PERIPH_CLK("i2s1", "i2s.0", NULL, 11, 0x100, mux_plla_audio_pllp_clkm, MUX | DIV_U71), - PERIPH_CLK("i2s2", "i2s.1", NULL, 18, 0x104, mux_plla_audio_pllp_clkm, MUX | DIV_U71), + PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET), + PERIPH_CLK("timer", "timer", NULL, 5, 0, 26000000, mux_clk_m, 0), + PERIPH_CLK("i2s1", "i2s.0", NULL, 11, 0x100, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), + PERIPH_CLK("i2s2", "i2s.1", NULL, 18, 0x104, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), /* FIXME: spdif has 2 clocks but 1 enable */ - PERIPH_CLK("spdif_out", "spdif_out", NULL, 10, 0x108, mux_plla_audio_pllp_clkm, MUX | DIV_U71), - PERIPH_CLK("spdif_in", "spdif_in", NULL, 10, 0x10c, mux_pllp_pllc_pllm, MUX | DIV_U71), - PERIPH_CLK("pwm", "pwm", NULL, 17, 0x110, mux_pllp_pllc_audio_clkm_clk32, MUX | DIV_U71), - PERIPH_CLK("spi", "spi", NULL, 43, 0x114, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("xio", "xio", NULL, 45, 0x120, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("twc", "twc", NULL, 16, 0x12c, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sbc1", "spi_tegra.0", NULL, 41, 0x134, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sbc2", "spi_tegra.1", NULL, 44, 0x118, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sbc3", "spi_tegra.2", NULL, 46, 0x11c, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sbc4", "spi_tegra.3", NULL, 68, 0x1b4, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("ide", "ide", NULL, 25, 0x144, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("ndflash", "tegra_nand", NULL, 13, 0x160, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("spdif_out", "spdif_out", NULL, 10, 0x108, 100000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), + PERIPH_CLK("spdif_in", "spdif_in", NULL, 10, 0x10c, 100000000, mux_pllp_pllc_pllm, MUX | DIV_U71), + PERIPH_CLK("pwm", "pwm", NULL, 17, 0x110, 432000000, mux_pllp_pllc_audio_clkm_clk32, MUX | DIV_U71), + PERIPH_CLK("spi", "spi", NULL, 43, 0x114, 40000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("xio", "xio", NULL, 45, 0x120, 150000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("twc", "twc", NULL, 16, 0x12c, 150000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("sbc1", "spi_tegra.0", NULL, 41, 0x134, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("sbc2", "spi_tegra.1", NULL, 44, 0x118, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("sbc3", "spi_tegra.2", NULL, 46, 0x11c, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("sbc4", "spi_tegra.3", NULL, 68, 0x1b4, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("ide", "ide", NULL, 25, 0x144, 100000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* requires min voltage */ + PERIPH_CLK("ndflash", "tegra_nand", NULL, 13, 0x160, 164000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */ /* FIXME: vfir shares an enable with uartb */ - PERIPH_CLK("vfir", "vfir", NULL, 7, 0x168, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sdmmc1", "sdhci-tegra.0", NULL, 14, 0x150, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sdmmc2", "sdhci-tegra.1", NULL, 9, 0x154, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sdmmc3", "sdhci-tegra.2", NULL, 69, 0x1bc, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("sdmmc4", "sdhci-tegra.3", NULL, 15, 0x160, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("vde", "vde", NULL, 61, 0x1c8, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("csite", "csite", NULL, 73, 0x1d4, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("vfir", "vfir", NULL, 7, 0x168, 72000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("sdmmc1", "sdhci-tegra.0", NULL, 14, 0x150, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */ + PERIPH_CLK("sdmmc2", "sdhci-tegra.1", NULL, 9, 0x154, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */ + PERIPH_CLK("sdmmc3", "sdhci-tegra.2", NULL, 69, 0x1bc, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */ + PERIPH_CLK("sdmmc4", "sdhci-tegra.3", NULL, 15, 0x160, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */ + PERIPH_CLK("vde", "vde", NULL, 61, 0x1c8, 250000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage and process_id */ + PERIPH_CLK("csite", "csite", NULL, 73, 0x1d4, 144000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* max rate ??? */ /* FIXME: what is la? */ - PERIPH_CLK("la", "la", NULL, 76, 0x1f8, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("owr", "owr", NULL, 71, 0x1cc, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("nor", "nor", NULL, 42, 0x1d0, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("mipi", "mipi", NULL, 50, 0x174, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("i2c1", "tegra-i2c.0", NULL, 12, 0x124, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("i2c2", "tegra-i2c.1", NULL, 54, 0x198, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("i2c3", "tegra-i2c.2", NULL, 67, 0x1b8, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("dvc", "tegra-i2c.3", NULL, 47, 0x128, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("i2c1_i2c", "tegra-i2c.0", "i2c", 0, 0, mux_pllp_out3, 0), - PERIPH_CLK("i2c2_i2c", "tegra-i2c.1", "i2c", 0, 0, mux_pllp_out3, 0), - PERIPH_CLK("i2c3_i2c", "tegra-i2c.2", "i2c", 0, 0, mux_pllp_out3, 0), - PERIPH_CLK("dvc_i2c", "tegra-i2c.3", "i2c", 0, 0, mux_pllp_out3, 0), - PERIPH_CLK("uarta", "uart.0", NULL, 6, 0x178, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("uartb", "uart.1", NULL, 7, 0x17c, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("uartc", "uart.2", NULL, 55, 0x1a0, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("uartd", "uart.3", NULL, 65, 0x1c0, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("uarte", "uart.4", NULL, 66, 0x1c4, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), - PERIPH_CLK("3d", "3d", NULL, 24, 0x158, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_MANUAL_RESET), - PERIPH_CLK("2d", "2d", NULL, 21, 0x15c, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), + PERIPH_CLK("la", "la", NULL, 76, 0x1f8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("owr", "tegra_w1", NULL, 71, 0x1cc, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), + PERIPH_CLK("nor", "nor", NULL, 42, 0x1d0, 92000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* requires min voltage */ + PERIPH_CLK("mipi", "mipi", NULL, 50, 0x174, 60000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */ + PERIPH_CLK("i2c1", "tegra-i2c.0", NULL, 12, 0x124, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16), + PERIPH_CLK("i2c2", "tegra-i2c.1", NULL, 54, 0x198, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16), + PERIPH_CLK("i2c3", "tegra-i2c.2", NULL, 67, 0x1b8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16), + PERIPH_CLK("dvc", "tegra-i2c.3", NULL, 47, 0x128, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16), + PERIPH_CLK("i2c1_i2c", "tegra-i2c.0", "i2c", 0, 0, 72000000, mux_pllp_out3, 0), + PERIPH_CLK("i2c2_i2c", "tegra-i2c.1", "i2c", 0, 0, 72000000, mux_pllp_out3, 0), + PERIPH_CLK("i2c3_i2c", "tegra-i2c.2", "i2c", 0, 0, 72000000, mux_pllp_out3, 0), + PERIPH_CLK("dvc_i2c", "tegra-i2c.3", "i2c", 0, 0, 72000000, mux_pllp_out3, 0), + PERIPH_CLK("uarta", "uart.0", NULL, 6, 0x178, 216000000, mux_pllp_pllc_pllm_clkm, MUX), + PERIPH_CLK("uartb", "uart.1", NULL, 7, 0x17c, 216000000, mux_pllp_pllc_pllm_clkm, MUX), + PERIPH_CLK("uartc", "uart.2", NULL, 55, 0x1a0, 216000000, mux_pllp_pllc_pllm_clkm, MUX), + PERIPH_CLK("uartd", "uart.3", NULL, 65, 0x1c0, 216000000, mux_pllp_pllc_pllm_clkm, MUX), + PERIPH_CLK("uarte", "uart.4", NULL, 66, 0x1c4, 216000000, mux_pllp_pllc_pllm_clkm, MUX), + PERIPH_CLK("3d", "3d", NULL, 24, 0x158, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_MANUAL_RESET), /* scales with voltage and process_id */ + PERIPH_CLK("2d", "2d", NULL, 21, 0x15c, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */ /* FIXME: vi and vi_sensor share an enable */ - PERIPH_CLK("vi", "vi", NULL, 20, 0x148, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), - PERIPH_CLK("vi_sensor", "vi_sensor", NULL, 20, 0x1a8, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), - PERIPH_CLK("epp", "epp", NULL, 19, 0x16c, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), - PERIPH_CLK("mpe", "mpe", NULL, 60, 0x170, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), - PERIPH_CLK("host1x", "host1x", NULL, 28, 0x180, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), + PERIPH_CLK("vi", "vi", NULL, 20, 0x148, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */ + PERIPH_CLK("vi_sensor", "vi_sensor", NULL, 20, 0x1a8, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_NO_RESET), /* scales with voltage and process_id */ + PERIPH_CLK("epp", "epp", NULL, 19, 0x16c, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */ + PERIPH_CLK("mpe", "mpe", NULL, 60, 0x170, 250000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */ + PERIPH_CLK("host1x", "host1x", NULL, 28, 0x180, 166000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */ /* FIXME: cve and tvo share an enable */ - PERIPH_CLK("cve", "cve", NULL, 49, 0x140, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), - PERIPH_CLK("tvo", "tvo", NULL, 49, 0x188, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), - PERIPH_CLK("hdmi", "hdmi", NULL, 51, 0x18c, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), - PERIPH_CLK("tvdac", "tvdac", NULL, 53, 0x194, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), - PERIPH_CLK("disp1", "tegrafb.0", NULL, 27, 0x138, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), - PERIPH_CLK("disp2", "tegrafb.1", NULL, 26, 0x13c, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), - PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, mux_clk_m, 0), - PERIPH_CLK("usb2", "usb.1", NULL, 58, 0, mux_clk_m, 0), - PERIPH_CLK("usb3", "usb.2", NULL, 59, 0, mux_clk_m, 0), - PERIPH_CLK("emc", "emc", NULL, 57, 0x19c, mux_pllm_pllc_pllp_clkm, MUX | DIV_U71 | PERIPH_EMC_ENB), - PERIPH_CLK("dsi", "dsi", NULL, 48, 0, mux_plld, 0), + PERIPH_CLK("cve", "cve", NULL, 49, 0x140, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */ + PERIPH_CLK("tvo", "tvo", NULL, 49, 0x188, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */ + PERIPH_CLK("hdmi", "hdmi", NULL, 51, 0x18c, 148500000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */ + PERIPH_CLK("tvdac", "tvdac", NULL, 53, 0x194, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */ + PERIPH_CLK("disp1", "tegrafb.0", NULL, 27, 0x138, 190000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* scales with voltage and process_id */ + PERIPH_CLK("disp2", "tegrafb.1", NULL, 26, 0x13c, 190000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* scales with voltage and process_id */ + 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 */ + 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, 0), /* scales with voltage */ + PERIPH_CLK("csi", "csi", NULL, 52, 0, 72000000, mux_pllp_out3, 0), + PERIPH_CLK("isp", "isp", NULL, 23, 0, 150000000, mux_clk_m, 0), /* same frequency as VI */ + PERIPH_CLK("csus", "csus", NULL, 92, 0, 150000000, mux_clk_m, PERIPH_NO_RESET), + PERIPH_CLK("pex", NULL, "pex", 70, 0, 26000000, mux_clk_m, PERIPH_MANUAL_RESET), + PERIPH_CLK("afi", NULL, "afi", 72, 0, 26000000, mux_clk_m, PERIPH_MANUAL_RESET), + PERIPH_CLK("pcie_xclk", NULL, "pcie_xclk", 74, 0, 26000000, mux_clk_m, PERIPH_MANUAL_RESET), }; #define CLK_DUPLICATE(_name, _dev, _con) \ @@ -1286,6 +1694,9 @@ struct clk_duplicate tegra_clk_duplicates[] = { CLK_DUPLICATE("uartc", "tegra_uart.2", NULL), CLK_DUPLICATE("uartd", "tegra_uart.3", NULL), CLK_DUPLICATE("uarte", "tegra_uart.4", NULL), + CLK_DUPLICATE("host1x", "tegrafb.0", "host1x"), + CLK_DUPLICATE("host1x", "tegrafb.1", "host1x"), + CLK_DUPLICATE("usbd", "tegra-ehci.0", NULL), }; #define CLK(dev, con, ck) \ @@ -1315,11 +1726,13 @@ struct clk_lookup tegra_clk_lookups[] = { CLK(NULL, "pll_d_out0", &tegra_pll_d_out0), CLK(NULL, "pll_u", &tegra_pll_u), CLK(NULL, "pll_x", &tegra_pll_x), - CLK(NULL, "cpu", &tegra_clk_cpu), - CLK(NULL, "sys", &tegra_clk_sys), + CLK(NULL, "pll_e", &tegra_pll_e), + CLK(NULL, "cclk", &tegra_clk_cclk), + CLK(NULL, "sclk", &tegra_clk_sclk), CLK(NULL, "hclk", &tegra_clk_hclk), CLK(NULL, "pclk", &tegra_clk_pclk), CLK(NULL, "clk_d", &tegra_clk_d), + CLK(NULL, "cpu", &tegra_clk_virtual_cpu), }; void __init tegra2_init_clocks(void) @@ -1356,4 +1769,75 @@ void __init tegra2_init_clocks(void) cd->name); } } + + init_audio_sync_clock_mux(); +} + +#ifdef CONFIG_PM +static u32 clk_rst_suspend[RST_DEVICES_NUM + CLK_OUT_ENB_NUM + + PERIPH_CLK_SOURCE_NUM + 3]; + +void tegra_clk_suspend(void) +{ + unsigned long off, i; + u32 *ctx = clk_rst_suspend; + + *ctx++ = clk_readl(OSC_CTRL) & OSC_CTRL_MASK; + + for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC; + off += 4) { + if (off == PERIPH_CLK_SOURCE_EMC) + continue; + *ctx++ = clk_readl(off); + } + + off = RST_DEVICES; + for (i = 0; i < RST_DEVICES_NUM; i++, off += 4) + *ctx++ = clk_readl(off); + + off = CLK_OUT_ENB; + for (i = 0; i < CLK_OUT_ENB_NUM; i++, off += 4) + *ctx++ = clk_readl(off); + + *ctx++ = clk_readl(MISC_CLK_ENB); + *ctx++ = clk_readl(CLK_MASK_ARM); +} + +void tegra_clk_resume(void) +{ + unsigned long off, i; + const u32 *ctx = clk_rst_suspend; + u32 val; + + val = clk_readl(OSC_CTRL) & ~OSC_CTRL_MASK; + val |= *ctx++; + clk_writel(val, OSC_CTRL); + + /* enable all clocks before configuring clock sources */ + clk_writel(0xbffffff9ul, CLK_OUT_ENB); + clk_writel(0xfefffff7ul, CLK_OUT_ENB + 4); + clk_writel(0x77f01bfful, CLK_OUT_ENB + 8); + wmb(); + + for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC; + off += 4) { + if (off == PERIPH_CLK_SOURCE_EMC) + continue; + clk_writel(*ctx++, off); + } + wmb(); + + off = RST_DEVICES; + for (i = 0; i < RST_DEVICES_NUM; i++, off += 4) + clk_writel(*ctx++, off); + wmb(); + + off = CLK_OUT_ENB; + for (i = 0; i < CLK_OUT_ENB_NUM; i++, off += 4) + clk_writel(*ctx++, off); + wmb(); + + clk_writel(*ctx++, MISC_CLK_ENB); + clk_writel(*ctx++, CLK_MASK_ARM); } +#endif diff --git a/arch/arm/mach-tegra/tegra2_dvfs.c b/arch/arm/mach-tegra/tegra2_dvfs.c new file mode 100644 index 00000000000..5529c238dd7 --- /dev/null +++ b/arch/arm/mach-tegra/tegra2_dvfs.c @@ -0,0 +1,86 @@ +/* + * arch/arm/mach-tegra/tegra2_dvfs.c + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross <ccross@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/kernel.h> + +#include "clock.h" +#include "tegra2_dvfs.h" + +static struct dvfs_table virtual_cpu_process_0[] = { + {314000000, 750}, + {456000000, 825}, + {608000000, 900}, + {760000000, 975}, + {817000000, 1000}, + {912000000, 1050}, + {1000000000, 1100}, + {0, 0}, +}; + +static struct dvfs_table virtual_cpu_process_1[] = { + {314000000, 750}, + {456000000, 825}, + {618000000, 900}, + {770000000, 975}, + {827000000, 1000}, + {922000000, 1050}, + {1000000000, 1100}, + {0, 0}, +}; + +static struct dvfs_table virtual_cpu_process_2[] = { + {494000000, 750}, + {675000000, 825}, + {817000000, 875}, + {922000000, 925}, + {1000000000, 975}, + {0, 0}, +}; + +static struct dvfs_table virtual_cpu_process_3[] = { + {730000000, 750}, + {760000000, 775}, + {845000000, 800}, + {1000000000, 875}, + {0, 0}, +}; + +struct dvfs tegra_dvfs_virtual_cpu_dvfs = { + .reg_id = "vdd_cpu", + .process_id_table = { + { + .process_id = 0, + .table = virtual_cpu_process_0, + }, + { + .process_id = 1, + .table = virtual_cpu_process_1, + }, + { + .process_id = 2, + .table = virtual_cpu_process_2, + }, + { + .process_id = 3, + .table = virtual_cpu_process_3, + }, + }, + .process_id_table_length = 4, + .cpu = 1, +}; diff --git a/arch/arm/mach-tegra/tegra2_dvfs.h b/arch/arm/mach-tegra/tegra2_dvfs.h new file mode 100644 index 00000000000..f8c1adba96a --- /dev/null +++ b/arch/arm/mach-tegra/tegra2_dvfs.h @@ -0,0 +1,20 @@ +/* + * arch/arm/mach-tegra/tegra2_dvfs.h + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Colin Cross <ccross@google.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +extern struct dvfs tegra_dvfs_virtual_cpu_dvfs; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4b9eec68fad..78f9fd02c1b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -329,6 +329,13 @@ config SPI_STMP3XXX help SPI driver for Freescale STMP37xx/378x SoC SSP interface +config SPI_TEGRA + tristate "Nvidia Tegra SPI controller" + depends on ARCH_TEGRA + select TEGRA_SYSTEM_DMA + help + SPI driver for NVidia Tegra SoCs + config SPI_TOPCLIFF_PCH tristate "Topcliff PCH SPI Controller" depends on PCI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 557aaadf56b..8bc1a5abac1 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_SPI_PPC4xx) += spi_ppc4xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o +obj-$(CONFIG_SPI_TEGRA) += spi_tegra.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o diff --git a/drivers/spi/spi_tegra.c b/drivers/spi/spi_tegra.c new file mode 100644 index 00000000000..bb7df02a547 --- /dev/null +++ b/drivers/spi/spi_tegra.c @@ -0,0 +1,618 @@ +/* + * Driver for Nvidia TEGRA spi controller. + * + * Copyright (C) 2010 Google, Inc. + * + * Author: + * Erik Gilling <konkers@android.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/delay.h> + +#include <linux/spi/spi.h> + +#include <mach/dma.h> + +#define SLINK_COMMAND 0x000 +#define SLINK_BIT_LENGTH(x) (((x) & 0x1f) << 0) +#define SLINK_WORD_SIZE(x) (((x) & 0x1f) << 5) +#define SLINK_BOTH_EN (1 << 10) +#define SLINK_CS_SW (1 << 11) +#define SLINK_CS_VALUE (1 << 12) +#define SLINK_CS_POLARITY (1 << 13) +#define SLINK_IDLE_SDA_DRIVE_LOW (0 << 16) +#define SLINK_IDLE_SDA_DRIVE_HIGH (1 << 16) +#define SLINK_IDLE_SDA_PULL_LOW (2 << 16) +#define SLINK_IDLE_SDA_PULL_HIGH (3 << 16) +#define SLINK_IDLE_SDA_MASK (3 << 16) +#define SLINK_CS_POLARITY1 (1 << 20) +#define SLINK_CK_SDA (1 << 21) +#define SLINK_CS_POLARITY2 (1 << 22) +#define SLINK_CS_POLARITY3 (1 << 23) +#define SLINK_IDLE_SCLK_DRIVE_LOW (0 << 24) +#define SLINK_IDLE_SCLK_DRIVE_HIGH (1 << 24) +#define SLINK_IDLE_SCLK_PULL_LOW (2 << 24) +#define SLINK_IDLE_SCLK_PULL_HIGH (3 << 24) +#define SLINK_IDLE_SCLK_MASK (3 << 24) +#define SLINK_M_S (1 << 28) +#define SLINK_WAIT (1 << 29) +#define SLINK_GO (1 << 30) +#define SLINK_ENB (1 << 31) + +#define SLINK_COMMAND2 0x004 +#define SLINK_LSBFE (1 << 0) +#define SLINK_SSOE (1 << 1) +#define SLINK_SPIE (1 << 4) +#define SLINK_BIDIROE (1 << 6) +#define SLINK_MODFEN (1 << 7) +#define SLINK_INT_SIZE(x) (((x) & 0x1f) << 8) +#define SLINK_CS_ACTIVE_BETWEEN (1 << 17) +#define SLINK_SS_EN_CS(x) (((x) & 0x3) << 18) +#define SLINK_SS_SETUP(x) (((x) & 0x3) << 20) +#define SLINK_FIFO_REFILLS_0 (0 << 22) +#define SLINK_FIFO_REFILLS_1 (1 << 22) +#define SLINK_FIFO_REFILLS_2 (2 << 22) +#define SLINK_FIFO_REFILLS_3 (3 << 22) +#define SLINK_FIFO_REFILLS_MASK (3 << 22) +#define SLINK_WAIT_PACK_INT(x) (((x) & 0x7) << 26) +#define SLINK_SPC0 (1 << 29) +#define SLINK_TXEN (1 << 30) +#define SLINK_RXEN (1 << 31) + +#define SLINK_STATUS 0x008 +#define SLINK_COUNT(val) (((val) >> 0) & 0x1f) +#define SLINK_WORD(val) (((val) >> 5) & 0x1f) +#define SLINK_BLK_CNT(val) (((val) >> 0) & 0xffff) +#define SLINK_MODF (1 << 16) +#define SLINK_RX_UNF (1 << 18) +#define SLINK_TX_OVF (1 << 19) +#define SLINK_TX_FULL (1 << 20) +#define SLINK_TX_EMPTY (1 << 21) +#define SLINK_RX_FULL (1 << 22) +#define SLINK_RX_EMPTY (1 << 23) +#define SLINK_TX_UNF (1 << 24) +#define SLINK_RX_OVF (1 << 25) +#define SLINK_TX_FLUSH (1 << 26) +#define SLINK_RX_FLUSH (1 << 27) +#define SLINK_SCLK (1 << 28) +#define SLINK_ERR (1 << 29) +#define SLINK_RDY (1 << 30) +#define SLINK_BSY (1 << 31) + +#define SLINK_MAS_DATA 0x010 +#define SLINK_SLAVE_DATA 0x014 + +#define SLINK_DMA_CTL 0x018 +#define SLINK_DMA_BLOCK_SIZE(x) (((x) & 0xffff) << 0) +#define SLINK_TX_TRIG_1 (0 << 16) +#define SLINK_TX_TRIG_4 (1 << 16) +#define SLINK_TX_TRIG_8 (2 << 16) +#define SLINK_TX_TRIG_16 (3 << 16) +#define SLINK_TX_TRIG_MASK (3 << 16) +#define SLINK_RX_TRIG_1 (0 << 18) +#define SLINK_RX_TRIG_4 (1 << 18) +#define SLINK_RX_TRIG_8 (2 << 18) +#define SLINK_RX_TRIG_16 (3 << 18) +#define SLINK_RX_TRIG_MASK (3 << 18) +#define SLINK_PACKED (1 << 20) +#define SLINK_PACK_SIZE_4 (0 << 21) +#define SLINK_PACK_SIZE_8 (1 << 21) +#define SLINK_PACK_SIZE_16 (2 << 21) +#define SLINK_PACK_SIZE_32 (3 << 21) +#define SLINK_PACK_SIZE_MASK (3 << 21) +#define SLINK_IE_TXC (1 << 26) +#define SLINK_IE_RXC (1 << 27) +#define SLINK_DMA_EN (1 << 31) + +#define SLINK_STATUS2 0x01c +#define SLINK_TX_FIFO_EMPTY_COUNT(val) (((val) & 0x3f) >> 0) +#define SLINK_RX_FIFO_FULL_COUNT(val) (((val) & 0x3f) >> 16) + +#define SLINK_TX_FIFO 0x100 +#define SLINK_RX_FIFO 0x180 + +static const unsigned long spi_tegra_req_sels[] = { + TEGRA_DMA_REQ_SEL_SL2B1, + TEGRA_DMA_REQ_SEL_SL2B2, + TEGRA_DMA_REQ_SEL_SL2B3, + TEGRA_DMA_REQ_SEL_SL2B4, +}; + +#define BB_LEN 32 + +struct spi_tegra_data { + struct spi_master *master; + struct platform_device *pdev; + spinlock_t lock; + + struct clk *clk; + void __iomem *base; + unsigned long phys; + + u32 cur_speed; + + struct list_head queue; + struct spi_transfer *cur; + unsigned cur_pos; + unsigned cur_len; + unsigned cur_bytes_per_word; + + /* The tegra spi controller has a bug which causes the first word + * in PIO transactions to be garbage. Since packed DMA transactions + * require transfers to be 4 byte aligned we need a bounce buffer + * for the generic case. + */ + struct tegra_dma_req rx_dma_req; + struct tegra_dma_channel *rx_dma; + u32 *rx_bb; + dma_addr_t rx_bb_phys; +}; + + +static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi, + unsigned long reg) +{ + return readl(tspi->base + reg); +} + +static inline void spi_tegra_writel(struct spi_tegra_data *tspi, + unsigned long val, + unsigned long reg) +{ + writel(val, tspi->base + reg); +} + +static void spi_tegra_go(struct spi_tegra_data *tspi) +{ + unsigned long val; + + wmb(); + + val = spi_tegra_readl(tspi, SLINK_DMA_CTL); + val &= ~SLINK_DMA_BLOCK_SIZE(~0) & ~SLINK_DMA_EN; + val |= SLINK_DMA_BLOCK_SIZE(tspi->rx_dma_req.size / 4 - 1); + spi_tegra_writel(tspi, val, SLINK_DMA_CTL); + + tegra_dma_enqueue_req(tspi->rx_dma, &tspi->rx_dma_req); + + val |= SLINK_DMA_EN; + spi_tegra_writel(tspi, val, SLINK_DMA_CTL); +} + +static unsigned spi_tegra_fill_tx_fifo(struct spi_tegra_data *tspi, + struct spi_transfer *t) +{ + unsigned len = min(t->len - tspi->cur_pos, BB_LEN * + tspi->cur_bytes_per_word); + u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_pos; + int i, j; + unsigned long val; + + val = spi_tegra_readl(tspi, SLINK_COMMAND); + val &= ~SLINK_WORD_SIZE(~0); + val |= SLINK_WORD_SIZE(len / tspi->cur_bytes_per_word - 1); + spi_tegra_writel(tspi, val, SLINK_COMMAND); + + for (i = 0; i < len; i += tspi->cur_bytes_per_word) { + val = 0; + for (j = 0; j < tspi->cur_bytes_per_word; j++) + val |= tx_buf[i + j] << j * 8; + + spi_tegra_writel(tspi, val, SLINK_TX_FIFO); + } + + tspi->rx_dma_req.size = len / tspi->cur_bytes_per_word * 4; + + return len; +} + +static unsigned spi_tegra_drain_rx_fifo(struct spi_tegra_data *tspi, + struct spi_transfer *t) +{ + unsigned len = tspi->cur_len; + u8 *rx_buf = (u8 *)t->rx_buf + tspi->cur_pos; + int i, j; + unsigned long val; + + for (i = 0; i < len; i += tspi->cur_bytes_per_word) { + val = tspi->rx_bb[i / tspi->cur_bytes_per_word]; + for (j = 0; j < tspi->cur_bytes_per_word; j++) + rx_buf[i + j] = (val >> (j * 8)) & 0xff; + } + + return len; +} + +static void spi_tegra_start_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master); + u32 speed; + u8 bits_per_word; + unsigned long val; + + speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz; + bits_per_word = t->bits_per_word ? t->bits_per_word : + spi->bits_per_word; + + tspi->cur_bytes_per_word = (bits_per_word - 1) / 8 + 1; + + if (speed != tspi->cur_speed) + clk_set_rate(tspi->clk, speed); + + if (tspi->cur_speed == 0) + clk_enable(tspi->clk); + + tspi->cur_speed = speed; + + val = spi_tegra_readl(tspi, SLINK_COMMAND2); + val &= ~SLINK_SS_EN_CS(~0) | SLINK_RXEN | SLINK_TXEN; + if (t->rx_buf) + val |= SLINK_RXEN; + if (t->tx_buf) + val |= SLINK_TXEN; + val |= SLINK_SS_EN_CS(spi->chip_select); + val |= SLINK_SPIE; + spi_tegra_writel(tspi, val, SLINK_COMMAND2); + + val = spi_tegra_readl(tspi, SLINK_COMMAND); + val &= ~SLINK_BIT_LENGTH(~0); + val |= SLINK_BIT_LENGTH(bits_per_word - 1); + + /* FIXME: should probably control CS manually so that we can be sure + * it does not go low between transfer and to support delay_usecs + * correctly. + */ + val &= ~SLINK_IDLE_SCLK_MASK & ~SLINK_CK_SDA & ~SLINK_CS_SW; + + if (spi->mode & SPI_CPHA) + val |= SLINK_CK_SDA; + + if (spi->mode & SPI_CPOL) + val |= SLINK_IDLE_SCLK_DRIVE_HIGH; + else + val |= SLINK_IDLE_SCLK_DRIVE_LOW; + + val |= SLINK_M_S; + + spi_tegra_writel(tspi, val, SLINK_COMMAND); + + spi_tegra_writel(tspi, SLINK_RX_FLUSH | SLINK_TX_FLUSH, SLINK_STATUS); + + tspi->cur = t; + tspi->cur_pos = 0; + tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, t); + + spi_tegra_go(tspi); +} + +static void spi_tegra_start_message(struct spi_device *spi, + struct spi_message *m) +{ + struct spi_transfer *t; + + m->actual_length = 0; + m->status = 0; + + t = list_first_entry(&m->transfers, struct spi_transfer, transfer_list); + spi_tegra_start_transfer(spi, t); +} + +static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req) +{ + struct spi_tegra_data *tspi = req->dev; + unsigned long flags; + struct spi_message *m; + struct spi_device *spi; + int timeout = 0; + unsigned long val; + + /* the SPI controller may come back with both the BSY and RDY bits + * set. In this case we need to wait for the BSY bit to clear so + * that we are sure the DMA is finished. 1000 reads was empirically + * determined to be long enough. + */ + while (timeout++ < 1000) { + if (!(spi_tegra_readl(tspi, SLINK_STATUS) & SLINK_BSY)) + break; + } + + spin_lock_irqsave(&tspi->lock, flags); + + val = spi_tegra_readl(tspi, SLINK_STATUS); + val |= SLINK_RDY; + spi_tegra_writel(tspi, val, SLINK_STATUS); + + m = list_first_entry(&tspi->queue, struct spi_message, queue); + + if (timeout >= 1000) + m->status = -EIO; + + spi = m->state; + + tspi->cur_pos += spi_tegra_drain_rx_fifo(tspi, tspi->cur); + m->actual_length += tspi->cur_pos; + + if (tspi->cur_pos < tspi->cur->len) { + tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, tspi->cur); + spi_tegra_go(tspi); + } else if (!list_is_last(&tspi->cur->transfer_list, + &m->transfers)) { + tspi->cur = list_first_entry(&tspi->cur->transfer_list, + struct spi_transfer, + transfer_list); + spi_tegra_start_transfer(spi, tspi->cur); + } else { + list_del(&m->queue); + + m->complete(m->context); + + if (!list_empty(&tspi->queue)) { + m = list_first_entry(&tspi->queue, struct spi_message, + queue); + spi = m->state; + spi_tegra_start_message(spi, m); + } else { + clk_disable(tspi->clk); + tspi->cur_speed = 0; + } + } + + spin_unlock_irqrestore(&tspi->lock, flags); +} + +static int spi_tegra_setup(struct spi_device *spi) +{ + struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master); + unsigned long cs_bit; + unsigned long val; + unsigned long flags; + + dev_dbg(&spi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n", + spi->bits_per_word, + spi->mode & SPI_CPOL ? "" : "~", + spi->mode & SPI_CPHA ? "" : "~", + spi->max_speed_hz); + + + switch (spi->chip_select) { + case 0: + cs_bit = SLINK_CS_POLARITY; + break; + + case 1: + cs_bit = SLINK_CS_POLARITY1; + break; + + case 2: + cs_bit = SLINK_CS_POLARITY2; + break; + + case 4: + cs_bit = SLINK_CS_POLARITY3; + break; + + default: + return -EINVAL; + } + + spin_lock_irqsave(&tspi->lock, flags); + + val = spi_tegra_readl(tspi, SLINK_COMMAND); + if (spi->mode & SPI_CS_HIGH) + val |= cs_bit; + else + val &= ~cs_bit; + spi_tegra_writel(tspi, val, SLINK_COMMAND); + + spin_unlock_irqrestore(&tspi->lock, flags); + + return 0; +} + +static int spi_tegra_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master); + struct spi_transfer *t; + unsigned long flags; + int was_empty; + + if (list_empty(&m->transfers) || !m->complete) + return -EINVAL; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->bits_per_word < 0 || t->bits_per_word > 32) + return -EINVAL; + + if (t->len == 0) + return -EINVAL; + + if (!t->rx_buf && !t->tx_buf) + return -EINVAL; + } + + m->state = spi; + + spin_lock_irqsave(&tspi->lock, flags); + was_empty = list_empty(&tspi->queue); + list_add_tail(&m->queue, &tspi->queue); + + if (was_empty) + spi_tegra_start_message(spi, m); + + spin_unlock_irqrestore(&tspi->lock, flags); + + return 0; +} + +static int __init spi_tegra_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct spi_tegra_data *tspi; + struct resource *r; + int ret; + + master = spi_alloc_master(&pdev->dev, sizeof *tspi); + if (master == NULL) { + dev_err(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + + /* the spi->mode bits understood by this driver: */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + + master->bus_num = pdev->id; + + master->setup = spi_tegra_setup; + master->transfer = spi_tegra_transfer; + master->num_chipselect = 4; + + dev_set_drvdata(&pdev->dev, master); + tspi = spi_master_get_devdata(master); + tspi->master = master; + tspi->pdev = pdev; + spin_lock_init(&tspi->lock); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENODEV; + goto err0; + } + + if (!request_mem_region(r->start, (r->end - r->start) + 1, + dev_name(&pdev->dev))) { + ret = -EBUSY; + goto err0; + } + + tspi->phys = r->start; + tspi->base = ioremap(r->start, r->end - r->start + 1); + if (!tspi->base) { + dev_err(&pdev->dev, "can't ioremap iomem\n"); + ret = -ENOMEM; + goto err1; + } + + tspi->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR_OR_NULL(tspi->clk)) { + dev_err(&pdev->dev, "can not get clock\n"); + ret = PTR_ERR(tspi->clk); + goto err2; + } + + INIT_LIST_HEAD(&tspi->queue); + + tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); + if (!tspi->rx_dma) { + dev_err(&pdev->dev, "can not allocate rx dma channel\n"); + ret = -ENODEV; + goto err3; + } + + tspi->rx_bb = dma_alloc_coherent(&pdev->dev, sizeof(u32) * BB_LEN, + &tspi->rx_bb_phys, GFP_KERNEL); + if (!tspi->rx_bb) { + dev_err(&pdev->dev, "can not allocate rx bounce buffer\n"); + ret = -ENOMEM; + goto err4; + } + + tspi->rx_dma_req.complete = tegra_spi_rx_dma_complete; + tspi->rx_dma_req.to_memory = 1; + tspi->rx_dma_req.dest_addr = tspi->rx_bb_phys; + tspi->rx_dma_req.dest_bus_width = 32; + tspi->rx_dma_req.source_addr = tspi->phys + SLINK_RX_FIFO; + tspi->rx_dma_req.source_bus_width = 32; + tspi->rx_dma_req.source_wrap = 4; + tspi->rx_dma_req.req_sel = spi_tegra_req_sels[pdev->id]; + tspi->rx_dma_req.dev = tspi; + + ret = spi_register_master(master); + + if (ret < 0) + goto err5; + + return ret; + +err5: + dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN, + tspi->rx_bb, tspi->rx_bb_phys); +err4: + tegra_dma_free_channel(tspi->rx_dma); +err3: + clk_put(tspi->clk); +err2: + iounmap(tspi->base); +err1: + release_mem_region(r->start, (r->end - r->start) + 1); +err0: + spi_master_put(master); + return ret; +} + +static int __devexit spi_tegra_remove(struct platform_device *pdev) +{ + struct spi_master *master; + struct spi_tegra_data *tspi; + struct resource *r; + + master = dev_get_drvdata(&pdev->dev); + tspi = spi_master_get_devdata(master); + + tegra_dma_free_channel(tspi->rx_dma); + + dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN, + tspi->rx_bb, tspi->rx_bb_phys); + + clk_put(tspi->clk); + iounmap(tspi->base); + + spi_master_put(master); + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(r->start, (r->end - r->start) + 1); + + return 0; +} + +MODULE_ALIAS("platform:spi_tegra"); + +static struct platform_driver spi_tegra_driver = { + .driver = { + .name = "spi_tegra", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(spi_tegra_remove), +}; + +static int __init spi_tegra_init(void) +{ + return platform_driver_probe(&spi_tegra_driver, spi_tegra_probe); +} +module_init(spi_tegra_init); + +static void __exit spi_tegra_exit(void) +{ + platform_driver_unregister(&spi_tegra_driver); +} +module_exit(spi_tegra_exit); + +MODULE_LICENSE("GPL"); |