Merge changes I3435fbda,I3ca11384,I74f29ae6,Ia9db958b,I6253b514 into msm-3.0
* changes:
msm: board: Clean up device declarations
msm: board-8960: Move WFD devices into board-msm8960-display
msm: board-8960: Create a separate board file for PMIC devices
msm: board-8960: Create a separate board file for display devices
msm: board-8960: Create a separate board file for camera devices
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 34ffd9e..3eeac92 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1410,6 +1410,12 @@
help
This option enables support for the ARM system coherency unit
+config ARM_ARCH_TIMER
+ bool "Architected timer support"
+ select TICK_ONESHOT
+ help
+ This option enables support for the ARM architected timer
+
config HAVE_ARM_TWD
bool
depends on SMP
diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h
new file mode 100644
index 0000000..e82217f
--- /dev/null
+++ b/arch/arm/include/asm/arch_timer.h
@@ -0,0 +1,8 @@
+#ifndef __ASMARM_ARCH_TIMER_H
+#define __ASMARM_ARCH_TIMER_H
+
+struct resource;
+
+int arch_timer_register(struct resource *res, int res_nr);
+
+#endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index a5b31af..c77c2fb 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -34,6 +34,7 @@
obj-$(CONFIG_SMP) += smp.o smp_tlb.o
obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o
obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o
+obj-$(CONFIG_ARM_ARCH_TIMER) += arch_timer.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
new file mode 100644
index 0000000..5b76911
--- /dev/null
+++ b/arch/arm/kernel/arch_timer.c
@@ -0,0 +1,319 @@
+/*
+ * linux/arch/arm/kernel/arch_timer.c
+ *
+ * Copyright (C) 2011 ARM Ltd.
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <linux/jiffies.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <asm/cputype.h>
+#include <asm/hardware/gic.h>
+
+static unsigned long arch_timer_rate;
+static int arch_timer_ppi;
+static int arch_timer_ppi2;
+static DEFINE_CLOCK_DATA(cd);
+
+static struct clock_event_device __percpu *arch_timer_evt;
+
+/*
+ * Architected system timer support.
+ */
+
+#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
+#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
+
+#define ARCH_TIMER_REG_CTRL 0
+#define ARCH_TIMER_REG_FREQ 1
+#define ARCH_TIMER_REG_TVAL 2
+
+static void arch_timer_reg_write(int reg, u32 val)
+{
+ switch (reg) {
+ case ARCH_TIMER_REG_CTRL:
+ asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
+ break;
+ case ARCH_TIMER_REG_TVAL:
+ asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val));
+ break;
+ }
+
+ isb();
+}
+
+static u32 arch_timer_reg_read(int reg)
+{
+ u32 val;
+
+ switch (reg) {
+ case ARCH_TIMER_REG_CTRL:
+ asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
+ break;
+ case ARCH_TIMER_REG_FREQ:
+ asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
+ break;
+ case ARCH_TIMER_REG_TVAL:
+ asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
+ break;
+ default:
+ BUG();
+ }
+
+ return val;
+}
+
+static irqreturn_t arch_timer_handler(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+ unsigned long ctrl;
+
+ ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+ if (ctrl & 0x4) {
+ ctrl |= ARCH_TIMER_CTRL_IT_MASK;
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+ evt->event_handler(evt);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static void arch_timer_stop(void)
+{
+ unsigned long ctrl;
+
+ ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+ ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+}
+
+static void arch_timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *clk)
+{
+ switch (mode) {
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ arch_timer_stop();
+ break;
+ default:
+ break;
+ }
+}
+
+static int arch_timer_set_next_event(unsigned long evt,
+ struct clock_event_device *unused)
+{
+ unsigned long ctrl;
+
+ ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+ ctrl |= ARCH_TIMER_CTRL_ENABLE;
+ ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
+
+ arch_timer_reg_write(ARCH_TIMER_REG_TVAL, evt);
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+
+ return 0;
+}
+
+static void __cpuinit arch_timer_setup(void *data)
+{
+ struct clock_event_device *clk = data;
+ int err;
+
+ /* Be safe... */
+ arch_timer_stop();
+
+ clk->features = CLOCK_EVT_FEAT_ONESHOT;
+ clk->name = "arch_sys_timer";
+ clk->rating = 450;
+ clk->set_mode = arch_timer_set_mode;
+ clk->set_next_event = arch_timer_set_next_event;
+ clk->irq = arch_timer_ppi;
+ clk->cpumask = cpumask_of(smp_processor_id());
+
+ clockevents_config_and_register(clk, arch_timer_rate,
+ 0xf, 0x7fffffff);
+
+ err = gic_request_ppi(clk->irq, arch_timer_handler, clk);
+ if (err) {
+ pr_err("%s: can't register interrupt %d on cpu %d (%d)\n",
+ clk->name, clk->irq, smp_processor_id(), err);
+ return;
+ }
+
+ if (arch_timer_ppi2 >= 0) {
+ err = gic_request_ppi(arch_timer_ppi2, arch_timer_handler, clk);
+ if (err) {
+ pr_warn("%s: can't register interrupt %d on cpu %d (%d)\n",
+ clk->name, arch_timer_ppi2, smp_processor_id(), err);
+ }
+ }
+}
+
+/* Is the optional system timer available? */
+static int local_timer_is_architected(void)
+{
+ return (cpu_architecture() >= CPU_ARCH_ARMv7) &&
+ ((read_cpuid_ext(CPUID_EXT_PFR1) >> 16) & 0xf) == 1;
+}
+
+static int arch_timer_available(void)
+{
+ unsigned long freq;
+
+ if (!local_timer_is_architected())
+ return -ENXIO;
+
+ if (arch_timer_rate == 0) {
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, 0);
+ freq = arch_timer_reg_read(ARCH_TIMER_REG_FREQ);
+
+ /* Check the timer frequency. */
+ if (freq == 0) {
+ pr_warn("Architected timer frequency not available\n");
+ return -EINVAL;
+ }
+
+ arch_timer_rate = freq;
+ pr_info("Architected local timer running at %lu.%02luMHz.\n",
+ arch_timer_rate / 1000000, (arch_timer_rate % 100000) / 100);
+ }
+
+ return 0;
+}
+
+static inline cycle_t arch_counter_get_cntpct(void)
+{
+ u32 cvall, cvalh;
+
+ asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
+
+ return ((u64) cvalh << 32) | cvall;
+}
+
+static inline cycle_t arch_counter_get_cntvct(void)
+{
+ u32 cvall, cvalh;
+
+ asm volatile("mrrc p15, 1, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
+
+ return ((u64) cvalh << 32) | cvall;
+}
+
+static cycle_t arch_counter_read(struct clocksource *cs)
+{
+ return arch_counter_get_cntpct();
+}
+
+static struct clocksource clocksource_counter = {
+ .name = "arch_sys_counter",
+ .rating = 400,
+ .read = arch_counter_read,
+ .mask = CLOCKSOURCE_MASK(56),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static u32 arch_counter_get_cntvct32(void)
+{
+ cycle_t cntvct;
+
+ cntvct = arch_counter_get_cntvct();
+
+ /*
+ * The sched_clock infrastructure only knows about counters
+ * with at most 32bits. Forget about the upper 24 bits for the
+ * time being...
+ */
+ return (u32)(cntvct & (u32)~0);
+}
+
+DEFINE_SCHED_CLOCK_FUNC(arch_timer_sched_clock)
+{
+ return cyc_to_sched_clock(&cd, arch_counter_get_cntvct32(), (u32)~0);
+}
+
+static void notrace arch_timer_update_sched_clock(void)
+{
+ update_sched_clock(&cd, arch_counter_get_cntvct32(), (u32)~0);
+}
+
+static void __cpuinit arch_timer_teardown(void *data)
+{
+ struct clock_event_device *clk = data;
+ pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n",
+ clk->irq, smp_processor_id());
+ gic_free_ppi(clk->irq, clk);
+ if (arch_timer_ppi2 >= 0)
+ gic_free_ppi(arch_timer_ppi2, clk);
+ arch_timer_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
+}
+
+static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *data)
+{
+ int cpu = (int)data;
+ struct clock_event_device *clk = per_cpu_ptr(arch_timer_evt, cpu);
+
+ switch(action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ smp_call_function_single(cpu, arch_timer_setup, clk, 1);
+ break;
+
+ case CPU_DOWN_PREPARE:
+ case CPU_DOWN_PREPARE_FROZEN:
+ smp_call_function_single(cpu, arch_timer_teardown, clk, 1);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata arch_timer_cpu_nb = {
+ .notifier_call = arch_timer_cpu_notify,
+};
+
+int arch_timer_register(struct resource *res, int res_nr)
+{
+ int err;
+
+ if (!res_nr || res[0].start < 0 || !(res[0].flags & IORESOURCE_IRQ))
+ return -EINVAL;
+
+ err = arch_timer_available();
+ if (err)
+ return err;
+
+ arch_timer_evt = alloc_percpu(struct clock_event_device);
+ if (!arch_timer_evt)
+ return -ENOMEM;
+
+ arch_timer_ppi = res[0].start;
+ if (res_nr > 1 && (res[1].flags & IORESOURCE_IRQ))
+ arch_timer_ppi2 = res[1].start;
+
+ clocksource_register_hz(&clocksource_counter, arch_timer_rate);
+
+ init_arch_sched_clock(&cd, arch_timer_update_sched_clock,
+ arch_timer_sched_clock, 32, arch_timer_rate);
+
+ /* Immediately configure the timer on the boot CPU */
+ arch_timer_setup(per_cpu_ptr(arch_timer_evt, smp_processor_id()));
+
+ register_cpu_notifier(&arch_timer_cpu_nb);
+
+ return 0;
+}
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index be324ac..b59ad93 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -26,6 +26,7 @@
#include <asm/irq_regs.h>
#include <asm/pmu.h>
#include <asm/stacktrace.h>
+#include <linux/cpu_pm.h>
static struct platform_device *pmu_device;
@@ -82,6 +83,8 @@
struct hw_perf_event *hwc);
u32 (*read_counter)(int idx);
void (*write_counter)(int idx, u32 val);
+ int (*set_event_filter) (struct hw_perf_event *evt,
+ struct perf_event_attr *attr);
void (*start)(void);
void (*stop)(void);
void (*reset)(void *);
@@ -472,6 +475,13 @@
pmu_device = NULL;
}
+static int
+event_requires_mode_exclusion(struct perf_event_attr *attr)
+{
+ return attr->exclude_idle || attr->exclude_user ||
+ attr->exclude_kernel || attr->exclude_hv;
+}
+
static atomic_t active_events = ATOMIC_INIT(0);
static DEFINE_MUTEX(pmu_reserve_mutex);
@@ -508,17 +518,6 @@
return mapping;
}
- /*
- * Check whether we need to exclude the counter from certain modes.
- * The ARM performance counters are on all of the time so if someone
- * has asked us for some excludes then we have to fail.
- */
- if (event->attr.exclude_kernel || event->attr.exclude_user ||
- event->attr.exclude_hv || event->attr.exclude_idle) {
- pr_debug("ARM performance counters do not support "
- "mode exclusion\n");
- return -EPERM;
- }
/*
* We don't assign an index until we actually place the event onto
@@ -534,13 +533,23 @@
* the event mapping and the counter to use. The counter to use is
* also the indx and the config_base is the event type.
*/
- hwc->config_base = (unsigned long)mapping;
- hwc->config = 0;
- hwc->event_base = 0;
+ hwc->config_base = 0;
+ hwc->config = 0;
+ hwc->event_base = 0;
+
+ if ((!armpmu->set_event_filter ||
+ armpmu->set_event_filter(hwc, &event->attr)) &&
+ event_requires_mode_exclusion(&event->attr)) {
+ pr_debug("ARM performance counters do not support "
+ "mode exclusion\n");
+ return -EPERM;
+ }
+
+ hwc->config_base |= (unsigned long)mapping;
if (!hwc->sample_period) {
hwc->sample_period = armpmu->max_period;
- hwc->last_period = hwc->sample_period;
+ hwc->last_period = hwc->sample_period;
local64_set(&hwc->period_left, hwc->sample_period);
}
@@ -643,6 +652,30 @@
#include "perf_event_msm_krait.c"
#include "perf_event_msm_krait_l2.c"
+static int perf_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd,
+ void *v)
+{
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ perf_pmu_disable(&pmu);
+ break;
+
+ case CPU_PM_ENTER_FAILED:
+ case CPU_PM_EXIT:
+ if (armpmu && armpmu->reset)
+ armpmu->reset(NULL);
+ perf_pmu_enable(&pmu);
+
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block perf_cpu_pm_notifier_block = {
+ .notifier_call = perf_cpu_pm_notifier,
+};
+
/*
* Ensure the PMU has sane values out of reset.
* This requires SMP to be available, so exists as a separate initcall.
@@ -725,6 +758,8 @@
perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW);
+ cpu_pm_register_notifier(&perf_cpu_pm_notifier_block);
+
return 0;
}
early_initcall(init_hw_perf_events);
diff --git a/arch/arm/kernel/perf_event_msm.c b/arch/arm/kernel/perf_event_msm.c
index 1c2206f..b40a226 100644
--- a/arch/arm/kernel/perf_event_msm.c
+++ b/arch/arm/kernel/perf_event_msm.c
@@ -585,6 +585,8 @@
*/
if (idx != ARMV7_CYCLE_COUNTER) {
val = hwc->config_base;
+ val &= ARMV7_EVTYPE_EVENT;
+
if (val > 0x40) {
event = get_scorpion_evtinfo(val, &evtinfo);
if (event == -EINVAL)
@@ -624,6 +626,8 @@
*/
if (idx != ARMV7_CYCLE_COUNTER) {
val = hwc->config_base;
+ val &= ARMV7_EVTYPE_EVENT;
+
if (val < 0x40) {
armv7_pmnc_write_evtsel(idx, hwc->config_base);
} else {
@@ -682,6 +686,26 @@
}
#endif
+static void scorpion_pmu_reset(void *info)
+{
+ u32 idx, nb_cnt = armpmu->num_events;
+
+ /* Stop all counters and their interrupts */
+ for (idx = 1; idx < nb_cnt; ++idx) {
+ armv7_pmnc_disable_counter(idx);
+ armv7_pmnc_disable_intens(idx);
+ }
+
+ /* Clear all pmresrs */
+ scorpion_clear_pmuregs();
+
+ /* Reset irq stat reg */
+ armv7_pmnc_getreset_flags();
+
+ /* Reset all ctrs to 0 */
+ armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C);
+}
+
static struct arm_pmu scorpion_pmu = {
.handle_irq = armv7pmu_handle_irq,
#ifdef CONFIG_SMP
@@ -696,6 +720,7 @@
.get_event_idx = armv7pmu_get_event_idx,
.start = armv7pmu_start,
.stop = armv7pmu_stop,
+ .reset = scorpion_pmu_reset,
.max_period = (1LLU << 32) - 1,
};
diff --git a/arch/arm/kernel/perf_event_msm_krait.c b/arch/arm/kernel/perf_event_msm_krait.c
index e3e9bf2..cf1f1ee 100644
--- a/arch/arm/kernel/perf_event_msm_krait.c
+++ b/arch/arm/kernel/perf_event_msm_krait.c
@@ -33,6 +33,9 @@
#define KRAIT_P2_L1_ITLB_ACCESS 0x12222
#define KRAIT_P2_L1_DTLB_ACCESS 0x12210
+#define KRAIT_EVENT_MASK 0xfffff
+#define KRAIT_MODE_EXCL_MASK 0xc0000000
+
u32 evt_type_base[][4] = {
{0x4c, 0x50, 0x54}, /* Pass 1 */
{0xcc, 0xd0, 0xd4, 0xd8}, /* Pass 2 */
@@ -198,7 +201,7 @@
if ((group > 3) || (reg > krait_max_l1_reg))
return -EINVAL;
- if (prefix != KRAIT_EVT_PREFIX || prefix != KRAIT_VENUMEVT_PREFIX)
+ if (prefix != KRAIT_EVT_PREFIX && prefix != KRAIT_VENUMEVT_PREFIX)
return -EINVAL;
if (prefix == KRAIT_VENUMEVT_PREFIX) {
@@ -391,6 +394,8 @@
*/
if (idx != ARMV7_CYCLE_COUNTER) {
val = hwc->config_base;
+ val &= KRAIT_EVENT_MASK;
+
if (val > 0x40) {
event = get_krait_evtinfo(val, &evtinfo);
if (event == -EINVAL)
@@ -430,6 +435,8 @@
*/
if (idx != ARMV7_CYCLE_COUNTER) {
val = hwc->config_base;
+ val &= KRAIT_EVENT_MASK;
+
if (val < 0x40) {
armv7_pmnc_write_evtsel(idx, hwc->config_base);
} else {
@@ -437,6 +444,10 @@
if (event == -EINVAL)
goto krait_out;
+
+ /* Restore Mode-exclusion bits */
+ event |= (hwc->config_base & KRAIT_MODE_EXCL_MASK);
+
/*
* Set event (if destined for PMNx counters)
* We don't need to set the event if it's a cycle count
@@ -461,6 +472,26 @@
raw_spin_unlock_irqrestore(&pmu_lock, flags);
}
+static void krait_pmu_reset(void *info)
+{
+ u32 idx, nb_cnt = armpmu->num_events;
+
+ /* Stop all counters and their interrupts */
+ for (idx = 1; idx < nb_cnt; ++idx) {
+ armv7_pmnc_disable_counter(idx);
+ armv7_pmnc_disable_intens(idx);
+ }
+
+ /* Clear all pmresrs */
+ krait_clear_pmuregs();
+
+ /* Reset irq stat reg */
+ armv7_pmnc_getreset_flags();
+
+ /* Reset all ctrs to 0 */
+ armv7_pmnc_write(ARMV7_PMNC_P | ARMV7_PMNC_C);
+}
+
static struct arm_pmu krait_pmu = {
.handle_irq = armv7pmu_handle_irq,
#ifdef CONFIG_SMP
@@ -475,6 +506,7 @@
.get_event_idx = armv7pmu_get_event_idx,
.start = armv7pmu_start,
.stop = armv7pmu_stop,
+ .reset = krait_pmu_reset,
.max_period = (1LLU << 32) - 1,
};
@@ -505,6 +537,9 @@
if (krait_ver > 0) {
evt_index = 1;
krait_max_l1_reg = 3;
+
+ krait_pmu.set_event_filter = armv7pmu_set_event_filter,
+
armv7_krait_perf_cache_map[C(ITLB)]
[C(OP_READ)]
[C(RESULT_ACCESS)] = KRAIT_P2_L1_ITLB_ACCESS;
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 4031c7b..277f1ce 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -715,7 +715,8 @@
/*
* EVTSEL: Event selection reg
*/
-#define ARMV7_EVTSEL_MASK 0xff /* Mask for writable bits */
+#define ARMV7_EVTYPE_EVENT 0xff /* Mask for writable bits */
+#define ARMV7_EVTYPE_MASK 0xc00000ff
/*
* SELECT: Counter selection reg
@@ -730,6 +731,39 @@
#define ARMV7_FLAG_MASK 0xffffffff /* Mask for writable bits */
#define ARMV7_OVERFLOWED_MASK ARMV7_FLAG_MASK
+/*
+ * Event filters for PMUv2
+ */
+#define ARMV7_EXCLUDE_PL1 (1 << 31)
+#define ARMV7_EXCLUDE_USER (1 << 30)
+#define ARMV7_INCLUDE_HYP (1 << 27)
+
+/*
+ * Add an event filter to a given event. This will only work for PMUv2 PMUs.
+ */
+int armv7pmu_set_event_filter(struct hw_perf_event *event,
+ struct perf_event_attr *attr)
+{
+ unsigned long config_base = 0;
+
+ if (attr->exclude_idle)
+ return -EPERM;
+ if (attr->exclude_user)
+ config_base |= ARMV7_EXCLUDE_USER;
+ if (attr->exclude_kernel)
+ config_base |= ARMV7_EXCLUDE_PL1;
+ if (!attr->exclude_hv)
+ config_base |= ARMV7_INCLUDE_HYP;
+
+ /*
+ * Install the filter into config_base as this is used to
+ * construct the event type.
+ */
+ event->config_base = config_base;
+
+ return 0;
+}
+
static inline unsigned long armv7_pmnc_read(void)
{
u32 val;
@@ -815,7 +849,8 @@
static inline void armv7_pmnc_write_evtsel(unsigned int idx, u32 val)
{
if (armv7_pmnc_select_counter(idx) == idx) {
- val &= ARMV7_EVTSEL_MASK;
+ val &= ARMV7_EVTYPE_MASK;
+
asm volatile("mcr p15, 0, %0, c9, c13, 1" : : "r" (val));
}
}
@@ -976,7 +1011,7 @@
* Set event (if destined for PMNx counters)
* We don't need to set the event if it's a cycle count
*/
- if (idx != ARMV7_CYCLE_COUNTER)
+ if (armpmu->set_event_filter || idx != ARMV7_CYCLE_COUNTER)
armv7_pmnc_write_evtsel(idx, hwc->config_base);
/*
@@ -1101,9 +1136,11 @@
struct hw_perf_event *event)
{
int idx;
+ unsigned long evtype = event->config_base & ARMV7_EVTYPE_EVENT;
+
/* Always place a cycle counter into the cycle counter. */
- if (event->config_base == ARMV7_PERFCTR_CPU_CYCLES) {
+ if (evtype == ARMV7_PERFCTR_CPU_CYCLES) {
if (test_and_set_bit(ARMV7_CYCLE_COUNTER, cpuc->used_mask))
return -EAGAIN;
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index 8ebac5b..ede298d 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -55,6 +55,7 @@
static uint32_t bam_dmux_write_cnt;
static uint32_t bam_dmux_write_cpy_cnt;
static uint32_t bam_dmux_write_cpy_bytes;
+static uint32_t bam_dmux_tx_sps_failure_cnt;
#define DBG(x...) do { \
if (msm_bam_dmux_debug_enable) \
@@ -83,11 +84,17 @@
__func__, bam_dmux_write_cpy_cnt, \
bam_dmux_write_cpy_bytes); \
} while (0)
+
+#define DBG_INC_TX_SPS_FAILURE_CNT() do { \
+ bam_dmux_tx_sps_failure_cnt++; \
+} while (0)
+
#else
#define DBG(x...) do { } while (0)
#define DBG_INC_READ_CNT(x...) do { } while (0)
#define DBG_INC_WRITE_CNT(x...) do { } while (0)
#define DBG_INC_WRITE_CPY(x...) do { } while (0)
+#define DBG_INC_TX_SPS_FAILURE_CNT() do { } while (0)
#endif
struct bam_ch_info {
@@ -377,6 +384,7 @@
DBG("%s sps_transfer_one failed rc=%d\n", __func__, rc);
spin_lock(&bam_tx_pool_spinlock);
list_del(&pkt->list_node);
+ DBG_INC_TX_SPS_FAILURE_CNT();
spin_unlock(&bam_tx_pool_spinlock);
kfree(pkt);
}
@@ -509,6 +517,7 @@
DBG("%s sps_transfer_one failed rc=%d\n", __func__, rc);
spin_lock(&bam_tx_pool_spinlock);
list_del(&pkt->list_node);
+ DBG_INC_TX_SPS_FAILURE_CNT();
spin_unlock(&bam_tx_pool_spinlock);
kfree(pkt);
}
@@ -815,6 +824,37 @@
return i;
}
+static int debug_ul_pkt_cnt(char *buf, int max)
+{
+ struct list_head *p;
+ unsigned long flags;
+ int n = 0;
+
+ spin_lock_irqsave(&bam_tx_pool_spinlock, flags);
+ __list_for_each(p, &bam_tx_pool) {
+ ++n;
+ }
+ spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
+
+ return scnprintf(buf, max, "Number of UL packets in flight: %d\n", n);
+}
+
+static int debug_stats(char *buf, int max)
+{
+ int i = 0;
+
+ i += scnprintf(buf + i, max - i,
+ "skb copy cnt: %u\n"
+ "skb copy bytes: %u\n"
+ "sps tx failures: %u\n",
+ bam_dmux_write_cpy_cnt,
+ bam_dmux_write_cpy_bytes,
+ bam_dmux_tx_sps_failure_cnt
+ );
+
+ return i;
+}
+
#define DEBUG_BUFMAX 4096
static char debug_buffer[DEBUG_BUFMAX];
@@ -1307,8 +1347,11 @@
struct dentry *dent;
dent = debugfs_create_dir("bam_dmux", 0);
- if (!IS_ERR(dent))
+ if (!IS_ERR(dent)) {
debug_create("tbl", 0444, dent, debug_tbl);
+ debug_create("ul_pkt_cnt", 0444, dent, debug_ul_pkt_cnt);
+ debug_create("stats", 0444, dent, debug_stats);
+ }
#endif
subsys_notif_register_notifier("modem", &restart_notifier);
return platform_driver_register(&bam_dmux_driver);
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index 2391f84..8aee414 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -911,3 +911,97 @@
{
return branch_reset(&to_branch_clk(clk)->b, action);
}
+
+static int cdiv_clk_enable(struct clk *c)
+{
+ unsigned long flags;
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ __branch_clk_enable_reg(&clk->b, clk->c.dbg_name);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ return 0;
+}
+
+static void cdiv_clk_disable(struct clk *c)
+{
+ unsigned long flags;
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ __branch_clk_disable_reg(&clk->b, clk->c.dbg_name);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+static int cdiv_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ u32 reg_val;
+
+ if (rate > clk->max_div)
+ return -EINVAL;
+ /* Check if frequency is actually changed. */
+ if (rate == clk->cur_div)
+ return 0;
+
+ spin_lock(&local_clock_reg_lock);
+ reg_val = readl_relaxed(clk->ns_reg);
+ reg_val &= ~(clk->ext_mask | (clk->max_div - 1) << clk->div_offset);
+ /* Non-zero rates mean set a divider, zero means use external input */
+ if (rate)
+ reg_val |= (rate - 1) << clk->div_offset;
+ else
+ reg_val |= clk->ext_mask;
+ writel_relaxed(reg_val, clk->ns_reg);
+ spin_unlock(&local_clock_reg_lock);
+
+ clk->cur_div = rate;
+ return 0;
+}
+
+static unsigned long cdiv_clk_get_rate(struct clk *c)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ return clk->cur_div;
+}
+
+static long cdiv_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ return rate > clk->max_div ? -EPERM : rate;
+}
+
+static int cdiv_clk_list_rate(struct clk *c, unsigned n)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ return n > clk->max_div ? -ENXIO : n;
+}
+
+static int cdiv_clk_handoff(struct clk *c)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ u32 reg_val;
+
+ reg_val = readl_relaxed(clk->ns_reg);
+ if (reg_val & clk->ext_mask) {
+ clk->cur_div = 0;
+ } else {
+ reg_val >>= clk->div_offset;
+ clk->cur_div = (reg_val & (clk->max_div - 1)) + 1;
+ }
+
+ return 0;
+}
+
+struct clk_ops clk_ops_cdiv = {
+ .enable = cdiv_clk_enable,
+ .disable = cdiv_clk_disable,
+ .auto_off = cdiv_clk_disable,
+ .handoff = cdiv_clk_handoff,
+ .set_rate = cdiv_clk_set_rate,
+ .get_rate = cdiv_clk_get_rate,
+ .list_rate = cdiv_clk_list_rate,
+ .round_rate = cdiv_clk_round_rate,
+ .is_local = local_clk_is_local,
+};
diff --git a/arch/arm/mach-msm/clock-local.h b/arch/arm/mach-msm/clock-local.h
index 2107567..e651d47 100644
--- a/arch/arm/mach-msm/clock-local.h
+++ b/arch/arm/mach-msm/clock-local.h
@@ -153,6 +153,35 @@
int rcg_clk_handoff(struct clk *c);
/**
+ * struct cdiv_clk - integer divider clock with external source selection
+ * @ns_reg: source select and divider settings register
+ * @ext_mask: bit to set to select an external source
+ * @cur_div: current divider setting (or 0 for external source)
+ * @max_div: maximum divider value supported (must be power of 2)
+ * @div_offset: number of bits to shift divider left by in @ns_reg
+ * @b: branch
+ * @c: clock
+ */
+struct cdiv_clk {
+ void __iomem *const ns_reg;
+ u32 ext_mask;
+
+ unsigned long cur_div;
+ u8 div_offset;
+ u32 max_div;
+
+ struct branch b;
+ struct clk c;
+};
+
+static inline struct cdiv_clk *to_cdiv_clk(struct clk *clk)
+{
+ return container_of(clk, struct cdiv_clk, c);
+}
+
+extern struct clk_ops clk_ops_cdiv;
+
+/**
* struct fixed_clk - fixed rate clock (used for crystal oscillators)
* @rate: output rate
* @c: clk
diff --git a/arch/arm/mach-msm/cpuidle.c b/arch/arm/mach-msm/cpuidle.c
index 4f22feb..6738955 100644
--- a/arch/arm/mach-msm/cpuidle.c
+++ b/arch/arm/mach-msm/cpuidle.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cpuidle.h>
+#include <linux/cpu_pm.h>
#include "cpuidle.h"
#include "pm.h"
@@ -61,8 +62,15 @@
atomic_notifier_call_chain(head, MSM_CPUIDLE_STATE_ENTER, NULL);
#endif
+#ifdef CONFIG_CPU_PM
+ cpu_pm_enter();
+#endif
ret = msm_pm_idle_enter((enum msm_pm_sleep_mode) (state->driver_data));
+#ifdef CONFIG_CPU_PM
+ cpu_pm_exit();
+#endif
+
#ifdef CONFIG_MSM_SLEEP_STATS
atomic_notifier_call_chain(head, MSM_CPUIDLE_STATE_EXIT, NULL);
#endif
diff --git a/arch/arm/mach-msm/rpm-regulator.c b/arch/arm/mach-msm/rpm-regulator.c
index 5c2bb8e..c10281b 100644
--- a/arch/arm/mach-msm/rpm-regulator.c
+++ b/arch/arm/mach-msm/rpm-regulator.c
@@ -51,14 +51,15 @@
#define SET_PART(_vreg, _part, _val) \
_vreg->req[_vreg->part->_part.word].value \
= (_vreg->req[_vreg->part->_part.word].value \
- & ~vreg->part->_part.mask) \
- | (((_val) << vreg->part->_part.shift) & vreg->part->_part.mask)
+ & ~_vreg->part->_part.mask) \
+ | (((_val) << _vreg->part->_part.shift) \
+ & _vreg->part->_part.mask)
#define GET_PART(_vreg, _part) \
- ((_vreg->req[_vreg->part->_part.word].value & vreg->part->_part.mask) \
- >> vreg->part->_part.shift)
+ ((_vreg->req[_vreg->part->_part.word].value & _vreg->part->_part.mask) \
+ >> _vreg->part->_part.shift)
-#define USES_PART(_vreg, _part) (vreg->part->_part.mask)
+#define USES_PART(_vreg, _part) (_vreg->part->_part.mask)
#define vreg_err(vreg, fmt, ...) \
pr_err("%s: " fmt, vreg->rdesc.name, ##__VA_ARGS__)
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index 8f24d81..9647646 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -235,11 +235,14 @@
t1 = __raw_readl(addr);
t2 = __raw_readl(addr);
t3 = __raw_readl(addr);
+ cpu_relax();
if ((t3-t2) <= 1)
return t3;
if ((t2-t1) <= 1)
return t2;
- if (++loop_count == 10) {
+ if (((t3-t2) == (t2-t1)) && (t3-t2) <= 8)
+ return t3;
+ if (++loop_count == 5) {
pr_err("msm_read_timer_count timer %s did not "
"stabilize: %u -> %u -> %u\n",
clock->clockevent.name, t1, t2, t3);
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 1af828b..fa91b53 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -68,8 +68,6 @@
#define SPS_SDCC_CONSUMER_PIPE_INDEX 2
#define SPS_CONS_PERIPHERAL 0
#define SPS_PROD_PERIPHERAL 1
-/* 16 KB */
-#define SPS_MAX_DESC_SIZE (16 * 1024)
#if defined(CONFIG_DEBUG_FS)
static void msmsdcc_dbg_createhost(struct msmsdcc_host *);
@@ -134,6 +132,42 @@
u32 c);
static inline void msmsdcc_delay(struct msmsdcc_host *host);
+static inline unsigned short msmsdcc_get_nr_sg(struct msmsdcc_host *host)
+{
+ unsigned short ret = NR_SG;
+
+ if (host->is_sps_mode) {
+ if (NR_SG > MAX_NR_SG_SPS)
+ ret = MAX_NR_SG_SPS;
+ } else { /* DMA or PIO mode */
+ if (NR_SG > MAX_NR_SG_DMA_PIO)
+ ret = MAX_NR_SG_DMA_PIO;
+ }
+
+ return ret;
+}
+
+static inline unsigned int msmsdcc_get_max_seg_size(struct msmsdcc_host *host)
+{
+ unsigned int max_seg_size;
+
+ /*
+ * SPS BAM has limitation of max. number of descriptors.
+ * max. # of descriptors = SPS_MAX_DESCS
+ * each descriptor can point to SPS_MAX_DESC_SIZE (16KB)
+ * So (nr_sg * max_seg_size) should be limited to the
+ * max. size that all of the descriptors can point to.
+ * i.e., (nr_sg * max_seg_size) = (SPS_MAX_DESCS * SPS_MAX_DESC_SIZE).
+ */
+ if (host->is_sps_mode) {
+ max_seg_size = (SPS_MAX_DESCS * SPS_MAX_DESC_SIZE) /
+ msmsdcc_get_nr_sg(host);
+ } else { /* DMA or PIO mode */
+ max_seg_size = MMC_MAX_REQ_SIZE;
+ }
+
+ return max_seg_size;
+}
#ifdef CONFIG_MMC_MSM_SPS_SUPPORT
static int msmsdcc_sps_reset_ep(struct msmsdcc_host *host,
@@ -717,8 +751,9 @@
dmov_box *box;
uint32_t rows;
unsigned int n;
- int i;
+ int i, err = 0, box_cmd_cnt = 0;
struct scatterlist *sg = data->sg;
+ unsigned int len, offset;
if ((host->dma.channel == -1) || (host->dma.crci == -1))
return -ENOENT;
@@ -728,7 +763,8 @@
host->dma.sg = data->sg;
host->dma.num_ents = data->sg_len;
- BUG_ON(host->dma.num_ents > NR_SG); /* Prevent memory corruption */
+ /* Prevent memory corruption */
+ BUG_ON(host->dma.num_ents > msmsdcc_get_nr_sg(host));
nc = host->dma.nc;
@@ -737,59 +773,8 @@
else
host->dma.dir = DMA_TO_DEVICE;
- /* host->curr.user_pages = (data->flags & MMC_DATA_USERPAGE); */
- host->curr.user_pages = 0;
- box = &nc->cmd[0];
- for (i = 0; i < host->dma.num_ents; i++) {
- box->cmd = CMD_MODE_BOX;
-
- /* Initialize sg dma address */
- sg->dma_address = pfn_to_dma(mmc_dev(host->mmc),
- page_to_pfn(sg_page(sg)))
- + sg->offset;
-
- if (i == (host->dma.num_ents - 1))
- box->cmd |= CMD_LC;
- rows = (sg_dma_len(sg) % MCI_FIFOSIZE) ?
- (sg_dma_len(sg) / MCI_FIFOSIZE) + 1 :
- (sg_dma_len(sg) / MCI_FIFOSIZE) ;
-
- if (data->flags & MMC_DATA_READ) {
- box->src_row_addr = msmsdcc_fifo_addr(host);
- box->dst_row_addr = sg_dma_address(sg);
-
- box->src_dst_len = (MCI_FIFOSIZE << 16) |
- (MCI_FIFOSIZE);
- box->row_offset = MCI_FIFOSIZE;
-
- box->num_rows = rows * ((1 << 16) + 1);
- box->cmd |= CMD_SRC_CRCI(host->dma.crci);
- } else {
- box->src_row_addr = sg_dma_address(sg);
- box->dst_row_addr = msmsdcc_fifo_addr(host);
-
- box->src_dst_len = (MCI_FIFOSIZE << 16) |
- (MCI_FIFOSIZE);
- box->row_offset = (MCI_FIFOSIZE << 16);
-
- box->num_rows = rows * ((1 << 16) + 1);
- box->cmd |= CMD_DST_CRCI(host->dma.crci);
- }
- box++;
- sg++;
- }
-
- /* location of command block must be 64 bit aligned */
- BUG_ON(host->dma.cmd_busaddr & 0x07);
-
- nc->cmdptr = (host->dma.cmd_busaddr >> 3) | CMD_PTR_LP;
- host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST |
- DMOV_CMD_ADDR(host->dma.cmdptr_busaddr);
- host->dma.hdr.complete_func = msmsdcc_dma_complete_func;
-
n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
host->dma.num_ents, host->dma.dir);
- /* dsb inside dma_map_sg will write nc out to mem as well */
if (n != host->dma.num_ents) {
pr_err("%s: Unable to map in all sg elements\n",
@@ -799,7 +784,79 @@
return -ENOMEM;
}
- return 0;
+ /* host->curr.user_pages = (data->flags & MMC_DATA_USERPAGE); */
+ host->curr.user_pages = 0;
+ box = &nc->cmd[0];
+ for (i = 0; i < host->dma.num_ents; i++) {
+ len = sg_dma_len(sg);
+ offset = 0;
+
+ do {
+ /* Check if we can do DMA */
+ if (!len || (box_cmd_cnt >= MMC_MAX_DMA_CMDS)) {
+ err = -ENOTSUPP;
+ goto unmap;
+ }
+
+ box->cmd = CMD_MODE_BOX;
+
+ if (len >= MMC_MAX_DMA_BOX_LENGTH) {
+ len = MMC_MAX_DMA_BOX_LENGTH;
+ len -= len % data->blksz;
+ }
+ rows = (len % MCI_FIFOSIZE) ?
+ (len / MCI_FIFOSIZE) + 1 :
+ (len / MCI_FIFOSIZE);
+
+ if (data->flags & MMC_DATA_READ) {
+ box->src_row_addr = msmsdcc_fifo_addr(host);
+ box->dst_row_addr = sg_dma_address(sg) + offset;
+ box->src_dst_len = (MCI_FIFOSIZE << 16) |
+ (MCI_FIFOSIZE);
+ box->row_offset = MCI_FIFOSIZE;
+ box->num_rows = rows * ((1 << 16) + 1);
+ box->cmd |= CMD_SRC_CRCI(host->dma.crci);
+ } else {
+ box->src_row_addr = sg_dma_address(sg) + offset;
+ box->dst_row_addr = msmsdcc_fifo_addr(host);
+ box->src_dst_len = (MCI_FIFOSIZE << 16) |
+ (MCI_FIFOSIZE);
+ box->row_offset = (MCI_FIFOSIZE << 16);
+ box->num_rows = rows * ((1 << 16) + 1);
+ box->cmd |= CMD_DST_CRCI(host->dma.crci);
+ }
+
+ offset += len;
+ len = sg_dma_len(sg) - offset;
+ box++;
+ box_cmd_cnt++;
+ } while (len);
+ sg++;
+ }
+ /* Mark last command */
+ box--;
+ box->cmd |= CMD_LC;
+
+ /* location of command block must be 64 bit aligned */
+ BUG_ON(host->dma.cmd_busaddr & 0x07);
+
+ nc->cmdptr = (host->dma.cmd_busaddr >> 3) | CMD_PTR_LP;
+ host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST |
+ DMOV_CMD_ADDR(host->dma.cmdptr_busaddr);
+ host->dma.hdr.complete_func = msmsdcc_dma_complete_func;
+
+ /* Flush all data to memory before starting dma */
+ mb();
+
+unmap:
+ if (err) {
+ dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg,
+ host->dma.num_ents, host->dma.dir);
+ pr_err("%s: cannot do DMA, fall back to PIO mode err=%d\n",
+ mmc_hostname(host->mmc), err);
+ }
+
+ return err;
}
#ifdef CONFIG_MMC_MSM_SPS_SUPPORT
@@ -825,7 +882,8 @@
struct scatterlist *sg = data->sg;
struct sps_pipe *sps_pipe_handle;
- BUG_ON(data->sg_len > NR_SG); /* Prevent memory corruption */
+ /* Prevent memory corruption */
+ BUG_ON(data->sg_len > msmsdcc_get_nr_sg(host));
host->sps.sg = data->sg;
host->sps.num_ents = data->sg_len;
@@ -3028,17 +3086,10 @@
* transfer. It's ignored for BAM-to-System mode transfer.
*/
sps_config->event_thresh = 0x10;
- /*
- * Max. no of scatter/gather buffers that can
- * be passed by block layer = 32 (NR_SG).
- * Each BAM descritor needs 64 bits (8 bytes).
- * One BAM descriptor is required per buffer transfer.
- * So we would require total 256 (32 * 8) bytes of descriptor FIFO.
- * But due to HW limitation we need to allocate atleast one extra
- * descriptor memory (256 bytes + 8 bytes). But in order to be
- * in power of 2, we are allocating 512 bytes of memory.
- */
- sps_config->desc.size = 512;
+
+ /* Allocate maximum descriptor fifo size */
+ sps_config->desc.size = SPS_MAX_DESC_FIFO_SIZE -
+ (SPS_MAX_DESC_FIFO_SIZE % SPS_MAX_DESC_LENGTH);
sps_config->desc.base = dma_alloc_coherent(mmc_dev(host->mmc),
sps_config->desc.size,
&sps_config->desc.phys_base,
@@ -3921,12 +3972,12 @@
if (plat->is_sdio_al_client)
mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY;
- mmc->max_segs = NR_SG;
- mmc->max_blk_size = 4096; /* MCI_DATA_CTL BLOCKSIZE up to 4096 */
- mmc->max_blk_count = 65535;
+ mmc->max_segs = msmsdcc_get_nr_sg(host);
+ mmc->max_blk_size = MMC_MAX_BLK_SIZE;
+ mmc->max_blk_count = MMC_MAX_BLK_CNT;
- mmc->max_req_size = 33554432; /* MCI_DATA_LENGTH is 25 bits */
- mmc->max_seg_size = mmc->max_req_size;
+ mmc->max_req_size = MMC_MAX_REQ_SIZE;
+ mmc->max_seg_size = msmsdcc_get_max_seg_size(host);
writel_relaxed(0, host->base + MMCIMASK0);
writel_relaxed(MCI_CLEAR_STATIC_MASK, host->base + MMCICLEAR);
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index 580da82..ca3eed8 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -207,7 +207,7 @@
#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
-#define NR_SG 32
+#define NR_SG 128
#define MSM_MMC_IDLE_TIMEOUT 5000 /* msecs */
@@ -217,10 +217,46 @@
*/
#define MSM_MMC_REQ_TIMEOUT 10000 /* msecs */
+/*
+ * Controller HW limitations
+ */
+#define MCI_DATALENGTH_BITS 25
+#define MMC_MAX_REQ_SIZE ((1 << MCI_DATALENGTH_BITS) - 1)
+/* MCI_DATA_CTL BLOCKSIZE up to 4096 */
+#define MMC_MAX_BLK_SIZE 4096
+#define MMC_MIN_BLK_SIZE 512
+#define MMC_MAX_BLK_CNT (MMC_MAX_REQ_SIZE / MMC_MIN_BLK_SIZE)
+
+/* 64KiB */
+#define MAX_SG_SIZE (64 * 1024)
+#define MAX_NR_SG_DMA_PIO (MMC_MAX_REQ_SIZE / MAX_SG_SIZE)
+
+/*
+ * BAM limitations
+ */
+/* upto 16 bits (64K - 1) */
+#define SPS_MAX_DESC_FIFO_SIZE 65535
+/* 16KiB */
+#define SPS_MAX_DESC_SIZE (16 * 1024)
+/* Each descriptor is of length 8 bytes */
+#define SPS_MAX_DESC_LENGTH 8
+#define SPS_MAX_DESCS (SPS_MAX_DESC_FIFO_SIZE / SPS_MAX_DESC_LENGTH)
+#define SPS_MAX_SG_DESCS (MAX_SG_SIZE / SPS_MAX_DESC_SIZE)
+#define MAX_NR_SG_SPS (SPS_MAX_DESCS / SPS_MAX_SG_DESCS)
+
+/*
+ * DMA limitations
+ */
+/* upto 16 bits (64K - 1) */
+#define MMC_MAX_DMA_ROWS (64 * 1024 - 1)
+#define MMC_MAX_DMA_BOX_LENGTH (MMC_MAX_DMA_ROWS * MCI_FIFOSIZE)
+#define MMC_MAX_DMA_CMDS (MAX_NR_SG_DMA_PIO * (MMC_MAX_REQ_SIZE / \
+ MMC_MAX_DMA_BOX_LENGTH))
+
struct clk;
struct msmsdcc_nc_dmadata {
- dmov_box cmd[NR_SG];
+ dmov_box cmd[MMC_MAX_DMA_CMDS];
uint32_t cmdptr;
};
diff --git a/drivers/regulator/pm8921-regulator.c b/drivers/regulator/pm8921-regulator.c
index 7e2ac6b..69a1318 100644
--- a/drivers/regulator/pm8921-regulator.c
+++ b/drivers/regulator/pm8921-regulator.c
@@ -85,9 +85,9 @@
#define LDO_CTRL_VPROG_MASK 0x1F
/* TEST register bank 0 */
-#define LDO_TEST_LPM_MASK 0x40
+#define LDO_TEST_LPM_MASK 0x04
#define LDO_TEST_LPM_SEL_CTRL 0x00
-#define LDO_TEST_LPM_SEL_TCXO 0x40
+#define LDO_TEST_LPM_SEL_TCXO 0x04
/* TEST register bank 2 */
#define LDO_TEST_VPROG_UPDATE_MASK 0x08
diff --git a/drivers/thermal/msm8960_tsens.c b/drivers/thermal/msm8960_tsens.c
index 0accb32..63d8666 100644
--- a/drivers/thermal/msm8960_tsens.c
+++ b/drivers/thermal/msm8960_tsens.c
@@ -99,6 +99,7 @@
#define TSENS_SENSOR_SHIFT 16
#define TSENS_RED_SHIFT 8
#define TSENS_8960_QFPROM_SHIFT 4
+#define TSENS_SENSOR_QFPROM_SHIFT 2
#define TSENS_SENSOR0_SHIFT 3
#define TSENS_MASK1 1
@@ -680,12 +681,12 @@
for (i = 0; i < tmdev->tsens_num_sensor; i++) {
main_sensor_addr = TSENS_8960_QFPROM_ADDR0 +
(TSENS_8960_QFPROM_SHIFT *
- (i & TSENS_8960_QFPROM_SHIFT >> TSENS_SENSOR0_SHIFT));
+ ((i & TSENS_8960_QFPROM_SHIFT) >> TSENS_SENSOR_QFPROM_SHIFT));
sensor_shift = (i % TSENS_8960_QFPROM_SHIFT) * TSENS_RED_SHIFT;
sensor_mask = TSENS_THRESHOLD_MAX_CODE << sensor_shift;
backup_sensor_addr = TSENS_8960_QFPROM_SPARE_ADDR0 +
(TSENS_8960_QFPROM_SHIFT *
- (i & TSENS_8960_QFPROM_SHIFT >> TSENS_SENSOR0_SHIFT));
+ ((i & TSENS_8960_QFPROM_SHIFT) >> TSENS_SENSOR_QFPROM_SHIFT));
tmdev->sensor[i].calib_data = (readl_relaxed(main_sensor_addr)
& sensor_mask) >> sensor_shift;
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
index 4a4bd15..8a1d56e 100644
--- a/drivers/video/msm/hdmi_msm.c
+++ b/drivers/video/msm/hdmi_msm.c
@@ -1975,13 +1975,14 @@
* Read HDMI_I2C_DATA with the following fields set
* RW = 0x1 (read)
* DATA = BCAPS (this is field where data is pulled from)
- * INDEX = 0x3 (where the data has been placed in buffer by hardware)
+ * INDEX = 0x5 (where the data has been placed in buffer by hardware)
* INDEX_WRITE = 0x1 (explicitly define offset) */
/* Write this data to DDC buffer */
- HDMI_OUTP_ND(0x0238, 0x1 | (3 << 16) | (1 << 31));
+ HDMI_OUTP_ND(0x0238, 0x1 | (5 << 16) | (1 << 31));
/* Discard first byte */
HDMI_INP_ND(0x0238);
+
for (ndx = 0; ndx < data_len; ++ndx) {
reg_val = HDMI_INP_ND(0x0238);
data_buf[ndx] = (uint8) ((reg_val & 0x0000FF00) >> 8);
diff --git a/include/linux/cpu_pm.h b/include/linux/cpu_pm.h
new file mode 100644
index 0000000..a165fd7
--- /dev/null
+++ b/include/linux/cpu_pm.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross <at> 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 _LINUX_CPU_PM_H
+#define _LINUX_CPU_PM_H
+
+#include <linux/kernel.h>
+#include <linux/notifier.h>
+
+/*
+ * When a CPU goes to a low power state that turns off power to the CPU's
+ * power domain, the contents of some blocks (floating point coprocessors,
+ * interrutp controllers, caches, timers) in the same power domain can
+ * be lost. The cpm_pm notifiers provide a method for platform idle, suspend,
+ * and hotplug implementations to notify the drivers for these blocks that
+ * they may be reset.
+ *
+ * All cpu_pm notifications must be called with interrupts disabled.
+ *
+ * The notifications are split into two classes, CPU notifications and CPU
+ * cluster notifications.
+ *
+ * CPU notifications apply to a single CPU, and must be called on the affected
+ * CPU. They are used to save per-cpu context for affected blocks.
+ *
+ * CPU cluster notifications apply to all CPUs in a single power domain. They
+ * are used to save any global context for affected blocks, and must be called
+ * after all the CPUs in the power domain have been notified of the low power
+ * state.
+ *
+ */
+
+/*
+ * Event codes passed as unsigned long val to notifier calls
+ */
+enum cpu_pm_event {
+ /* A single cpu is entering a low power state */
+ CPU_PM_ENTER,
+
+ /* A single cpu failed to enter a low power state */
+ CPU_PM_ENTER_FAILED,
+
+ /* A single cpu is exiting a low power state */
+ CPU_PM_EXIT,
+
+ /* A cpu power domain is entering a low power state */
+ CPU_CLUSTER_PM_ENTER,
+
+ /* A cpu power domain failed to enter a low power state */
+ CPU_CLUSTER_PM_ENTER_FAILED,
+
+ /* A cpu power domain is exiting a low power state */
+ CPU_CLUSTER_PM_EXIT,
+};
+
+int cpu_pm_register_notifier(struct notifier_block *nb);
+int cpu_pm_unregister_notifier(struct notifier_block *nb);
+
+/*
+ * cpm_pm_enter
+ *
+ * Notifies listeners that a single cpu is entering a low power state that may
+ * cause some blocks in the same power domain as the cpu to reset.
+ *
+ * Must be called on the affected cpu with interrupts disabled. Platform is
+ * responsible for ensuring that cpu_pm_enter is not called twice on the same
+ * cpu before cpu_pm_exit is called.
+ */
+int cpu_pm_enter(void);
+
+/*
+ * cpm_pm_exit
+ *
+ * Notifies listeners that a single cpu is exiting a low power state that may
+ * have caused some blocks in the same power domain as the cpu to reset.
+ *
+ * Must be called on the affected cpu with interrupts disabled.
+ */
+int cpu_pm_exit(void);
+
+/*
+ * cpm_cluster_pm_enter
+ *
+ * Notifies listeners that all cpus in a power domain are entering a low power
+ * state that may cause some blocks in the same power domain to reset.
+ *
+ * Must be called after cpu_pm_enter has been called on all cpus in the power
+ * domain, and before cpu_pm_exit has been called on any cpu in the power
+ * domain.
+ *
+ * Must be called with interrupts disabled.
+ */
+int cpu_cluster_pm_enter(void);
+
+/*
+ * cpm_pm_enter
+ *
+ * Notifies listeners that a single cpu is entering a low power state that may
+ * cause some blocks in the same power domain as the cpu to reset.
+ *
+ * Must be called after cpu_pm_enter has been called on all cpus in the power
+ * domain, and before cpu_pm_exit has been called on any cpu in the power
+ * domain.
+ *
+ * Must be called with interrupts disabled.
+ */
+int cpu_cluster_pm_exit(void);
+
+#endif
diff --git a/kernel/Makefile b/kernel/Makefile
index d06467f..63bf7a5 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -101,6 +101,7 @@
obj-$(CONFIG_TRACEPOINTS) += trace/
obj-$(CONFIG_SMP) += sched_cpupri.o
obj-$(CONFIG_IRQ_WORK) += irq_work.o
+obj-$(CONFIG_CPU_PM) += cpu_pm.o
obj-$(CONFIG_PERF_EVENTS) += events/
diff --git a/kernel/cpu_pm.c b/kernel/cpu_pm.c
new file mode 100644
index 0000000..1cd23b8
--- /dev/null
+++ b/kernel/cpu_pm.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross <at> 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/cpu_pm.h>
+#include <linux/module.h>
+#include <linux/notifier.h>
+#include <linux/spinlock.h>
+
+static DEFINE_RWLOCK(cpu_pm_notifier_lock);
+static RAW_NOTIFIER_HEAD(cpu_pm_notifier_chain);
+
+int cpu_pm_register_notifier(struct notifier_block *nb)
+{
+ unsigned long flags;
+ int ret;
+
+ write_lock_irqsave(&cpu_pm_notifier_lock, flags);
+ ret = raw_notifier_chain_register(&cpu_pm_notifier_chain, nb);
+ write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_pm_register_notifier);
+
+int cpu_pm_unregister_notifier(struct notifier_block *nb)
+{
+ unsigned long flags;
+ int ret;
+
+ write_lock_irqsave(&cpu_pm_notifier_lock, flags);
+ ret = raw_notifier_chain_unregister(&cpu_pm_notifier_chain, nb);
+ write_unlock_irqrestore(&cpu_pm_notifier_lock, flags);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
+
+static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
+{
+ int ret;
+
+ ret = __raw_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
+ nr_to_call, nr_calls);
+
+ return notifier_to_errno(ret);
+}
+
+int cpu_pm_enter(void)
+{
+ int nr_calls;
+ int ret = 0;
+
+ read_lock(&cpu_pm_notifier_lock);
+ ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
+ if (ret)
+ cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
+ read_unlock(&cpu_pm_notifier_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_pm_enter);
+
+int cpu_pm_exit(void)
+{
+ int ret;
+
+ read_lock(&cpu_pm_notifier_lock);
+ ret = cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
+ read_unlock(&cpu_pm_notifier_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_pm_exit);
+
+int cpu_cluster_pm_enter(void)
+{
+ int nr_calls;
+ int ret = 0;
+
+ read_lock(&cpu_pm_notifier_lock);
+ ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
+ if (ret)
+ cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
+ read_unlock(&cpu_pm_notifier_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
+
+int cpu_cluster_pm_exit(void)
+{
+ int ret;
+
+ read_lock(&cpu_pm_notifier_lock);
+ ret = cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
+ read_unlock(&cpu_pm_notifier_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit);
diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig
index b90fb99..ac4641e 100644
--- a/kernel/power/Kconfig
+++ b/kernel/power/Kconfig
@@ -24,6 +24,10 @@
config HAS_EARLYSUSPEND
bool
+config CPU_PM
+ def_bool y
+ depends on SUSPEND || CPU_IDLE
+
config WAKELOCK
bool "Wake lock"
depends on PM && RTC_CLASS