Merge branch 'android-tegra-2.6.36' into android-tegra-moto-2.6.36

This commit is contained in:
Colin Cross
2010-12-08 19:55:07 -08:00
12 changed files with 352 additions and 36 deletions

View File

@@ -93,6 +93,10 @@ config WIFI_CONTROL_FUNC
endif
config TEGRA_EMC_SCALING_ENABLE
bool "Enable scaling the memory frequency"
default n
config TEGRA_CPU_DVFS
bool "Enable voltage scaling on Tegra CPU"
default y

View File

@@ -26,6 +26,7 @@ obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_dvfs.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_fuse.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += suspend-t2.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_save.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o
obj-$(CONFIG_CPU_V7) += cortex-a9.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o

View File

@@ -350,6 +350,7 @@ int clk_set_rate(struct clk *c, unsigned long rate)
int ret = 0;
unsigned long flags;
unsigned long old_rate;
long new_rate;
clk_lock_save(c, flags);
@@ -363,6 +364,17 @@ int clk_set_rate(struct clk *c, unsigned long rate)
if (rate > c->max_rate)
rate = c->max_rate;
if (c->ops && c->ops->round_rate) {
new_rate = c->ops->round_rate(c, rate);
if (new_rate < 0) {
ret = new_rate;
goto out;
}
rate = new_rate;
}
if (clk_is_auto_dvfs(c) && rate > old_rate && c->refcnt > 0) {
ret = tegra_dvfs_set_rate(c, rate);
if (ret)

View File

@@ -59,6 +59,7 @@ static struct cpufreq_frequency_table freq_table[] = {
#define NUM_CPUS 2
static struct clk *cpu_clk;
static struct clk *emc_clk;
static struct workqueue_struct *workqueue;
@@ -115,6 +116,17 @@ static int tegra_update_cpu_speed(unsigned long rate)
if (freqs.old == freqs.new)
return ret;
/*
* Vote on memory bus frequency based on cpu frequency
* This sets the minimum frequency, display or avp may request higher
*/
if (rate >= 816000)
clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */
else if (rate >= 456000)
clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */
else
clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */
for_each_online_cpu(freqs.cpu)
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
@@ -336,6 +348,13 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
if (IS_ERR(cpu_clk))
return PTR_ERR(cpu_clk);
emc_clk = clk_get_sys("cpu", "emc");
if (IS_ERR(emc_clk)) {
clk_put(cpu_clk);
return PTR_ERR(emc_clk);
}
clk_enable(emc_clk);
cpufreq_frequency_table_cpuinfo(policy, freq_table);
cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
policy->cur = tegra_getspeed(policy->cpu);
@@ -358,6 +377,8 @@ static int tegra_cpu_init(struct cpufreq_policy *policy)
static int tegra_cpu_exit(struct cpufreq_policy *policy)
{
cpufreq_frequency_table_cpuinfo(policy, freq_table);
clk_disable(emc_clk);
clk_put(emc_clk);
clk_put(cpu_clk);
return 0;
}

View File

@@ -32,6 +32,7 @@
#include "clock.h"
#include "fuse.h"
#include "tegra2_emc.h"
#define RST_DEVICES 0x004
#define RST_DEVICES_SET 0x300
@@ -1021,6 +1022,53 @@ static struct clk_ops tegra_periph_clk_ops = {
.reset = &tegra2_periph_clk_reset,
};
/* External memory controller clock ops */
static void tegra2_emc_clk_init(struct clk *c)
{
tegra2_periph_clk_init(c);
c->max_rate = clk_get_rate_locked(c);
}
static long tegra2_emc_clk_round_rate(struct clk *c, unsigned long rate)
{
long new_rate = rate;
new_rate = tegra_emc_round_rate(new_rate);
if (new_rate < 0)
return c->max_rate;
BUG_ON(new_rate != tegra2_periph_clk_round_rate(c, new_rate));
return new_rate;
}
static int tegra2_emc_clk_set_rate(struct clk *c, unsigned long rate)
{
int ret;
/* The Tegra2 memory controller has an interlock with the clock
* block that allows memory shadowed registers to be updated,
* and then transfer them to the main registers at the same
* time as the clock update without glitches. */
ret = tegra_emc_set_rate(rate);
if (ret < 0)
return ret;
ret = tegra2_periph_clk_set_rate(c, rate);
udelay(1);
return ret;
}
static struct clk_ops tegra_emc_clk_ops = {
.init = &tegra2_emc_clk_init,
.enable = &tegra2_periph_clk_enable,
.disable = &tegra2_periph_clk_disable,
.set_parent = &tegra2_periph_clk_set_parent,
.set_rate = &tegra2_emc_clk_set_rate,
.round_rate = &tegra2_emc_clk_round_rate,
.reset = &tegra2_periph_clk_reset,
};
/* Clock doubler ops */
static void tegra2_clk_double_init(struct clk *c)
{
@@ -1157,7 +1205,8 @@ static void tegra_clk_shared_bus_update(struct clk *bus)
rate = max(c->u.shared_bus_user.rate, rate);
}
clk_set_rate(bus, rate);
if (rate != clk_get_rate(bus))
clk_set_rate(bus, rate);
};
static void tegra_clk_shared_bus_init(struct clk *c)
@@ -1839,6 +1888,18 @@ static struct clk_mux_sel mux_clk_32k[] = {
{ 0, 0},
};
static struct clk tegra_clk_emc = {
.name = "emc",
.ops = &tegra_emc_clk_ops,
.reg = 0x19c,
.max_rate = 800000000,
.inputs = mux_pllm_pllc_pllp_clkm,
.flags = MUX | DIV_U71 | PERIPH_EMC_ENB,
.u.periph = {
.clk_num = 57,
},
};
#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \
{ \
.name = _name, \
@@ -1927,13 +1988,17 @@ struct clk tegra_list_clks[] = {
PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
PERIPH_CLK("usb2", "tegra-ehci.1", NULL, 58, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
PERIPH_CLK("usb3", "tegra-ehci.2", NULL, 59, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
PERIPH_CLK("emc", "emc", NULL, 57, 0x19c, 800000000, mux_pllm_pllc_pllp_clkm, MUX | DIV_U71 | PERIPH_EMC_ENB),
PERIPH_CLK("dsi", "dsi", NULL, 48, 0, 500000000, mux_plld, 0), /* scales with voltage */
PERIPH_CLK("csi", "tegra_camera", "csi", 52, 0, 72000000, mux_pllp_out3, 0),
PERIPH_CLK("isp", "tegra_camera", "isp", 23, 0, 150000000, mux_clk_m, 0), /* same frequency as VI */
PERIPH_CLK("csus", "tegra_camera", "csus", 92, 0, 150000000, mux_clk_m, PERIPH_NO_RESET),
SHARED_CLK("avp.sclk", "tegra-avp", "sclk", &tegra_clk_sclk),
SHARED_CLK("avp.emc", "tegra-avp", "emc", &tegra_clk_emc),
SHARED_CLK("cpu.emc", "cpu", "emc", &tegra_clk_emc),
SHARED_CLK("disp1.emc", "tegradc.0", "emc", &tegra_clk_emc),
SHARED_CLK("disp2.emc", "tegradc.1", "emc", &tegra_clk_emc),
SHARED_CLK("hdmi.emc", "hdmi", "emc", &tegra_clk_emc),
};
#define CLK_DUPLICATE(_name, _dev, _con) \
@@ -2009,6 +2074,7 @@ struct clk *tegra_ptr_clks[] = {
&tegra_clk_virtual_cpu,
&tegra_clk_blink,
&tegra_clk_cop,
&tegra_clk_emc,
};
static void tegra2_init_one_clock(struct clk *c)

View File

@@ -150,6 +150,7 @@ static struct dvfs dvfs_init[] = {
CPU_DVFS("cpu", 3, MHZ, 730, 760, 845, 845, 1000),
/* Core voltages (mV): 950, 1000, 1100, 1200, 1275 */
CORE_DVFS("emc", 1, KHZ, 57000, 333000, 333000, 666000, 666000),
#if 0
/*

View File

@@ -0,0 +1,172 @@
/*
* 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 <linux/clk.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <mach/iomap.h>
#include "tegra2_emc.h"
#ifdef CONFIG_TEGRA_EMC_SCALING_ENABLE
static bool emc_enable = true;
#else
static bool emc_enable;
#endif
module_param(emc_enable, bool, 0644);
static void __iomem *emc = IO_ADDRESS(TEGRA_EMC_BASE);
static const struct tegra_emc_table *tegra_emc_table;
static int tegra_emc_table_size;
static inline void emc_writel(u32 val, unsigned long addr)
{
writel(val, emc + addr);
}
static inline u32 emc_readl(unsigned long addr)
{
return readl(emc + addr);
}
static const unsigned long emc_reg_addr[TEGRA_EMC_NUM_REGS] = {
0x2c, /* RC */
0x30, /* RFC */
0x34, /* RAS */
0x38, /* RP */
0x3c, /* R2W */
0x40, /* W2R */
0x44, /* R2P */
0x48, /* W2P */
0x4c, /* RD_RCD */
0x50, /* WR_RCD */
0x54, /* RRD */
0x58, /* REXT */
0x5c, /* WDV */
0x60, /* QUSE */
0x64, /* QRST */
0x68, /* QSAFE */
0x6c, /* RDV */
0x70, /* REFRESH */
0x74, /* BURST_REFRESH_NUM */
0x78, /* PDEX2WR */
0x7c, /* PDEX2RD */
0x80, /* PCHG2PDEN */
0x84, /* ACT2PDEN */
0x88, /* AR2PDEN */
0x8c, /* RW2PDEN */
0x90, /* TXSR */
0x94, /* TCKE */
0x98, /* TFAW */
0x9c, /* TRPAB */
0xa0, /* TCLKSTABLE */
0xa4, /* TCLKSTOP */
0xa8, /* TREFBW */
0xac, /* QUSE_EXTRA */
0x114, /* FBIO_CFG6 */
0xb0, /* ODT_WRITE */
0xb4, /* ODT_READ */
0x104, /* FBIO_CFG5 */
0x2bc, /* CFG_DIG_DLL */
0x2c0, /* DLL_XFORM_DQS */
0x2c4, /* DLL_XFORM_QUSE */
0x2e0, /* ZCAL_REF_CNT */
0x2e4, /* ZCAL_WAIT_CNT */
0x2a8, /* AUTO_CAL_INTERVAL */
0x2d0, /* CFG_CLKTRIM_0 */
0x2d4, /* CFG_CLKTRIM_1 */
0x2d8, /* CFG_CLKTRIM_2 */
};
/* Select the closest EMC rate that is higher than the requested rate */
long tegra_emc_round_rate(unsigned long rate)
{
int i;
int best = -1;
unsigned long distance = ULONG_MAX;
if (!tegra_emc_table)
return -EINVAL;
if (!emc_enable)
return -EINVAL;
pr_debug("%s: %lu\n", __func__, rate);
/* The EMC clock rate is twice the bus rate, and the bus rate is
* measured in kHz */
rate = rate / 2 / 1000;
for (i = 0; i < tegra_emc_table_size; i++) {
if (tegra_emc_table[i].rate >= rate &&
(tegra_emc_table[i].rate - rate) < distance) {
distance = tegra_emc_table[i].rate - rate;
best = i;
}
}
if (best < 0)
return -EINVAL;
pr_debug("%s: using %lu\n", __func__, tegra_emc_table[best].rate);
return tegra_emc_table[best].rate * 2 * 1000;
}
/* The EMC registers have shadow registers. When the EMC clock is updated
* in the clock controller, the shadow registers are copied to the active
* registers, allowing glitchless memory bus frequency changes.
* This function updates the shadow registers for a new clock frequency,
* and relies on the clock lock on the emc clock to avoid races between
* multiple frequency changes */
int tegra_emc_set_rate(unsigned long rate)
{
int i;
int j;
if (!tegra_emc_table)
return -EINVAL;
/* The EMC clock rate is twice the bus rate, and the bus rate is
* measured in kHz */
rate = rate / 2 / 1000;
for (i = 0; i < tegra_emc_table_size; i++)
if (tegra_emc_table[i].rate == rate)
break;
if (i >= tegra_emc_table_size)
return -EINVAL;
pr_debug("%s: setting to %lu\n", __func__, rate);
for (j = 0; j < TEGRA_EMC_NUM_REGS; j++)
emc_writel(tegra_emc_table[i].regs[j], emc_reg_addr[j]);
emc_readl(tegra_emc_table[i].regs[TEGRA_EMC_NUM_REGS - 1]);
return 0;
}
void tegra_init_emc(const struct tegra_emc_table *table, int table_size)
{
tegra_emc_table = table;
tegra_emc_table_size = table_size;
}

View File

@@ -0,0 +1,27 @@
/*
* 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.
*
*/
#define TEGRA_EMC_NUM_REGS 46
struct tegra_emc_table {
unsigned long rate;
u32 regs[TEGRA_EMC_NUM_REGS];
};
int tegra_emc_set_rate(unsigned long rate);
long tegra_emc_round_rate(unsigned long rate);
void tegra_init_emc(const struct tegra_emc_table *table, int table_size);

View File

@@ -82,6 +82,7 @@ struct avp_svc_info {
struct avp_clk clks[NUM_CLK_REQUESTS];
/* used for dvfs */
struct clk *sclk;
struct clk *emcclk;
struct mutex clk_lock;
@@ -352,6 +353,7 @@ static void do_svc_module_clock(struct avp_svc_info *avp_svc,
aclk = &avp_svc->clks[mod->clk_req];
if (msg->enable) {
if (aclk->refcnt++ == 0) {
clk_enable(avp_svc->emcclk);
clk_enable(avp_svc->sclk);
clk_enable(aclk->clk);
}
@@ -362,6 +364,7 @@ static void do_svc_module_clock(struct avp_svc_info *avp_svc,
} else if (--aclk->refcnt == 0) {
clk_disable(aclk->clk);
clk_disable(avp_svc->sclk);
clk_disable(avp_svc->emcclk);
}
}
mutex_unlock(&avp_svc->clk_lock);
@@ -631,8 +634,9 @@ void avp_svc_stop(struct avp_svc_info *avp_svc)
pr_info("%s: remote left clock '%s' on\n", __func__,
aclk->mod->name);
clk_disable(aclk->clk);
/* sclk was enabled once for every clock */
/* sclk/emcclk was enabled once for every clock */
clk_disable(avp_svc->sclk);
clk_disable(avp_svc->emcclk);
}
aclk->refcnt = 0;
}
@@ -682,6 +686,21 @@ struct avp_svc_info *avp_svc_init(struct platform_device *pdev,
ret = -ENOENT;
goto err_get_clks;
}
avp_svc->emcclk = clk_get(&pdev->dev, "emc");
if (IS_ERR(avp_svc->emcclk)) {
pr_err("avp_svc: Couldn't get emcclk for dvfs\n");
ret = -ENOENT;
goto err_get_clks;
}
/*
* The emc is a shared clock, it will be set to the highest
* requested rate from any user. Set the rate to ULONG_MAX to
* always request the max rate whenever this request is enabled
*/
clk_set_rate(avp_svc->emcclk, ULONG_MAX);
avp_svc->rpc_node = rpc_node;
mutex_init(&avp_svc->clk_lock);
@@ -694,6 +713,8 @@ err_get_clks:
clk_put(avp_svc->clks[i].clk);
if (!IS_ERR_OR_NULL(avp_svc->sclk))
clk_put(avp_svc->sclk);
if (!IS_ERR_OR_NULL(avp_svc->emcclk))
clk_put(avp_svc->emcclk);
err_alloc:
return ERR_PTR(ret);
}
@@ -705,6 +726,7 @@ void avp_svc_destroy(struct avp_svc_info *avp_svc)
for (i = 0; i < NUM_CLK_REQUESTS; i++)
clk_put(avp_svc->clks[i].clk);
clk_put(avp_svc->sclk);
clk_put(avp_svc->emcclk);
kfree(avp_svc);
}

View File

@@ -967,6 +967,7 @@ static bool _tegra_dc_enable(struct tegra_dc *dc)
tegra_dc_setup_clk(dc, dc->clk);
clk_enable(dc->clk);
clk_enable(dc->emc_clk);
enable_irq(dc->irq);
tegra_dc_init(dc);
@@ -997,6 +998,7 @@ static void _tegra_dc_disable(struct tegra_dc *dc)
if (dc->out_ops && dc->out_ops->disable)
dc->out_ops->disable(dc);
clk_disable(dc->emc_clk);
clk_disable(dc->clk);
tegra_dvfs_set_rate(dc->clk, 0);
@@ -1029,6 +1031,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
{
struct tegra_dc *dc;
struct clk *clk;
struct clk *emc_clk;
struct resource *res;
struct resource *base_res;
struct resource *fb_mem = NULL;
@@ -1085,7 +1088,22 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
goto err_iounmap_reg;
}
emc_clk = clk_get(&ndev->dev, "emc");
if (IS_ERR_OR_NULL(emc_clk)) {
dev_err(&ndev->dev, "can't get emc clock\n");
ret = -ENOENT;
goto err_put_clk;
}
/*
* The emc is a shared clock, it will be set to the highest
* requested rate from any user. Set the rate to ULONG_MAX to
* always request the max rate whenever this request is enabled
*/
clk_set_rate(emc_clk, ULONG_MAX);
dc->clk = clk;
dc->emc_clk = emc_clk;
dc->base_res = base_res;
dc->base = base;
dc->irq = irq;
@@ -1108,7 +1126,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
dev_name(&ndev->dev), dc)) {
dev_err(&ndev->dev, "request_irq %d failed\n", irq);
ret = -EBUSY;
goto err_put_clk;
goto err_put_emc_clk;
}
/* hack to ballence enable_irq calls in _tegra_dc_enable() */
@@ -1158,6 +1176,8 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
err_free_irq:
free_irq(irq, dc);
err_put_emc_clk:
clk_put(emc_clk);
err_put_clk:
clk_put(clk);
err_iounmap_reg:
@@ -1187,6 +1207,7 @@ static int tegra_dc_remove(struct nvhost_device *ndev)
_tegra_dc_disable(dc);
free_irq(dc->irq, dc);
clk_put(dc->emc_clk);
clk_put(dc->clk);
iounmap(dc->base);
if (dc->fb_mem)

