mirror of
https://github.com/hardkernel/linux.git
synced 2026-04-19 03:50:43 +09:00
Merge branch 'android-tegra-2.6.36' into android-tegra-moto-2.6.36
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
/*
|
||||
|
||||
172
arch/arm/mach-tegra/tegra2_emc.c
Normal file
172
arch/arm/mach-tegra/tegra2_emc.c
Normal 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;
|
||||
}
|
||||
27
arch/arm/mach-tegra/tegra2_emc.h
Normal file
27
arch/arm/mach-tegra/tegra2_emc.h
Normal 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);
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -60,6 +60,7 @@ struct tegra_dc {
|
||||
int irq;
|
||||
|
||||
struct clk *clk;
|
||||
struct clk *emc_clk;
|
||||
|
||||
bool enabled;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user