mirror of
https://github.com/torvalds/linux.git
synced 2024-12-29 06:12:08 +00:00
Merge branch 'for-next' of git://android.git.kernel.org/kernel/tegra
* 'for-next' of git://android.git.kernel.org/kernel/tegra: spi: tegra: fix error setting on timeout spi: add spi_tegra driver tegra: harmony: enable PCI Express tegra: add PCI Express support tegra: add PCI Express clocks [ARM] tegra: Add APB DMA support [ARM] tegra: Add cpufreq support [ARM] tegra: common: Update common clock init table [ARM] tegra: clock: Add dvfs support, bug fixes, and cleanups [ARM] tegra: Add support for reading fuses [ARM] tegra: gpio: Add suspend and wake support [ARM] tegra: pinmux: add safe values, move tegra2, add suspend [ARM] tegra: add suspend and mirror irqs to legacy controller [ARM] tegra: Add legacy irq support [ARM] tegra: update iomap
This commit is contained in:
commit
b18cae4224
@ -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).
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
57
arch/arm/mach-tegra/board-harmony-pcie.c
Normal file
57
arch/arm/mach-tegra/board-harmony-pcie.c
Normal file
@ -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
|
@ -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
|
||||
|
@ -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_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);
|
||||
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = dvfs_set_rate(c->dvfs, rate);
|
||||
|
||||
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;
|
||||
|
||||
spin_lock_irqsave(&clock_lock, flags);
|
||||
|
||||
pr_debug("%s: %s\n", __func__, c->name);
|
||||
|
||||
if (c->ops && c->ops->set_rate)
|
||||
ret = c->ops->set_rate(c, rate);
|
||||
else
|
||||
ret = -ENOSYS;
|
||||
|
||||
propagate_rate(c);
|
||||
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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
185
arch/arm/mach-tegra/cpu-tegra.c
Normal file
185
arch/arm/mach-tegra/cpu-tegra.c
Normal file
@ -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);
|
752
arch/arm/mach-tegra/dma.c
Normal file
752
arch/arm/mach-tegra/dma.c
Normal file
@ -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
|
84
arch/arm/mach-tegra/fuse.c
Normal file
84
arch/arm/mach-tegra/fuse.c
Normal file
@ -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;
|
||||
}
|
24
arch/arm/mach-tegra/fuse.h
Normal file
24
arch/arm/mach-tegra/fuse.h
Normal file
@ -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);
|
@ -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;
|
||||
|
@ -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
|
||||
|
155
arch/arm/mach-tegra/include/mach/dma.h
Normal file
155
arch/arm/mach-tegra/include/mach/dma.h
Normal file
@ -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
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
31
arch/arm/mach-tegra/include/mach/legacy_irq.h
Normal file
31
arch/arm/mach-tegra/include/mach/legacy_irq.h
Normal file
@ -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
|
174
arch/arm/mach-tegra/include/mach/pinmux-t2.h
Normal file
174
arch/arm/mach-tegra/include/mach/pinmux-t2.h
Normal file
@ -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
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
void tegra_pinmux_config_pingroup(enum tegra_pingroup pingroup,
|
||||
enum tegra_mux_func func, enum tegra_pullupdown pupd,
|
||||
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[];
|
||||
|
||||
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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
114
arch/arm/mach-tegra/legacy_irq.c
Normal file
114
arch/arm/mach-tegra/legacy_irq.c
Normal file
@ -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);
|
||||
}
|
915
arch/arm/mach-tegra/pcie.c
Normal file
915
arch/arm/mach-tegra/pcie.c
Normal file
@ -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;
|
||||
}
|
260
arch/arm/mach-tegra/pinmux-t2-tables.c
Normal file
260
arch/arm/mach-tegra/pinmux-t2-tables.c
Normal file
@ -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
|
@ -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));
|
||||
|
File diff suppressed because it is too large
Load Diff
86
arch/arm/mach-tegra/tegra2_dvfs.c
Normal file
86
arch/arm/mach-tegra/tegra2_dvfs.c
Normal file
@ -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,
|
||||
};
|
20
arch/arm/mach-tegra/tegra2_dvfs.h
Normal file
20
arch/arm/mach-tegra/tegra2_dvfs.h
Normal file
@ -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;
|
@ -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
|
||||
|
@ -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
|
||||
|
618
drivers/spi/spi_tegra.c
Normal file
618
drivers/spi/spi_tegra.c
Normal file
@ -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");
|
Loading…
Reference in New Issue
Block a user