View File

@@ -60,6 +60,7 @@ struct tegra_dc {
int irq;
struct clk *clk;
struct clk *emc_clk;
bool enabled;

View File

@@ -220,7 +220,6 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
struct mm_struct *mm = current->mm;
struct page *page;
int err;
struct vm_area_struct *vma;
/*
* The futex address must be "naturally" aligned.
@@ -246,37 +245,6 @@ get_futex_key(u32 __user *uaddr, int fshared, union futex_key *key)
return 0;
}
/*
* The futex is hashed differently depending on whether
* it's in a shared or private mapping. So check vma first.
*/
vma = find_extend_vma(mm, address);
if (unlikely(!vma))
return -EFAULT;
/*
* Permissions.
*/
if (unlikely((vma->vm_flags & (VM_IO|VM_READ)) != VM_READ))
return (vma->vm_flags & VM_IO) ? -EPERM : -EACCES;
/*
* Private mappings are handled in a simple way.
*
* NOTE: When userspace waits on a MAP_SHARED mapping, even if
* it's a read-only handle, it's expected that futexes attach to
* the object not the particular process. Therefore we use
* VM_MAYSHARE here, not VM_SHARED which is restricted to shared
* mappings of _writable_ handles.
*/
if (likely(!(vma->vm_flags & VM_MAYSHARE))) {
key->both.offset |= FUT_OFF_MMSHARED; /* reference taken on mm */
key->private.mm = mm;
key->private.address = address;
get_futex_key_refs(key);
return 0;
}
again:
err = get_user_pages_fast(address, 1, 1, &page);
if (err < 0)