Merge "ASOC: msm: Enable audio for copper target." into msm-3.4
diff --git a/arch/arm/boot/dts/msmcopper.dtsi b/arch/arm/boot/dts/msmcopper.dtsi
index a37f4ff..77cbbe0 100644
--- a/arch/arm/boot/dts/msmcopper.dtsi
+++ b/arch/arm/boot/dts/msmcopper.dtsi
@@ -274,7 +274,7 @@
qcom,ssusb@F9200000 {
compatible = "qcom,dwc-usb3-msm";
- reg = <0xF9200000 0xCCFF>;
+ reg = <0xF9200000 0xFA000>;
interrupts = <0 131 0>;
SSUSB_VDDCX-supply = <&pm8841_s2>;
SSUSB_1p8-supply = <&pm8941_l6>;
@@ -296,6 +296,53 @@
qcom,firmware-name = "adsp";
};
+ qcom,msm-pcm {
+ compatible = "qcom,msm-pcm-dsp";
+ };
+
+ qcom,msm-pcm-routing {
+ compatible = "qcom,msm-pcm-routing";
+ };
+
+ qcom,msm-pcm-lpa {
+ compatible = "qcom,msm-pcm-lpa";
+ };
+
+ qcom,msm-voip-dsp {
+ compatible = "qcom,msm-voip-dsp";
+ };
+
+ qcom,msm-stub-codec {
+ compatible = "qcom,msm-stub-codec";
+ };
+
+ qcom,msm-dai-fe {
+ compatible = "qcom,msm-dai-fe";
+ };
+
+ qcom,msm-dai-q6 {
+ compatible = "qcom,msm-dai-q6";
+ qcom,msm-cpudai-auxpcm-clk = "pcm_clk";
+ qcom,msm-cpudai-auxpcm-mode = <0>;
+ qcom,msm-cpudai-auxpcm-sync = <1>;
+ qcom,msm-cpudai-auxpcm-frame = <5>;
+ qcom,msm-cpudai-auxpcm-quant = <2>;
+ qcom,msm-cpudai-auxpcm-slot = <1>;
+ qcom,msm-cpudai-auxpcm-data = <0>;
+ qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>;
+
+ qcom,msm-dai-q6-rx {
+ qcom,msm-dai-q6-id = <4106>;
+ };
+ qcom,msm-dai-q6-tx {
+ qcom,msm-dai-q6-id = <4107>;
+ };
+ };
+
+ qcom,msm-pcm-hostless {
+ compatible = "qcom,msm-pcm-hostless";
+ };
+
qcom,mss@fc880000 {
compatible = "qcom,pil-q6v5-mss";
reg = <0xfc880000 0x100>,
diff --git a/arch/arm/include/asm/perf_event.h b/arch/arm/include/asm/perf_event.h
index 2fecc60..a40f81e 100644
--- a/arch/arm/include/asm/perf_event.h
+++ b/arch/arm/include/asm/perf_event.h
@@ -25,7 +25,9 @@
ARM_PERF_PMU_ID_CA7,
ARM_PERF_PMU_ID_SCORPION,
ARM_PERF_PMU_ID_SCORPIONMP,
+ ARM_PERF_PMU_ID_SCORPIONMP_L2,
ARM_PERF_PMU_ID_KRAIT,
+ ARM_PERF_PMU_ID_KRAIT_L2,
ARM_NUM_PMU_IDS,
};
diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h
index 1e54b58..37cbfcb 100644
--- a/arch/arm/include/asm/pmu.h
+++ b/arch/arm/include/asm/pmu.h
@@ -21,7 +21,7 @@
*/
enum arm_pmu_type {
ARM_PMU_DEVICE_CPU = 0,
- ARM_PMU_DEVICE_L2 = 1,
+ ARM_PMU_DEVICE_L2CC = 1,
ARM_NUM_PMU_DEVICES,
};
@@ -129,11 +129,13 @@
u64 max_period;
struct platform_device *plat_device;
struct pmu_hw_events *(*get_hw_events)(void);
+ int (*test_set_event_constraints)(struct perf_event *event);
+ int (*clear_event_constraints)(struct perf_event *event);
};
#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
-int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type);
+int armpmu_register(struct arm_pmu *armpmu, char *name, int type);
u64 armpmu_event_update(struct perf_event *event,
struct hw_perf_event *hwc,
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index af6e7e6..e37b28b 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -28,6 +28,8 @@
#include <asm/pmu.h>
#include <asm/stacktrace.h>
+#include <linux/cpu_pm.h>
+
/*
* ARMv6 supports a maximum of 3 events, starting from index 0. If we add
* another platform that supports more, we need to increase this to be the
@@ -271,6 +273,10 @@
hw_events->events[idx] = NULL;
clear_bit(idx, hw_events->used_mask);
+ /* Clear event constraints. */
+ if (armpmu->clear_event_constraints)
+ armpmu->clear_event_constraints(event);
+
perf_event_update_userpage(event);
}
@@ -284,6 +290,17 @@
int err = 0;
perf_pmu_disable(event->pmu);
+ /*
+ * Tests if event is constrained. If not sets it so that next
+ * collision can be detected.
+ */
+ if (armpmu->test_set_event_constraints)
+ if (armpmu->test_set_event_constraints(event) < 0) {
+ pr_err("Event: %llx failed constraint check.\n",
+ event->attr.config);
+ event->state = PERF_EVENT_STATE_OFF;
+ goto out;
+ }
/* If we don't have a space for the counter then finish early. */
idx = armpmu->get_event_idx(hw_events, hwc);
@@ -383,10 +400,7 @@
{
int i, irq, irqs;
struct platform_device *pmu_device = armpmu->plat_device;
-#if 0
- struct arm_pmu_platdata *plat =
- dev_get_platdata(&pmu_device->dev);
-#endif
+
irqs = min(pmu_device->num_resources, num_possible_cpus());
for (i = 0; i < irqs; ++i) {
@@ -394,13 +408,6 @@
continue;
irq = platform_get_irq(pmu_device, i);
armpmu->free_pmu_irq(irq);
-#if 0
- if (irq >= 0) {
- if (plat && plat->disable_irq)
- plat->disable_irq(irq);
- free_irq(irq, armpmu);
- }
-#endif
}
release_pmu(armpmu->type);
@@ -462,19 +469,6 @@
return err;
}
-#if 0
- err = request_irq(irq, handle_irq,
- IRQF_DISABLED | IRQF_NOBALANCING,
- "arm-pmu", armpmu);
- if (err) {
- pr_err("unable to request IRQ%d for ARM PMU counters\n",
- irq);
- armpmu_release_hardware(armpmu);
- return err;
- } else if (plat && plat->enable_irq)
- plat->enable_irq(irq);
-#endif
-
cpumask_set_cpu(i, &armpmu->active_irqs);
}
@@ -538,6 +532,7 @@
return -EPERM;
}
+
/*
* Store the event encoding into the config_base field.
*/
@@ -616,7 +611,7 @@
armpmu->stop();
}
-static void __init armpmu_init(struct arm_pmu *armpmu)
+static void armpmu_init(struct arm_pmu *armpmu)
{
atomic_set(&armpmu->active_events, 0);
mutex_init(&armpmu->reserve_mutex);
@@ -633,7 +628,7 @@
};
}
-int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type)
+int armpmu_register(struct arm_pmu *armpmu, char *name, int type)
{
armpmu_init(armpmu);
return perf_pmu_register(&armpmu->pmu, name, type);
@@ -644,6 +639,7 @@
#include "perf_event_v6.c"
#include "perf_event_v7.c"
#include "perf_event_msm_krait.c"
+#include "perf_event_msm.c"
/*
* Ensure the PMU has sane values out of reset.
@@ -734,10 +730,76 @@
return NOTIFY_OK;
}
+static void armpmu_update_counters(void)
+{
+ struct pmu_hw_events *hw_events;
+ int idx;
+
+ if (!cpu_pmu)
+ return;
+
+ hw_events = cpu_pmu->get_hw_events();
+
+ for (idx = 0; idx <= cpu_pmu->num_events; ++idx) {
+ struct perf_event *event = hw_events->events[idx];
+
+ if (!event)
+ continue;
+
+ armpmu_read(event);
+ }
+}
+
+static int cpu_has_active_perf(void)
+{
+ struct pmu_hw_events *hw_events;
+ int enabled;
+
+ if (!cpu_pmu)
+ return 0;
+
+ hw_events = cpu_pmu->get_hw_events();
+ enabled = bitmap_weight(hw_events->used_mask, cpu_pmu->num_events);
+
+ if (enabled)
+ /*Even one event's existence is good enough.*/
+ return 1;
+
+ return 0;
+}
+
static struct notifier_block __cpuinitdata pmu_cpu_notifier = {
.notifier_call = pmu_cpu_notify,
};
+/*TODO: Unify with pending patch from ARM */
+static int perf_cpu_pm_notifier(struct notifier_block *self, unsigned long cmd,
+ void *v)
+{
+ switch (cmd) {
+ case CPU_PM_ENTER:
+ if (cpu_has_active_perf()) {
+ armpmu_update_counters();
+ perf_pmu_disable(&cpu_pmu->pmu);
+ }
+ break;
+
+ case CPU_PM_ENTER_FAILED:
+ case CPU_PM_EXIT:
+ if (cpu_has_active_perf() && cpu_pmu->reset) {
+ cpu_pmu->reset(NULL);
+ perf_pmu_enable(&cpu_pmu->pmu);
+ }
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block perf_cpu_pm_notifier_block = {
+ .notifier_call = perf_cpu_pm_notifier,
+};
+
/*
* CPU PMU identification and registration.
*/
@@ -790,11 +852,11 @@
} else if (0x51 == implementor) {
switch (part_number) {
case 0x00F0: /* 8x50 & 7x30*/
-// cpu_pmu = armv7_scorpion_pmu_init();
+ cpu_pmu = armv7_scorpion_pmu_init();
break;
case 0x02D0: /* 8x60 */
// fabricmon_pmu_init();
-// cpu_pmu = armv7_scorpionmp_pmu_init();
+ cpu_pmu = armv7_scorpionmp_pmu_init();
// scorpionmp_l2_pmu_init();
break;
case 0x0490: /* 8960 sim */
@@ -814,6 +876,7 @@
cpu_pmu_init(cpu_pmu);
register_cpu_notifier(&pmu_cpu_notifier);
armpmu_register(cpu_pmu, "cpu", PERF_TYPE_RAW);
+ cpu_pm_register_notifier(&perf_cpu_pm_notifier_block);
} else {
pr_info("no hardware support available\n");
}
diff --git a/arch/arm/kernel/perf_event_msm.c b/arch/arm/kernel/perf_event_msm.c
index 0ca5164..46fa8fe 100644
--- a/arch/arm/kernel/perf_event_msm.c
+++ b/arch/arm/kernel/perf_event_msm.c
@@ -12,7 +12,7 @@
*/
#include <linux/cpumask.h>
-
+#include <asm/cp15.h>
#include <asm/vfp.h>
#include <asm/system.h>
#include "../vfp/vfpinstr.h"
@@ -143,12 +143,16 @@
* combined.
*/
[C(OP_READ)] = {
- [C(RESULT_ACCESS)] = ARMV7_PERFCTR_DCACHE_ACCESS,
- [C(RESULT_MISS)] = ARMV7_PERFCTR_DCACHE_REFILL,
+ [C(RESULT_ACCESS)]
+ = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
+ [C(RESULT_MISS)]
+ = ARMV7_PERFCTR_L1_DCACHE_REFILL,
},
[C(OP_WRITE)] = {
- [C(RESULT_ACCESS)] = ARMV7_PERFCTR_DCACHE_ACCESS,
- [C(RESULT_MISS)] = ARMV7_PERFCTR_DCACHE_REFILL,
+ [C(RESULT_ACCESS)]
+ = ARMV7_PERFCTR_L1_DCACHE_ACCESS,
+ [C(RESULT_MISS)]
+ = ARMV7_PERFCTR_L1_DCACHE_REFILL,
},
[C(OP_PREFETCH)] = {
[C(RESULT_ACCESS)] = CACHE_OP_UNSUPPORTED,
@@ -236,6 +240,13 @@
},
};
+static int msm_scorpion_map_event(struct perf_event *event)
+{
+ return map_cpu_event(event, &armv7_scorpion_perf_map,
+ &armv7_scorpion_perf_cache_map, 0xfffff);
+}
+
+
struct scorpion_evt {
/*
* The scorpion_evt_type field corresponds to the actual Scorpion
@@ -483,9 +494,9 @@
u32 v_orig_val;
u32 f_orig_val;
- /* CPACR Enable CP10 access */
+ /* CPACR Enable CP10 and CP11 access */
v_orig_val = get_copro_access();
- venum_new_val = v_orig_val | CPACC_SVC(10);
+ venum_new_val = v_orig_val | CPACC_SVC(10) | CPACC_SVC(11);
set_copro_access(venum_new_val);
/* Store orig venum val */
__get_cpu_var(venum_orig_val) = v_orig_val;
@@ -551,17 +562,13 @@
static void scorpion_clear_pmuregs(void)
{
- unsigned long flags;
-
scorpion_write_lpm0(0);
scorpion_write_lpm1(0);
scorpion_write_lpm2(0);
scorpion_write_l2lpm(0);
- raw_spin_lock_irqsave(&pmu_lock, flags);
scorpion_pre_vlpm();
scorpion_write_vlpm(0);
scorpion_post_vlpm();
- raw_spin_unlock_irqrestore(&pmu_lock, flags);
}
static void scorpion_clearpmu(u32 grp, u32 val, u32 evt_code)
@@ -585,9 +592,11 @@
u32 gr;
unsigned long event;
struct scorpion_evt evtinfo;
+ struct pmu_hw_events *events = cpu_pmu->get_hw_events();
+
/* Disable counter and interrupt */
- raw_spin_lock_irqsave(&pmu_lock, flags);
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
/* Disable counter */
armv7_pmnc_disable_counter(idx);
@@ -596,7 +605,7 @@
* Clear lpm code (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 (idx != ARMV7_IDX_CYCLE_COUNTER) {
val = hwc->config_base;
val &= SCORPION_EVTYPE_EVENT;
@@ -613,10 +622,11 @@
armv7_pmnc_disable_intens(idx);
scorpion_dis_out:
- raw_spin_unlock_irqrestore(&pmu_lock, flags);
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
-static void scorpion_pmu_enable_event(struct hw_perf_event *hwc, int idx)
+static void scorpion_pmu_enable_event(struct hw_perf_event *hwc,
+ int idx, int cpu)
{
unsigned long flags;
u32 val = 0;
@@ -624,12 +634,13 @@
unsigned long event;
struct scorpion_evt evtinfo;
unsigned long long prev_count = local64_read(&hwc->prev_count);
+ struct pmu_hw_events *events = cpu_pmu->get_hw_events();
/*
* Enable counter and interrupt, and set the counter to count
* the event that we're interested in.
*/
- raw_spin_lock_irqsave(&pmu_lock, flags);
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
/* Disable counter */
armv7_pmnc_disable_counter(idx);
@@ -638,7 +649,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 (idx != ARMV7_IDX_CYCLE_COUNTER) {
val = hwc->config_base;
val &= SCORPION_EVTYPE_EVENT;
@@ -673,59 +684,12 @@
armv7_pmnc_enable_counter(idx);
scorpion_out:
- raw_spin_unlock_irqrestore(&pmu_lock, flags);
-}
-
-static void enable_irq_callback(void *info)
-{
- int irq = *(unsigned int *)info;
-
- enable_percpu_irq(irq, IRQ_TYPE_EDGE_RISING);
-}
-
-static void disable_irq_callback(void *info)
-{
- int irq = *(unsigned int *)info;
-
- disable_percpu_irq(irq);
-}
-
-static int
-msm_request_irq(int irq, irq_handler_t *handle_irq)
-{
- int err = 0;
- int cpu;
-
- err = request_percpu_irq(irq, *handle_irq, "armpmu",
- &cpu_hw_events);
-
- if (!err) {
- for_each_cpu(cpu, cpu_online_mask) {
- smp_call_function_single(cpu,
- enable_irq_callback, &irq, 1);
- }
- }
-
- return err;
-}
-
-static void
-msm_free_irq(int irq)
-{
- int cpu;
-
- if (irq >= 0) {
- for_each_cpu(cpu, cpu_online_mask) {
- smp_call_function_single(cpu,
- disable_irq_callback, &irq, 1);
- }
- free_percpu_irq(irq, &cpu_hw_events);
- }
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
}
static void scorpion_pmu_reset(void *info)
{
- u32 idx, nb_cnt = armpmu->num_events;
+ u32 idx, nb_cnt = cpu_pmu->num_events;
/* Stop all counters and their interrupts */
for (idx = 1; idx < nb_cnt; ++idx) {
@@ -751,7 +715,7 @@
.disable = scorpion_pmu_disable_event,
.read_counter = armv7pmu_read_counter,
.write_counter = armv7pmu_write_counter,
- .raw_event_mask = 0xFFFFF,
+ .map_event = msm_scorpion_map_event,
.get_event_idx = armv7pmu_get_event_idx,
.start = armv7pmu_start,
.stop = armv7pmu_stop,
@@ -759,33 +723,29 @@
.max_period = (1LLU << 32) - 1,
};
-static const struct arm_pmu *__init armv7_scorpion_pmu_init(void)
+static struct arm_pmu *__init armv7_scorpion_pmu_init(void)
{
scorpion_pmu.id = ARM_PERF_PMU_ID_SCORPION;
scorpion_pmu.name = "ARMv7 Scorpion";
- scorpion_pmu.cache_map = &armv7_scorpion_perf_cache_map;
- scorpion_pmu.event_map = &armv7_scorpion_perf_map;
scorpion_pmu.num_events = armv7_read_num_pmnc_events();
scorpion_clear_pmuregs();
return &scorpion_pmu;
}
-static const struct arm_pmu *__init armv7_scorpionmp_pmu_init(void)
+static struct arm_pmu *__init armv7_scorpionmp_pmu_init(void)
{
scorpion_pmu.id = ARM_PERF_PMU_ID_SCORPIONMP;
scorpion_pmu.name = "ARMv7 Scorpion-MP";
- scorpion_pmu.cache_map = &armv7_scorpion_perf_cache_map;
- scorpion_pmu.event_map = &armv7_scorpion_perf_map;
scorpion_pmu.num_events = armv7_read_num_pmnc_events();
scorpion_clear_pmuregs();
return &scorpion_pmu;
}
#else
-static const struct arm_pmu *__init armv7_scorpion_pmu_init(void)
+static struct arm_pmu *__init armv7_scorpion_pmu_init(void)
{
return NULL;
}
-static const struct arm_pmu *__init armv7_scorpionmp_pmu_init(void)
+static struct arm_pmu *__init armv7_scorpionmp_pmu_init(void)
{
return NULL;
}
diff --git a/arch/arm/kernel/perf_event_msm_krait.c b/arch/arm/kernel/perf_event_msm_krait.c
index 8dbd047..1b115b4 100644
--- a/arch/arm/kernel/perf_event_msm_krait.c
+++ b/arch/arm/kernel/perf_event_msm_krait.c
@@ -58,6 +58,17 @@
static u32 krait_ver, evt_index;
static u32 krait_max_l1_reg;
+
+/*
+ * Every 4 bytes represents a prefix.
+ * Every nibble represents a register.
+ * Every bit represents a group within a register.
+ *
+ * This supports up to 4 groups per register, upto 8
+ * registers per prefix and upto 2 prefixes.
+ */
+static DEFINE_PER_CPU(u64, pmu_bitmap);
+
static const unsigned armv7_krait_perf_map[PERF_COUNT_HW_MAX] = {
[PERF_COUNT_HW_CPU_CYCLES] = ARMV7_PERFCTR_CPU_CYCLES,
[PERF_COUNT_HW_INSTRUCTIONS] = ARMV7_PERFCTR_INSTR_EXECUTED,
@@ -556,6 +567,58 @@
}
}
+/*
+ * We check for column exclusion constraints here.
+ * Two events cant have same reg and same group.
+ */
+static int msm_test_set_ev_constraint(struct perf_event *event)
+{
+ u32 krait_evt_type = event->attr.config & KRAIT_EVENT_MASK;
+ u8 prefix = (krait_evt_type & 0xF0000) >> 16;
+ u8 reg = (krait_evt_type & 0x0F000) >> 12;
+ u8 group = krait_evt_type & 0x0000F;
+ u64 cpu_pmu_bitmap = __get_cpu_var(pmu_bitmap);
+ u64 bitmap_t;
+
+ /* Return if non MSM event. */
+ if (!prefix)
+ return 0;
+
+ bitmap_t = 1 << (((prefix - 1) * 32) + (reg * 4) + group);
+
+ /* Set it if not already set. */
+ if (!(cpu_pmu_bitmap & bitmap_t)) {
+ cpu_pmu_bitmap |= bitmap_t;
+ __get_cpu_var(pmu_bitmap) = cpu_pmu_bitmap;
+ return 1;
+ }
+ /* Bit is already set. Constraint failed. */
+ return -EPERM;
+}
+
+static int msm_clear_ev_constraint(struct perf_event *event)
+{
+ u32 krait_evt_type = event->attr.config & KRAIT_EVENT_MASK;
+ u8 prefix = (krait_evt_type & 0xF0000) >> 16;
+ u8 reg = (krait_evt_type & 0x0F000) >> 12;
+ u8 group = krait_evt_type & 0x0000F;
+ u64 cpu_pmu_bitmap = __get_cpu_var(pmu_bitmap);
+ u64 bitmap_t;
+
+ /* Return if non MSM event. */
+ if (!prefix)
+ return 0;
+
+ bitmap_t = 1 << (((prefix - 1) * 32) + (reg * 4) + group);
+
+ /* Clear constraint bit. */
+ cpu_pmu_bitmap &= ~(bitmap_t);
+
+ __get_cpu_var(pmu_bitmap) = cpu_pmu_bitmap;
+
+ return 1;
+}
+
static struct arm_pmu krait_pmu = {
.handle_irq = armv7pmu_handle_irq,
.request_pmu_irq = msm_request_irq,
@@ -568,6 +631,8 @@
.start = armv7pmu_start,
.stop = armv7pmu_stop,
.reset = krait_pmu_reset,
+ .test_set_event_constraints = msm_test_set_ev_constraint,
+ .clear_event_constraints = msm_clear_ev_constraint,
.max_period = (1LLU << 32) - 1,
};
diff --git a/arch/arm/kernel/perf_event_msm_krait_l2.c b/arch/arm/kernel/perf_event_msm_krait_l2.c
deleted file mode 100644
index baa05ac..0000000
--- a/arch/arm/kernel/perf_event_msm_krait_l2.c
+++ /dev/null
@@ -1,667 +0,0 @@
-/*
- * Copyright (c) 2011, 2012 Code Aurora Forum. 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 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- */
-#ifdef CONFIG_ARCH_MSM_KRAIT
-
-#include <linux/irq.h>
-
-#include <mach/msm-krait-l2-accessors.h>
-
-#define MAX_L2_PERIOD ((1ULL << 32) - 1)
-#define MAX_KRAIT_L2_CTRS 5
-
-#define L2PMCCNTR 0x409
-#define L2PMCCNTCR 0x408
-#define L2PMCCNTSR 0x40A
-#define L2CYCLE_CTR_BIT 31
-#define L2CYCLE_CTR_EVENT_IDX 4
-#define L2CYCLE_CTR_RAW_CODE 0xfe
-
-#define L2PMOVSR 0x406
-
-#define L2PMCR 0x400
-#define L2PMCR_RESET_ALL 0x6
-#define L2PMCR_GLOBAL_ENABLE 0x1
-#define L2PMCR_GLOBAL_DISABLE 0x0
-
-#define L2PMCNTENSET 0x403
-#define L2PMCNTENCLR 0x402
-
-#define L2PMINTENSET 0x405
-#define L2PMINTENCLR 0x404
-
-#define IA_L2PMXEVCNTCR_BASE 0x420
-#define IA_L2PMXEVTYPER_BASE 0x424
-#define IA_L2PMRESX_BASE 0x410
-#define IA_L2PMXEVFILTER_BASE 0x423
-#define IA_L2PMXEVCNTR_BASE 0x421
-
-/* event format is -e rsRCCG See get_event_desc() */
-
-#define EVENT_REG_MASK 0xf000
-#define EVENT_GROUPSEL_MASK 0x000f
-#define EVENT_GROUPCODE_MASK 0x0ff0
-#define EVENT_REG_SHIFT 12
-#define EVENT_GROUPCODE_SHIFT 4
-
-#define RESRX_VALUE_EN 0x80000000
-
-static struct platform_device *l2_pmu_device;
-
-struct hw_krait_l2_pmu {
- struct perf_event *events[MAX_KRAIT_L2_CTRS];
- unsigned long active_mask[BITS_TO_LONGS(MAX_KRAIT_L2_CTRS)];
- raw_spinlock_t lock;
-};
-
-struct hw_krait_l2_pmu hw_krait_l2_pmu;
-
-struct event_desc {
- int event_groupsel;
- int event_reg;
- int event_group_code;
-};
-
-void get_event_desc(u64 config, struct event_desc *evdesc)
-{
- /* L2PMEVCNTRX */
- evdesc->event_reg = (config & EVENT_REG_MASK) >> EVENT_REG_SHIFT;
- /* Group code (row ) */
- evdesc->event_group_code =
- (config & EVENT_GROUPCODE_MASK) >> EVENT_GROUPCODE_SHIFT;
- /* Group sel (col) */
- evdesc->event_groupsel = (config & EVENT_GROUPSEL_MASK);
-
- pr_debug("%s: reg: %x, group_code: %x, groupsel: %x\n", __func__,
- evdesc->event_reg, evdesc->event_group_code,
- evdesc->event_groupsel);
-}
-
-static void set_evcntcr(int ctr)
-{
- u32 evtcr_reg = (ctr * 16) + IA_L2PMXEVCNTCR_BASE;
-
- set_l2_indirect_reg(evtcr_reg, 0x0);
-}
-
-static void set_evtyper(int event_groupsel, int event_reg, int ctr)
-{
- u32 evtype_reg = (ctr * 16) + IA_L2PMXEVTYPER_BASE;
- u32 evtype_val = event_groupsel + (4 * event_reg);
-
- set_l2_indirect_reg(evtype_reg, evtype_val);
-}
-
-static void set_evres(int event_groupsel, int event_reg, int event_group_code)
-{
- u32 group_reg = event_reg + IA_L2PMRESX_BASE;
- u32 group_val =
- RESRX_VALUE_EN | (event_group_code << (8 * event_groupsel));
- u32 resr_val;
- u32 group_byte = 0xff;
- u32 group_mask = ~(group_byte << (8 * event_groupsel));
-
- resr_val = get_l2_indirect_reg(group_reg);
- resr_val &= group_mask;
- resr_val |= group_val;
-
- set_l2_indirect_reg(group_reg, resr_val);
-}
-
-static void set_evfilter_task_mode(int ctr)
-{
- u32 filter_reg = (ctr * 16) + IA_L2PMXEVFILTER_BASE;
- u32 filter_val = 0x000f0030 | 1 << smp_processor_id();
-
- set_l2_indirect_reg(filter_reg, filter_val);
-}
-
-static void set_evfilter_sys_mode(int ctr)
-{
- u32 filter_reg = (ctr * 16) + IA_L2PMXEVFILTER_BASE;
- u32 filter_val = 0x000f003f;
-
- set_l2_indirect_reg(filter_reg, filter_val);
-}
-
-static void enable_intenset(u32 idx)
-{
- if (idx == L2CYCLE_CTR_EVENT_IDX)
- set_l2_indirect_reg(L2PMINTENSET, 1 << L2CYCLE_CTR_BIT);
- else
- set_l2_indirect_reg(L2PMINTENSET, 1 << idx);
-}
-
-static void disable_intenclr(u32 idx)
-{
- if (idx == L2CYCLE_CTR_EVENT_IDX)
- set_l2_indirect_reg(L2PMINTENCLR, 1 << L2CYCLE_CTR_BIT);
- else
- set_l2_indirect_reg(L2PMINTENCLR, 1 << idx);
-}
-
-static void enable_counter(u32 idx)
-{
- if (idx == L2CYCLE_CTR_EVENT_IDX)
- set_l2_indirect_reg(L2PMCNTENSET, 1 << L2CYCLE_CTR_BIT);
- else
- set_l2_indirect_reg(L2PMCNTENSET, 1 << idx);
-}
-
-static void disable_counter(u32 idx)
-{
- if (idx == L2CYCLE_CTR_EVENT_IDX)
- set_l2_indirect_reg(L2PMCNTENCLR, 1 << L2CYCLE_CTR_BIT);
- else
- set_l2_indirect_reg(L2PMCNTENCLR, 1 << idx);
-}
-
-static u64 read_counter(u32 idx)
-{
- u32 val;
- u32 counter_reg = (idx * 16) + IA_L2PMXEVCNTR_BASE;
-
- if (idx == L2CYCLE_CTR_EVENT_IDX)
- val = get_l2_indirect_reg(L2PMCCNTR);
- else
- val = get_l2_indirect_reg(counter_reg);
-
- return val;
-}
-
-static void write_counter(u32 idx, u32 val)
-{
- u32 counter_reg = (idx * 16) + IA_L2PMXEVCNTR_BASE;
-
- if (idx == L2CYCLE_CTR_EVENT_IDX)
- set_l2_indirect_reg(L2PMCCNTR, val);
- else
- set_l2_indirect_reg(counter_reg, val);
-}
-
-static int
-pmu_event_set_period(struct perf_event *event,
- struct hw_perf_event *hwc, int idx)
-{
- s64 left = local64_read(&hwc->period_left);
- s64 period = hwc->sample_period;
- int ret = 0;
-
- if (unlikely(left <= -period)) {
- left = period;
- local64_set(&hwc->period_left, left);
- hwc->last_period = period;
- ret = 1;
- }
-
- if (unlikely(left <= 0)) {
- left += period;
- local64_set(&hwc->period_left, left);
- hwc->last_period = period;
- ret = 1;
- }
-
- if (left > (s64) MAX_L2_PERIOD)
- left = MAX_L2_PERIOD;
-
- local64_set(&hwc->prev_count, (u64)-left);
-
- write_counter(idx, (u64) (-left) & 0xffffffff);
-
- perf_event_update_userpage(event);
-
- return ret;
-}
-
-static u64
-pmu_event_update(struct perf_event *event, struct hw_perf_event *hwc, int idx,
- int overflow)
-{
- u64 prev_raw_count, new_raw_count;
- u64 delta;
-
-again:
- prev_raw_count = local64_read(&hwc->prev_count);
- new_raw_count = read_counter(idx);
-
- if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
- new_raw_count) != prev_raw_count)
- goto again;
-
- new_raw_count &= MAX_L2_PERIOD;
- prev_raw_count &= MAX_L2_PERIOD;
-
- if (overflow)
- delta = MAX_L2_PERIOD - prev_raw_count + new_raw_count;
- else
- delta = new_raw_count - prev_raw_count;
-
- local64_add(delta, &event->count);
- local64_sub(delta, &hwc->period_left);
-
- pr_debug("%s: new: %lld, prev: %lld, event: %ld count: %lld\n",
- __func__, new_raw_count, prev_raw_count,
- hwc->config_base, local64_read(&event->count));
-
- return new_raw_count;
-}
-
-static void krait_l2_read(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
-
- pmu_event_update(event, hwc, hwc->idx, 0);
-}
-
-static void krait_l2_stop_counter(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
-
- if (!(hwc->state & PERF_HES_STOPPED)) {
- disable_intenclr(idx);
- disable_counter(idx);
-
- pmu_event_update(event, hwc, idx, 0);
- hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
- }
-
- pr_debug("%s: event: %ld ctr: %d stopped\n", __func__, hwc->config_base,
- idx);
-}
-
-static void krait_l2_start_counter(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
- struct event_desc evdesc;
-
- if (flags & PERF_EF_RELOAD)
- WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
-
- hwc->state = 0;
-
- pmu_event_set_period(event, hwc, idx);
-
- if (hwc->config_base == L2CYCLE_CTR_RAW_CODE)
- goto out;
-
- set_evcntcr(idx);
-
- memset(&evdesc, 0, sizeof(evdesc));
-
- get_event_desc(hwc->config_base, &evdesc);
-
- set_evtyper(evdesc.event_groupsel, evdesc.event_reg, idx);
-
- set_evres(evdesc.event_groupsel, evdesc.event_reg,
- evdesc.event_group_code);
-
- if (event->cpu < 0)
- set_evfilter_task_mode(idx);
- else
- set_evfilter_sys_mode(idx);
-
-out:
- enable_intenset(idx);
- enable_counter(idx);
-
- pr_debug
- ("%s: ctr: %d group: %ld group_code: %lld started from cpu:%d\n",
- __func__, idx, hwc->config_base, hwc->config, smp_processor_id());
-}
-
-static void krait_l2_del_event(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
- unsigned long iflags;
-
- raw_spin_lock_irqsave(&hw_krait_l2_pmu.lock, iflags);
-
- clear_bit(idx, (long unsigned int *)(&hw_krait_l2_pmu.active_mask));
-
- krait_l2_stop_counter(event, PERF_EF_UPDATE);
- hw_krait_l2_pmu.events[idx] = NULL;
- hwc->idx = -1;
-
- raw_spin_unlock_irqrestore(&hw_krait_l2_pmu.lock, iflags);
-
- pr_debug("%s: event: %ld deleted\n", __func__, hwc->config_base);
-
- perf_event_update_userpage(event);
-}
-
-static int krait_l2_add_event(struct perf_event *event, int flags)
-{
- int ctr = 0;
- struct hw_perf_event *hwc = &event->hw;
- unsigned long iflags;
- int err = 0;
-
- perf_pmu_disable(event->pmu);
-
- raw_spin_lock_irqsave(&hw_krait_l2_pmu.lock, iflags);
-
- /* Cycle counter has a resrvd index */
- if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
- if (hw_krait_l2_pmu.events[L2CYCLE_CTR_EVENT_IDX]) {
- pr_err("%s: Stale cycle ctr event ptr !\n", __func__);
- err = -EINVAL;
- goto out;
- }
- hwc->idx = L2CYCLE_CTR_EVENT_IDX;
- hw_krait_l2_pmu.events[L2CYCLE_CTR_EVENT_IDX] = event;
- set_bit(L2CYCLE_CTR_EVENT_IDX,
- (long unsigned int *)&hw_krait_l2_pmu.active_mask);
- goto skip_ctr_loop;
- }
-
- for (ctr = 0; ctr < MAX_KRAIT_L2_CTRS - 1; ctr++) {
- if (!hw_krait_l2_pmu.events[ctr]) {
- hwc->idx = ctr;
- hw_krait_l2_pmu.events[ctr] = event;
- set_bit(ctr,
- (long unsigned int *)
- &hw_krait_l2_pmu.active_mask);
- break;
- }
- }
-
- if (hwc->idx < 0) {
- err = -ENOSPC;
- pr_err("%s: No space for event: %llx!!\n", __func__,
- event->attr.config);
- goto out;
- }
-
-skip_ctr_loop:
-
- disable_counter(hwc->idx);
-
- hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
-
- if (flags & PERF_EF_START)
- krait_l2_start_counter(event, PERF_EF_RELOAD);
-
- perf_event_update_userpage(event);
-
- pr_debug("%s: event: %ld, ctr: %d added from cpu:%d\n",
- __func__, hwc->config_base, hwc->idx, smp_processor_id());
-out:
- raw_spin_unlock_irqrestore(&hw_krait_l2_pmu.lock, iflags);
-
- /* Resume the PMU even if this event could not be added */
- perf_pmu_enable(event->pmu);
-
- return err;
-}
-
-static void krait_l2_pmu_enable(struct pmu *pmu)
-{
- isb();
- set_l2_indirect_reg(L2PMCR, L2PMCR_GLOBAL_ENABLE);
-}
-
-static void krait_l2_pmu_disable(struct pmu *pmu)
-{
- set_l2_indirect_reg(L2PMCR, L2PMCR_GLOBAL_DISABLE);
- isb();
-}
-
-u32 get_reset_pmovsr(void)
-{
- int val;
-
- val = get_l2_indirect_reg(L2PMOVSR);
- /* reset it */
- val &= 0xffffffff;
- set_l2_indirect_reg(L2PMOVSR, val);
-
- return val;
-}
-
-static irqreturn_t krait_l2_handle_irq(int irq_num, void *dev)
-{
- unsigned long pmovsr;
- struct perf_sample_data data;
- struct pt_regs *regs;
- struct perf_event *event;
- struct hw_perf_event *hwc;
- int bitp;
- int idx = 0;
-
- pmovsr = get_reset_pmovsr();
-
- if (!(pmovsr & 0xffffffff))
- return IRQ_NONE;
-
- regs = get_irq_regs();
-
- perf_sample_data_init(&data, 0);
-
- raw_spin_lock(&hw_krait_l2_pmu.lock);
-
- while (pmovsr) {
- bitp = __ffs(pmovsr);
-
- if (bitp == L2CYCLE_CTR_BIT)
- idx = L2CYCLE_CTR_EVENT_IDX;
- else
- idx = bitp;
-
- event = hw_krait_l2_pmu.events[idx];
-
- if (!event)
- goto next;
-
- if (!test_bit(idx, hw_krait_l2_pmu.active_mask))
- goto next;
-
- hwc = &event->hw;
- pmu_event_update(event, hwc, idx, 1);
- data.period = event->hw.last_period;
-
- if (!pmu_event_set_period(event, hwc, idx))
- goto next;
-
- if (perf_event_overflow(event, 0, &data, regs))
- disable_counter(hwc->idx);
-next:
- pmovsr &= (pmovsr - 1);
- }
-
- raw_spin_unlock(&hw_krait_l2_pmu.lock);
-
- irq_work_run();
-
- return IRQ_HANDLED;
-}
-
-static atomic_t active_l2_events = ATOMIC_INIT(0);
-static DEFINE_MUTEX(krait_pmu_reserve_mutex);
-
-static int pmu_reserve_hardware(void)
-{
- int i, err = -ENODEV, irq;
-
- l2_pmu_device = reserve_pmu(ARM_PMU_DEVICE_L2);
-
- if (IS_ERR(l2_pmu_device)) {
- pr_warning("unable to reserve pmu\n");
- return PTR_ERR(l2_pmu_device);
- }
-
- if (l2_pmu_device->num_resources < 1) {
- pr_err("no irqs for PMUs defined\n");
- return -ENODEV;
- }
-
- if (strncmp(l2_pmu_device->name, "l2-arm-pmu", 6)) {
- pr_err("Incorrect pdev reserved !\n");
- return -EINVAL;
- }
-
- for (i = 0; i < l2_pmu_device->num_resources; ++i) {
- irq = platform_get_irq(l2_pmu_device, i);
- if (irq < 0)
- continue;
-
- err = request_irq(irq, krait_l2_handle_irq,
- IRQF_DISABLED | IRQF_NOBALANCING,
- "krait-l2-pmu", NULL);
- if (err) {
- pr_warning("unable to request IRQ%d for Krait L2 perf "
- "counters\n", irq);
- break;
- }
-
- irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq));
- }
-
- if (err) {
- for (i = i - 1; i >= 0; --i) {
- irq = platform_get_irq(l2_pmu_device, i);
- if (irq >= 0)
- free_irq(irq, NULL);
- }
- release_pmu(l2_pmu_device);
- l2_pmu_device = NULL;
- }
-
- return err;
-}
-
-static void pmu_release_hardware(void)
-{
- int i, irq;
-
- for (i = l2_pmu_device->num_resources - 1; i >= 0; --i) {
- irq = platform_get_irq(l2_pmu_device, i);
- if (irq >= 0)
- free_irq(irq, NULL);
- }
-
- krait_l2_pmu_disable(NULL);
-
- release_pmu(l2_pmu_device);
- l2_pmu_device = NULL;
-}
-
-static void pmu_perf_event_destroy(struct perf_event *event)
-{
- if (atomic_dec_and_mutex_lock
- (&active_l2_events, &krait_pmu_reserve_mutex)) {
- pmu_release_hardware();
- mutex_unlock(&krait_pmu_reserve_mutex);
- }
-}
-
-static int krait_l2_event_init(struct perf_event *event)
-{
- int err = 0;
- struct hw_perf_event *hwc = &event->hw;
- int status = 0;
-
- switch (event->attr.type) {
- case PERF_TYPE_SHARED:
- break;
-
- default:
- return -ENOENT;
- }
-
- hwc->idx = -1;
-
- event->destroy = pmu_perf_event_destroy;
-
- if (!atomic_inc_not_zero(&active_l2_events)) {
- /* 0 active events */
- mutex_lock(&krait_pmu_reserve_mutex);
- err = pmu_reserve_hardware();
- mutex_unlock(&krait_pmu_reserve_mutex);
- if (!err)
- atomic_inc(&active_l2_events);
- else
- return err;
- }
-
- hwc->config = 0;
- hwc->event_base = 0;
-
- /* Check if we came via perf default syms */
- if (event->attr.config == PERF_COUNT_HW_L2_CYCLES)
- hwc->config_base = L2CYCLE_CTR_RAW_CODE;
- else
- hwc->config_base = event->attr.config;
-
- /* Only one CPU can control the cycle counter */
- if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
- /* Check if its already running */
- status = get_l2_indirect_reg(L2PMCCNTSR);
- if (status == 0x2) {
- err = -ENOSPC;
- goto out;
- }
- }
-
- if (!hwc->sample_period) {
- hwc->sample_period = MAX_L2_PERIOD;
- hwc->last_period = hwc->sample_period;
- local64_set(&hwc->period_left, hwc->sample_period);
- }
-
- pr_debug("%s: event: %lld init'd\n", __func__, event->attr.config);
-
-out:
- if (err < 0)
- pmu_perf_event_destroy(event);
-
- return err;
-}
-
-static struct pmu krait_l2_pmu = {
- .pmu_enable = krait_l2_pmu_enable,
- .pmu_disable = krait_l2_pmu_disable,
- .event_init = krait_l2_event_init,
- .add = krait_l2_add_event,
- .del = krait_l2_del_event,
- .start = krait_l2_start_counter,
- .stop = krait_l2_stop_counter,
- .read = krait_l2_read,
-};
-
-static const struct arm_pmu *__init krait_l2_pmu_init(void)
-{
- /* Register our own PMU here */
- perf_pmu_register(&krait_l2_pmu, "Krait L2", PERF_TYPE_SHARED);
-
- memset(&hw_krait_l2_pmu, 0, sizeof(hw_krait_l2_pmu));
-
- /* Reset all ctrs */
- set_l2_indirect_reg(L2PMCR, L2PMCR_RESET_ALL);
-
- /* Avoid spurious interrupt if any */
- get_reset_pmovsr();
-
- raw_spin_lock_init(&hw_krait_l2_pmu.lock);
-
- /* Don't return an arm_pmu here */
- return NULL;
-}
-#else
-
-static const struct arm_pmu *__init krait_l2_pmu_init(void)
-{
- return NULL;
-}
-#endif
diff --git a/arch/arm/kernel/perf_event_msm_l2.c b/arch/arm/kernel/perf_event_msm_l2.c
deleted file mode 100644
index 26753d8..0000000
--- a/arch/arm/kernel/perf_event_msm_l2.c
+++ /dev/null
@@ -1,1013 +0,0 @@
-/*
- * Copyright (c) 2011, 2012 Code Aurora Forum. 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 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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.
- */
-#ifdef CONFIG_ARCH_MSM8X60
-
-#include <linux/irq.h>
-
-#define MAX_BB_L2_PERIOD ((1ULL << 32) - 1)
-#define MAX_BB_L2_CTRS 5
-#define BB_L2CYCLE_CTR_BIT 31
-#define BB_L2CYCLE_CTR_EVENT_IDX 4
-#define BB_L2CYCLE_CTR_RAW_CODE 0xfe
-#define SCORPIONL2_PMNC_E (1 << 0) /* Enable all counters */
-#define SCORPION_L2_EVT_PREFIX 3
-#define SCORPION_MAX_L2_REG 4
-
-/*
- * Lock to protect r/m/w sequences to the L2 PMU.
- */
-DEFINE_RAW_SPINLOCK(bb_l2_pmu_lock);
-
-static struct platform_device *bb_l2_pmu_device;
-
-struct hw_bb_l2_pmu {
- struct perf_event *events[MAX_BB_L2_CTRS];
- unsigned long active_mask[BITS_TO_LONGS(MAX_BB_L2_CTRS)];
- raw_spinlock_t lock;
-};
-
-struct hw_bb_l2_pmu hw_bb_l2_pmu;
-
-struct bb_l2_scorp_evt {
- u32 evt_type;
- u32 val;
- u8 grp;
- u32 evt_type_act;
-};
-
-enum scorpion_perf_types {
- SCORPIONL2_TOTAL_BANK_REQ = 0x90,
- SCORPIONL2_DSIDE_READ = 0x91,
- SCORPIONL2_DSIDE_WRITE = 0x92,
- SCORPIONL2_ISIDE_READ = 0x93,
- SCORPIONL2_L2CACHE_ISIDE_READ = 0x94,
- SCORPIONL2_L2CACHE_BANK_REQ = 0x95,
- SCORPIONL2_L2CACHE_DSIDE_READ = 0x96,
- SCORPIONL2_L2CACHE_DSIDE_WRITE = 0x97,
- SCORPIONL2_L2NOCACHE_DSIDE_WRITE = 0x98,
- SCORPIONL2_L2NOCACHE_ISIDE_READ = 0x99,
- SCORPIONL2_L2NOCACHE_TOTAL_REQ = 0x9a,
- SCORPIONL2_L2NOCACHE_DSIDE_READ = 0x9b,
- SCORPIONL2_DSIDE_READ_NOL1 = 0x9c,
- SCORPIONL2_L2CACHE_WRITETHROUGH = 0x9d,
- SCORPIONL2_BARRIERS = 0x9e,
- SCORPIONL2_HARDWARE_TABLE_WALKS = 0x9f,
- SCORPIONL2_MVA_POC = 0xa0,
- SCORPIONL2_L2CACHE_HW_TABLE_WALKS = 0xa1,
- SCORPIONL2_SETWAY_CACHE_OPS = 0xa2,
- SCORPIONL2_DSIDE_WRITE_HITS = 0xa3,
- SCORPIONL2_ISIDE_READ_HITS = 0xa4,
- SCORPIONL2_CACHE_DSIDE_READ_NOL1 = 0xa5,
- SCORPIONL2_TOTAL_CACHE_HITS = 0xa6,
- SCORPIONL2_CACHE_MATCH_MISS = 0xa7,
- SCORPIONL2_DREAD_HIT_L1_DATA = 0xa8,
- SCORPIONL2_L2LINE_LOCKED = 0xa9,
- SCORPIONL2_HW_TABLE_WALK_HIT = 0xaa,
- SCORPIONL2_CACHE_MVA_POC = 0xab,
- SCORPIONL2_L2ALLOC_DWRITE_MISS = 0xac,
- SCORPIONL2_CORRECTED_TAG_ARRAY = 0xad,
- SCORPIONL2_CORRECTED_DATA_ARRAY = 0xae,
- SCORPIONL2_CORRECTED_REPLACEMENT_ARRAY = 0xaf,
- SCORPIONL2_PMBUS_MPAAF = 0xb0,
- SCORPIONL2_PMBUS_MPWDAF = 0xb1,
- SCORPIONL2_PMBUS_MPBRT = 0xb2,
- SCORPIONL2_CPU0_GRANT = 0xb3,
- SCORPIONL2_CPU1_GRANT = 0xb4,
- SCORPIONL2_CPU0_NOGRANT = 0xb5,
- SCORPIONL2_CPU1_NOGRANT = 0xb6,
- SCORPIONL2_CPU0_LOSING_ARB = 0xb7,
- SCORPIONL2_CPU1_LOSING_ARB = 0xb8,
- SCORPIONL2_SLAVEPORT_NOGRANT = 0xb9,
- SCORPIONL2_SLAVEPORT_BPQ_FULL = 0xba,
- SCORPIONL2_SLAVEPORT_LOSING_ARB = 0xbb,
- SCORPIONL2_SLAVEPORT_GRANT = 0xbc,
- SCORPIONL2_SLAVEPORT_GRANTLOCK = 0xbd,
- SCORPIONL2_L2EM_STREX_PASS = 0xbe,
- SCORPIONL2_L2EM_STREX_FAIL = 0xbf,
- SCORPIONL2_LDREX_RESERVE_L2EM = 0xc0,
- SCORPIONL2_SLAVEPORT_LDREX = 0xc1,
- SCORPIONL2_CPU0_L2EM_CLEARED = 0xc2,
- SCORPIONL2_CPU1_L2EM_CLEARED = 0xc3,
- SCORPIONL2_SLAVEPORT_L2EM_CLEARED = 0xc4,
- SCORPIONL2_CPU0_CLAMPED = 0xc5,
- SCORPIONL2_CPU1_CLAMPED = 0xc6,
- SCORPIONL2_CPU0_WAIT = 0xc7,
- SCORPIONL2_CPU1_WAIT = 0xc8,
- SCORPIONL2_CPU0_NONAMBAS_WAIT = 0xc9,
- SCORPIONL2_CPU1_NONAMBAS_WAIT = 0xca,
- SCORPIONL2_CPU0_DSB_WAIT = 0xcb,
- SCORPIONL2_CPU1_DSB_WAIT = 0xcc,
- SCORPIONL2_AXI_READ = 0xcd,
- SCORPIONL2_AXI_WRITE = 0xce,
-
- SCORPIONL2_1BEAT_WRITE = 0xcf,
- SCORPIONL2_2BEAT_WRITE = 0xd0,
- SCORPIONL2_4BEAT_WRITE = 0xd1,
- SCORPIONL2_8BEAT_WRITE = 0xd2,
- SCORPIONL2_12BEAT_WRITE = 0xd3,
- SCORPIONL2_16BEAT_WRITE = 0xd4,
- SCORPIONL2_1BEAT_DSIDE_READ = 0xd5,
- SCORPIONL2_2BEAT_DSIDE_READ = 0xd6,
- SCORPIONL2_4BEAT_DSIDE_READ = 0xd7,
- SCORPIONL2_8BEAT_DSIDE_READ = 0xd8,
- SCORPIONL2_CSYS_READ_1BEAT = 0xd9,
- SCORPIONL2_CSYS_READ_2BEAT = 0xda,
- SCORPIONL2_CSYS_READ_4BEAT = 0xdb,
- SCORPIONL2_CSYS_READ_8BEAT = 0xdc,
- SCORPIONL2_4BEAT_IFETCH_READ = 0xdd,
- SCORPIONL2_8BEAT_IFETCH_READ = 0xde,
- SCORPIONL2_CSYS_WRITE_1BEAT = 0xdf,
- SCORPIONL2_CSYS_WRITE_2BEAT = 0xe0,
- SCORPIONL2_AXI_READ_DATA_BEAT = 0xe1,
- SCORPIONL2_AXI_WRITE_EVT1 = 0xe2,
- SCORPIONL2_AXI_WRITE_EVT2 = 0xe3,
- SCORPIONL2_LDREX_REQ = 0xe4,
- SCORPIONL2_STREX_PASS = 0xe5,
- SCORPIONL2_STREX_FAIL = 0xe6,
- SCORPIONL2_CPREAD = 0xe7,
- SCORPIONL2_CPWRITE = 0xe8,
- SCORPIONL2_BARRIER_REQ = 0xe9,
- SCORPIONL2_AXI_READ_SLVPORT = 0xea,
- SCORPIONL2_AXI_WRITE_SLVPORT = 0xeb,
- SCORPIONL2_AXI_READ_SLVPORT_DATABEAT = 0xec,
- SCORPIONL2_AXI_WRITE_SLVPORT_DATABEAT = 0xed,
- SCORPIONL2_SNOOPKILL_PREFILTER = 0xee,
- SCORPIONL2_SNOOPKILL_FILTEROUT = 0xef,
- SCORPIONL2_SNOOPED_IC = 0xf0,
- SCORPIONL2_SNOOPED_BP = 0xf1,
- SCORPIONL2_SNOOPED_BARRIERS = 0xf2,
- SCORPIONL2_SNOOPED_TLB = 0xf3,
- BB_L2_MAX_EVT,
-};
-
-static const struct bb_l2_scorp_evt sc_evt[] = {
- {SCORPIONL2_TOTAL_BANK_REQ, 0x80000001, 0, 0x00},
- {SCORPIONL2_DSIDE_READ, 0x80000100, 0, 0x01},
- {SCORPIONL2_DSIDE_WRITE, 0x80010000, 0, 0x02},
- {SCORPIONL2_ISIDE_READ, 0x81000000, 0, 0x03},
- {SCORPIONL2_L2CACHE_ISIDE_READ, 0x80000002, 0, 0x00},
- {SCORPIONL2_L2CACHE_BANK_REQ, 0x80000200, 0, 0x01},
- {SCORPIONL2_L2CACHE_DSIDE_READ, 0x80020000, 0, 0x02},
- {SCORPIONL2_L2CACHE_DSIDE_WRITE, 0x82000000, 0, 0x03},
- {SCORPIONL2_L2NOCACHE_DSIDE_WRITE, 0x80000003, 0, 0x00},
- {SCORPIONL2_L2NOCACHE_ISIDE_READ, 0x80000300, 0, 0x01},
- {SCORPIONL2_L2NOCACHE_TOTAL_REQ, 0x80030000, 0, 0x02},
- {SCORPIONL2_L2NOCACHE_DSIDE_READ, 0x83000000, 0, 0x03},
- {SCORPIONL2_DSIDE_READ_NOL1, 0x80000004, 0, 0x00},
- {SCORPIONL2_L2CACHE_WRITETHROUGH, 0x80000400, 0, 0x01},
- {SCORPIONL2_BARRIERS, 0x84000000, 0, 0x03},
- {SCORPIONL2_HARDWARE_TABLE_WALKS, 0x80000005, 0, 0x00},
- {SCORPIONL2_MVA_POC, 0x80000500, 0, 0x01},
- {SCORPIONL2_L2CACHE_HW_TABLE_WALKS, 0x80050000, 0, 0x02},
- {SCORPIONL2_SETWAY_CACHE_OPS, 0x85000000, 0, 0x03},
- {SCORPIONL2_DSIDE_WRITE_HITS, 0x80000006, 0, 0x00},
- {SCORPIONL2_ISIDE_READ_HITS, 0x80000600, 0, 0x01},
- {SCORPIONL2_CACHE_DSIDE_READ_NOL1, 0x80060000, 0, 0x02},
- {SCORPIONL2_TOTAL_CACHE_HITS, 0x86000000, 0, 0x03},
- {SCORPIONL2_CACHE_MATCH_MISS, 0x80000007, 0, 0x00},
- {SCORPIONL2_DREAD_HIT_L1_DATA, 0x87000000, 0, 0x03},
- {SCORPIONL2_L2LINE_LOCKED, 0x80000008, 0, 0x00},
- {SCORPIONL2_HW_TABLE_WALK_HIT, 0x80000800, 0, 0x01},
- {SCORPIONL2_CACHE_MVA_POC, 0x80080000, 0, 0x02},
- {SCORPIONL2_L2ALLOC_DWRITE_MISS, 0x88000000, 0, 0x03},
- {SCORPIONL2_CORRECTED_TAG_ARRAY, 0x80001A00, 0, 0x01},
- {SCORPIONL2_CORRECTED_DATA_ARRAY, 0x801A0000, 0, 0x02},
- {SCORPIONL2_CORRECTED_REPLACEMENT_ARRAY, 0x9A000000, 0, 0x03},
- {SCORPIONL2_PMBUS_MPAAF, 0x80001C00, 0, 0x01},
- {SCORPIONL2_PMBUS_MPWDAF, 0x801C0000, 0, 0x02},
- {SCORPIONL2_PMBUS_MPBRT, 0x9C000000, 0, 0x03},
-
- {SCORPIONL2_CPU0_GRANT, 0x80000001, 1, 0x04},
- {SCORPIONL2_CPU1_GRANT, 0x80000100, 1, 0x05},
- {SCORPIONL2_CPU0_NOGRANT, 0x80020000, 1, 0x06},
- {SCORPIONL2_CPU1_NOGRANT, 0x82000000, 1, 0x07},
- {SCORPIONL2_CPU0_LOSING_ARB, 0x80040000, 1, 0x06},
- {SCORPIONL2_CPU1_LOSING_ARB, 0x84000000, 1, 0x07},
- {SCORPIONL2_SLAVEPORT_NOGRANT, 0x80000007, 1, 0x04},
- {SCORPIONL2_SLAVEPORT_BPQ_FULL, 0x80000700, 1, 0x05},
- {SCORPIONL2_SLAVEPORT_LOSING_ARB, 0x80070000, 1, 0x06},
- {SCORPIONL2_SLAVEPORT_GRANT, 0x87000000, 1, 0x07},
- {SCORPIONL2_SLAVEPORT_GRANTLOCK, 0x80000008, 1, 0x04},
- {SCORPIONL2_L2EM_STREX_PASS, 0x80000009, 1, 0x04},
- {SCORPIONL2_L2EM_STREX_FAIL, 0x80000900, 1, 0x05},
- {SCORPIONL2_LDREX_RESERVE_L2EM, 0x80090000, 1, 0x06},
- {SCORPIONL2_SLAVEPORT_LDREX, 0x89000000, 1, 0x07},
- {SCORPIONL2_CPU0_L2EM_CLEARED, 0x800A0000, 1, 0x06},
- {SCORPIONL2_CPU1_L2EM_CLEARED, 0x8A000000, 1, 0x07},
- {SCORPIONL2_SLAVEPORT_L2EM_CLEARED, 0x80000B00, 1, 0x05},
- {SCORPIONL2_CPU0_CLAMPED, 0x8000000E, 1, 0x04},
- {SCORPIONL2_CPU1_CLAMPED, 0x80000E00, 1, 0x05},
- {SCORPIONL2_CPU0_WAIT, 0x800F0000, 1, 0x06},
- {SCORPIONL2_CPU1_WAIT, 0x8F000000, 1, 0x07},
- {SCORPIONL2_CPU0_NONAMBAS_WAIT, 0x80000010, 1, 0x04},
- {SCORPIONL2_CPU1_NONAMBAS_WAIT, 0x80001000, 1, 0x05},
- {SCORPIONL2_CPU0_DSB_WAIT, 0x80000014, 1, 0x04},
- {SCORPIONL2_CPU1_DSB_WAIT, 0x80001400, 1, 0x05},
-
- {SCORPIONL2_AXI_READ, 0x80000001, 2, 0x08},
- {SCORPIONL2_AXI_WRITE, 0x80000100, 2, 0x09},
- {SCORPIONL2_1BEAT_WRITE, 0x80010000, 2, 0x0a},
- {SCORPIONL2_2BEAT_WRITE, 0x80010000, 2, 0x0b},
- {SCORPIONL2_4BEAT_WRITE, 0x80000002, 2, 0x08},
- {SCORPIONL2_8BEAT_WRITE, 0x80000200, 2, 0x09},
- {SCORPIONL2_12BEAT_WRITE, 0x80020000, 2, 0x0a},
- {SCORPIONL2_16BEAT_WRITE, 0x82000000, 2, 0x0b},
- {SCORPIONL2_1BEAT_DSIDE_READ, 0x80000003, 2, 0x08},
- {SCORPIONL2_2BEAT_DSIDE_READ, 0x80000300, 2, 0x09},
- {SCORPIONL2_4BEAT_DSIDE_READ, 0x80030000, 2, 0x0a},
- {SCORPIONL2_8BEAT_DSIDE_READ, 0x83000000, 2, 0x0b},
- {SCORPIONL2_CSYS_READ_1BEAT, 0x80000004, 2, 0x08},
- {SCORPIONL2_CSYS_READ_2BEAT, 0x80000400, 2, 0x09},
- {SCORPIONL2_CSYS_READ_4BEAT, 0x80040000, 2, 0x0a},
- {SCORPIONL2_CSYS_READ_8BEAT, 0x84000000, 2, 0x0b},
- {SCORPIONL2_4BEAT_IFETCH_READ, 0x80000005, 2, 0x08},
- {SCORPIONL2_8BEAT_IFETCH_READ, 0x80000500, 2, 0x09},
- {SCORPIONL2_CSYS_WRITE_1BEAT, 0x80050000, 2, 0x0a},
- {SCORPIONL2_CSYS_WRITE_2BEAT, 0x85000000, 2, 0x0b},
- {SCORPIONL2_AXI_READ_DATA_BEAT, 0x80000600, 2, 0x09},
- {SCORPIONL2_AXI_WRITE_EVT1, 0x80060000, 2, 0x0a},
- {SCORPIONL2_AXI_WRITE_EVT2, 0x86000000, 2, 0x0b},
- {SCORPIONL2_LDREX_REQ, 0x80000007, 2, 0x08},
- {SCORPIONL2_STREX_PASS, 0x80000700, 2, 0x09},
- {SCORPIONL2_STREX_FAIL, 0x80070000, 2, 0x0a},
- {SCORPIONL2_CPREAD, 0x80000008, 2, 0x08},
- {SCORPIONL2_CPWRITE, 0x80000800, 2, 0x09},
- {SCORPIONL2_BARRIER_REQ, 0x88000000, 2, 0x0b},
-
- {SCORPIONL2_AXI_READ_SLVPORT, 0x80000001, 3, 0x0c},
- {SCORPIONL2_AXI_WRITE_SLVPORT, 0x80000100, 3, 0x0d},
- {SCORPIONL2_AXI_READ_SLVPORT_DATABEAT, 0x80010000, 3, 0x0e},
- {SCORPIONL2_AXI_WRITE_SLVPORT_DATABEAT, 0x81000000, 3, 0x0f},
-
- {SCORPIONL2_SNOOPKILL_PREFILTER, 0x80000001, 4, 0x10},
- {SCORPIONL2_SNOOPKILL_FILTEROUT, 0x80000100, 4, 0x11},
- {SCORPIONL2_SNOOPED_IC, 0x80000002, 4, 0x10},
- {SCORPIONL2_SNOOPED_BP, 0x80000200, 4, 0x11},
- {SCORPIONL2_SNOOPED_BARRIERS, 0x80020000, 4, 0x12},
- {SCORPIONL2_SNOOPED_TLB, 0x82000000, 4, 0x13},
-};
-
-static u32 bb_l2_read_l2pm0(void)
-{
- u32 val;
- asm volatile ("mrc p15, 3, %0, c15, c7, 0" : "=r" (val));
- return val;
-}
-
-static void bb_l2_write_l2pm0(u32 val)
-{
- asm volatile ("mcr p15, 3, %0, c15, c7, 0" : : "r" (val));
-}
-
-static u32 bb_l2_read_l2pm1(void)
-{
- u32 val;
- asm volatile ("mrc p15, 3, %0, c15, c7, 1" : "=r" (val));
- return val;
-}
-
-static void bb_l2_write_l2pm1(u32 val)
-{
- asm volatile ("mcr p15, 3, %0, c15, c7, 1" : : "r" (val));
-}
-
-static u32 bb_l2_read_l2pm2(void)
-{
- u32 val;
- asm volatile ("mrc p15, 3, %0, c15, c7, 2" : "=r" (val));
- return val;
-}
-
-static void bb_l2_write_l2pm2(u32 val)
-{
- asm volatile ("mcr p15, 3, %0, c15, c7, 2" : : "r" (val));
-}
-
-static u32 bb_l2_read_l2pm3(void)
-{
- u32 val;
- asm volatile ("mrc p15, 3, %0, c15, c7, 3" : "=r" (val));
- return val;
-}
-
-static void bb_l2_write_l2pm3(u32 val)
-{
- asm volatile ("mcr p15, 3, %0, c15, c7, 3" : : "r" (val));
-}
-
-static u32 bb_l2_read_l2pm4(void)
-{
- u32 val;
- asm volatile ("mrc p15, 3, %0, c15, c7, 4" : "=r" (val));
- return val;
-}
-
-static void bb_l2_write_l2pm4(u32 val)
-{
- asm volatile ("mcr p15, 3, %0, c15, c7, 4" : : "r" (val));
-}
-
-struct bb_scorpion_access_funcs {
- u32(*read) (void);
- void (*write) (u32);
- void (*pre) (void);
- void (*post) (void);
-};
-
-struct bb_scorpion_access_funcs bb_l2_func[] = {
- {bb_l2_read_l2pm0, bb_l2_write_l2pm0, NULL, NULL},
- {bb_l2_read_l2pm1, bb_l2_write_l2pm1, NULL, NULL},
- {bb_l2_read_l2pm2, bb_l2_write_l2pm2, NULL, NULL},
- {bb_l2_read_l2pm3, bb_l2_write_l2pm3, NULL, NULL},
- {bb_l2_read_l2pm4, bb_l2_write_l2pm4, NULL, NULL},
-};
-
-#define COLMN0MASK 0x000000ff
-#define COLMN1MASK 0x0000ff00
-#define COLMN2MASK 0x00ff0000
-
-static u32 bb_l2_get_columnmask(u32 setval)
-{
- if (setval & COLMN0MASK)
- return 0xffffff00;
- else if (setval & COLMN1MASK)
- return 0xffff00ff;
- else if (setval & COLMN2MASK)
- return 0xff00ffff;
- else
- return 0x80ffffff;
-}
-
-static void bb_l2_evt_setup(u32 gr, u32 setval)
-{
- u32 val;
- if (bb_l2_func[gr].pre)
- bb_l2_func[gr].pre();
- val = bb_l2_get_columnmask(setval) & bb_l2_func[gr].read();
- val = val | setval;
- bb_l2_func[gr].write(val);
- if (bb_l2_func[gr].post)
- bb_l2_func[gr].post();
-}
-
-#define BB_L2_EVT_START_IDX 0x90
-#define BB_L2_INV_EVTYPE 0
-
-static unsigned int get_bb_l2_evtinfo(unsigned int evt_type,
- struct bb_l2_scorp_evt *evtinfo)
-{
- u32 idx;
- u8 prefix;
- u8 reg;
- u8 code;
- u8 group;
-
- prefix = (evt_type & 0xF0000) >> 16;
- if (prefix == SCORPION_L2_EVT_PREFIX) {
- reg = (evt_type & 0x0F000) >> 12;
- code = (evt_type & 0x00FF0) >> 4;
- group = evt_type & 0x0000F;
-
- if ((group > 3) || (reg > SCORPION_MAX_L2_REG))
- return BB_L2_INV_EVTYPE;
-
- evtinfo->val = 0x80000000 | (code << (group * 8));
- evtinfo->grp = reg;
- evtinfo->evt_type_act = group | (reg << 2);
- return evtinfo->evt_type_act;
- }
-
- if (evt_type < BB_L2_EVT_START_IDX || evt_type >= BB_L2_MAX_EVT)
- return BB_L2_INV_EVTYPE;
- idx = evt_type - BB_L2_EVT_START_IDX;
- if (sc_evt[idx].evt_type == evt_type) {
- evtinfo->val = sc_evt[idx].val;
- evtinfo->grp = sc_evt[idx].grp;
- evtinfo->evt_type_act = sc_evt[idx].evt_type_act;
- return sc_evt[idx].evt_type_act;
- }
- return BB_L2_INV_EVTYPE;
-}
-
-static inline void bb_l2_pmnc_write(unsigned long val)
-{
- val &= 0xff;
- asm volatile ("mcr p15, 3, %0, c15, c4, 0" : : "r" (val));
-}
-
-static inline unsigned long bb_l2_pmnc_read(void)
-{
- u32 val;
- asm volatile ("mrc p15, 3, %0, c15, c4, 0" : "=r" (val));
- return val;
-}
-
-static void bb_l2_set_evcntcr(void)
-{
- u32 val = 0x0;
- asm volatile ("mcr p15, 3, %0, c15, c6, 4" : : "r" (val));
-}
-
-static inline void bb_l2_set_evtyper(int ctr, int val)
-{
- /* select ctr */
- asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (ctr));
-
- /* write into EVTYPER */
- asm volatile ("mcr p15, 3, %0, c15, c6, 7" : : "r" (val));
-}
-
-static void bb_l2_set_evfilter_task_mode(void)
-{
- u32 filter_val = 0x000f0030 | 1 << smp_processor_id();
-
- asm volatile ("mcr p15, 3, %0, c15, c6, 3" : : "r" (filter_val));
-}
-
-static void bb_l2_set_evfilter_sys_mode(void)
-{
- u32 filter_val = 0x000f003f;
-
- asm volatile ("mcr p15, 3, %0, c15, c6, 3" : : "r" (filter_val));
-}
-
-static void bb_l2_enable_intenset(u32 idx)
-{
- if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
- asm volatile ("mcr p15, 3, %0, c15, c5, 1" : : "r"
- (1 << BB_L2CYCLE_CTR_BIT));
- } else {
- asm volatile ("mcr p15, 3, %0, c15, c5, 1" : : "r" (1 << idx));
- }
-}
-
-static void bb_l2_disable_intenclr(u32 idx)
-{
- if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
- asm volatile ("mcr p15, 3, %0, c15, c5, 0" : : "r"
- (1 << BB_L2CYCLE_CTR_BIT));
- } else {
- asm volatile ("mcr p15, 3, %0, c15, c5, 0" : : "r" (1 << idx));
- }
-}
-
-static void bb_l2_enable_counter(u32 idx)
-{
- if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
- asm volatile ("mcr p15, 3, %0, c15, c4, 3" : : "r"
- (1 << BB_L2CYCLE_CTR_BIT));
- } else {
- asm volatile ("mcr p15, 3, %0, c15, c4, 3" : : "r" (1 << idx));
- }
-}
-
-static void bb_l2_disable_counter(u32 idx)
-{
- if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
- asm volatile ("mcr p15, 3, %0, c15, c4, 2" : : "r"
- (1 << BB_L2CYCLE_CTR_BIT));
-
- } else {
- asm volatile ("mcr p15, 3, %0, c15, c4, 2" : : "r" (1 << idx));
- }
-}
-
-static u64 bb_l2_read_counter(u32 idx)
-{
- u32 val;
- unsigned long flags;
-
- if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
- asm volatile ("mrc p15, 3, %0, c15, c4, 5" : "=r" (val));
- } else {
- raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags);
- asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (idx));
-
- /* read val from counter */
- asm volatile ("mrc p15, 3, %0, c15, c6, 5" : "=r" (val));
- raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags);
- }
-
- return val;
-}
-
-static void bb_l2_write_counter(u32 idx, u32 val)
-{
- unsigned long flags;
-
- if (idx == BB_L2CYCLE_CTR_EVENT_IDX) {
- asm volatile ("mcr p15, 3, %0, c15, c4, 5" : : "r" (val));
- } else {
- raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags);
- /* select counter */
- asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (idx));
-
- /* write val into counter */
- asm volatile ("mcr p15, 3, %0, c15, c6, 5" : : "r" (val));
- raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags);
- }
-}
-
-static int
-bb_pmu_event_set_period(struct perf_event *event,
- struct hw_perf_event *hwc, int idx)
-{
- s64 left = local64_read(&hwc->period_left);
- s64 period = hwc->sample_period;
- int ret = 0;
-
- if (unlikely(left <= -period)) {
- left = period;
- local64_set(&hwc->period_left, left);
- hwc->last_period = period;
- ret = 1;
- }
-
- if (unlikely(left <= 0)) {
- left += period;
- local64_set(&hwc->period_left, left);
- hwc->last_period = period;
- ret = 1;
- }
-
- if (left > (s64) MAX_BB_L2_PERIOD)
- left = MAX_BB_L2_PERIOD;
-
- local64_set(&hwc->prev_count, (u64)-left);
-
- bb_l2_write_counter(idx, (u64) (-left) & 0xffffffff);
-
- perf_event_update_userpage(event);
-
- return ret;
-}
-
-static u64
-bb_pmu_event_update(struct perf_event *event, struct hw_perf_event *hwc,
- int idx, int overflow)
-{
- u64 prev_raw_count, new_raw_count;
- u64 delta;
-
-again:
- prev_raw_count = local64_read(&hwc->prev_count);
- new_raw_count = bb_l2_read_counter(idx);
-
- if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
- new_raw_count) != prev_raw_count)
- goto again;
-
- new_raw_count &= MAX_BB_L2_PERIOD;
- prev_raw_count &= MAX_BB_L2_PERIOD;
-
- if (overflow) {
- delta = MAX_BB_L2_PERIOD - prev_raw_count + new_raw_count;
- pr_err("%s: delta: %lld\n", __func__, delta);
- } else
- delta = new_raw_count - prev_raw_count;
-
- local64_add(delta, &event->count);
- local64_sub(delta, &hwc->period_left);
-
- pr_debug("%s: new: %lld, prev: %lld, event: %ld count: %lld\n",
- __func__, new_raw_count, prev_raw_count,
- hwc->config_base, local64_read(&event->count));
-
- return new_raw_count;
-}
-
-static void bb_l2_read(struct perf_event *event)
-{
- struct hw_perf_event *hwc = &event->hw;
-
- bb_pmu_event_update(event, hwc, hwc->idx, 0);
-}
-
-static void bb_l2_stop_counter(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
-
- if (!(hwc->state & PERF_HES_STOPPED)) {
- bb_l2_disable_intenclr(idx);
- bb_l2_disable_counter(idx);
-
- bb_pmu_event_update(event, hwc, idx, 0);
- hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
- }
-
- pr_debug("%s: event: %ld ctr: %d stopped\n", __func__, hwc->config_base,
- idx);
-}
-
-static void bb_l2_start_counter(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
- struct bb_l2_scorp_evt evtinfo;
- int evtype = hwc->config_base;
- int ev_typer;
- unsigned long iflags;
- int cpu_id = smp_processor_id();
-
- if (flags & PERF_EF_RELOAD)
- WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
-
- hwc->state = 0;
-
- bb_pmu_event_set_period(event, hwc, idx);
-
- if (hwc->config_base == BB_L2CYCLE_CTR_RAW_CODE)
- goto out;
-
- memset(&evtinfo, 0, sizeof(evtinfo));
-
- ev_typer = get_bb_l2_evtinfo(evtype, &evtinfo);
-
- raw_spin_lock_irqsave(&bb_l2_pmu_lock, iflags);
-
- bb_l2_set_evtyper(idx, ev_typer);
-
- bb_l2_set_evcntcr();
-
- if (event->cpu < 0)
- bb_l2_set_evfilter_task_mode();
- else
- bb_l2_set_evfilter_sys_mode();
-
- bb_l2_evt_setup(evtinfo.grp, evtinfo.val);
-
- raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, iflags);
-
-out:
-
- bb_l2_enable_intenset(idx);
-
- bb_l2_enable_counter(idx);
-
- pr_debug("%s: idx: %d, event: %d, val: %x, cpu: %d\n",
- __func__, idx, evtype, evtinfo.val, cpu_id);
-}
-
-static void bb_l2_del_event(struct perf_event *event, int flags)
-{
- struct hw_perf_event *hwc = &event->hw;
- int idx = hwc->idx;
- unsigned long iflags;
-
- raw_spin_lock_irqsave(&hw_bb_l2_pmu.lock, iflags);
-
- clear_bit(idx, (long unsigned int *)(&hw_bb_l2_pmu.active_mask));
-
- bb_l2_stop_counter(event, PERF_EF_UPDATE);
- hw_bb_l2_pmu.events[idx] = NULL;
- hwc->idx = -1;
-
- raw_spin_unlock_irqrestore(&hw_bb_l2_pmu.lock, iflags);
-
- pr_debug("%s: event: %ld deleted\n", __func__, hwc->config_base);
-
- perf_event_update_userpage(event);
-}
-
-static int bb_l2_add_event(struct perf_event *event, int flags)
-{
- int ctr = 0;
- struct hw_perf_event *hwc = &event->hw;
- unsigned long iflags;
- int err = 0;
-
- perf_pmu_disable(event->pmu);
-
- raw_spin_lock_irqsave(&hw_bb_l2_pmu.lock, iflags);
-
- /* Cycle counter has a resrvd index */
- if (hwc->config_base == BB_L2CYCLE_CTR_RAW_CODE) {
- if (hw_bb_l2_pmu.events[BB_L2CYCLE_CTR_EVENT_IDX]) {
- pr_err("%s: Stale cycle ctr event ptr !\n", __func__);
- err = -EINVAL;
- goto out;
- }
- hwc->idx = BB_L2CYCLE_CTR_EVENT_IDX;
- hw_bb_l2_pmu.events[BB_L2CYCLE_CTR_EVENT_IDX] = event;
- set_bit(BB_L2CYCLE_CTR_EVENT_IDX,
- (long unsigned int *)&hw_bb_l2_pmu.active_mask);
- goto skip_ctr_loop;
- }
-
- for (ctr = 0; ctr < MAX_BB_L2_CTRS - 1; ctr++) {
- if (!hw_bb_l2_pmu.events[ctr]) {
- hwc->idx = ctr;
- hw_bb_l2_pmu.events[ctr] = event;
- set_bit(ctr, (long unsigned int *)
- &hw_bb_l2_pmu.active_mask);
- break;
- }
- }
-
- if (hwc->idx < 0) {
- err = -ENOSPC;
- pr_err("%s: No space for event: %llx!!\n", __func__,
- event->attr.config);
- goto out;
- }
-
-skip_ctr_loop:
-
- bb_l2_disable_counter(hwc->idx);
-
- hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
-
- if (flags & PERF_EF_START)
- bb_l2_start_counter(event, PERF_EF_RELOAD);
-
- perf_event_update_userpage(event);
-
- pr_debug("%s: event: %ld, ctr: %d added from cpu:%d\n",
- __func__, hwc->config_base, hwc->idx, smp_processor_id());
-out:
- raw_spin_unlock_irqrestore(&hw_bb_l2_pmu.lock, iflags);
-
- /* Resume the PMU even if this event could not be added */
- perf_pmu_enable(event->pmu);
-
- return err;
-}
-
-static void bb_l2_pmu_enable(struct pmu *pmu)
-{
- unsigned long flags;
- isb();
- raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags);
- /* Enable all counters */
- bb_l2_pmnc_write(bb_l2_pmnc_read() | SCORPIONL2_PMNC_E);
- raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags);
-}
-
-static void bb_l2_pmu_disable(struct pmu *pmu)
-{
- unsigned long flags;
- raw_spin_lock_irqsave(&bb_l2_pmu_lock, flags);
- /* Disable all counters */
- bb_l2_pmnc_write(bb_l2_pmnc_read() & ~SCORPIONL2_PMNC_E);
- raw_spin_unlock_irqrestore(&bb_l2_pmu_lock, flags);
- isb();
-}
-
-static inline u32 bb_l2_get_reset_pmovsr(void)
-{
- u32 val;
-
- /* Read */
- asm volatile ("mrc p15, 3, %0, c15, c4, 1" : "=r" (val));
-
- /* Write to clear flags */
- val &= 0xffffffff;
- asm volatile ("mcr p15, 3, %0, c15, c4, 1" : : "r" (val));
-
- return val;
-}
-
-static irqreturn_t bb_l2_handle_irq(int irq_num, void *dev)
-{
- unsigned long pmovsr;
- struct perf_sample_data data;
- struct pt_regs *regs;
- struct perf_event *event;
- struct hw_perf_event *hwc;
- int bitp;
- int idx = 0;
-
- pmovsr = bb_l2_get_reset_pmovsr();
-
- if (!(pmovsr & 0xffffffff))
- return IRQ_NONE;
-
- regs = get_irq_regs();
-
- perf_sample_data_init(&data, 0);
-
- raw_spin_lock(&hw_bb_l2_pmu.lock);
-
- while (pmovsr) {
- bitp = __ffs(pmovsr);
-
- if (bitp == BB_L2CYCLE_CTR_BIT)
- idx = BB_L2CYCLE_CTR_EVENT_IDX;
- else
- idx = bitp;
-
- event = hw_bb_l2_pmu.events[idx];
-
- if (!event)
- goto next;
-
- if (!test_bit(idx, hw_bb_l2_pmu.active_mask))
- goto next;
-
- hwc = &event->hw;
- bb_pmu_event_update(event, hwc, idx, 1);
- data.period = event->hw.last_period;
-
- if (!bb_pmu_event_set_period(event, hwc, idx))
- goto next;
-
- if (perf_event_overflow(event, 0, &data, regs))
- bb_l2_disable_counter(hwc->idx);
-next:
- pmovsr &= (pmovsr - 1);
- }
-
- raw_spin_unlock(&hw_bb_l2_pmu.lock);
-
- irq_work_run();
-
- return IRQ_HANDLED;
-}
-
-static atomic_t active_bb_l2_events = ATOMIC_INIT(0);
-static DEFINE_MUTEX(bb_pmu_reserve_mutex);
-
-static int bb_pmu_reserve_hardware(void)
-{
- int i, err = -ENODEV, irq;
-
- bb_l2_pmu_device = reserve_pmu(ARM_PMU_DEVICE_L2);
-
- if (IS_ERR(bb_l2_pmu_device)) {
- pr_warning("unable to reserve pmu\n");
- return PTR_ERR(bb_l2_pmu_device);
- }
-
- if (bb_l2_pmu_device->num_resources < 1) {
- pr_err("no irqs for PMUs defined\n");
- return -ENODEV;
- }
-
- if (strncmp(bb_l2_pmu_device->name, "l2-arm-pmu", 6)) {
- pr_err("Incorrect pdev reserved !\n");
- return -EINVAL;
- }
-
- for (i = 0; i < bb_l2_pmu_device->num_resources; ++i) {
- irq = platform_get_irq(bb_l2_pmu_device, i);
- if (irq < 0)
- continue;
-
- err = request_irq(irq, bb_l2_handle_irq,
- IRQF_DISABLED | IRQF_NOBALANCING,
- "bb-l2-pmu", NULL);
- if (err) {
- pr_warning("unable to request IRQ%d for Krait L2 perf "
- "counters\n", irq);
- break;
- }
-
- irq_get_chip(irq)->irq_unmask(irq_get_irq_data(irq));
- }
-
- if (err) {
- for (i = i - 1; i >= 0; --i) {
- irq = platform_get_irq(bb_l2_pmu_device, i);
- if (irq >= 0)
- free_irq(irq, NULL);
- }
- release_pmu(bb_l2_pmu_device);
- bb_l2_pmu_device = NULL;
- }
-
- return err;
-}
-
-static void bb_pmu_release_hardware(void)
-{
- int i, irq;
-
- for (i = bb_l2_pmu_device->num_resources - 1; i >= 0; --i) {
- irq = platform_get_irq(bb_l2_pmu_device, i);
- if (irq >= 0)
- free_irq(irq, NULL);
- }
-
- bb_l2_pmu_disable(NULL);
-
- release_pmu(bb_l2_pmu_device);
- bb_l2_pmu_device = NULL;
-}
-
-static void bb_pmu_perf_event_destroy(struct perf_event *event)
-{
- if (atomic_dec_and_mutex_lock
- (&active_bb_l2_events, &bb_pmu_reserve_mutex)) {
- bb_pmu_release_hardware();
- mutex_unlock(&bb_pmu_reserve_mutex);
- }
-}
-
-static int bb_l2_event_init(struct perf_event *event)
-{
- int err = 0;
- struct hw_perf_event *hwc = &event->hw;
- int status = 0;
-
- switch (event->attr.type) {
- case PERF_TYPE_SHARED:
- break;
-
- default:
- return -ENOENT;
- }
-
- hwc->idx = -1;
-
- event->destroy = bb_pmu_perf_event_destroy;
-
- if (!atomic_inc_not_zero(&active_bb_l2_events)) {
- /* 0 active events */
- mutex_lock(&bb_pmu_reserve_mutex);
- err = bb_pmu_reserve_hardware();
- mutex_unlock(&bb_pmu_reserve_mutex);
- if (!err)
- atomic_inc(&active_bb_l2_events);
- else
- return err;
- }
-
- hwc->config = 0;
- hwc->event_base = 0;
-
- /* Check if we came via perf default syms */
- if (event->attr.config == PERF_COUNT_HW_L2_CYCLES)
- hwc->config_base = BB_L2CYCLE_CTR_RAW_CODE;
- else
- hwc->config_base = event->attr.config;
-
- /* Only one CPU can control the cycle counter */
- if (hwc->config_base == BB_L2CYCLE_CTR_RAW_CODE) {
- /* Check if its already running */
- asm volatile ("mrc p15, 3, %0, c15, c4, 6" : "=r" (status));
- if (status == 0x2) {
- err = -ENOSPC;
- goto out;
- }
- }
-
- if (!hwc->sample_period) {
- hwc->sample_period = MAX_BB_L2_PERIOD;
- hwc->last_period = hwc->sample_period;
- local64_set(&hwc->period_left, hwc->sample_period);
- }
-
- pr_debug("%s: event: %lld init'd\n", __func__, event->attr.config);
-
-out:
- if (err < 0)
- bb_pmu_perf_event_destroy(event);
-
- return err;
-}
-
-static struct pmu bb_l2_pmu = {
- .pmu_enable = bb_l2_pmu_enable,
- .pmu_disable = bb_l2_pmu_disable,
- .event_init = bb_l2_event_init,
- .add = bb_l2_add_event,
- .del = bb_l2_del_event,
- .start = bb_l2_start_counter,
- .stop = bb_l2_stop_counter,
- .read = bb_l2_read,
-};
-
-static const struct arm_pmu *__init scorpionmp_l2_pmu_init(void)
-{
- /* Register our own PMU here */
- perf_pmu_register(&bb_l2_pmu, "BB L2", PERF_TYPE_SHARED);
-
- memset(&hw_bb_l2_pmu, 0, sizeof(hw_bb_l2_pmu));
-
- /* Avoid spurious interrupts at startup */
- bb_l2_get_reset_pmovsr();
-
- raw_spin_lock_init(&hw_bb_l2_pmu.lock);
-
- /* Don't return an arm_pmu here */
- return NULL;
-}
-#else
-
-static const struct arm_pmu *__init scorpionmp_l2_pmu_init(void)
-{
- return NULL;
-}
-
-#endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 6034fe9..1896059 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -25,7 +25,8 @@
obj-y += acpuclock.o
obj-$(CONFIG_ARCH_MSM7X27) += acpuclock-7627.o clock-pll.o
obj-$(CONFIG_ARCH_MSM_SCORPION) += pmu.o
-obj-$(CONFIG_ARCH_MSM_KRAIT) += msm-krait-l2-accessors.o pmu.o
+obj-$(CONFIG_ARCH_MSM_SCORPIONMP) += perf_event_msm_l2.o
+obj-$(CONFIG_ARCH_MSM_KRAIT) += msm-krait-l2-accessors.o pmu.o perf_event_msm_krait_l2.o
obj-$(CONFIG_ARCH_MSM7X27A) += pmu.o
ifndef CONFIG_MSM_SMP
diff --git a/arch/arm/mach-msm/acpuclock-7627.c b/arch/arm/mach-msm/acpuclock-7627.c
index 7c2c556..f9ff226 100644
--- a/arch/arm/mach-msm/acpuclock-7627.c
+++ b/arch/arm/mach-msm/acpuclock-7627.c
@@ -18,6 +18,7 @@
#include <linux/version.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/string.h>
@@ -27,6 +28,8 @@
#include <linux/mutex.h>
#include <linux/io.h>
#include <linux/sort.h>
+#include <linux/platform_device.h>
+
#include <mach/board.h>
#include <mach/msm_iomap.h>
#include <mach/socinfo.h>
@@ -402,7 +405,7 @@
#ifdef CONFIG_CPU_FREQ_MSM
static struct cpufreq_frequency_table freq_table[NR_CPUS][20];
-static void __init cpufreq_table_init(void)
+static void __devinit cpufreq_table_init(void)
{
int cpu;
for_each_possible_cpu(cpu) {
@@ -693,7 +696,7 @@
return rc;
}
-static void __init acpuclk_hw_init(void)
+static void __devinit acpuclk_hw_init(void)
{
struct clkctl_acpu_speed *speed;
uint32_t div, sel, reg_clksel;
@@ -775,7 +778,7 @@
* Clock driver initialization
*---------------------------------------------------------------------------*/
#define MHZ 1000000
-static void __init select_freq_plan(void)
+static void __devinit select_freq_plan(void)
{
unsigned long pll_mhz[ACPU_PLL_END];
struct pll_freq_tbl_map *t;
@@ -835,7 +838,7 @@
* Hardware requires the CPU to be dropped to less than MAX_WAIT_FOR_IRQ_KHZ
* before entering a wait for irq low-power mode. Find a suitable rate.
*/
-static unsigned long __init find_wait_for_irq_khz(void)
+static unsigned long __devinit find_wait_for_irq_khz(void)
{
unsigned long found_khz = 0;
int i;
@@ -847,7 +850,7 @@
return found_khz;
}
-static void __init lpj_init(void)
+static void __devinit lpj_init(void)
{
int i = 0, cpu;
const struct clkctl_acpu_speed *base_clk = drv_state.current_speed;
@@ -868,7 +871,7 @@
}
}
-static void __init precompute_stepping(void)
+static void __devinit precompute_stepping(void)
{
int i, step_idx;
@@ -909,7 +912,7 @@
}
}
-static void __init print_acpu_freq_tbl(void)
+static void __devinit print_acpu_freq_tbl(void)
{
struct clkctl_acpu_speed *t;
short down_idx[ACPU_PLL_END];
@@ -947,15 +950,17 @@
.switch_time_us = 50,
};
-static int __init acpuclk_7627_init(struct acpuclk_soc_data *soc_data)
+static int __devinit acpuclk_7627_probe(struct platform_device *pdev)
{
+ const struct acpuclk_pdata *pdata = pdev->dev.platform_data;
+
pr_info("%s()\n", __func__);
drv_state.ebi1_clk = clk_get(NULL, "ebi1_acpu_clk");
BUG_ON(IS_ERR(drv_state.ebi1_clk));
mutex_init(&drv_state.lock);
- drv_state.max_speed_delta_khz = soc_data->max_speed_delta_khz;
+ drv_state.max_speed_delta_khz = pdata->max_speed_delta_khz;
select_freq_plan();
acpuclk_7627_data.wait_for_irq_khz = find_wait_for_irq_khz();
precompute_stepping();
@@ -970,23 +975,16 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_7x27_soc_data __initdata = {
- .max_speed_delta_khz = 400000,
- .init = acpuclk_7627_init,
+static struct platform_driver acpuclk_7627_driver = {
+ .probe = acpuclk_7627_probe,
+ .driver = {
+ .name = "acpuclk-7627",
+ .owner = THIS_MODULE,
+ },
};
-struct acpuclk_soc_data acpuclk_7x27a_soc_data __initdata = {
- .max_speed_delta_khz = 400000,
- .init = acpuclk_7627_init,
-};
-
-struct acpuclk_soc_data acpuclk_7x27aa_soc_data __initdata = {
- .max_speed_delta_khz = 504000,
- .init = acpuclk_7627_init,
-};
-
-struct acpuclk_soc_data acpuclk_8625_soc_data __initdata = {
- /* TODO: Need to update speed delta from H/w Team */
- .max_speed_delta_khz = 604800,
- .init = acpuclk_7627_init,
-};
+static int __init acpuclk_7627_init(void)
+{
+ return platform_driver_register(&acpuclk_7627_driver);
+}
+postcore_initcall(acpuclk_7627_init);
diff --git a/arch/arm/mach-msm/acpuclock-7x30.c b/arch/arm/mach-msm/acpuclock-7x30.c
index 29b0065..b49613e 100644
--- a/arch/arm/mach-msm/acpuclock-7x30.c
+++ b/arch/arm/mach-msm/acpuclock-7x30.c
@@ -16,6 +16,7 @@
#include <linux/version.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/string.h>
@@ -25,6 +26,7 @@
#include <linux/mutex.h>
#include <linux/io.h>
#include <linux/sort.h>
+#include <linux/platform_device.h>
#include <mach/board.h>
#include <mach/msm_iomap.h>
#include <asm/mach-types.h>
@@ -315,7 +317,7 @@
* Clock driver initialization
*---------------------------------------------------------------------------*/
-static void __init acpuclk_hw_init(void)
+static void __devinit acpuclk_hw_init(void)
{
struct clkctl_acpu_speed *s;
uint32_t div, sel, src_num;
@@ -393,7 +395,7 @@
}
/* Initalize the lpj field in the acpu_freq_tbl. */
-static void __init lpj_init(void)
+static void __devinit lpj_init(void)
{
int i;
const struct clkctl_acpu_speed *base_clk = drv_state.current_speed;
@@ -431,7 +433,7 @@
* Truncate the frequency table at the current PLL2 rate and determine the
* backup PLL to use when scaling PLL2.
*/
-void __init pll2_fixup(void)
+void __devinit pll2_fixup(void)
{
struct clkctl_acpu_speed *speed = acpu_freq_tbl;
u8 pll2_l = readl_relaxed(PLL2_L_VAL_ADDR) & 0xFF;
@@ -453,7 +455,7 @@
#define RPM_BYPASS_MASK (1 << 3)
#define PMIC_MODE_MASK (1 << 4)
-static void __init populate_plls(void)
+static void __devinit populate_plls(void)
{
acpuclk_sources[PLL_1] = clk_get_sys("acpu", "pll1_clk");
BUG_ON(IS_ERR(acpuclk_sources[PLL_1]));
@@ -479,7 +481,7 @@
.switch_time_us = 50,
};
-static int __init acpuclk_7x30_init(struct acpuclk_soc_data *soc_data)
+static int __devinit acpuclk_7x30_probe(struct platform_device *pdev)
{
pr_info("%s()\n", __func__);
@@ -494,6 +496,16 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_7x30_soc_data __initdata = {
- .init = acpuclk_7x30_init,
+static struct platform_driver acpuclk_7x30_driver = {
+ .probe = acpuclk_7x30_probe,
+ .driver = {
+ .name = "acpuclk-7x30",
+ .owner = THIS_MODULE,
+ },
};
+
+static int __init acpuclk_7x30_init(void)
+{
+ return platform_driver_register(&acpuclk_7x30_driver);
+}
+postcore_initcall(acpuclk_7x30_init);
diff --git a/arch/arm/mach-msm/acpuclock-8960.c b/arch/arm/mach-msm/acpuclock-8960.c
index 99003f9..d29fee6 100644
--- a/arch/arm/mach-msm/acpuclock-8960.c
+++ b/arch/arm/mach-msm/acpuclock-8960.c
@@ -13,6 +13,7 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -22,6 +23,7 @@
#include <linux/cpufreq.h>
#include <linux/cpu.h>
#include <linux/regulator/consumer.h>
+#include <linux/platform_device.h>
#include <asm/mach-types.h>
#include <asm/cpu.h>
@@ -660,7 +662,7 @@
{ 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(15), 1175000 },
{ 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(15), 1187500 },
{ 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(15), 1187500 },
- { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(15), 1212500 },
+ { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(15), 1200000 },
{ 0, { 0 } }
};
@@ -687,7 +689,7 @@
{ 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(15), 1125000 },
{ 0, { 1404000, HFPLL, 1, 0, 0x34 }, L2(15), 1137500 },
{ 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(15), 1137500 },
- { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(15), 1175000 },
+ { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(15), 1150000 },
{ 0, { 0 } }
};
@@ -1649,7 +1651,7 @@
.wait_for_irq_khz = STBY_KHZ,
};
-static int __init acpuclk_8960_init(struct acpuclk_soc_data *soc_data)
+static int __init acpuclk_8960_probe(struct platform_device *pdev)
{
struct acpu_level *max_acpu_level = select_freq_plan();
@@ -1667,14 +1669,15 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_8960_soc_data __initdata = {
- .init = acpuclk_8960_init,
+static struct platform_driver acpuclk_8960_driver = {
+ .driver = {
+ .name = "acpuclk-8960",
+ .owner = THIS_MODULE,
+ },
};
-struct acpuclk_soc_data acpuclk_8930_soc_data __initdata = {
- .init = acpuclk_8960_init,
-};
-
-struct acpuclk_soc_data acpuclk_8064_soc_data __initdata = {
- .init = acpuclk_8960_init,
-};
+static int __init acpuclk_8960_init(void)
+{
+ return platform_driver_probe(&acpuclk_8960_driver, acpuclk_8960_probe);
+}
+device_initcall(acpuclk_8960_init);
diff --git a/arch/arm/mach-msm/acpuclock-8x50.c b/arch/arm/mach-msm/acpuclock-8x50.c
index cde5a14..996f883 100644
--- a/arch/arm/mach-msm/acpuclock-8x50.c
+++ b/arch/arm/mach-msm/acpuclock-8x50.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2008-2012, Code Aurora Forum. 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 and
@@ -12,6 +12,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -20,6 +21,7 @@
#include <linux/cpufreq.h>
#include <linux/clk.h>
#include <linux/mfd/tps65023.h>
+#include <linux/platform_device.h>
#include <mach/board.h>
#include <mach/msm_iomap.h>
@@ -130,7 +132,7 @@
#ifdef CONFIG_CPU_FREQ_MSM
static struct cpufreq_frequency_table freq_table[20];
-static void __init cpufreq_table_init(void)
+static void __devinit cpufreq_table_init(void)
{
unsigned int i;
unsigned int freq_cnt = 0;
@@ -504,7 +506,7 @@
return rc;
}
-static void __init acpuclk_hw_init(void)
+static void __devinit acpuclk_hw_init(void)
{
struct clkctl_acpu_speed *speed;
uint32_t div, sel, regval;
@@ -582,7 +584,7 @@
#define PLL0_M_VAL_ADDR (MSM_CLK_CTL_BASE + 0x308)
-static void __init acpu_freq_tbl_fixup(void)
+static void __devinit acpu_freq_tbl_fixup(void)
{
void __iomem *ct_csr_base;
uint32_t tcsr_spare2, pll0_m_val;
@@ -645,7 +647,7 @@
}
/* Initalize the lpj field in the acpu_freq_tbl. */
-static void __init lpj_init(void)
+static void __devinit lpj_init(void)
{
int i;
const struct clkctl_acpu_speed *base_clk = drv_state.current_speed;
@@ -657,7 +659,7 @@
}
#ifdef CONFIG_MSM_CPU_AVS
-static int __init acpu_avs_init(int (*set_vdd) (int), int khz)
+static int __devinit acpu_avs_init(int (*set_vdd) (int), int khz)
{
int i;
int freq_count = 0;
@@ -704,7 +706,7 @@
.switch_time_us = 20,
};
-static int __init acpuclk_8x50_init(struct acpuclk_soc_data *soc_data)
+static int __devinit acpuclk_8x50_probe(struct platform_device *pdev)
{
mutex_init(&drv_state.lock);
drv_state.acpu_set_vdd = qsd8x50_tps65023_set_dcdc1;
@@ -736,6 +738,16 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_8x50_soc_data __initdata = {
- .init = acpuclk_8x50_init,
+static struct platform_driver acpuclk_8x50_driver = {
+ .probe = acpuclk_8x50_probe,
+ .driver = {
+ .name = "acpuclk-8x50",
+ .owner = THIS_MODULE,
+ },
};
+
+static int __init acpuclk_8x50_init(void)
+{
+ return platform_driver_register(&acpuclk_8x50_driver);
+}
+postcore_initcall(acpuclk_8x50_init);
diff --git a/arch/arm/mach-msm/acpuclock-8x60.c b/arch/arm/mach-msm/acpuclock-8x60.c
index 48efa18..ef34b3c 100644
--- a/arch/arm/mach-msm/acpuclock-8x60.c
+++ b/arch/arm/mach-msm/acpuclock-8x60.c
@@ -11,6 +11,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -20,6 +21,7 @@
#include <linux/cpufreq.h>
#include <linux/cpu.h>
#include <linux/regulator/consumer.h>
+#include <linux/platform_device.h>
#include <asm/cpu.h>
@@ -734,7 +736,7 @@
}
/* AVS needs SAW_VCTL to be intitialized correctly, before enable,
- * and is not initialized at acpuclk_init().
+ * and is not initialized during probe.
*/
if (reason == SETRATE_CPUFREQ)
AVS_DISABLE(cpu);
@@ -1062,7 +1064,7 @@
.wait_for_irq_khz = MAX_AXI,
};
-static int __init acpuclk_8x60_init(struct acpuclk_soc_data *soc_data)
+static int __init acpuclk_8x60_probe(struct platform_device *pdev)
{
struct clkctl_acpu_speed *max_freq;
int cpu;
@@ -1091,6 +1093,15 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_8x60_soc_data __initdata = {
- .init = acpuclk_8x60_init,
+static struct platform_driver acpuclk_8x60_driver = {
+ .driver = {
+ .name = "acpuclk-8x60",
+ .owner = THIS_MODULE,
+ },
};
+
+static int __init acpuclk_8x60_init(void)
+{
+ return platform_driver_probe(&acpuclk_8x60_driver, acpuclk_8x60_probe);
+}
+device_initcall(acpuclk_8x60_init);
diff --git a/arch/arm/mach-msm/acpuclock-9615.c b/arch/arm/mach-msm/acpuclock-9615.c
index 24b81b9..db7bab3 100644
--- a/arch/arm/mach-msm/acpuclock-9615.c
+++ b/arch/arm/mach-msm/acpuclock-9615.c
@@ -14,6 +14,7 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/delay.h>
@@ -22,6 +23,7 @@
#include <linux/errno.h>
#include <linux/cpufreq.h>
#include <linux/clk.h>
+#include <linux/platform_device.h>
#include <asm/cpu.h>
@@ -306,7 +308,7 @@
.wait_for_irq_khz = 19200,
};
-static int __init acpuclk_9615_init(struct acpuclk_soc_data *soc_data)
+static int __init acpuclk_9615_probe(struct platform_device *pdev)
{
unsigned long max_cpu_khz = 0;
int i;
@@ -351,6 +353,15 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_9615_soc_data __initdata = {
- .init = acpuclk_9615_init,
+static struct platform_driver acpuclk_9615_driver = {
+ .driver = {
+ .name = "acpuclk-9615",
+ .owner = THIS_MODULE,
+ },
};
+
+static int __init acpuclk_9615_init(void)
+{
+ return platform_driver_probe(&acpuclk_9615_driver, acpuclk_9615_probe);
+}
+device_initcall(acpuclk_9615_init);
diff --git a/arch/arm/mach-msm/acpuclock-fsm9xxx.c b/arch/arm/mach-msm/acpuclock-fsm9xxx.c
index 3cdc58d..af1c0eb 100644
--- a/arch/arm/mach-msm/acpuclock-fsm9xxx.c
+++ b/arch/arm/mach-msm/acpuclock-fsm9xxx.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2010-2012, Code Aurora Forum. 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 and
@@ -11,8 +11,10 @@
*
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/platform_device.h>
#include <mach/board.h>
#include "acpuclock.h"
@@ -40,13 +42,22 @@
.get_rate = acpuclk_9xxx_get_rate,
};
-static int __init acpuclk_9xxx_init(struct acpuclk_soc_data *soc_data)
+static int __init acpuclk_9xxx_probe(struct platform_device *pdev)
{
acpuclk_register(&acpuclk_9xxx_data);
pr_info("ACPU running at %lu KHz\n", acpuclk_get_rate(0));
return 0;
}
-struct acpuclk_soc_data acpuclk_9xxx_soc_data __initdata = {
- .init = acpuclk_9xxx_init,
+static struct platform_driver acpuclk_9xxx_driver = {
+ .driver = {
+ .name = "acpuclk-9xxx",
+ .owner = THIS_MODULE,
+ },
};
+
+static int __init acpuclk_9xxx_init(void)
+{
+ return platform_driver_probe(&acpuclk_9xxx_driver, acpuclk_9xxx_probe);
+}
+device_initcall(acpuclk_9xxx_init);
diff --git a/arch/arm/mach-msm/acpuclock.c b/arch/arm/mach-msm/acpuclock.c
index 91071c4..be056e6 100644
--- a/arch/arm/mach-msm/acpuclock.c
+++ b/arch/arm/mach-msm/acpuclock.c
@@ -53,24 +53,7 @@
return rate;
}
-void __init acpuclk_register(struct acpuclk_data *data)
+void __devinit acpuclk_register(struct acpuclk_data *data)
{
acpuclk_data = data;
}
-
-int __init acpuclk_init(struct acpuclk_soc_data *soc_data)
-{
- int rc;
-
- if (!soc_data->init)
- return -EINVAL;
-
- rc = soc_data->init(soc_data);
- if (rc)
- return rc;
-
- if (!acpuclk_data)
- return -ENODEV;
-
- return 0;
-}
diff --git a/arch/arm/mach-msm/acpuclock.h b/arch/arm/mach-msm/acpuclock.h
index c5f0ee3..e73a2af 100644
--- a/arch/arm/mach-msm/acpuclock.h
+++ b/arch/arm/mach-msm/acpuclock.h
@@ -31,12 +31,11 @@
};
/**
- * struct acpuclk_soc_data - SoC data for acpuclk_init()
+ * struct acpuclk_pdata - Platform data for acpuclk
*/
-struct acpuclk_soc_data {
+struct acpuclk_pdata {
unsigned long max_speed_delta_khz;
unsigned int max_axi_khz;
- int (*init)(struct acpuclk_soc_data *);
};
/**
@@ -91,25 +90,4 @@
*/
void acpuclk_register(struct acpuclk_data *data);
-/**
- * acpuclk_init() - acpuclock driver initialization function
- *
- * Return 0 for success.
- */
-int acpuclk_init(struct acpuclk_soc_data *);
-
-/* SoC-specific acpuclock initialization functions. */
-extern struct acpuclk_soc_data acpuclk_7x27_soc_data;
-extern struct acpuclk_soc_data acpuclk_7x27a_soc_data;
-extern struct acpuclk_soc_data acpuclk_7x27aa_soc_data;
-extern struct acpuclk_soc_data acpuclk_7x30_soc_data;
-extern struct acpuclk_soc_data acpuclk_8x50_soc_data;
-extern struct acpuclk_soc_data acpuclk_8x60_soc_data;
-extern struct acpuclk_soc_data acpuclk_8960_soc_data;
-extern struct acpuclk_soc_data acpuclk_9xxx_soc_data;
-extern struct acpuclk_soc_data acpuclk_9615_soc_data;
-extern struct acpuclk_soc_data acpuclk_8930_soc_data;
-extern struct acpuclk_soc_data acpuclk_8064_soc_data;
-extern struct acpuclk_soc_data acpuclk_8625_soc_data;
-
#endif
diff --git a/arch/arm/mach-msm/board-8064-regulator.c b/arch/arm/mach-msm/board-8064-regulator.c
index f7d5403..622b213 100644
--- a/arch/arm/mach-msm/board-8064-regulator.c
+++ b/arch/arm/mach-msm/board-8064-regulator.c
@@ -560,7 +560,7 @@
RPM_SMPS(S2, 0, 1, 0, 1300000, 1300000, NULL, 0, 1p60, NONE, NONE),
RPM_SMPS(S3, 0, 1, 1, 500000, 1150000, NULL, 100000, 4p80, NONE, NONE),
RPM_SMPS(S4, 1, 1, 0, 1800000, 1800000, NULL, 100000, 1p60, AUTO, AUTO),
- RPM_SMPS(S7, 0, 1, 0, 1300000, 1300000, NULL, 100000, 3p20, NONE, NONE),
+ RPM_SMPS(S7, 0, 0, 0, 1300000, 1300000, NULL, 100000, 3p20, NONE, NONE),
RPM_SMPS(S8, 0, 1, 0, 2200000, 2200000, NULL, 0, 1p60, NONE, NONE),
/* ID a_on pd ss min_uV max_uV supply sys_uA init_ip */
@@ -586,7 +586,7 @@
RPM_LDO(L23, 0, 1, 0, 1800000, 1800000, NULL, 0, 0),
RPM_LDO(L24, 0, 1, 1, 750000, 1150000, "8921_s1", 10000, 10000),
RPM_LDO(L25, 1, 1, 0, 1250000, 1250000, "8921_s1", 10000, 10000),
- RPM_LDO(L27, 0, 1, 0, 1100000, 1100000, "8921_s7", 0, 0),
+ RPM_LDO(L27, 0, 0, 0, 1100000, 1100000, "8921_s7", 0, 0),
RPM_LDO(L28, 0, 1, 0, 1050000, 1050000, "8921_s7", 0, 0),
RPM_LDO(L29, 0, 1, 0, 2000000, 2000000, NULL, 0, 0),
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 40b87fc..1d231ef 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -74,7 +74,6 @@
#include "msm_watchdog.h"
#include "board-8064.h"
-#include "acpuclock.h"
#include "spm.h"
#include <mach/mpm.h>
#include "rpm_resources.h"
@@ -2110,6 +2109,7 @@
};
static struct platform_device *common_devices[] __initdata = {
+ &msm8960_device_acpuclk,
&apq8064_device_dmov,
&apq8064_device_qup_spi_gsbi5,
&apq8064_device_ext_5v_vreg,
@@ -2911,7 +2911,6 @@
ARRAY_SIZE(apq8064_slim_devices));
apq8064_init_dsps();
msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data));
- acpuclk_init(&acpuclk_8064_soc_data);
msm_spm_l2_init(msm_spm_l2_data);
BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
msm_pm_init_sleep_status_data(&msm_pm_slp_sts_data);
diff --git a/arch/arm/mach-msm/board-8930-storage.c b/arch/arm/mach-msm/board-8930-storage.c
index 65da578..5e51c5a 100644
--- a/arch/arm/mach-msm/board-8930-storage.c
+++ b/arch/arm/mach-msm/board-8930-storage.c
@@ -220,7 +220,7 @@
#define MSM_MPM_PIN_SDC3_DAT1 21
static unsigned int sdc1_sup_clk_rates[] = {
- 400000, 24000000, 48000000,
+ 400000, 24000000, 48000000, 96000000
};
#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
@@ -288,6 +288,16 @@
void __init msm8930_init_mmc(void)
{
#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
+ /*
+ * When eMMC runs in DDR mode on CDP platform, we have
+ * seen instability due to DATA CRC errors. These errors are
+ * attributed to long physical path between MSM and eMMC on CDP.
+ * So let's not enable the DDR mode on CDP platform but let other
+ * platforms take advantage of eMMC DDR mode.
+ */
+ if (!machine_is_msm8930_cdp())
+ msm8960_sdc1_data.uhs_caps |= (MMC_CAP_1_8V_DDR |
+ MMC_CAP_UHS_DDR50);
/* SDC1 : eMMC card connected */
msm_add_sdcc(1, &msm8960_sdc1_data);
#endif
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index 6c4cbd3..e075630 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -86,7 +86,6 @@
#include <mach/cpuidle.h>
#include "rpm_resources.h"
#include <mach/mpm.h>
-#include "acpuclock.h"
#include "smd_private.h"
#include "pm-boot.h"
#include "msm_watchdog.h"
@@ -2035,6 +2034,7 @@
};
static struct platform_device *common_devices[] __initdata = {
+ &msm8960_device_acpuclk,
&msm8960_device_dmov,
&msm_device_smd,
&msm8960_device_uart_gsbi5,
@@ -2433,7 +2433,6 @@
msm8930_init_cam();
#endif
msm8930_init_mmc();
- acpuclk_init(&acpuclk_8930_soc_data);
mxt_init_vkeys_8930();
register_i2c_devices();
msm8930_init_fb();
diff --git a/arch/arm/mach-msm/board-8960-storage.c b/arch/arm/mach-msm/board-8960-storage.c
index e674e91..4b09f82 100644
--- a/arch/arm/mach-msm/board-8960-storage.c
+++ b/arch/arm/mach-msm/board-8960-storage.c
@@ -271,7 +271,7 @@
#define MSM_MPM_PIN_SDC3_DAT1 21
static unsigned int sdc1_sup_clk_rates[] = {
- 400000, 24000000, 48000000
+ 400000, 24000000, 48000000, 96000000
};
#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
@@ -361,6 +361,16 @@
void __init msm8960_init_mmc(void)
{
#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
+ /*
+ * When eMMC runs in DDR mode on CDP platform, we have
+ * seen instability due to DATA CRC errors. These errors are
+ * attributed to long physical path between MSM and eMMC on CDP.
+ * So let's not enable the DDR mode on CDP platform but let other
+ * platforms take advantage of eMMC DDR mode.
+ */
+ if (!machine_is_msm8960_cdp())
+ msm8960_sdc1_data.uhs_caps |= (MMC_CAP_1_8V_DDR |
+ MMC_CAP_UHS_DDR50);
/* SDC1 : eMMC card connected */
msm_add_sdcc(1, &msm8960_sdc1_data);
#endif
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index 8f49afd..22ef940 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -97,7 +97,6 @@
#include <mach/cpuidle.h>
#include "rpm_resources.h"
#include <mach/mpm.h>
-#include "acpuclock.h"
#include "smd_private.h"
#include "pm-boot.h"
#include "msm_watchdog.h"
@@ -2508,6 +2507,7 @@
#endif
static struct platform_device *common_devices[] __initdata = {
+ &msm8960_device_acpuclk,
&msm8960_device_dmov,
&msm_device_smd,
&msm_device_uart_dm6,
@@ -3060,7 +3060,6 @@
platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
msm8960_pm8921_gpio_mpp_init();
platform_add_devices(sim_devices, ARRAY_SIZE(sim_devices));
- acpuclk_init(&acpuclk_8960_soc_data);
msm8960_device_qup_spi_gsbi1.dev.platform_data =
&msm8960_qup_spi_gsbi1_pdata;
@@ -3173,7 +3172,6 @@
msm8960_init_cam();
#endif
msm8960_init_mmc();
- acpuclk_init(&acpuclk_8960_soc_data);
if (machine_is_msm8960_liquid())
mxt_init_hw_liquid();
register_i2c_devices();
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index dc376b5..1089d61 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -50,7 +50,6 @@
#include "devices.h"
#include "board-9615.h"
#include "pm.h"
-#include "acpuclock.h"
#include "pm-boot.h"
#include <mach/gpiomux.h>
@@ -850,6 +849,7 @@
};
static struct platform_device *common_devices[] = {
+ &msm9615_device_acpuclk,
&msm9615_device_dmov,
&msm_device_smd,
#ifdef CONFIG_LTC4088_CHARGER
@@ -979,7 +979,6 @@
msm_device_usb_bam.dev.platform_data = &msm_usb_bam_pdata;
platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
msm9615_pm8xxx_gpio_mpp_init();
- acpuclk_init(&acpuclk_9615_soc_data);
/* Ensure ar6000pm device is registered before MMC/SDC */
msm9615_init_ar6000pm();
diff --git a/arch/arm/mach-msm/board-fsm9xxx.c b/arch/arm/mach-msm/board-fsm9xxx.c
index 6ad3cef..b071353 100644
--- a/arch/arm/mach-msm/board-fsm9xxx.c
+++ b/arch/arm/mach-msm/board-fsm9xxx.c
@@ -38,7 +38,6 @@
#include <mach/socinfo.h>
#include "devices.h"
#include "timer.h"
-#include "acpuclock.h"
#include "pm.h"
#include "spm.h"
#include <linux/regulator/consumer.h>
@@ -804,11 +803,17 @@
},
};
+static struct platform_device fsm9xxx_device_acpuclk = {
+ .name = "acpuclk-9xxx",
+ .id = -1,
+};
+
/*
* Devices
*/
static struct platform_device *devices[] __initdata = {
+ &fsm9xxx_device_acpuclk,
&msm_device_smd,
&msm_device_dmov,
&msm_device_nand,
@@ -873,8 +878,6 @@
static void __init fsm9xxx_init(void)
{
- acpuclk_init(&acpuclk_9xxx_soc_data);
-
regulator_has_full_constraints();
#if defined(CONFIG_I2C_SSBI) || defined(CONFIG_MSM_SSBI)
diff --git a/arch/arm/mach-msm/board-msm7x27.c b/arch/arm/mach-msm/board-msm7x27.c
index d42458f..a7fed3e 100644
--- a/arch/arm/mach-msm/board-msm7x27.c
+++ b/arch/arm/mach-msm/board-msm7x27.c
@@ -65,7 +65,6 @@
#include "board-msm7627-regulator.h"
#include "devices.h"
#include "clock.h"
-#include "acpuclock.h"
#include "msm-keypad-devices.h"
#include "pm.h"
#include "pm-boot.h"
@@ -1775,7 +1774,7 @@
}
}
#endif
- acpuclk_init(&acpuclk_7x27_soc_data);
+ platform_device_register(&msm7x27_device_acpuclk);
usb_mpp_init();
diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c
index 1521a6c..dc473e6 100644
--- a/arch/arm/mach-msm/board-msm7x27a.c
+++ b/arch/arm/mach-msm/board-msm7x27a.c
@@ -162,7 +162,7 @@
#define MSM_PMEM_MDP_SIZE 0x2300000
#define MSM7x25A_MSM_PMEM_MDP_SIZE 0x1500000
-#define MSM_PMEM_ADSP_SIZE 0x1100000
+#define MSM_PMEM_ADSP_SIZE 0x1200000
#define MSM7x25A_MSM_PMEM_ADSP_SIZE 0xB91000
#endif
diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c
index 010d9ec..2834f24 100644
--- a/arch/arm/mach-msm/board-msm7x30.c
+++ b/arch/arm/mach-msm/board-msm7x30.c
@@ -105,7 +105,7 @@
*/
#define MSM_V4L2_VIDEO_OVERLAY_BUF_SIZE 2764800
-#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
+#ifdef CONFIG_FB_MSM_HDMI_ADV7520_PANEL
#define MSM_FB_EXT_BUF_SIZE (1280 * 720 * 2 * 1) /* 2 bpp x 1 page */
#else
#define MSM_FB_EXT_BUF_SIZE 0
@@ -6956,7 +6956,7 @@
msm7x30_init_uart2();
#endif
msm_spm_init(&msm_spm_data, 1);
- acpuclk_init(&acpuclk_7x30_soc_data);
+ platform_device_register(&msm7x30_device_acpuclk);
if (machine_is_msm7x30_surf() || machine_is_msm7x30_fluid())
msm7x30_cfg_smsc911x();
diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c
index 54c8fbd..098ad6e 100644
--- a/arch/arm/mach-msm/board-msm8x60.c
+++ b/arch/arm/mach-msm/board-msm8x60.c
@@ -100,7 +100,6 @@
#include "peripheral-loader.h"
#include <linux/platform_data/qcom_crypto_device.h>
#include "rpm_resources.h"
-#include "acpuclock.h"
#include "pm-boot.h"
#include "board-storage-common-a.h"
@@ -5135,6 +5134,7 @@
};
static struct platform_device *surf_devices[] __initdata = {
+ &msm8x60_device_acpuclk,
&msm_device_smd,
&msm_device_uart_dm12,
&msm_pil_q6v3,
@@ -10339,9 +10339,6 @@
*/
msm8x60_init_buses();
platform_add_devices(early_devices, ARRAY_SIZE(early_devices));
- /* CPU frequency control is not supported on simulated targets. */
- if (!machine_is_msm8x60_rumi3() && !machine_is_msm8x60_sim())
- acpuclk_init(&acpuclk_8x60_soc_data);
/*
* Enable EBI2 only for boards which make use of it. Leave
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index d8d8934..9c80c8b 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -130,7 +130,7 @@
#ifdef CONFIG_ARCH_MSM7X27A
#define MSM_PMEM_MDP_SIZE 0x2300000
-#define MSM_PMEM_ADSP_SIZE 0x1100000
+#define MSM_PMEM_ADSP_SIZE 0x1200000
#endif
static struct android_usb_platform_data android_usb_pdata = {
diff --git a/arch/arm/mach-msm/board-qsd8x50.c b/arch/arm/mach-msm/board-qsd8x50.c
index 6a39316..4df4266 100644
--- a/arch/arm/mach-msm/board-qsd8x50.c
+++ b/arch/arm/mach-msm/board-qsd8x50.c
@@ -2409,7 +2409,7 @@
{
msm_clock_init(&qds8x50_clock_init_data);
qsd8x50_cfg_smc91x();
- acpuclk_init(&acpuclk_8x50_soc_data);
+ platform_device_register(&msm8x50_device_acpuclk);
msm_hsusb_pdata.swfi_latency =
msm_pm_data
diff --git a/arch/arm/mach-msm/clock-copper.c b/arch/arm/mach-msm/clock-copper.c
index 99443a5..5727c34 100644
--- a/arch/arm/mach-msm/clock-copper.c
+++ b/arch/arm/mach-msm/clock-copper.c
@@ -3716,7 +3716,8 @@
static struct branch_clk mmss_mmssnoc_axi_clk = {
.cbcr_reg = MMSS_MMSSNOC_AXI_CBCR,
.parent = &axi_clk_src.c,
- .has_sibling = 1,
+ /* The bus driver needs set_rate to go through to the parent */
+ .has_sibling = 0,
.base = &virt_bases[MMSS_BASE],
.c = {
.dbg_name = "mmss_mmssnoc_axi_clk",
@@ -4837,8 +4838,8 @@
CLK_LOOKUP("ocmem_a_clk", ocmemgx_msmbus_a_clk.c, "msm_bus"),
CLK_LOOKUP("bus_clk", ocmemnoc_clk.c, "msm_ocmem_noc"),
CLK_LOOKUP("bus_a_clk", ocmemnoc_clk.c, "msm_ocmem_noc"),
- CLK_LOOKUP("bus_clk", axi_clk_src.c, "msm_mmss_noc"),
- CLK_LOOKUP("bus_a_clk", axi_clk_src.c, "msm_mmss_noc"),
+ CLK_LOOKUP("bus_clk", mmss_mmssnoc_axi_clk.c, "msm_mmss_noc"),
+ CLK_LOOKUP("bus_a_clk", mmss_mmssnoc_axi_clk.c, "msm_mmss_noc"),
};
static struct pll_config_regs gpll0_regs __initdata = {
diff --git a/arch/arm/mach-msm/clock-local2.c b/arch/arm/mach-msm/clock-local2.c
index 467d5d1..9fe9591 100644
--- a/arch/arm/mach-msm/clock-local2.c
+++ b/arch/arm/mach-msm/clock-local2.c
@@ -408,6 +408,19 @@
return -EPERM;
}
+static long branch_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ struct branch_clk *branch = to_branch_clk(c);
+
+ if (branch->max_div)
+ return rate <= (branch->max_div) ? rate : -EPERM;
+
+ if (!branch->has_sibling)
+ return clk_round_rate(branch->parent, rate);
+
+ return -EPERM;
+}
+
static unsigned long branch_clk_get_rate(struct clk *c)
{
struct branch_clk *branch = to_branch_clk(c);
@@ -580,6 +593,7 @@
.set_rate = branch_clk_set_rate,
.get_rate = branch_clk_get_rate,
.list_rate = branch_clk_list_rate,
+ .round_rate = branch_clk_round_rate,
.reset = branch_clk_reset,
.get_parent = branch_clk_get_parent,
.handoff = branch_clk_handoff,
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 6a9f7f0..550a283 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -202,6 +202,11 @@
},
};
+struct platform_device msm8960_device_acpuclk = {
+ .name = "acpuclk-8960",
+ .id = -1,
+};
+
#define SHARED_IMEM_TZ_BASE 0x2a03f720
static struct resource tzlog_resources[] = {
{
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index f13c266..06d8653 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -107,6 +107,11 @@
},
};
+struct platform_device msm9615_device_acpuclk = {
+ .name = "acpuclk-9615",
+ .id = -1,
+};
+
#define MSM_USB_BAM_BASE 0x12502000
#define MSM_USB_BAM_SIZE SZ_16K
#define MSM_HSIC_BAM_BASE 0x12542000
diff --git a/arch/arm/mach-msm/devices-msm7x27.c b/arch/arm/mach-msm/devices-msm7x27.c
index ffd10fa..4619cca 100644
--- a/arch/arm/mach-msm/devices-msm7x27.c
+++ b/arch/arm/mach-msm/devices-msm7x27.c
@@ -27,6 +27,7 @@
#include "devices.h"
#include "footswitch.h"
+#include "acpuclock.h"
#include <asm/mach/flash.h>
@@ -431,6 +432,16 @@
msm_pm_set_irq_extns(&msm7x27_pm_irq_calls);
}
+static struct acpuclk_pdata msm7x27_acpuclk_pdata = {
+ .max_speed_delta_khz = 400000,
+};
+
+struct platform_device msm7x27_device_acpuclk = {
+ .name = "acpuclk-7627",
+ .id = -1,
+ .dev.platform_data = &msm7x27_acpuclk_pdata,
+};
+
#define MSM_SDC1_BASE 0xA0400000
#define MSM_SDC2_BASE 0xA0500000
#define MSM_SDC3_BASE 0xA0600000
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index adc9169..b3454cd 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -211,6 +211,37 @@
},
};
+static struct acpuclk_pdata msm7x27a_acpuclk_pdata = {
+ .max_speed_delta_khz = 400000,
+};
+
+struct platform_device msm7x27a_device_acpuclk = {
+ .name = "acpuclk-7627",
+ .id = -1,
+ .dev.platform_data = &msm7x27a_acpuclk_pdata,
+};
+
+static struct acpuclk_pdata msm7x27aa_acpuclk_pdata = {
+ .max_speed_delta_khz = 504000,
+};
+
+struct platform_device msm7x27aa_device_acpuclk = {
+ .name = "acpuclk-7627",
+ .id = -1,
+ .dev.platform_data = &msm7x27aa_acpuclk_pdata,
+};
+
+static struct acpuclk_pdata msm8625_acpuclk_pdata = {
+ /* TODO: Need to update speed delta from H/w Team */
+ .max_speed_delta_khz = 604800,
+};
+
+struct platform_device msm8625_device_acpuclk = {
+ .name = "acpuclk-7627",
+ .id = -1,
+ .dev.platform_data = &msm8625_acpuclk_pdata,
+};
+
struct platform_device msm_device_smd = {
.name = "msm_smd",
.id = -1,
@@ -1623,16 +1654,15 @@
msm_clock_init(&msm7x27a_clock_init_data);
if (cpu_is_msm7x27aa() || cpu_is_msm7x25ab())
- acpuclk_init(&acpuclk_7x27aa_soc_data);
+ platform_device_register(&msm7x27aa_device_acpuclk);
else if (cpu_is_msm8625()) {
if (msm8625_cpu_id() == MSM8625)
- acpuclk_init(&acpuclk_7x27aa_soc_data);
+ platform_device_register(&msm7x27aa_device_acpuclk);
else if (msm8625_cpu_id() == MSM8625A)
- acpuclk_init(&acpuclk_8625_soc_data);
- } else {
- acpuclk_init(&acpuclk_7x27a_soc_data);
- }
-
+ platform_device_register(&msm8625_device_acpuclk);
+ } else {
+ platform_device_register(&msm7x27a_device_acpuclk);
+ }
return 0;
}
diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c
index de70429..ff747e2 100644
--- a/arch/arm/mach-msm/devices-msm7x30.c
+++ b/arch/arm/mach-msm/devices-msm7x30.c
@@ -42,6 +42,11 @@
#include "pm.h"
#include "irq.h"
+struct platform_device msm7x30_device_acpuclk = {
+ .name = "acpuclk-7x30",
+ .id = -1,
+};
+
/* EBI THERMAL DRIVER */
static struct resource msm_ebi0_thermal_resources[] = {
{
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index 37844c1..d8bf054 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -163,6 +163,11 @@
},
};
+struct platform_device msm8x60_device_acpuclk = {
+ .name = "acpuclk-8x60",
+ .id = -1,
+};
+
#ifdef CONFIG_MSM_DSPS
#define GSBI12_DEV (&msm_dsps_device.dev)
#else
diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c
index ee8a2cf..2ecc852 100644
--- a/arch/arm/mach-msm/devices-qsd8x50.c
+++ b/arch/arm/mach-msm/devices-qsd8x50.c
@@ -34,6 +34,11 @@
#include <mach/rpc_hsusb.h>
#include "pm.h"
+struct platform_device msm8x50_device_acpuclk = {
+ .name = "acpuclk-8x50",
+ .id = -1,
+};
+
static struct resource resources_uart1[] = {
{
.start = INT_UART1,
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 17cce7b..ea47727 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -412,3 +412,13 @@
extern struct platform_device mdm_sglte_device;
extern struct platform_device apq_device_tz_log;
+
+extern struct platform_device msm7x27_device_acpuclk;
+extern struct platform_device msm7x27a_device_acpuclk;
+extern struct platform_device msm7x27aa_device_acpuclk;
+extern struct platform_device msm7x30_device_acpuclk;
+extern struct platform_device msm8625_device_acpuclk;
+extern struct platform_device msm8x50_device_acpuclk;
+extern struct platform_device msm8x60_device_acpuclk;
+extern struct platform_device msm8960_device_acpuclk;
+extern struct platform_device msm9615_device_acpuclk;
diff --git a/arch/arm/mach-msm/perf_event_msm_krait_l2.c b/arch/arm/mach-msm/perf_event_msm_krait_l2.c
new file mode 100644
index 0000000..d82f4dd
--- /dev/null
+++ b/arch/arm/mach-msm/perf_event_msm_krait_l2.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2011, 2012 Code Aurora Forum. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/irq.h>
+#include <asm/pmu.h>
+#include <linux/platform_device.h>
+
+#include <mach/msm-krait-l2-accessors.h>
+
+#define MAX_L2_PERIOD ((1ULL << 32) - 1)
+#define MAX_KRAIT_L2_CTRS 5
+
+#define L2PMCCNTR 0x409
+#define L2PMCCNTCR 0x408
+#define L2PMCCNTSR 0x40A
+#define L2CYCLE_CTR_BIT 31
+#define L2CYCLE_CTR_EVENT_IDX 4
+#define L2CYCLE_CTR_RAW_CODE 0xfe
+
+#define L2PMOVSR 0x406
+
+#define L2PMCR 0x400
+#define L2PMCR_RESET_ALL 0x6
+#define L2PMCR_GLOBAL_ENABLE 0x1
+#define L2PMCR_GLOBAL_DISABLE 0x0
+
+#define L2PMCNTENSET 0x403
+#define L2PMCNTENCLR 0x402
+
+#define L2PMINTENSET 0x405
+#define L2PMINTENCLR 0x404
+
+#define IA_L2PMXEVCNTCR_BASE 0x420
+#define IA_L2PMXEVTYPER_BASE 0x424
+#define IA_L2PMRESX_BASE 0x410
+#define IA_L2PMXEVFILTER_BASE 0x423
+#define IA_L2PMXEVCNTR_BASE 0x421
+
+/* event format is -e rsRCCG See get_event_desc() */
+
+#define EVENT_REG_MASK 0xf000
+#define EVENT_GROUPSEL_MASK 0x000f
+#define EVENT_GROUPCODE_MASK 0x0ff0
+#define EVENT_REG_SHIFT 12
+#define EVENT_GROUPCODE_SHIFT 4
+
+#define RESRX_VALUE_EN 0x80000000
+
+static u32 l2_orig_filter_prefix = 0x000f0030;
+
+static u32 pmu_type;
+
+static struct arm_pmu krait_l2_pmu;
+
+static struct perf_event *l2_events[MAX_KRAIT_L2_CTRS];
+static unsigned long l2_used_mask[BITS_TO_LONGS(MAX_KRAIT_L2_CTRS)];
+
+static struct pmu_hw_events krait_l2_pmu_hw_events = {
+ .events = l2_events,
+ .used_mask = l2_used_mask,
+ .pmu_lock = __RAW_SPIN_LOCK_UNLOCKED(krait_l2_pmu_hw_events.pmu_lock),
+};
+
+struct event_desc {
+ int event_groupsel;
+ int event_reg;
+ int event_group_code;
+};
+
+static struct pmu_hw_events *krait_l2_get_hw_events(void)
+{
+ return &krait_l2_pmu_hw_events;
+}
+
+void get_event_desc(u64 config, struct event_desc *evdesc)
+{
+ /* L2PMEVCNTRX */
+ evdesc->event_reg = (config & EVENT_REG_MASK) >> EVENT_REG_SHIFT;
+ /* Group code (row ) */
+ evdesc->event_group_code =
+ (config & EVENT_GROUPCODE_MASK) >> EVENT_GROUPCODE_SHIFT;
+ /* Group sel (col) */
+ evdesc->event_groupsel = (config & EVENT_GROUPSEL_MASK);
+
+ pr_debug("%s: reg: %x, group_code: %x, groupsel: %x\n", __func__,
+ evdesc->event_reg, evdesc->event_group_code,
+ evdesc->event_groupsel);
+}
+
+static void set_evcntcr(int ctr)
+{
+ u32 evtcr_reg = (ctr * 16) + IA_L2PMXEVCNTCR_BASE;
+
+ set_l2_indirect_reg(evtcr_reg, 0x0);
+}
+
+static void set_evtyper(int event_groupsel, int event_reg, int ctr)
+{
+ u32 evtype_reg = (ctr * 16) + IA_L2PMXEVTYPER_BASE;
+ u32 evtype_val = event_groupsel + (4 * event_reg);
+
+ set_l2_indirect_reg(evtype_reg, evtype_val);
+}
+
+static void set_evres(int event_groupsel, int event_reg, int event_group_code)
+{
+ u32 group_reg = event_reg + IA_L2PMRESX_BASE;
+ u32 group_val =
+ RESRX_VALUE_EN | (event_group_code << (8 * event_groupsel));
+ u32 resr_val;
+ u32 group_byte = 0xff;
+ u32 group_mask = ~(group_byte << (8 * event_groupsel));
+
+ resr_val = get_l2_indirect_reg(group_reg);
+ resr_val &= group_mask;
+ resr_val |= group_val;
+
+ set_l2_indirect_reg(group_reg, resr_val);
+}
+
+static void set_evfilter_task_mode(int ctr)
+{
+ u32 filter_reg = (ctr * 16) + IA_L2PMXEVFILTER_BASE;
+ u32 filter_val = l2_orig_filter_prefix | 1 << smp_processor_id();
+
+ set_l2_indirect_reg(filter_reg, filter_val);
+}
+
+static void set_evfilter_sys_mode(int ctr)
+{
+ u32 filter_reg = (ctr * 16) + IA_L2PMXEVFILTER_BASE;
+ u32 filter_val = l2_orig_filter_prefix | 0xf;
+
+ set_l2_indirect_reg(filter_reg, filter_val);
+}
+
+static void enable_intenset(u32 idx)
+{
+ if (idx == L2CYCLE_CTR_EVENT_IDX)
+ set_l2_indirect_reg(L2PMINTENSET, 1 << L2CYCLE_CTR_BIT);
+ else
+ set_l2_indirect_reg(L2PMINTENSET, 1 << idx);
+}
+
+static void disable_intenclr(u32 idx)
+{
+ if (idx == L2CYCLE_CTR_EVENT_IDX)
+ set_l2_indirect_reg(L2PMINTENCLR, 1 << L2CYCLE_CTR_BIT);
+ else
+ set_l2_indirect_reg(L2PMINTENCLR, 1 << idx);
+}
+
+static void enable_counter(u32 idx)
+{
+ if (idx == L2CYCLE_CTR_EVENT_IDX)
+ set_l2_indirect_reg(L2PMCNTENSET, 1 << L2CYCLE_CTR_BIT);
+ else
+ set_l2_indirect_reg(L2PMCNTENSET, 1 << idx);
+}
+
+static void disable_counter(u32 idx)
+{
+ if (idx == L2CYCLE_CTR_EVENT_IDX)
+ set_l2_indirect_reg(L2PMCNTENCLR, 1 << L2CYCLE_CTR_BIT);
+ else
+ set_l2_indirect_reg(L2PMCNTENCLR, 1 << idx);
+}
+
+static u32 krait_l2_read_counter(int idx)
+{
+ u32 val;
+ u32 counter_reg = (idx * 16) + IA_L2PMXEVCNTR_BASE;
+
+ if (idx == L2CYCLE_CTR_EVENT_IDX)
+ val = get_l2_indirect_reg(L2PMCCNTR);
+ else
+ val = get_l2_indirect_reg(counter_reg);
+
+ return val;
+}
+
+static void krait_l2_write_counter(int idx, u32 val)
+{
+ u32 counter_reg = (idx * 16) + IA_L2PMXEVCNTR_BASE;
+
+ if (idx == L2CYCLE_CTR_EVENT_IDX)
+ set_l2_indirect_reg(L2PMCCNTR, val);
+ else
+ set_l2_indirect_reg(counter_reg, val);
+}
+
+static void krait_l2_stop_counter(struct hw_perf_event *hwc, int idx)
+{
+ disable_intenclr(idx);
+ disable_counter(idx);
+
+ pr_debug("%s: event: %ld ctr: %d stopped\n", __func__,
+ hwc->config_base, idx);
+}
+
+static void krait_l2_enable(struct hw_perf_event *hwc, int idx, int cpu)
+{
+ struct event_desc evdesc;
+ unsigned long iflags;
+
+ raw_spin_lock_irqsave(&krait_l2_pmu_hw_events.pmu_lock, iflags);
+
+ if (hwc->config_base == L2CYCLE_CTR_RAW_CODE)
+ goto out;
+
+ set_evcntcr(idx);
+
+ memset(&evdesc, 0, sizeof(evdesc));
+
+ get_event_desc(hwc->config_base, &evdesc);
+
+ set_evtyper(evdesc.event_groupsel, evdesc.event_reg, idx);
+
+ set_evres(evdesc.event_groupsel, evdesc.event_reg,
+ evdesc.event_group_code);
+
+ if (cpu < 0)
+ set_evfilter_task_mode(idx);
+ else
+ set_evfilter_sys_mode(idx);
+
+out:
+ enable_intenset(idx);
+ enable_counter(idx);
+
+ raw_spin_unlock_irqrestore(&krait_l2_pmu_hw_events.pmu_lock, iflags);
+
+ pr_debug("%s: ctr: %d group: %ld group_code: %lld started from cpu:%d\n",
+ __func__, idx, hwc->config_base, hwc->config, smp_processor_id());
+}
+
+static void krait_l2_disable(struct hw_perf_event *hwc, int idx)
+{
+ unsigned long iflags;
+
+ raw_spin_lock_irqsave(&krait_l2_pmu_hw_events.pmu_lock, iflags);
+
+ krait_l2_stop_counter(hwc, idx);
+
+ raw_spin_unlock_irqrestore(&krait_l2_pmu_hw_events.pmu_lock, iflags);
+
+ pr_debug("%s: event: %ld deleted\n", __func__, hwc->config_base);
+
+}
+
+static int krait_l2_get_event_idx(struct pmu_hw_events *cpuc,
+ struct hw_perf_event *hwc)
+{
+ int ctr = 0;
+
+ if (hwc->config_base == L2CYCLE_CTR_RAW_CODE) {
+ if (!test_and_set_bit(L2CYCLE_CTR_EVENT_IDX, cpuc->used_mask))
+ return L2CYCLE_CTR_EVENT_IDX;
+ }
+
+ for (ctr = 0; ctr < MAX_KRAIT_L2_CTRS - 1; ctr++) {
+ if (!test_and_set_bit(ctr, cpuc->used_mask))
+ return ctr;
+ }
+
+ return -EAGAIN;
+}
+
+static void krait_l2_start(void)
+{
+ isb();
+ set_l2_indirect_reg(L2PMCR, L2PMCR_GLOBAL_ENABLE);
+}
+
+static void krait_l2_stop(void)
+{
+ set_l2_indirect_reg(L2PMCR, L2PMCR_GLOBAL_DISABLE);
+ isb();
+}
+
+u32 get_reset_pmovsr(void)
+{
+ int val;
+
+ val = get_l2_indirect_reg(L2PMOVSR);
+ /* reset it */
+ val &= 0xffffffff;
+ set_l2_indirect_reg(L2PMOVSR, val);
+
+ return val;
+}
+
+static irqreturn_t krait_l2_handle_irq(int irq_num, void *dev)
+{
+ unsigned long pmovsr;
+ struct perf_sample_data data;
+ struct pt_regs *regs;
+ struct perf_event *event;
+ struct hw_perf_event *hwc;
+ int bitp;
+ int idx = 0;
+
+ pmovsr = get_reset_pmovsr();
+
+ if (!(pmovsr & 0xffffffff))
+ return IRQ_NONE;
+
+ regs = get_irq_regs();
+
+ perf_sample_data_init(&data, 0);
+
+ while (pmovsr) {
+ bitp = __ffs(pmovsr);
+
+ if (bitp == L2CYCLE_CTR_BIT)
+ idx = L2CYCLE_CTR_EVENT_IDX;
+ else
+ idx = bitp;
+
+ event = krait_l2_pmu_hw_events.events[idx];
+
+ if (!event)
+ goto next;
+
+ if (!test_bit(idx, krait_l2_pmu_hw_events.used_mask))
+ goto next;
+
+ hwc = &event->hw;
+
+ armpmu_event_update(event, hwc, idx);
+
+ data.period = event->hw.last_period;
+
+ if (!armpmu_event_set_period(event, hwc, idx))
+ goto next;
+
+ if (perf_event_overflow(event, &data, regs))
+ disable_counter(hwc->idx);
+next:
+ pmovsr &= (pmovsr - 1);
+ }
+
+ irq_work_run();
+
+ return IRQ_HANDLED;
+}
+
+static int krait_l2_map_event(struct perf_event *event)
+{
+ if (pmu_type > 0 && pmu_type == event->attr.type)
+ return event->attr.config & 0xfffff;
+ else
+ return -ENOENT;
+}
+
+static int
+krait_l2_pmu_generic_request_irq(int irq, irq_handler_t *handle_irq)
+{
+ return request_irq(irq, *handle_irq,
+ IRQF_DISABLED | IRQF_NOBALANCING,
+ "krait-l2-armpmu", NULL);
+}
+
+static void
+krait_l2_pmu_generic_free_irq(int irq)
+{
+ if (irq >= 0)
+ free_irq(irq, NULL);
+}
+
+static struct arm_pmu krait_l2_pmu = {
+ .id = ARM_PERF_PMU_ID_KRAIT_L2,
+ .type = ARM_PMU_DEVICE_L2CC,
+ .name = "Krait L2CC PMU",
+ .start = krait_l2_start,
+ .stop = krait_l2_stop,
+ .handle_irq = krait_l2_handle_irq,
+ .request_pmu_irq = krait_l2_pmu_generic_request_irq,
+ .free_pmu_irq = krait_l2_pmu_generic_free_irq,
+ .enable = krait_l2_enable,
+ .disable = krait_l2_disable,
+ .get_event_idx = krait_l2_get_event_idx,
+ .read_counter = krait_l2_read_counter,
+ .write_counter = krait_l2_write_counter,
+ .map_event = krait_l2_map_event,
+ .max_period = (1LLU << 32) - 1,
+ .get_hw_events = krait_l2_get_hw_events,
+ .num_events = MAX_KRAIT_L2_CTRS,
+};
+
+static int __devinit krait_l2_pmu_device_probe(struct platform_device *pdev)
+{
+ krait_l2_pmu.plat_device = pdev;
+
+ if (!armpmu_register(&krait_l2_pmu, "krait-l2", -1))
+ pmu_type = krait_l2_pmu.pmu.type;
+
+ return 0;
+}
+
+static struct platform_driver krait_l2_pmu_driver = {
+ .driver = {
+ .name = "l2-arm-pmu",
+ },
+ .probe = krait_l2_pmu_device_probe,
+};
+
+static int __init register_krait_l2_pmu_driver(void)
+{
+ /* Reset all ctrs */
+ set_l2_indirect_reg(L2PMCR, L2PMCR_RESET_ALL);
+
+ /* Avoid spurious interrupt if any */
+ get_reset_pmovsr();
+
+ return platform_driver_register(&krait_l2_pmu_driver);
+}
+device_initcall(register_krait_l2_pmu_driver);
diff --git a/arch/arm/mach-msm/perf_event_msm_l2.c b/arch/arm/mach-msm/perf_event_msm_l2.c
new file mode 100644
index 0000000..3310d92
--- /dev/null
+++ b/arch/arm/mach-msm/perf_event_msm_l2.c
@@ -0,0 +1,760 @@
+/*
+ * Copyright (c) 2011, 2012 Code Aurora Forum. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/irq.h>
+#include <asm/pmu.h>
+#include <linux/platform_device.h>
+
+
+#define MAX_SCORPION_L2_CTRS 5
+#define SCORPION_L2CYCLE_CTR_BIT 31
+#define SCORPION_L2CYCLE_CTR_EVENT_IDX 4
+#define SCORPION_L2CYCLE_CTR_RAW_CODE 0xfe
+#define SCORPIONL2_PMNC_E (1 << 0) /* Enable all counters */
+#define SCORPION_L2_EVT_PREFIX 3
+#define SCORPION_MAX_L2_REG 4
+
+static u32 pmu_type;
+
+static struct arm_pmu scorpion_l2_pmu;
+
+static struct perf_event *l2_events[MAX_SCORPION_L2_CTRS];
+static unsigned long l2_used_mask[BITS_TO_LONGS(MAX_SCORPION_L2_CTRS)];
+
+static struct pmu_hw_events scorpion_l2_pmu_hw_events = {
+ .events = l2_events,
+ .used_mask = l2_used_mask,
+ .pmu_lock =
+ __RAW_SPIN_LOCK_UNLOCKED(scorpion_l2_pmu_hw_events.pmu_lock),
+};
+
+struct scorpion_l2_scorp_evt {
+ u32 evt_type;
+ u32 val;
+ u8 grp;
+ u32 evt_type_act;
+};
+
+enum scorpion_perf_types {
+ SCORPIONL2_TOTAL_BANK_REQ = 0x90,
+ SCORPIONL2_DSIDE_READ = 0x91,
+ SCORPIONL2_DSIDE_WRITE = 0x92,
+ SCORPIONL2_ISIDE_READ = 0x93,
+ SCORPIONL2_L2CACHE_ISIDE_READ = 0x94,
+ SCORPIONL2_L2CACHE_BANK_REQ = 0x95,
+ SCORPIONL2_L2CACHE_DSIDE_READ = 0x96,
+ SCORPIONL2_L2CACHE_DSIDE_WRITE = 0x97,
+ SCORPIONL2_L2NOCACHE_DSIDE_WRITE = 0x98,
+ SCORPIONL2_L2NOCACHE_ISIDE_READ = 0x99,
+ SCORPIONL2_L2NOCACHE_TOTAL_REQ = 0x9a,
+ SCORPIONL2_L2NOCACHE_DSIDE_READ = 0x9b,
+ SCORPIONL2_DSIDE_READ_NOL1 = 0x9c,
+ SCORPIONL2_L2CACHE_WRITETHROUGH = 0x9d,
+ SCORPIONL2_BARRIERS = 0x9e,
+ SCORPIONL2_HARDWARE_TABLE_WALKS = 0x9f,
+ SCORPIONL2_MVA_POC = 0xa0,
+ SCORPIONL2_L2CACHE_HW_TABLE_WALKS = 0xa1,
+ SCORPIONL2_SETWAY_CACHE_OPS = 0xa2,
+ SCORPIONL2_DSIDE_WRITE_HITS = 0xa3,
+ SCORPIONL2_ISIDE_READ_HITS = 0xa4,
+ SCORPIONL2_CACHE_DSIDE_READ_NOL1 = 0xa5,
+ SCORPIONL2_TOTAL_CACHE_HITS = 0xa6,
+ SCORPIONL2_CACHE_MATCH_MISS = 0xa7,
+ SCORPIONL2_DREAD_HIT_L1_DATA = 0xa8,
+ SCORPIONL2_L2LINE_LOCKED = 0xa9,
+ SCORPIONL2_HW_TABLE_WALK_HIT = 0xaa,
+ SCORPIONL2_CACHE_MVA_POC = 0xab,
+ SCORPIONL2_L2ALLOC_DWRITE_MISS = 0xac,
+ SCORPIONL2_CORRECTED_TAG_ARRAY = 0xad,
+ SCORPIONL2_CORRECTED_DATA_ARRAY = 0xae,
+ SCORPIONL2_CORRECTED_REPLACEMENT_ARRAY = 0xaf,
+ SCORPIONL2_PMBUS_MPAAF = 0xb0,
+ SCORPIONL2_PMBUS_MPWDAF = 0xb1,
+ SCORPIONL2_PMBUS_MPBRT = 0xb2,
+ SCORPIONL2_CPU0_GRANT = 0xb3,
+ SCORPIONL2_CPU1_GRANT = 0xb4,
+ SCORPIONL2_CPU0_NOGRANT = 0xb5,
+ SCORPIONL2_CPU1_NOGRANT = 0xb6,
+ SCORPIONL2_CPU0_LOSING_ARB = 0xb7,
+ SCORPIONL2_CPU1_LOSING_ARB = 0xb8,
+ SCORPIONL2_SLAVEPORT_NOGRANT = 0xb9,
+ SCORPIONL2_SLAVEPORT_BPQ_FULL = 0xba,
+ SCORPIONL2_SLAVEPORT_LOSING_ARB = 0xbb,
+ SCORPIONL2_SLAVEPORT_GRANT = 0xbc,
+ SCORPIONL2_SLAVEPORT_GRANTLOCK = 0xbd,
+ SCORPIONL2_L2EM_STREX_PASS = 0xbe,
+ SCORPIONL2_L2EM_STREX_FAIL = 0xbf,
+ SCORPIONL2_LDREX_RESERVE_L2EM = 0xc0,
+ SCORPIONL2_SLAVEPORT_LDREX = 0xc1,
+ SCORPIONL2_CPU0_L2EM_CLEARED = 0xc2,
+ SCORPIONL2_CPU1_L2EM_CLEARED = 0xc3,
+ SCORPIONL2_SLAVEPORT_L2EM_CLEARED = 0xc4,
+ SCORPIONL2_CPU0_CLAMPED = 0xc5,
+ SCORPIONL2_CPU1_CLAMPED = 0xc6,
+ SCORPIONL2_CPU0_WAIT = 0xc7,
+ SCORPIONL2_CPU1_WAIT = 0xc8,
+ SCORPIONL2_CPU0_NONAMBAS_WAIT = 0xc9,
+ SCORPIONL2_CPU1_NONAMBAS_WAIT = 0xca,
+ SCORPIONL2_CPU0_DSB_WAIT = 0xcb,
+ SCORPIONL2_CPU1_DSB_WAIT = 0xcc,
+ SCORPIONL2_AXI_READ = 0xcd,
+ SCORPIONL2_AXI_WRITE = 0xce,
+
+ SCORPIONL2_1BEAT_WRITE = 0xcf,
+ SCORPIONL2_2BEAT_WRITE = 0xd0,
+ SCORPIONL2_4BEAT_WRITE = 0xd1,
+ SCORPIONL2_8BEAT_WRITE = 0xd2,
+ SCORPIONL2_12BEAT_WRITE = 0xd3,
+ SCORPIONL2_16BEAT_WRITE = 0xd4,
+ SCORPIONL2_1BEAT_DSIDE_READ = 0xd5,
+ SCORPIONL2_2BEAT_DSIDE_READ = 0xd6,
+ SCORPIONL2_4BEAT_DSIDE_READ = 0xd7,
+ SCORPIONL2_8BEAT_DSIDE_READ = 0xd8,
+ SCORPIONL2_CSYS_READ_1BEAT = 0xd9,
+ SCORPIONL2_CSYS_READ_2BEAT = 0xda,
+ SCORPIONL2_CSYS_READ_4BEAT = 0xdb,
+ SCORPIONL2_CSYS_READ_8BEAT = 0xdc,
+ SCORPIONL2_4BEAT_IFETCH_READ = 0xdd,
+ SCORPIONL2_8BEAT_IFETCH_READ = 0xde,
+ SCORPIONL2_CSYS_WRITE_1BEAT = 0xdf,
+ SCORPIONL2_CSYS_WRITE_2BEAT = 0xe0,
+ SCORPIONL2_AXI_READ_DATA_BEAT = 0xe1,
+ SCORPIONL2_AXI_WRITE_EVT1 = 0xe2,
+ SCORPIONL2_AXI_WRITE_EVT2 = 0xe3,
+ SCORPIONL2_LDREX_REQ = 0xe4,
+ SCORPIONL2_STREX_PASS = 0xe5,
+ SCORPIONL2_STREX_FAIL = 0xe6,
+ SCORPIONL2_CPREAD = 0xe7,
+ SCORPIONL2_CPWRITE = 0xe8,
+ SCORPIONL2_BARRIER_REQ = 0xe9,
+ SCORPIONL2_AXI_READ_SLVPORT = 0xea,
+ SCORPIONL2_AXI_WRITE_SLVPORT = 0xeb,
+ SCORPIONL2_AXI_READ_SLVPORT_DATABEAT = 0xec,
+ SCORPIONL2_AXI_WRITE_SLVPORT_DATABEAT = 0xed,
+ SCORPIONL2_SNOOPKILL_PREFILTER = 0xee,
+ SCORPIONL2_SNOOPKILL_FILTEROUT = 0xef,
+ SCORPIONL2_SNOOPED_IC = 0xf0,
+ SCORPIONL2_SNOOPED_BP = 0xf1,
+ SCORPIONL2_SNOOPED_BARRIERS = 0xf2,
+ SCORPIONL2_SNOOPED_TLB = 0xf3,
+ SCORPION_L2_MAX_EVT,
+};
+
+static const struct scorpion_l2_scorp_evt sc_evt[] = {
+ {SCORPIONL2_TOTAL_BANK_REQ, 0x80000001, 0, 0x00},
+ {SCORPIONL2_DSIDE_READ, 0x80000100, 0, 0x01},
+ {SCORPIONL2_DSIDE_WRITE, 0x80010000, 0, 0x02},
+ {SCORPIONL2_ISIDE_READ, 0x81000000, 0, 0x03},
+ {SCORPIONL2_L2CACHE_ISIDE_READ, 0x80000002, 0, 0x00},
+ {SCORPIONL2_L2CACHE_BANK_REQ, 0x80000200, 0, 0x01},
+ {SCORPIONL2_L2CACHE_DSIDE_READ, 0x80020000, 0, 0x02},
+ {SCORPIONL2_L2CACHE_DSIDE_WRITE, 0x82000000, 0, 0x03},
+ {SCORPIONL2_L2NOCACHE_DSIDE_WRITE, 0x80000003, 0, 0x00},
+ {SCORPIONL2_L2NOCACHE_ISIDE_READ, 0x80000300, 0, 0x01},
+ {SCORPIONL2_L2NOCACHE_TOTAL_REQ, 0x80030000, 0, 0x02},
+ {SCORPIONL2_L2NOCACHE_DSIDE_READ, 0x83000000, 0, 0x03},
+ {SCORPIONL2_DSIDE_READ_NOL1, 0x80000004, 0, 0x00},
+ {SCORPIONL2_L2CACHE_WRITETHROUGH, 0x80000400, 0, 0x01},
+ {SCORPIONL2_BARRIERS, 0x84000000, 0, 0x03},
+ {SCORPIONL2_HARDWARE_TABLE_WALKS, 0x80000005, 0, 0x00},
+ {SCORPIONL2_MVA_POC, 0x80000500, 0, 0x01},
+ {SCORPIONL2_L2CACHE_HW_TABLE_WALKS, 0x80050000, 0, 0x02},
+ {SCORPIONL2_SETWAY_CACHE_OPS, 0x85000000, 0, 0x03},
+ {SCORPIONL2_DSIDE_WRITE_HITS, 0x80000006, 0, 0x00},
+ {SCORPIONL2_ISIDE_READ_HITS, 0x80000600, 0, 0x01},
+ {SCORPIONL2_CACHE_DSIDE_READ_NOL1, 0x80060000, 0, 0x02},
+ {SCORPIONL2_TOTAL_CACHE_HITS, 0x86000000, 0, 0x03},
+ {SCORPIONL2_CACHE_MATCH_MISS, 0x80000007, 0, 0x00},
+ {SCORPIONL2_DREAD_HIT_L1_DATA, 0x87000000, 0, 0x03},
+ {SCORPIONL2_L2LINE_LOCKED, 0x80000008, 0, 0x00},
+ {SCORPIONL2_HW_TABLE_WALK_HIT, 0x80000800, 0, 0x01},
+ {SCORPIONL2_CACHE_MVA_POC, 0x80080000, 0, 0x02},
+ {SCORPIONL2_L2ALLOC_DWRITE_MISS, 0x88000000, 0, 0x03},
+ {SCORPIONL2_CORRECTED_TAG_ARRAY, 0x80001A00, 0, 0x01},
+ {SCORPIONL2_CORRECTED_DATA_ARRAY, 0x801A0000, 0, 0x02},
+ {SCORPIONL2_CORRECTED_REPLACEMENT_ARRAY, 0x9A000000, 0, 0x03},
+ {SCORPIONL2_PMBUS_MPAAF, 0x80001C00, 0, 0x01},
+ {SCORPIONL2_PMBUS_MPWDAF, 0x801C0000, 0, 0x02},
+ {SCORPIONL2_PMBUS_MPBRT, 0x9C000000, 0, 0x03},
+
+ {SCORPIONL2_CPU0_GRANT, 0x80000001, 1, 0x04},
+ {SCORPIONL2_CPU1_GRANT, 0x80000100, 1, 0x05},
+ {SCORPIONL2_CPU0_NOGRANT, 0x80020000, 1, 0x06},
+ {SCORPIONL2_CPU1_NOGRANT, 0x82000000, 1, 0x07},
+ {SCORPIONL2_CPU0_LOSING_ARB, 0x80040000, 1, 0x06},
+ {SCORPIONL2_CPU1_LOSING_ARB, 0x84000000, 1, 0x07},
+ {SCORPIONL2_SLAVEPORT_NOGRANT, 0x80000007, 1, 0x04},
+ {SCORPIONL2_SLAVEPORT_BPQ_FULL, 0x80000700, 1, 0x05},
+ {SCORPIONL2_SLAVEPORT_LOSING_ARB, 0x80070000, 1, 0x06},
+ {SCORPIONL2_SLAVEPORT_GRANT, 0x87000000, 1, 0x07},
+ {SCORPIONL2_SLAVEPORT_GRANTLOCK, 0x80000008, 1, 0x04},
+ {SCORPIONL2_L2EM_STREX_PASS, 0x80000009, 1, 0x04},
+ {SCORPIONL2_L2EM_STREX_FAIL, 0x80000900, 1, 0x05},
+ {SCORPIONL2_LDREX_RESERVE_L2EM, 0x80090000, 1, 0x06},
+ {SCORPIONL2_SLAVEPORT_LDREX, 0x89000000, 1, 0x07},
+ {SCORPIONL2_CPU0_L2EM_CLEARED, 0x800A0000, 1, 0x06},
+ {SCORPIONL2_CPU1_L2EM_CLEARED, 0x8A000000, 1, 0x07},
+ {SCORPIONL2_SLAVEPORT_L2EM_CLEARED, 0x80000B00, 1, 0x05},
+ {SCORPIONL2_CPU0_CLAMPED, 0x8000000E, 1, 0x04},
+ {SCORPIONL2_CPU1_CLAMPED, 0x80000E00, 1, 0x05},
+ {SCORPIONL2_CPU0_WAIT, 0x800F0000, 1, 0x06},
+ {SCORPIONL2_CPU1_WAIT, 0x8F000000, 1, 0x07},
+ {SCORPIONL2_CPU0_NONAMBAS_WAIT, 0x80000010, 1, 0x04},
+ {SCORPIONL2_CPU1_NONAMBAS_WAIT, 0x80001000, 1, 0x05},
+ {SCORPIONL2_CPU0_DSB_WAIT, 0x80000014, 1, 0x04},
+ {SCORPIONL2_CPU1_DSB_WAIT, 0x80001400, 1, 0x05},
+
+ {SCORPIONL2_AXI_READ, 0x80000001, 2, 0x08},
+ {SCORPIONL2_AXI_WRITE, 0x80000100, 2, 0x09},
+ {SCORPIONL2_1BEAT_WRITE, 0x80010000, 2, 0x0a},
+ {SCORPIONL2_2BEAT_WRITE, 0x80010000, 2, 0x0b},
+ {SCORPIONL2_4BEAT_WRITE, 0x80000002, 2, 0x08},
+ {SCORPIONL2_8BEAT_WRITE, 0x80000200, 2, 0x09},
+ {SCORPIONL2_12BEAT_WRITE, 0x80020000, 2, 0x0a},
+ {SCORPIONL2_16BEAT_WRITE, 0x82000000, 2, 0x0b},
+ {SCORPIONL2_1BEAT_DSIDE_READ, 0x80000003, 2, 0x08},
+ {SCORPIONL2_2BEAT_DSIDE_READ, 0x80000300, 2, 0x09},
+ {SCORPIONL2_4BEAT_DSIDE_READ, 0x80030000, 2, 0x0a},
+ {SCORPIONL2_8BEAT_DSIDE_READ, 0x83000000, 2, 0x0b},
+ {SCORPIONL2_CSYS_READ_1BEAT, 0x80000004, 2, 0x08},
+ {SCORPIONL2_CSYS_READ_2BEAT, 0x80000400, 2, 0x09},
+ {SCORPIONL2_CSYS_READ_4BEAT, 0x80040000, 2, 0x0a},
+ {SCORPIONL2_CSYS_READ_8BEAT, 0x84000000, 2, 0x0b},
+ {SCORPIONL2_4BEAT_IFETCH_READ, 0x80000005, 2, 0x08},
+ {SCORPIONL2_8BEAT_IFETCH_READ, 0x80000500, 2, 0x09},
+ {SCORPIONL2_CSYS_WRITE_1BEAT, 0x80050000, 2, 0x0a},
+ {SCORPIONL2_CSYS_WRITE_2BEAT, 0x85000000, 2, 0x0b},
+ {SCORPIONL2_AXI_READ_DATA_BEAT, 0x80000600, 2, 0x09},
+ {SCORPIONL2_AXI_WRITE_EVT1, 0x80060000, 2, 0x0a},
+ {SCORPIONL2_AXI_WRITE_EVT2, 0x86000000, 2, 0x0b},
+ {SCORPIONL2_LDREX_REQ, 0x80000007, 2, 0x08},
+ {SCORPIONL2_STREX_PASS, 0x80000700, 2, 0x09},
+ {SCORPIONL2_STREX_FAIL, 0x80070000, 2, 0x0a},
+ {SCORPIONL2_CPREAD, 0x80000008, 2, 0x08},
+ {SCORPIONL2_CPWRITE, 0x80000800, 2, 0x09},
+ {SCORPIONL2_BARRIER_REQ, 0x88000000, 2, 0x0b},
+
+ {SCORPIONL2_AXI_READ_SLVPORT, 0x80000001, 3, 0x0c},
+ {SCORPIONL2_AXI_WRITE_SLVPORT, 0x80000100, 3, 0x0d},
+ {SCORPIONL2_AXI_READ_SLVPORT_DATABEAT, 0x80010000, 3, 0x0e},
+ {SCORPIONL2_AXI_WRITE_SLVPORT_DATABEAT, 0x81000000, 3, 0x0f},
+
+ {SCORPIONL2_SNOOPKILL_PREFILTER, 0x80000001, 4, 0x10},
+ {SCORPIONL2_SNOOPKILL_FILTEROUT, 0x80000100, 4, 0x11},
+ {SCORPIONL2_SNOOPED_IC, 0x80000002, 4, 0x10},
+ {SCORPIONL2_SNOOPED_BP, 0x80000200, 4, 0x11},
+ {SCORPIONL2_SNOOPED_BARRIERS, 0x80020000, 4, 0x12},
+ {SCORPIONL2_SNOOPED_TLB, 0x82000000, 4, 0x13},
+};
+
+static struct pmu_hw_events *scorpion_l2_get_hw_events(void)
+{
+ return &scorpion_l2_pmu_hw_events;
+}
+static u32 scorpion_l2_read_l2pm0(void)
+{
+ u32 val;
+ asm volatile ("mrc p15, 3, %0, c15, c7, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_l2_write_l2pm0(u32 val)
+{
+ asm volatile ("mcr p15, 3, %0, c15, c7, 0" : : "r" (val));
+}
+
+static u32 scorpion_l2_read_l2pm1(void)
+{
+ u32 val;
+ asm volatile ("mrc p15, 3, %0, c15, c7, 1" : "=r" (val));
+ return val;
+}
+
+static void scorpion_l2_write_l2pm1(u32 val)
+{
+ asm volatile ("mcr p15, 3, %0, c15, c7, 1" : : "r" (val));
+}
+
+static u32 scorpion_l2_read_l2pm2(void)
+{
+ u32 val;
+ asm volatile ("mrc p15, 3, %0, c15, c7, 2" : "=r" (val));
+ return val;
+}
+
+static void scorpion_l2_write_l2pm2(u32 val)
+{
+ asm volatile ("mcr p15, 3, %0, c15, c7, 2" : : "r" (val));
+}
+
+static u32 scorpion_l2_read_l2pm3(void)
+{
+ u32 val;
+ asm volatile ("mrc p15, 3, %0, c15, c7, 3" : "=r" (val));
+ return val;
+}
+
+static void scorpion_l2_write_l2pm3(u32 val)
+{
+ asm volatile ("mcr p15, 3, %0, c15, c7, 3" : : "r" (val));
+}
+
+static u32 scorpion_l2_read_l2pm4(void)
+{
+ u32 val;
+ asm volatile ("mrc p15, 3, %0, c15, c7, 4" : "=r" (val));
+ return val;
+}
+
+static void scorpion_l2_write_l2pm4(u32 val)
+{
+ asm volatile ("mcr p15, 3, %0, c15, c7, 4" : : "r" (val));
+}
+
+struct scorpion_scorpion_access_funcs {
+ u32(*read) (void);
+ void (*write) (u32);
+ void (*pre) (void);
+ void (*post) (void);
+};
+
+struct scorpion_scorpion_access_funcs scorpion_l2_func[] = {
+ {scorpion_l2_read_l2pm0, scorpion_l2_write_l2pm0, NULL, NULL},
+ {scorpion_l2_read_l2pm1, scorpion_l2_write_l2pm1, NULL, NULL},
+ {scorpion_l2_read_l2pm2, scorpion_l2_write_l2pm2, NULL, NULL},
+ {scorpion_l2_read_l2pm3, scorpion_l2_write_l2pm3, NULL, NULL},
+ {scorpion_l2_read_l2pm4, scorpion_l2_write_l2pm4, NULL, NULL},
+};
+
+#define COLMN0MASK 0x000000ff
+#define COLMN1MASK 0x0000ff00
+#define COLMN2MASK 0x00ff0000
+
+static u32 scorpion_l2_get_columnmask(u32 setval)
+{
+ if (setval & COLMN0MASK)
+ return 0xffffff00;
+ else if (setval & COLMN1MASK)
+ return 0xffff00ff;
+ else if (setval & COLMN2MASK)
+ return 0xff00ffff;
+ else
+ return 0x80ffffff;
+}
+
+static void scorpion_l2_evt_setup(u32 gr, u32 setval)
+{
+ u32 val;
+ if (scorpion_l2_func[gr].pre)
+ scorpion_l2_func[gr].pre();
+ val = scorpion_l2_get_columnmask(setval) & scorpion_l2_func[gr].read();
+ val = val | setval;
+ scorpion_l2_func[gr].write(val);
+ if (scorpion_l2_func[gr].post)
+ scorpion_l2_func[gr].post();
+}
+
+#define SCORPION_L2_EVT_START_IDX 0x90
+#define SCORPION_L2_INV_EVTYPE 0
+
+static unsigned int get_scorpion_l2_evtinfo(unsigned int evt_type,
+ struct scorpion_l2_scorp_evt *evtinfo)
+{
+ u32 idx;
+ u8 prefix;
+ u8 reg;
+ u8 code;
+ u8 group;
+
+ prefix = (evt_type & 0xF0000) >> 16;
+ if (prefix == SCORPION_L2_EVT_PREFIX) {
+ reg = (evt_type & 0x0F000) >> 12;
+ code = (evt_type & 0x00FF0) >> 4;
+ group = evt_type & 0x0000F;
+
+ if ((group > 3) || (reg > SCORPION_MAX_L2_REG))
+ return SCORPION_L2_INV_EVTYPE;
+
+ evtinfo->val = 0x80000000 | (code << (group * 8));
+ evtinfo->grp = reg;
+ evtinfo->evt_type_act = group | (reg << 2);
+ return evtinfo->evt_type_act;
+ }
+
+ if (evt_type < SCORPION_L2_EVT_START_IDX
+ || evt_type >= SCORPION_L2_MAX_EVT)
+ return SCORPION_L2_INV_EVTYPE;
+
+ idx = evt_type - SCORPION_L2_EVT_START_IDX;
+
+ if (sc_evt[idx].evt_type == evt_type) {
+ evtinfo->val = sc_evt[idx].val;
+ evtinfo->grp = sc_evt[idx].grp;
+ evtinfo->evt_type_act = sc_evt[idx].evt_type_act;
+ return sc_evt[idx].evt_type_act;
+ }
+ return SCORPION_L2_INV_EVTYPE;
+}
+
+static inline void scorpion_l2_pmnc_write(unsigned long val)
+{
+ val &= 0xff;
+ asm volatile ("mcr p15, 3, %0, c15, c4, 0" : : "r" (val));
+}
+
+static inline unsigned long scorpion_l2_pmnc_read(void)
+{
+ u32 val;
+ asm volatile ("mrc p15, 3, %0, c15, c4, 0" : "=r" (val));
+ return val;
+}
+
+static void scorpion_l2_set_evcntcr(void)
+{
+ u32 val = 0x0;
+ asm volatile ("mcr p15, 3, %0, c15, c6, 4" : : "r" (val));
+}
+
+static inline void scorpion_l2_set_evtyper(int ctr, int val)
+{
+ /* select ctr */
+ asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (ctr));
+
+ /* write into EVTYPER */
+ asm volatile ("mcr p15, 3, %0, c15, c6, 7" : : "r" (val));
+}
+
+static void scorpion_l2_set_evfilter_task_mode(void)
+{
+ u32 filter_val = 0x000f0030 | 1 << smp_processor_id();
+
+ asm volatile ("mcr p15, 3, %0, c15, c6, 3" : : "r" (filter_val));
+}
+
+static void scorpion_l2_set_evfilter_sys_mode(void)
+{
+ u32 filter_val = 0x000f003f;
+
+ asm volatile ("mcr p15, 3, %0, c15, c6, 3" : : "r" (filter_val));
+}
+
+static void scorpion_l2_enable_intenset(u32 idx)
+{
+ if (idx == SCORPION_L2CYCLE_CTR_EVENT_IDX) {
+ asm volatile ("mcr p15, 3, %0, c15, c5, 1" : : "r"
+ (1 << SCORPION_L2CYCLE_CTR_BIT));
+ } else {
+ asm volatile ("mcr p15, 3, %0, c15, c5, 1" : : "r" (1 << idx));
+ }
+}
+
+static void scorpion_l2_disable_intenclr(u32 idx)
+{
+ if (idx == SCORPION_L2CYCLE_CTR_EVENT_IDX) {
+ asm volatile ("mcr p15, 3, %0, c15, c5, 0" : : "r"
+ (1 << SCORPION_L2CYCLE_CTR_BIT));
+ } else {
+ asm volatile ("mcr p15, 3, %0, c15, c5, 0" : : "r" (1 << idx));
+ }
+}
+
+static void scorpion_l2_enable_counter(u32 idx)
+{
+ if (idx == SCORPION_L2CYCLE_CTR_EVENT_IDX) {
+ asm volatile ("mcr p15, 3, %0, c15, c4, 3" : : "r"
+ (1 << SCORPION_L2CYCLE_CTR_BIT));
+ } else {
+ asm volatile ("mcr p15, 3, %0, c15, c4, 3" : : "r" (1 << idx));
+ }
+}
+
+static void scorpion_l2_disable_counter(u32 idx)
+{
+ if (idx == SCORPION_L2CYCLE_CTR_EVENT_IDX) {
+ asm volatile ("mcr p15, 3, %0, c15, c4, 2" : : "r"
+ (1 << SCORPION_L2CYCLE_CTR_BIT));
+ } else {
+ asm volatile ("mcr p15, 3, %0, c15, c4, 2" : : "r" (1 << idx));
+ }
+}
+
+static u32 scorpion_l2_read_counter(int idx)
+{
+ u32 val;
+ unsigned long iflags;
+
+ if (idx == SCORPION_L2CYCLE_CTR_EVENT_IDX) {
+ asm volatile ("mrc p15, 3, %0, c15, c4, 5" : "=r" (val));
+ } else {
+ raw_spin_lock_irqsave(&scorpion_l2_pmu_hw_events.pmu_lock,
+ iflags);
+ asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (idx));
+
+ /* read val from counter */
+ asm volatile ("mrc p15, 3, %0, c15, c6, 5" : "=r" (val));
+ raw_spin_unlock_irqrestore(&scorpion_l2_pmu_hw_events.pmu_lock,
+ iflags);
+ }
+
+ return val;
+}
+
+static void scorpion_l2_write_counter(int idx, u32 val)
+{
+ unsigned long iflags;
+
+ if (idx == SCORPION_L2CYCLE_CTR_EVENT_IDX) {
+ asm volatile ("mcr p15, 3, %0, c15, c4, 5" : : "r" (val));
+ } else {
+ raw_spin_lock_irqsave(&scorpion_l2_pmu_hw_events.pmu_lock,
+ iflags);
+
+ /* select counter */
+ asm volatile ("mcr p15, 3, %0, c15, c6, 0" : : "r" (idx));
+
+ /* write val into counter */
+ asm volatile ("mcr p15, 3, %0, c15, c6, 5" : : "r" (val));
+ raw_spin_unlock_irqrestore(&scorpion_l2_pmu_hw_events.pmu_lock,
+ iflags);
+ }
+}
+
+static void scorpion_l2_stop_counter(struct hw_perf_event *hwc, int idx)
+{
+ scorpion_l2_disable_intenclr(idx);
+ scorpion_l2_disable_counter(idx);
+ pr_debug("%s: event: %ld ctr: %d stopped\n", __func__,
+ hwc->config_base, idx);
+}
+
+static void scorpion_l2_enable(struct hw_perf_event *hwc, int idx, int cpu)
+{
+ struct scorpion_l2_scorp_evt evtinfo;
+ int evtype = hwc->config_base;
+ int ev_typer;
+ unsigned long iflags;
+
+ raw_spin_lock_irqsave(&scorpion_l2_pmu_hw_events.pmu_lock, iflags);
+
+ if (hwc->config_base == SCORPION_L2CYCLE_CTR_RAW_CODE)
+ goto out;
+
+ memset(&evtinfo, 0, sizeof(evtinfo));
+
+ ev_typer = get_scorpion_l2_evtinfo(evtype, &evtinfo);
+
+ scorpion_l2_set_evtyper(idx, ev_typer);
+
+ scorpion_l2_set_evcntcr();
+
+ if (cpu < 0)
+ scorpion_l2_set_evfilter_task_mode();
+ else
+ scorpion_l2_set_evfilter_sys_mode();
+
+ scorpion_l2_evt_setup(evtinfo.grp, evtinfo.val);
+
+out:
+
+ scorpion_l2_enable_intenset(idx);
+
+ scorpion_l2_enable_counter(idx);
+
+ raw_spin_unlock_irqrestore(&scorpion_l2_pmu_hw_events.pmu_lock, iflags);
+
+ pr_debug("%s: ctr: %d group: %ld group_code: %lld started from cpu:%d\n",
+ __func__, idx, hwc->config_base, hwc->config, smp_processor_id());
+}
+
+static void scorpion_l2_disable(struct hw_perf_event *hwc, int idx)
+{
+ unsigned long iflags;
+
+ raw_spin_lock_irqsave(&scorpion_l2_pmu_hw_events.pmu_lock, iflags);
+
+ scorpion_l2_stop_counter(hwc, idx);
+
+ raw_spin_unlock_irqrestore(&scorpion_l2_pmu_hw_events.pmu_lock, iflags);
+
+ pr_debug("%s: event: %ld deleted\n", __func__, hwc->config_base);
+}
+
+static int scorpion_l2_get_event_idx(struct pmu_hw_events *cpuc,
+ struct hw_perf_event *hwc)
+{
+ int ctr = 0;
+
+ if (hwc->config_base == SCORPION_L2CYCLE_CTR_RAW_CODE) {
+ if (!test_and_set_bit(SCORPION_L2CYCLE_CTR_EVENT_IDX,
+ cpuc->used_mask))
+ return SCORPION_L2CYCLE_CTR_EVENT_IDX;
+ }
+
+ for (ctr = 0; ctr < MAX_SCORPION_L2_CTRS - 1; ctr++) {
+ if (!test_and_set_bit(ctr, cpuc->used_mask))
+ return ctr;
+ }
+
+ return -EAGAIN;
+}
+
+static void scorpion_l2_start(void)
+{
+ isb();
+ /* Enable all counters */
+ scorpion_l2_pmnc_write(scorpion_l2_pmnc_read() | SCORPIONL2_PMNC_E);
+}
+
+static void scorpion_l2_stop(void)
+{
+ /* Disable all counters */
+ scorpion_l2_pmnc_write(scorpion_l2_pmnc_read() & ~SCORPIONL2_PMNC_E);
+ isb();
+}
+
+static inline u32 scorpion_l2_get_reset_pmovsr(void)
+{
+ u32 val;
+
+ /* Read */
+ asm volatile ("mrc p15, 3, %0, c15, c4, 1" : "=r" (val));
+
+ /* Write to clear flags */
+ val &= 0xffffffff;
+ asm volatile ("mcr p15, 3, %0, c15, c4, 1" : : "r" (val));
+
+ return val;
+}
+
+static irqreturn_t scorpion_l2_handle_irq(int irq_num, void *dev)
+{
+ unsigned long pmovsr;
+ struct perf_sample_data data;
+ struct pt_regs *regs;
+ struct perf_event *event;
+ struct hw_perf_event *hwc;
+ int bitp;
+ int idx = 0;
+
+ pmovsr = scorpion_l2_get_reset_pmovsr();
+
+ if (!(pmovsr & 0xffffffff))
+ return IRQ_NONE;
+
+ regs = get_irq_regs();
+
+ perf_sample_data_init(&data, 0);
+
+ while (pmovsr) {
+ bitp = __ffs(pmovsr);
+
+ if (bitp == SCORPION_L2CYCLE_CTR_BIT)
+ idx = SCORPION_L2CYCLE_CTR_EVENT_IDX;
+ else
+ idx = bitp;
+
+ event = scorpion_l2_pmu_hw_events.events[idx];
+
+ if (!event)
+ goto next;
+
+ if (!test_bit(idx, scorpion_l2_pmu_hw_events.used_mask))
+ goto next;
+
+ hwc = &event->hw;
+
+ armpmu_event_update(event, hwc, idx);
+
+ data.period = event->hw.last_period;
+
+ if (!armpmu_event_set_period(event, hwc, idx))
+ goto next;
+
+ if (perf_event_overflow(event, &data, regs))
+ scorpion_l2_disable_counter(hwc->idx);
+next:
+ pmovsr &= (pmovsr - 1);
+ }
+
+ irq_work_run();
+
+ return IRQ_HANDLED;
+}
+
+static int scorpion_l2_map_event(struct perf_event *event)
+{
+ if (pmu_type > 0 && pmu_type == event->attr.type)
+ return event->attr.config & 0xfffff;
+ else
+ return -ENOENT;
+}
+
+static int
+scorpion_l2_pmu_generic_request_irq(int irq, irq_handler_t *handle_irq)
+{
+ return request_irq(irq, *handle_irq,
+ IRQF_DISABLED | IRQF_NOBALANCING,
+ "scorpion-l2-armpmu", NULL);
+}
+
+static void
+scorpion_l2_pmu_generic_free_irq(int irq)
+{
+ if (irq >= 0)
+ free_irq(irq, NULL);
+}
+
+static struct arm_pmu scorpion_l2_pmu = {
+ .id = ARM_PERF_PMU_ID_SCORPIONMP_L2,
+ .type = ARM_PMU_DEVICE_L2CC,
+ .name = "Scorpion L2CC PMU",
+ .start = scorpion_l2_start,
+ .stop = scorpion_l2_stop,
+ .handle_irq = scorpion_l2_handle_irq,
+ .request_pmu_irq = scorpion_l2_pmu_generic_request_irq,
+ .free_pmu_irq = scorpion_l2_pmu_generic_free_irq,
+ .enable = scorpion_l2_enable,
+ .disable = scorpion_l2_disable,
+ .read_counter = scorpion_l2_read_counter,
+ .get_event_idx = scorpion_l2_get_event_idx,
+ .write_counter = scorpion_l2_write_counter,
+ .map_event = scorpion_l2_map_event,
+ .max_period = (1LLU << 32) - 1,
+ .get_hw_events = scorpion_l2_get_hw_events,
+ .num_events = MAX_SCORPION_L2_CTRS,
+};
+
+static int __devinit scorpion_l2_pmu_device_probe(struct platform_device *pdev)
+{
+ scorpion_l2_pmu.plat_device = pdev;
+
+ if (!armpmu_register(&scorpion_l2_pmu, "scorpion-l2", -1))
+ pmu_type = scorpion_l2_pmu.pmu.type;
+
+ return 0;
+}
+
+static struct platform_driver scorpion_l2_pmu_driver = {
+ .driver = {
+ .name = "l2-arm-pmu",
+ },
+ .probe = scorpion_l2_pmu_device_probe,
+};
+
+static int __init register_scorpion_l2_pmu_driver(void)
+{
+ /* Avoid spurious interrupt if any */
+ scorpion_l2_get_reset_pmovsr();
+
+ return platform_driver_register(&scorpion_l2_pmu_driver);
+}
+device_initcall(register_scorpion_l2_pmu_driver);
diff --git a/arch/arm/mach-msm/pmu.c b/arch/arm/mach-msm/pmu.c
index 1f82468..5e339da 100644
--- a/arch/arm/mach-msm/pmu.c
+++ b/arch/arm/mach-msm/pmu.c
@@ -33,7 +33,7 @@
static struct platform_device l2_pmu_device = {
.name = "l2-arm-pmu",
- .id = ARM_PMU_DEVICE_L2,
+ .id = ARM_PMU_DEVICE_L2CC,
.resource = l2_pmu_resource,
.num_resources = ARRAY_SIZE(l2_pmu_resource),
};
diff --git a/drivers/char/diag/diag_dci.c b/drivers/char/diag/diag_dci.c
index 5cbf888..2860055 100644
--- a/drivers/char/diag/diag_dci.c
+++ b/drivers/char/diag/diag_dci.c
@@ -16,6 +16,7 @@
#include <linux/diagchar.h>
#include <linux/sched.h>
#include <linux/err.h>
+#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/pm_runtime.h>
#include <linux/platform_device.h>
@@ -115,6 +116,23 @@
queue_work(driver->diag_wq, &(driver->diag_read_smd_dci_work));
}
+void diag_dci_notify_client(int peripheral_mask)
+{
+ int i, stat;
+
+ /* Notify the DCI process that the peripheral DCI Channel is up */
+ for (i = 0; i < MAX_DCI_CLIENT; i++) {
+ if (driver->dci_notify_tbl[i].list & peripheral_mask) {
+ pr_debug("diag: sending signal now\n");
+ stat = send_sig(driver->dci_notify_tbl[i].signal_type,
+ driver->dci_notify_tbl[i].client, 0);
+ if (stat)
+ pr_err("diag: Err send sig stat: %d\n", stat);
+ break;
+ }
+ } /* end of loop for all DCI clients */
+}
+
static int diag_dci_probe(struct platform_device *pdev)
{
int err = 0;
@@ -125,6 +143,8 @@
if (err)
pr_err("diag: cannot open DCI port, Id = %d, err ="
" %d\n", pdev->id, err);
+ else
+ diag_dci_notify_client(DIAG_CON_MPSS);
}
return err;
}
@@ -302,6 +322,12 @@
if (driver->dci_tbl == NULL)
goto err;
}
+ if (driver->dci_notify_tbl == NULL) {
+ driver->dci_notify_tbl = kzalloc(MAX_DCI_CLIENT *
+ sizeof(struct dci_notification_tbl), GFP_KERNEL);
+ if (driver->dci_notify_tbl == NULL)
+ goto err;
+ }
if (driver->apps_dci_buf == NULL) {
driver->apps_dci_buf = kzalloc(APPS_BUF_SIZE, GFP_KERNEL);
if (driver->apps_dci_buf == NULL)
@@ -316,6 +342,7 @@
err:
pr_err("diag: Could not initialize diag DCI buffers");
kfree(driver->dci_tbl);
+ kfree(driver->dci_notify_tbl);
kfree(driver->apps_dci_buf);
kfree(driver->buf_in_dci);
kfree(driver->write_ptr_dci);
@@ -328,6 +355,7 @@
driver->ch_dci = 0;
platform_driver_unregister(&msm_diag_dci_driver);
kfree(driver->dci_tbl);
+ kfree(driver->dci_notify_tbl);
kfree(driver->apps_dci_buf);
kfree(driver->buf_in_dci);
kfree(driver->write_ptr_dci);
diff --git a/drivers/char/diag/diag_dci.h b/drivers/char/diag/diag_dci.h
index cc6e0cf..c0b82df 100644
--- a/drivers/char/diag/diag_dci.h
+++ b/drivers/char/diag/diag_dci.h
@@ -22,6 +22,12 @@
int tag;
};
+struct dci_notification_tbl {
+ struct task_struct *client;
+ uint16_t list; /* bit mask */
+ int signal_type;
+};
+
#define DIAG_CON_APSS (0x0001) /* Bit mask for APSS */
#define DIAG_CON_MPSS (0x0002) /* Bit mask for MPSS */
#define DIAG_CON_LPASS (0x0004) /* Bit mask for LPASS */
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index 7e7b514..6a7b931 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -18,6 +18,7 @@
#include <linux/mempool.h>
#include <linux/mutex.h>
#include <linux/workqueue.h>
+#include <linux/sched.h>
#include <mach/msm_smd.h>
#include <asm/atomic.h>
#include <asm/mach-types.h>
@@ -139,6 +140,7 @@
int use_device_tree;
/* DCI related variables */
struct diag_dci_tbl *dci_tbl;
+ struct dci_notification_tbl *dci_notify_tbl;
int dci_tag;
int dci_client_id;
struct mutex dci_mutex;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index d6a6e66..547f42f 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -343,6 +343,7 @@
int success = -1;
void *temp_buf;
uint16_t support_list = 0;
+ struct dci_notification_tbl *notify_params;
if (iocmd == DIAG_IOCTL_COMMAND_REG) {
struct bindpkt_params_per_process *pkt_params =
@@ -413,13 +414,23 @@
} else if (iocmd == DIAG_IOCTL_DCI_REG) {
if (driver->dci_state == DIAG_DCI_NO_REG)
return DIAG_DCI_NO_REG;
- /* use the 'list' later on to notify user space */
if (driver->num_dci_client >= MAX_DCI_CLIENT)
return DIAG_DCI_NO_REG;
+ notify_params = (struct dci_notification_tbl *) ioarg;
mutex_lock(&driver->dci_mutex);
driver->num_dci_client++;
pr_debug("diag: id = %d\n", driver->dci_client_id);
driver->dci_client_id++;
+ for (i = 0; i < MAX_DCI_CLIENT; i++) {
+ if (driver->dci_notify_tbl[i].client == NULL) {
+ driver->dci_notify_tbl[i].client = current;
+ driver->dci_notify_tbl[i].list =
+ notify_params->list;
+ driver->dci_notify_tbl[i].signal_type =
+ notify_params->signal_type;
+ break;
+ }
+ }
mutex_unlock(&driver->dci_mutex);
return driver->dci_client_id;
} else if (iocmd == DIAG_IOCTL_DCI_DEINIT) {
@@ -433,6 +444,12 @@
success = i;
}
}
+ for (i = 0; i < MAX_DCI_CLIENT; i++) {
+ if (driver->dci_notify_tbl[i].client == current) {
+ driver->dci_notify_tbl[i].client = NULL;
+ break;
+ }
+ }
/* if any registrations were deleted successfully OR a valid
client_id was sent in DEINIT call , then its DCI client */
if (success >= 0 || ioarg)
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index 83d65b1..69aa411 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -1016,7 +1016,7 @@
ENCODE_RSP_AND_SEND(8+rt_mask_size-1);
return 0;
}
- ptr += MAX_SSID_PER_RANGE*4;
+ rt_mask_ptr += MAX_SSID_PER_RANGE*4;
}
} else
buf = temp;
diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c
index fb43562..bac9f8a 100644
--- a/drivers/gpio/gpio-msm-v2.c
+++ b/drivers/gpio/gpio-msm-v2.c
@@ -12,6 +12,7 @@
*/
#include <linux/bitmap.h>
#include <linux/bitops.h>
+#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/io.h>
@@ -147,14 +148,12 @@
void __msm_gpio_set_intr_cfg_enable(unsigned gpio, unsigned val)
{
if (val) {
- set_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE,
- GPIO_INTR_CFG(gpio));
+ set_gpio_bits(INTR_ENABLE, GPIO_INTR_CFG(gpio));
__raw_writel(TARGET_PROC_SCORPION, GPIO_INTR_CFG_SU(gpio));
} else {
__raw_writel(TARGET_PROC_NONE, GPIO_INTR_CFG_SU(gpio));
- clr_gpio_bits(INTR_RAW_STATUS_EN | INTR_ENABLE,
- GPIO_INTR_CFG(gpio));
+ clr_gpio_bits(INTR_ENABLE, GPIO_INTR_CFG(gpio));
}
}
@@ -173,7 +172,23 @@
else
cfg &= ~INTR_POL_CTL_HI;
+ /* RAW_STATUS_EN is left on for all gpio irqs. Due to the
+ * internal circuitry of TLMM, toggling the RAW_STATUS
+ * could cause the INTR_STATUS to be set for EDGE interrupts.
+ */
+ cfg |= INTR_RAW_STATUS_EN;
__raw_writel(cfg, GPIO_INTR_CFG(gpio));
+
+ /* Sometimes it might take a little while to update
+ * the interrupt status after the RAW_STATUS is enabled
+ */
+ udelay(5);
+
+ /* Clear the interrupt status to clear out any spurious
+ * irq as a result of the above operation
+ */
+ __msm_gpio_set_intr_status(gpio);
+
}
void __gpio_tlmm_config(unsigned config)
diff --git a/drivers/gpio/gpio-msm-v3.c b/drivers/gpio/gpio-msm-v3.c
index 49ad517..6086de3 100644
--- a/drivers/gpio/gpio-msm-v3.c
+++ b/drivers/gpio/gpio-msm-v3.c
@@ -12,6 +12,7 @@
*/
#include <linux/bitmap.h>
#include <linux/bitops.h>
+#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/io.h>
@@ -155,10 +156,9 @@
cfg = __raw_readl(GPIO_INTR_CFG(gpio));
if (val) {
cfg &= ~(INTR_TARGET_PROC_NONE | INTR_DIR_CONN_EN);
- cfg |= INTR_RAW_STATUS_EN | INTR_ENABLE | INTR_TARGET_PROC_APPS;
+ cfg |= INTR_ENABLE | INTR_TARGET_PROC_APPS;
} else {
- cfg &= ~(INTR_TARGET_PROC_APPS | INTR_RAW_STATUS_EN |
- INTR_ENABLE);
+ cfg &= ~(INTR_TARGET_PROC_APPS | INTR_ENABLE);
cfg |= INTR_TARGET_PROC_NONE;
}
__raw_writel(cfg, GPIO_INTR_CFG(gpio));
@@ -184,7 +184,22 @@
else
cfg &= ~INTR_POL_CTL_HI;
+ /* RAW_STATUS_EN is left on for all gpio irqs. Due to the
+ * internal circuitry of TLMM, toggling the RAW_STATUS
+ * could cause the INTR_STATUS to be set for EDGE interrupts.
+ */
+ cfg |= INTR_RAW_STATUS_EN;
__raw_writel(cfg, GPIO_INTR_CFG(gpio));
+
+ /* Sometimes it might take a little while to update
+ * the interrupt status after the RAW_STATUS is enabled
+ */
+ udelay(5);
+
+ /* Clear the interrupt status to clear out any spurious
+ * irq as a result of the above operation
+ */
+ __msm_gpio_set_intr_status(gpio);
}
void __gpio_tlmm_config(unsigned config)
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index 7e84aa7..5c7ab3a 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -1898,7 +1898,8 @@
mutex_unlock(&dev->lock);
}
-int ion_secure_heap(struct ion_device *dev, int heap_id)
+int ion_secure_heap(struct ion_device *dev, int heap_id, int version,
+ void *data)
{
struct rb_node *n;
int ret_val = 0;
@@ -1915,7 +1916,7 @@
if (ION_HEAP(heap->id) != heap_id)
continue;
if (heap->ops->secure_heap)
- ret_val = heap->ops->secure_heap(heap);
+ ret_val = heap->ops->secure_heap(heap, version, data);
else
ret_val = -EINVAL;
break;
@@ -1924,7 +1925,8 @@
return ret_val;
}
-int ion_unsecure_heap(struct ion_device *dev, int heap_id)
+int ion_unsecure_heap(struct ion_device *dev, int heap_id, int version,
+ void *data)
{
struct rb_node *n;
int ret_val = 0;
@@ -1941,7 +1943,7 @@
if (ION_HEAP(heap->id) != heap_id)
continue;
if (heap->ops->secure_heap)
- ret_val = heap->ops->unsecure_heap(heap);
+ ret_val = heap->ops->unsecure_heap(heap, version, data);
else
ret_val = -EINVAL;
break;
diff --git a/drivers/gpu/ion/ion_cp_heap.c b/drivers/gpu/ion/ion_cp_heap.c
index a857988a..c5e9caf 100644
--- a/drivers/gpu/ion/ion_cp_heap.c
+++ b/drivers/gpu/ion/ion_cp_heap.c
@@ -40,6 +40,7 @@
#include <asm/mach/map.h>
#include <asm/cacheflush.h>
+#include "msm/ion_cp_common.h"
/**
* struct ion_cp_heap - container for the heap and shared heap data
@@ -97,6 +98,7 @@
int iommu_map_all;
int iommu_2x_map_domain;
unsigned int has_outer_cache;
+ atomic_t protect_cnt;
};
enum {
@@ -105,10 +107,12 @@
};
static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size,
- unsigned int permission_type);
+ unsigned int permission_type, int version,
+ void *data);
static int ion_cp_unprotect_mem(unsigned int phy_base, unsigned int size,
- unsigned int permission_type);
+ unsigned int permission_type, int version,
+ void *data);
/**
* Get the total number of kernel mappings.
@@ -125,13 +129,13 @@
* the correct FMEM state if this heap is a reusable heap.
* Must be called with heap->lock locked.
*/
-static int ion_cp_protect(struct ion_heap *heap)
+static int ion_cp_protect(struct ion_heap *heap, int version, void *data)
{
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
int ret_value = 0;
- if (cp_heap->heap_protected == HEAP_NOT_PROTECTED) {
+ if (atomic_inc_return(&cp_heap->protect_cnt) == 1) {
/* Make sure we are in C state when the heap is protected. */
if (cp_heap->reusable && !cp_heap->allocated_bytes) {
ret_value = fmem_set_state(FMEM_C_STATE);
@@ -140,7 +144,8 @@
}
ret_value = ion_cp_protect_mem(cp_heap->secure_base,
- cp_heap->secure_size, cp_heap->permission_type);
+ cp_heap->secure_size, cp_heap->permission_type,
+ version, data);
if (ret_value) {
pr_err("Failed to protect memory for heap %s - "
"error code: %d\n", heap->name, ret_value);
@@ -150,6 +155,7 @@
pr_err("%s: unable to transition heap to T-state\n",
__func__);
}
+ atomic_dec(&cp_heap->protect_cnt);
} else {
cp_heap->heap_protected = HEAP_PROTECTED;
pr_debug("Protected heap %s @ 0x%lx\n",
@@ -157,6 +163,9 @@
}
}
out:
+ pr_debug("%s: protect count is %d\n", __func__,
+ atomic_read(&cp_heap->protect_cnt));
+ BUG_ON(atomic_read(&cp_heap->protect_cnt) < 0);
return ret_value;
}
@@ -165,15 +174,15 @@
* the correct FMEM state if this heap is a reusable heap.
* Must be called with heap->lock locked.
*/
-static void ion_cp_unprotect(struct ion_heap *heap)
+static void ion_cp_unprotect(struct ion_heap *heap, int version, void *data)
{
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
- if (cp_heap->heap_protected == HEAP_PROTECTED) {
+ if (atomic_dec_and_test(&cp_heap->protect_cnt)) {
int error_code = ion_cp_unprotect_mem(
cp_heap->secure_base, cp_heap->secure_size,
- cp_heap->permission_type);
+ cp_heap->permission_type, version, data);
if (error_code) {
pr_err("Failed to un-protect memory for heap %s - "
"error code: %d\n", heap->name, error_code);
@@ -189,6 +198,9 @@
}
}
}
+ pr_debug("%s: protect count is %d\n", __func__,
+ atomic_read(&cp_heap->protect_cnt));
+ BUG_ON(atomic_read(&cp_heap->protect_cnt) < 0);
}
ion_phys_addr_t ion_cp_allocate(struct ion_heap *heap,
@@ -641,14 +653,14 @@
return 0;
}
-int ion_cp_secure_heap(struct ion_heap *heap)
+int ion_cp_secure_heap(struct ion_heap *heap, int version, void *data)
{
int ret_value;
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
mutex_lock(&cp_heap->lock);
if (cp_heap->umap_count == 0 && cp_heap->kmap_cached_count == 0) {
- ret_value = ion_cp_protect(heap);
+ ret_value = ion_cp_protect(heap, version, data);
} else {
pr_err("ION cannot secure heap with outstanding mappings: "
"User space: %lu, kernel space (cached): %lu\n",
@@ -660,13 +672,13 @@
return ret_value;
}
-int ion_cp_unsecure_heap(struct ion_heap *heap)
+int ion_cp_unsecure_heap(struct ion_heap *heap, int version, void *data)
{
int ret_value = 0;
struct ion_cp_heap *cp_heap =
container_of(heap, struct ion_cp_heap, heap);
mutex_lock(&cp_heap->lock);
- ion_cp_unprotect(heap);
+ ion_cp_unprotect(heap, version, data);
mutex_unlock(&cp_heap->lock);
return ret_value;
}
@@ -925,6 +937,7 @@
cp_heap->secure_base = cp_heap->base;
cp_heap->secure_size = heap_data->size;
cp_heap->has_outer_cache = heap_data->has_outer_cache;
+ atomic_set(&cp_heap->protect_cnt, 0);
if (heap_data->extra_data) {
struct ion_cp_heap_pdata *extra_data =
heap_data->extra_data;
@@ -991,8 +1004,7 @@
unsigned char lock;
} __attribute__ ((__packed__));
-
-static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size,
+static int ion_cp_protect_mem_v1(unsigned int phy_base, unsigned int size,
unsigned int permission_type)
{
struct cp_lock_msg cmd;
@@ -1005,7 +1017,7 @@
&cmd, sizeof(cmd), NULL, 0);
}
-static int ion_cp_unprotect_mem(unsigned int phy_base, unsigned int size,
+static int ion_cp_unprotect_mem_v1(unsigned int phy_base, unsigned int size,
unsigned int permission_type)
{
struct cp_lock_msg cmd;
@@ -1017,3 +1029,70 @@
return scm_call(SCM_SVC_CP, SCM_CP_LOCK_CMD_ID,
&cmd, sizeof(cmd), NULL, 0);
}
+
+#define V2_CHUNK_SIZE SZ_1M
+
+static int ion_cp_change_mem_v2(unsigned int phy_base, unsigned int size,
+ void *data, int lock)
+{
+ enum cp_mem_usage usage = (enum cp_mem_usage) data;
+ unsigned long *chunk_list;
+ int nchunks;
+ int ret;
+ int i;
+
+ if (usage < 0 || usage >= MAX_USAGE)
+ return -EINVAL;
+
+ if (!IS_ALIGNED(size, V2_CHUNK_SIZE)) {
+ pr_err("%s: heap size is not aligned to %x\n",
+ __func__, V2_CHUNK_SIZE);
+ return -EINVAL;
+ }
+
+ nchunks = size / V2_CHUNK_SIZE;
+
+ chunk_list = allocate_contiguous_ebi(sizeof(unsigned long)*nchunks,
+ SZ_4K, 0);
+ if (!chunk_list)
+ return -ENOMEM;
+
+ for (i = 0; i < nchunks; i++)
+ chunk_list[i] = phy_base + i * V2_CHUNK_SIZE;
+
+ ret = ion_cp_change_chunks_state(memory_pool_node_paddr(chunk_list),
+ nchunks, V2_CHUNK_SIZE, usage, lock);
+
+ free_contiguous_memory(chunk_list);
+ return ret;
+}
+
+static int ion_cp_protect_mem(unsigned int phy_base, unsigned int size,
+ unsigned int permission_type, int version,
+ void *data)
+{
+ switch (version) {
+ case ION_CP_V1:
+ return ion_cp_protect_mem_v1(phy_base, size, permission_type);
+ case ION_CP_V2:
+ return ion_cp_change_mem_v2(phy_base, size, data,
+ SCM_CP_PROTECT);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ion_cp_unprotect_mem(unsigned int phy_base, unsigned int size,
+ unsigned int permission_type, int version,
+ void *data)
+{
+ switch (version) {
+ case ION_CP_V1:
+ return ion_cp_unprotect_mem_v1(phy_base, size, permission_type);
+ case ION_CP_V2:
+ return ion_cp_change_mem_v2(phy_base, size, data,
+ SCM_CP_UNPROTECT);
+ default:
+ return -EINVAL;
+ }
+}
diff --git a/drivers/gpu/ion/ion_iommu_heap.c b/drivers/gpu/ion/ion_iommu_heap.c
index 621144b..9ea6f2b 100644
--- a/drivers/gpu/ion/ion_iommu_heap.c
+++ b/drivers/gpu/ion/ion_iommu_heap.c
@@ -196,7 +196,7 @@
data->mapped_size, align,
&data->iova_addr);
- if (!data->iova_addr)
+ if (ret)
goto out;
domain = msm_get_iommu_domain(domain_num);
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h
index 6d636ee..6940e2f 100644
--- a/drivers/gpu/ion/ion_priv.h
+++ b/drivers/gpu/ion/ion_priv.h
@@ -158,8 +158,8 @@
void (*unmap_iommu)(struct ion_iommu_map *data);
int (*print_debug)(struct ion_heap *heap, struct seq_file *s,
const struct rb_root *mem_map);
- int (*secure_heap)(struct ion_heap *heap);
- int (*unsecure_heap)(struct ion_heap *heap);
+ int (*secure_heap)(struct ion_heap *heap, int version, void *data);
+ int (*unsecure_heap)(struct ion_heap *heap, int version, void *data);
};
/**
diff --git a/drivers/gpu/ion/msm/Makefile b/drivers/gpu/ion/msm/Makefile
index bedd8d2..1893405 100644
--- a/drivers/gpu/ion/msm/Makefile
+++ b/drivers/gpu/ion/msm/Makefile
@@ -1 +1 @@
-obj-y += msm_ion.o
+obj-y += msm_ion.o ion_cp_common.o
diff --git a/drivers/gpu/ion/msm/ion_cp_common.c b/drivers/gpu/ion/msm/ion_cp_common.c
new file mode 100644
index 0000000..b274ba2
--- /dev/null
+++ b/drivers/gpu/ion/msm/ion_cp_common.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 Google, Inc
+ * Copyright (c) 2011-2012, Code Aurora Forum. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/types.h>
+#include <mach/scm.h>
+
+#include "ion_cp_common.h"
+
+#define MEM_PROTECT_LOCK_ID 0x05
+
+struct cp2_mem_chunks {
+ unsigned int *chunk_list;
+ unsigned int chunk_list_size;
+ unsigned int chunk_size;
+} __attribute__ ((__packed__));
+
+struct cp2_lock_req {
+ struct cp2_mem_chunks chunks;
+ unsigned int mem_usage;
+ unsigned int lock;
+} __attribute__ ((__packed__));
+
+int ion_cp_change_chunks_state(unsigned long chunks, unsigned int nchunks,
+ unsigned int chunk_size,
+ enum cp_mem_usage usage,
+ int lock)
+{
+ struct cp2_lock_req request;
+
+ request.mem_usage = usage;
+ request.lock = lock;
+
+ request.chunks.chunk_list = (unsigned int *)chunks;
+ request.chunks.chunk_list_size = nchunks;
+ request.chunks.chunk_size = chunk_size;
+
+ return scm_call(SCM_SVC_CP, MEM_PROTECT_LOCK_ID,
+ &request, sizeof(request), NULL, 0);
+
+}
+
diff --git a/drivers/gpu/ion/msm/ion_cp_common.h b/drivers/gpu/ion/msm/ion_cp_common.h
new file mode 100644
index 0000000..69dd19e
--- /dev/null
+++ b/drivers/gpu/ion/msm/ion_cp_common.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012, Code Aurora Forum. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 ION_CP_COMMON_H
+#define ION_CP_COMMON_H
+
+#include <asm-generic/errno-base.h>
+#include <linux/ion.h>
+
+#define ION_CP_V1 1
+#define ION_CP_V2 2
+
+#if defined(CONFIG_ION_MSM)
+/*
+ * ion_cp2_protect_mem - secures memory via trustzone
+ *
+ * @chunks - physical address of the array containing the chunks to
+ * be locked down
+ * @nchunks - number of entries in the array
+ * @chunk_size - size of each memory chunk
+ * @usage - usage hint
+ * @lock - 1 for lock, 0 for unlock
+ *
+ * return value is the result of the scm call
+ */
+int ion_cp_change_chunks_state(unsigned long chunks, unsigned int nchunks,
+ unsigned int chunk_size, enum cp_mem_usage usage,
+ int lock);
+
+#else
+static inline int ion_cp_change_chunks_state(unsigned long chunks,
+ unsigned int nchunks, unsigned int chunk_size,
+ enum cp_mem_usage usage, int lock)
+{
+ return -ENODEV;
+}
+#endif
+
+#endif
diff --git a/drivers/gpu/ion/msm/msm_ion.c b/drivers/gpu/ion/msm/msm_ion.c
index f6a4cf4..eec3fe0 100644
--- a/drivers/gpu/ion/msm/msm_ion.c
+++ b/drivers/gpu/ion/msm/msm_ion.c
@@ -21,6 +21,7 @@
#include <mach/ion.h>
#include <mach/msm_memtypes.h>
#include "../ion_priv.h"
+#include "ion_cp_common.h"
static struct ion_device *idev;
static int num_heaps;
@@ -35,16 +36,28 @@
int msm_ion_secure_heap(int heap_id)
{
- return ion_secure_heap(idev, heap_id);
+ return ion_secure_heap(idev, heap_id, ION_CP_V1, NULL);
}
EXPORT_SYMBOL(msm_ion_secure_heap);
int msm_ion_unsecure_heap(int heap_id)
{
- return ion_unsecure_heap(idev, heap_id);
+ return ion_unsecure_heap(idev, heap_id, ION_CP_V1, NULL);
}
EXPORT_SYMBOL(msm_ion_unsecure_heap);
+int msm_ion_secure_heap_2_0(int heap_id, enum cp_mem_usage usage)
+{
+ return ion_secure_heap(idev, heap_id, ION_CP_V2, (void *)usage);
+}
+EXPORT_SYMBOL(msm_ion_secure_heap_2_0);
+
+int msm_ion_unsecure_heap_2_0(int heap_id, enum cp_mem_usage usage)
+{
+ return ion_unsecure_heap(idev, heap_id, ION_CP_V2, (void *)usage);
+}
+EXPORT_SYMBOL(msm_ion_unsecure_heap_2_0);
+
int msm_ion_do_cache_op(struct ion_client *client, struct ion_handle *handle,
void *vaddr, unsigned long len, unsigned int cmd)
{
diff --git a/drivers/gpu/msm/a3xx_reg.h b/drivers/gpu/msm/a3xx_reg.h
index 0a71982..bd58b4e 100644
--- a/drivers/gpu/msm/a3xx_reg.h
+++ b/drivers/gpu/msm/a3xx_reg.h
@@ -46,6 +46,7 @@
#define A3XX_RBBM_HW_VERSION 0x000
#define A3XX_RBBM_HW_RELEASE 0x001
#define A3XX_RBBM_HW_CONFIGURATION 0x002
+#define A3XX_RBBM_CLOCK_CTL 0x010
#define A3XX_RBBM_SP_HYST_CNT 0x012
#define A3XX_RBBM_SW_RESET_CMD 0x018
#define A3XX_RBBM_AHB_CTL0 0x020
@@ -507,4 +508,7 @@
#define RBBM_BLOCK_ID_MARB_2 0x2a
#define RBBM_BLOCK_ID_MARB_3 0x2b
+/* RBBM_CLOCK_CTL default value */
+#define A3XX_RBBM_CLOCK_CTL_DEFAULT 0x00000000
+
#endif
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 3de3af9..4991a2e 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -1592,10 +1592,17 @@
adreno_dev->gpudev->irq_control(adreno_dev, state);
}
-static unsigned int adreno_gpuid(struct kgsl_device *device)
+static unsigned int adreno_gpuid(struct kgsl_device *device,
+ unsigned int *chipid)
{
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+ /* Some applications need to know the chip ID too, so pass
+ * that as a parameter */
+
+ if (chipid != NULL)
+ *chipid = adreno_dev->chip_id;
+
/* Standard KGSL gpuid format:
* top word is 0x0002 for 2D or 0x0003 for 3D
* Bottom word is core specific identifer
diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c
index c3c266e..d846d3d 100644
--- a/drivers/gpu/msm/adreno_a2xx.c
+++ b/drivers/gpu/msm/adreno_a2xx.c
@@ -1450,43 +1450,55 @@
return ret;
}
-static void a2xx_drawctxt_draw_workaround(struct adreno_device *adreno_dev)
+static void a2xx_drawctxt_workaround(struct adreno_device *adreno_dev)
{
struct kgsl_device *device = &adreno_dev->dev;
unsigned int cmd[11];
unsigned int *cmds = &cmd[0];
- adreno_dev->gpudev->ctx_switches_since_last_draw++;
- /* If there have been > than ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW
- * calls to context switches w/o gmem being saved then we need to
- * execute this workaround */
- if (adreno_dev->gpudev->ctx_switches_since_last_draw >
- ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW)
- adreno_dev->gpudev->ctx_switches_since_last_draw = 0;
- else
- return;
- /*
- * Issue an empty draw call to avoid possible hangs due to
- * repeated idles without intervening draw calls.
- * On adreno 225 the PC block has a cache that is only
- * flushed on draw calls and repeated idles can make it
- * overflow. The gmem save path contains draw calls so
- * this workaround isn't needed there.
- */
- *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
- *cmds++ = (0x4 << 16) | (REG_PA_SU_SC_MODE_CNTL - 0x2000);
- *cmds++ = 0;
- *cmds++ = cp_type3_packet(CP_DRAW_INDX, 5);
- *cmds++ = 0;
- *cmds++ = 1<<14;
- *cmds++ = 0;
- *cmds++ = device->mmu.setstate_memory.gpuaddr;
- *cmds++ = 0;
- *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
- *cmds++ = 0x00000000;
+ if (adreno_is_a225(adreno_dev)) {
+ adreno_dev->gpudev->ctx_switches_since_last_draw++;
+ /* If there have been > than
+ * ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW calls to context
+ * switches w/o gmem being saved then we need to execute
+ * this workaround */
+ if (adreno_dev->gpudev->ctx_switches_since_last_draw >
+ ADRENO_NUM_CTX_SWITCH_ALLOWED_BEFORE_DRAW)
+ adreno_dev->gpudev->ctx_switches_since_last_draw = 0;
+ else
+ return;
+ /*
+ * Issue an empty draw call to avoid possible hangs due to
+ * repeated idles without intervening draw calls.
+ * On adreno 225 the PC block has a cache that is only
+ * flushed on draw calls and repeated idles can make it
+ * overflow. The gmem save path contains draw calls so
+ * this workaround isn't needed there.
+ */
+ *cmds++ = cp_type3_packet(CP_SET_CONSTANT, 2);
+ *cmds++ = (0x4 << 16) | (REG_PA_SU_SC_MODE_CNTL - 0x2000);
+ *cmds++ = 0;
+ *cmds++ = cp_type3_packet(CP_DRAW_INDX, 5);
+ *cmds++ = 0;
+ *cmds++ = 1<<14;
+ *cmds++ = 0;
+ *cmds++ = device->mmu.setstate_memory.gpuaddr;
+ *cmds++ = 0;
+ *cmds++ = cp_type3_packet(CP_WAIT_FOR_IDLE, 1);
+ *cmds++ = 0x00000000;
+ } else {
+ /* On Adreno 20x/220, if the events for shader space reuse
+ * gets dropped, the CP block would wait indefinitely.
+ * Sending CP_SET_SHADER_BASES packet unblocks the CP from
+ * this wait.
+ */
+ *cmds++ = cp_type3_packet(CP_SET_SHADER_BASES, 1);
+ *cmds++ = adreno_encode_istore_size(adreno_dev)
+ | adreno_dev->pix_shader_start;
+ }
adreno_ringbuffer_issuecmds(device, KGSL_CMD_FLAGS_PMODE,
- &cmd[0], 11);
+ &cmd[0], cmds - cmd);
}
static void a2xx_drawctxt_save(struct adreno_device *adreno_dev,
@@ -1540,8 +1552,8 @@
adreno_dev->gpudev->ctx_switches_since_last_draw = 0;
context->flags |= CTXT_FLAGS_GMEM_RESTORE;
- } else if (adreno_is_a225(adreno_dev))
- a2xx_drawctxt_draw_workaround(adreno_dev);
+ } else if (adreno_is_a2xx(adreno_dev))
+ a2xx_drawctxt_workaround(adreno_dev);
}
static void a2xx_drawctxt_restore(struct adreno_device *adreno_dev,
@@ -1999,7 +2011,7 @@
.ctxt_create = a2xx_drawctxt_create,
.ctxt_save = a2xx_drawctxt_save,
.ctxt_restore = a2xx_drawctxt_restore,
- .ctxt_draw_workaround = a2xx_drawctxt_draw_workaround,
+ .ctxt_draw_workaround = a2xx_drawctxt_workaround,
.irq_handler = a2xx_irq_handler,
.irq_control = a2xx_irq_control,
.snapshot = a2xx_snapshot,
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index e258072..58a0963 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -433,6 +433,9 @@
unsigned int *cmds = tmp_ctx.cmd;
unsigned int *start = cmds;
+ *cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1);
+ *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT;
+
*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 3);
*cmds++ = CP_REG(A3XX_RB_MODE_CONTROL);
@@ -1162,6 +1165,9 @@
unsigned int *cmds = tmp_ctx.cmd;
unsigned int *start = cmds;
+ *cmds++ = cp_type0_packet(A3XX_RBBM_CLOCK_CTL, 1);
+ *cmds++ = A3XX_RBBM_CLOCK_CTL_DEFAULT;
+
*cmds++ = cp_type3_packet(CP_SET_CONSTANT, 5);
*cmds++ = CP_REG(A3XX_HLSQ_CONTROL_0_REG);
/* HLSQ_CONTROL_0_REG */
diff --git a/drivers/gpu/msm/adreno_a3xx_snapshot.c b/drivers/gpu/msm/adreno_a3xx_snapshot.c
index c8c7c44..60aab64 100644
--- a/drivers/gpu/msm/adreno_a3xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a3xx_snapshot.c
@@ -19,6 +19,29 @@
#define DEBUG_SECTION_SZ(_dwords) (((_dwords) * sizeof(unsigned int)) \
+ sizeof(struct kgsl_snapshot_debug))
+#define SHADER_MEMORY_SIZE 0x4000
+
+static int a3xx_snapshot_shader_memory(struct kgsl_device *device,
+ void *snapshot, int remain, void *priv)
+{
+ struct kgsl_snapshot_debug *header = snapshot;
+ unsigned int *data = snapshot + sizeof(*header);
+ int i;
+
+ if (remain < DEBUG_SECTION_SZ(SHADER_MEMORY_SIZE)) {
+ SNAPSHOT_ERR_NOMEM(device, "SHADER MEMORY");
+ return 0;
+ }
+
+ header->type = SNAPSHOT_DEBUG_SHADER_MEMORY;
+ header->size = SHADER_MEMORY_SIZE;
+
+ for (i = 0; i < SHADER_MEMORY_SIZE; i++)
+ adreno_regread(device, 0x4000 + i, &data[i]);
+
+ return DEBUG_SECTION_SZ(SHADER_MEMORY_SIZE);
+}
+
#define VPC_MEMORY_BANKS 4
#define VPC_MEMORY_SIZE 512
@@ -272,6 +295,12 @@
KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
a3xx_snapshot_cp_meq, NULL);
+ /* Shader working/shadow memory */
+ snapshot = kgsl_snapshot_add_section(device,
+ KGSL_SNAPSHOT_SECTION_DEBUG, snapshot, remain,
+ a3xx_snapshot_shader_memory, NULL);
+
+
/* CP PFP and PM4 */
/* Reading these will hang the GPU if it isn't already hung */
diff --git a/drivers/gpu/msm/adreno_pm4types.h b/drivers/gpu/msm/adreno_pm4types.h
index fb44b25..016862b 100644
--- a/drivers/gpu/msm/adreno_pm4types.h
+++ b/drivers/gpu/msm/adreno_pm4types.h
@@ -203,7 +203,14 @@
#define type0_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1)
#define type0_pkt_offset(pkt) ((pkt) & 0x7FFF)
-#define pkt_is_type3(pkt) (((pkt) & 0xC0000000) == CP_TYPE3_PKT)
+/*
+ * Check both for the type3 opcode and make sure that the reserved bits [1:7]
+ * and 15 are 0
+ */
+
+#define pkt_is_type3(pkt) \
+ ((((pkt) & 0xC0000000) == CP_TYPE3_PKT) && \
+ (((pkt) & 0x80FE) == 0))
#define cp_type3_opcode(pkt) (((pkt) >> 8) & 0xFF)
#define type3_pkt_size(pkt) ((((pkt) >> 16) & 0x3FFF) + 1)
diff --git a/drivers/gpu/msm/adreno_snapshot.c b/drivers/gpu/msm/adreno_snapshot.c
index 5572695..08a01b0 100644
--- a/drivers/gpu/msm/adreno_snapshot.c
+++ b/drivers/gpu/msm/adreno_snapshot.c
@@ -161,6 +161,22 @@
static unsigned int sp_vs_pvt_mem_addr;
static unsigned int sp_fs_pvt_mem_addr;
+/*
+ * Each load state block has two possible types. Each type has a different
+ * number of dwords per unit. Use this handy lookup table to make sure
+ * we dump the right amount of data from the indirect buffer
+ */
+
+static int load_state_unit_sizes[7][2] = {
+ { 2, 4 },
+ { 0, 1 },
+ { 2, 4 },
+ { 0, 1 },
+ { 8, 2 },
+ { 8, 2 },
+ { 8, 2 },
+};
+
static void ib_parse_load_state(struct kgsl_device *device, unsigned int *pkt,
unsigned int ptbase)
{
@@ -170,11 +186,9 @@
* The object here is to find indirect shaders i.e - shaders loaded from
* GPU memory instead of directly in the command. These should be added
* to the list of memory objects to dump. So look at the load state
- * call and see if 1) the shader block is a shader (block = 4, 5 or 6)
- * 2) that the block is indirect (source = 4). If these all match then
- * add the memory address to the list. The size of the object will
- * differ depending on the type. Type 0 (instructions) are 8 dwords per
- * unit and type 1 (constants) are 2 dwords per unit.
+ * if the block is indirect (source = 4). If so then add the memory
+ * address to the list. The size of the object differs depending on the
+ * type per the load_state_unit_sizes array above.
*/
if (type3_pkt_size(pkt[0]) < 2)
@@ -192,9 +206,13 @@
source = (pkt[1] >> 16) & 0x07;
type = pkt[2] & 0x03;
- if ((block == 4 || block == 5 || block == 6) && source == 4) {
- int unitsize = (type == 0) ? 8 : 2;
- int ret;
+ if (source == 4) {
+ int unitsize, ret;
+
+ if (type == 0)
+ unitsize = load_state_unit_sizes[block][0];
+ else
+ unitsize = load_state_unit_sizes[block][1];
/* Freeze the GPU buffer containing the shader */
@@ -528,7 +546,6 @@
unsigned int ptbase, rptr, *rbptr, ibbase;
int index, size, i;
int parse_ibs = 0, ib_parse_start;
- int skip_pktsize = 1;
/* Get the physical address of the MMU pagetable */
ptbase = kgsl_mmu_get_current_ptbase(&device->mmu);
@@ -541,13 +558,14 @@
/*
* Figure out the window of ringbuffer data to dump. First we need to
- * find where the last processed IB ws submitted
+ * find where the last processed IB ws submitted. Start walking back
+ * from the rptr
*/
index = rptr;
rbptr = rb->buffer_desc.hostptr;
- while (index != rb->wptr) {
+ do {
index--;
if (index < 0) {
@@ -563,7 +581,7 @@
if (adreno_cmd_is_ib(rbptr[index]) &&
rbptr[index + 1] == ibbase)
break;
- }
+ } while (index != rb->wptr);
/*
* index points at the last submitted IB. We can only trust that the
@@ -636,18 +654,15 @@
* try to adust for that by modifying the rptr to match a
* packet boundary. Unfortunately for us, it is hard to tell
* which dwords are legitimate type0 header and which are just
- * random data so just walk over type0 packets until we get
- * to the first type3, and from that point on start checking the
- * size of the packet and adjusting accordingly
+ * random data so only do the adjustments for type3 packets
*/
- if (skip_pktsize && pkt_is_type3(rbptr[index]))
- skip_pktsize = 0;
-
- if (skip_pktsize == 0) {
- unsigned int pktsize = type3_pkt_size(rbptr[index]);
+ if (pkt_is_type3(rbptr[index])) {
+ unsigned int pktsize =
+ type3_pkt_size(rbptr[index]);
if (index + pktsize > rptr)
- rptr = (index + pktsize) % rb->sizedwords;
+ rptr = (index + pktsize) %
+ rb->sizedwords;
}
/*
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 81de64f..5883f08 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -2598,9 +2598,17 @@
kgsl_drm_exit();
kgsl_cffdump_destroy();
kgsl_core_debugfs_close();
- kgsl_sharedmem_uninit_sysfs();
- device_unregister(&kgsl_driver.virtdev);
+ /*
+ * We call kgsl_sharedmem_uninit_sysfs() and device_unregister()
+ * only if kgsl_driver.virtdev has been populated.
+ * We check at least one member of kgsl_driver.virtdev to
+ * see if it is not NULL (and thus, has been populated).
+ */
+ if (kgsl_driver.virtdev.class) {
+ kgsl_sharedmem_uninit_sysfs();
+ device_unregister(&kgsl_driver.virtdev);
+ }
if (kgsl_driver.class) {
class_destroy(kgsl_driver.class);
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 64acff8..932c995 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -90,7 +90,7 @@
void (*power_stats)(struct kgsl_device *device,
struct kgsl_power_stats *stats);
void (*irqctrl)(struct kgsl_device *device, int state);
- unsigned int (*gpuid)(struct kgsl_device *device);
+ unsigned int (*gpuid)(struct kgsl_device *device, unsigned int *chipid);
void * (*snapshot)(struct kgsl_device *device, void *snapshot,
int *remain, int hang);
irqreturn_t (*irq_handler)(struct kgsl_device *device);
@@ -287,9 +287,10 @@
return device->ftbl->idle(device, timeout);
}
-static inline unsigned int kgsl_gpuid(struct kgsl_device *device)
+static inline unsigned int kgsl_gpuid(struct kgsl_device *device,
+ unsigned int *chipid)
{
- return device->ftbl->gpuid(device);
+ return device->ftbl->gpuid(device, chipid);
}
static inline unsigned int kgsl_readtimestamp(struct kgsl_device *device,
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index b3f2d1e..e42c7b6 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -837,6 +837,10 @@
{
unsigned int pt_base;
struct kgsl_iommu *iommu = mmu->priv;
+ /* We cannot enable or disable the clocks in interrupt context, this
+ function is called from interrupt context if there is an axi error */
+ if (in_interrupt())
+ return 0;
/* Return the current pt base by reading IOMMU pt_base register */
kgsl_iommu_enable_clk(mmu, KGSL_IOMMU_CONTEXT_USER);
pt_base = readl_relaxed(iommu->iommu_units[0].reg_map.hostptr +
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 8935b29..824d806 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -35,6 +35,56 @@
struct list_head node;
};
+struct snapshot_obj_itr {
+ void *buf; /* Buffer pointer to write to */
+ int pos; /* Current position in the sequence */
+ loff_t offset; /* file offset to start writing from */
+ size_t remain; /* Bytes remaining in buffer */
+ size_t write; /* Bytes written so far */
+};
+
+static void obj_itr_init(struct snapshot_obj_itr *itr, void *buf,
+ loff_t offset, size_t remain)
+{
+ itr->buf = buf;
+ itr->offset = offset;
+ itr->remain = remain;
+ itr->pos = 0;
+ itr->write = 0;
+}
+
+static int obj_itr_out(struct snapshot_obj_itr *itr, void *src, int size)
+{
+ if (itr->remain == 0)
+ return 0;
+
+ if ((itr->pos + size) <= itr->offset)
+ goto done;
+
+ /* Handle the case that offset is in the middle of the buffer */
+
+ if (itr->offset > itr->pos) {
+ src += (itr->offset - itr->pos);
+ size -= (itr->offset - itr->pos);
+
+ /* Advance pos to the offset start */
+ itr->pos = itr->offset;
+ }
+
+ if (size > itr->remain)
+ size = itr->remain;
+
+ memcpy(itr->buf, src, size);
+
+ itr->buf += size;
+ itr->write += size;
+ itr->remain -= size;
+
+done:
+ itr->pos += size;
+ return size;
+}
+
/* idr_for_each function to count the number of contexts */
static int snapshot_context_count(int id, void *ptr, void *data)
@@ -182,76 +232,46 @@
(sizeof(struct kgsl_snapshot_section_header) + \
sizeof(struct kgsl_snapshot_gpu_object))
-#define GPU_OBJ_SECTION_SIZE(_o) \
- (GPU_OBJ_HEADER_SZ + ((_o)->size))
-
static int kgsl_snapshot_dump_object(struct kgsl_device *device,
- struct kgsl_snapshot_object *obj, void *buf,
- unsigned int off, unsigned int count)
+ struct kgsl_snapshot_object *obj, struct snapshot_obj_itr *itr)
{
- unsigned char headers[GPU_OBJ_HEADER_SZ];
- struct kgsl_snapshot_section_header *sect =
- (struct kgsl_snapshot_section_header *) headers;
- struct kgsl_snapshot_gpu_object *header =
- (struct kgsl_snapshot_gpu_object *) (headers + sizeof(*sect));
- int ret = 0;
+ struct kgsl_snapshot_section_header sect;
+ struct kgsl_snapshot_gpu_object header;
+ int ret;
- /* Construct a local copy of the headers */
+ sect.magic = SNAPSHOT_SECTION_MAGIC;
+ sect.id = KGSL_SNAPSHOT_SECTION_GPU_OBJECT;
- sect->magic = SNAPSHOT_SECTION_MAGIC;
- sect->id = KGSL_SNAPSHOT_SECTION_GPU_OBJECT;
- sect->size = GPU_OBJ_SECTION_SIZE(obj);
+ /*
+ * Header size is in dwords, object size is in bytes -
+ * round up if the object size isn't dword aligned
+ */
- header->type = obj->type;
+ sect.size = GPU_OBJ_HEADER_SZ + ALIGN(obj->size, 4);
- /* Header size is in dwords, object size is in bytes */
- header->size = obj->size >> 2;
- header->gpuaddr = obj->gpuaddr;
- header->ptbase = obj->ptbase;
+ ret = obj_itr_out(itr, §, sizeof(sect));
+ if (ret == 0)
+ return 0;
- /* Copy out any part of the header block that is needed */
+ header.size = ALIGN(obj->size, 4) >> 2;
+ header.gpuaddr = obj->gpuaddr;
+ header.ptbase = obj->ptbase;
+ header.type = obj->type;
- if (off < GPU_OBJ_HEADER_SZ) {
- int size = count < GPU_OBJ_HEADER_SZ - off ?
- count : GPU_OBJ_HEADER_SZ - off;
+ ret = obj_itr_out(itr, &header, sizeof(header));
+ if (ret == 0)
+ return 0;
- memcpy(buf, headers + off, size);
+ ret = obj_itr_out(itr, obj->entry->memdesc.hostptr + obj->offset,
+ obj->size);
+ if (ret == 0)
+ return 0;
- count -= size;
- ret += size;
- }
+ /* Pad the end to a dword boundary if we need to */
- /* Now copy whatever part of the data is needed */
-
- if (off < (GPU_OBJ_HEADER_SZ + obj->size)) {
- int offset;
- int size = count < obj->size ? count : obj->size;
-
- /*
- * If the desired gpuaddr isn't at the beginning of the region,
- * then offset the source pointer
- */
-
- offset = obj->offset;
-
- /*
- * Then adjust it to account for the offset for the output
- * buffer.
- */
-
- if (off > GPU_OBJ_HEADER_SZ) {
- int loff = (off - GPU_OBJ_HEADER_SZ);
-
- /* Adjust the size so we don't walk off the end */
-
- if ((loff + size) > obj->size)
- size = obj->size - loff;
-
- offset += loff;
- }
-
- memcpy(buf + ret, obj->entry->memdesc.hostptr + offset, size);
- ret += size;
+ if (obj->size % 4) {
+ unsigned int dummy = 0;
+ ret = obj_itr_out(itr, &dummy, obj->size % 4);
}
return ret;
@@ -488,7 +508,7 @@
header->magic = SNAPSHOT_MAGIC;
- header->gpuid = kgsl_gpuid(device);
+ header->gpuid = kgsl_gpuid(device, &header->chipid);
/* Get a pointer to the first section (right after the header) */
snapshot = ((void *) device->snapshot) + sizeof(*header);
@@ -539,7 +559,9 @@
{
struct kgsl_device *device = kobj_to_device(kobj);
struct kgsl_snapshot_object *obj, *tmp;
- unsigned int size, src, dst = 0;
+ struct kgsl_snapshot_section_header head;
+ struct snapshot_obj_itr itr;
+ int ret;
if (device == NULL)
return 0;
@@ -551,80 +573,46 @@
/* Get the mutex to keep things from changing while we are dumping */
mutex_lock(&device->mutex);
- if (off < device->snapshot_size) {
- size = count < (device->snapshot_size - off) ?
- count : device->snapshot_size - off;
+ obj_itr_init(&itr, buf, off, count);
- memcpy(buf, device->snapshot + off, size);
+ ret = obj_itr_out(&itr, device->snapshot, device->snapshot_size);
- count -= size;
- dst += size;
- }
-
- if (count == 0)
+ if (ret == 0)
goto done;
- src = device->snapshot_size;
+ list_for_each_entry(obj, &device->snapshot_obj_list, node)
+ kgsl_snapshot_dump_object(device, obj, &itr);
- list_for_each_entry(obj, &device->snapshot_obj_list, node) {
+ {
+ head.magic = SNAPSHOT_SECTION_MAGIC;
+ head.id = KGSL_SNAPSHOT_SECTION_END;
+ head.size = sizeof(head);
- int objsize = GPU_OBJ_SECTION_SIZE(obj);
- int offset;
-
- /* If the offset is beyond this object, then move on */
-
- if (off >= (src + objsize)) {
- src += objsize;
- continue;
- }
-
- /* Adjust the offset to be relative to the object */
- offset = (off >= src) ? (off - src) : 0;
-
- size = kgsl_snapshot_dump_object(device, obj, buf + dst,
- offset, count);
-
- count -= size;
- dst += size;
-
- if (count == 0)
- goto done;
-
- /* Move on to the next object - update src accordingly */
- src += objsize;
+ obj_itr_out(&itr, &head, sizeof(head));
}
- /* Add the end section */
+ /*
+ * Make sure everything has been written out before destroying things.
+ * The best way to confirm this is to go all the way through without
+ * writing any bytes - so only release if we get this far and
+ * itr->write is 0
+ */
- if (off < (src + sizeof(struct kgsl_snapshot_section_header))) {
- if (count >= sizeof(struct kgsl_snapshot_section_header)) {
- struct kgsl_snapshot_section_header *head =
- (void *) (buf + dst);
+ if (itr.write == 0) {
+ list_for_each_entry_safe(obj, tmp, &device->snapshot_obj_list,
+ node)
+ kgsl_snapshot_put_object(device, obj);
- head->magic = SNAPSHOT_SECTION_MAGIC;
- head->id = KGSL_SNAPSHOT_SECTION_END;
- head->size = sizeof(*head);
+ if (device->snapshot_frozen)
+ KGSL_DRV_ERR(device, "Snapshot objects released\n");
- dst += sizeof(*head);
- } else {
- goto done;
- }
+ device->snapshot_frozen = 0;
}
- /* Release the buffers and unfreeze the snapshot */
-
- list_for_each_entry_safe(obj, tmp, &device->snapshot_obj_list, node)
- kgsl_snapshot_put_object(device, obj);
-
- if (device->snapshot_frozen)
- KGSL_DRV_ERR(device, "Snapshot objects released\n");
-
- device->snapshot_frozen = 0;
-
done:
mutex_unlock(&device->mutex);
- return dst;
+ return itr.write;
}
/* Show the timestamp of the last collected snapshot */
diff --git a/drivers/gpu/msm/kgsl_snapshot.h b/drivers/gpu/msm/kgsl_snapshot.h
index 304f4bb..d54afcf 100644
--- a/drivers/gpu/msm/kgsl_snapshot.h
+++ b/drivers/gpu/msm/kgsl_snapshot.h
@@ -18,7 +18,8 @@
/* Snapshot header */
-#define SNAPSHOT_MAGIC 0x504D0001
+/* High word is static, low word is snapshot version ID */
+#define SNAPSHOT_MAGIC 0x504D0002
/* GPU ID scheme:
* [16:31] - core identifer (0x0002 for 2D or 0x0003 for 3D)
@@ -28,6 +29,8 @@
struct kgsl_snapshot_header {
__u32 magic; /* Magic identifier */
__u32 gpuid; /* GPU ID - see above */
+ /* Added in snapshot version 2 */
+ __u32 chipid; /* Chip ID from the GPU */
} __packed;
/* Section header */
@@ -140,6 +143,7 @@
#define SNAPSHOT_DEBUG_CP_PM4_RAM 8
#define SNAPSHOT_DEBUG_CP_PFP_RAM 9
#define SNAPSHOT_DEBUG_CP_ROQ 10
+#define SNAPSHOT_DEBUG_SHADER_MEMORY 11
struct kgsl_snapshot_debug {
int type; /* Type identifier for the attached tata */
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
index f4bbf69..bc2685c 100644
--- a/drivers/gpu/msm/z180.c
+++ b/drivers/gpu/msm/z180.c
@@ -896,8 +896,11 @@
}
}
-static unsigned int z180_gpuid(struct kgsl_device *device)
+static unsigned int z180_gpuid(struct kgsl_device *device, unsigned int *chipid)
{
+ if (chipid != NULL)
+ *chipid = 0;
+
/* Standard KGSL gpuid format:
* top word is 0x0002 for 2D or 0x0003 for 3D
* Bottom word is core specific identifer
diff --git a/drivers/i2c/busses/i2c-msm.c b/drivers/i2c/busses/i2c-msm.c
index b753fd0..4b48a69 100644
--- a/drivers/i2c/busses/i2c-msm.c
+++ b/drivers/i2c/busses/i2c-msm.c
@@ -636,7 +636,7 @@
spin_lock_init(&dev->lock);
platform_set_drvdata(pdev, dev);
- clk_enable(clk);
+ clk_prepare_enable(clk);
if (pdata->rmutex) {
struct remote_mutex_id rmid;
@@ -697,7 +697,8 @@
/* Config GPIOs for primary and secondary lines */
pdata->msm_i2c_config_gpio(dev->adap_pri.nr, 1);
pdata->msm_i2c_config_gpio(dev->adap_aux.nr, 1);
- clk_disable(dev->clk);
+ clk_disable_unprepare(dev->clk);
+ clk_prepare(dev->clk);
setup_timer(&dev->pwr_timer, msm_i2c_pwr_timer, (unsigned long) dev);
return 0;
@@ -706,7 +707,7 @@
i2c_del_adapter(&dev->adap_pri);
i2c_del_adapter(&dev->adap_aux);
err_i2c_add_adapter_failed:
- clk_disable(clk);
+ clk_disable_unprepare(clk);
iounmap(dev->base);
err_ioremap_failed:
kfree(dev);
@@ -736,6 +737,7 @@
free_irq(dev->irq, dev);
i2c_del_adapter(&dev->adap_pri);
i2c_del_adapter(&dev->adap_aux);
+ clk_unprepare(dev->clk);
clk_put(dev->clk);
iounmap(dev->base);
kfree(dev);
@@ -759,6 +761,7 @@
del_timer_sync(&dev->pwr_timer);
if (dev->clk_state != 0)
msm_i2c_pwr_mgmt(dev, 0);
+ clk_unprepare(dev->clk);
}
return 0;
@@ -767,6 +770,7 @@
static int msm_i2c_resume(struct platform_device *pdev)
{
struct msm_i2c_dev *dev = platform_get_drvdata(pdev);
+ clk_prepare(dev->clk);
dev->suspended = 0;
return 0;
}
diff --git a/drivers/input/misc/mpu3050.c b/drivers/input/misc/mpu3050.c
index 5403c57..8b6e172 100644
--- a/drivers/input/misc/mpu3050.c
+++ b/drivers/input/misc/mpu3050.c
@@ -40,6 +40,8 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/input/mpu3050.h>
+#include <linux/regulator/consumer.h>
#define MPU3050_CHIP_ID 0x69
@@ -112,8 +114,81 @@
struct i2c_client *client;
struct device *dev;
struct input_dev *idev;
+ struct mpu3050_gyro_platform_data *platform_data;
+ struct delayed_work input_work;
+ u32 use_poll;
};
+struct sensor_regulator {
+ struct regulator *vreg;
+ const char *name;
+ u32 min_uV;
+ u32 max_uV;
+};
+
+struct sensor_regulator mpu_vreg[] = {
+ {NULL, "vdd", 2100000, 3600000},
+ {NULL, "vlogic", 1800000, 1800000},
+};
+
+static int mpu3050_config_regulator(struct i2c_client *client, bool on)
+{
+ int rc = 0, i;
+ int num_reg = sizeof(mpu_vreg) / sizeof(struct sensor_regulator);
+
+ if (on) {
+ for (i = 0; i < num_reg; i++) {
+ mpu_vreg[i].vreg = regulator_get(&client->dev,
+ mpu_vreg[i].name);
+ if (IS_ERR(mpu_vreg[i].vreg)) {
+ rc = PTR_ERR(mpu_vreg[i].vreg);
+ pr_err("%s:regulator get failed rc=%d\n",
+ __func__, rc);
+ goto error_vdd;
+ }
+
+ if (regulator_count_voltages(mpu_vreg[i].vreg) > 0) {
+ rc = regulator_set_voltage(mpu_vreg[i].vreg,
+ mpu_vreg[i].min_uV, mpu_vreg[i].max_uV);
+ if (rc) {
+ pr_err("%s:set_voltage failed rc=%d\n",
+ __func__, rc);
+ regulator_put(mpu_vreg[i].vreg);
+ goto error_vdd;
+ }
+ }
+
+ rc = regulator_enable(mpu_vreg[i].vreg);
+ if (rc) {
+ pr_err("%s: regulator_enable failed rc =%d\n",
+ __func__,
+ rc);
+
+ if (regulator_count_voltages(
+ mpu_vreg[i].vreg) > 0) {
+ regulator_set_voltage(mpu_vreg[i].vreg,
+ 0, mpu_vreg[i].max_uV);
+ }
+ regulator_put(mpu_vreg[i].vreg);
+ goto error_vdd;
+ }
+ }
+ return rc;
+ } else {
+ i = num_reg;
+ }
+error_vdd:
+ while (--i >= 0) {
+ if (regulator_count_voltages(mpu_vreg[i].vreg) > 0) {
+ regulator_set_voltage(mpu_vreg[i].vreg, 0,
+ mpu_vreg[i].max_uV);
+ }
+ regulator_disable(mpu_vreg[i].vreg);
+ regulator_put(mpu_vreg[i].vreg);
+ }
+ return rc;
+}
+
/**
* mpu3050_xyz_read_reg - read the axes values
* @buffer: provide register addr and get register
@@ -179,11 +254,21 @@
{
u8 value;
+ if (val) {
+ mpu3050_config_regulator(client, 1);
+ udelay(10);
+ }
+
value = i2c_smbus_read_byte_data(client, MPU3050_PWR_MGM);
value = (value & ~MPU3050_PWR_MGM_MASK) |
(((val << MPU3050_PWR_MGM_POS) & MPU3050_PWR_MGM_MASK) ^
MPU3050_PWR_MGM_MASK);
i2c_smbus_write_byte_data(client, MPU3050_PWR_MGM, value);
+
+ if (!val) {
+ udelay(10);
+ mpu3050_config_regulator(client, 0);
+ }
}
/**
@@ -210,6 +295,9 @@
pm_runtime_put(sensor->dev);
return error;
}
+ if (sensor->use_poll)
+ schedule_delayed_work(&sensor->input_work,
+ msecs_to_jiffies(MPU3050_DEFAULT_POLL_INTERVAL));
return 0;
}
@@ -225,6 +313,9 @@
{
struct mpu3050_sensor *sensor = input_get_drvdata(input);
+ if (sensor->use_poll)
+ cancel_delayed_work_sync(&sensor->input_work);
+
pm_runtime_put(sensor->dev);
}
@@ -252,6 +343,33 @@
}
/**
+ * mpu3050_input_work_fn - polling work
+ * @work: the work struct
+ *
+ * Called by the work queue; read sensor data and generate an input
+ * event
+ */
+static void mpu3050_input_work_fn(struct work_struct *work)
+{
+ struct mpu3050_sensor *sensor;
+ struct axis_data axis;
+
+ sensor = container_of((struct delayed_work *)work,
+ struct mpu3050_sensor, input_work);
+
+ mpu3050_read_xyz(sensor->client, &axis);
+
+ input_report_abs(sensor->idev, ABS_X, axis.x);
+ input_report_abs(sensor->idev, ABS_Y, axis.y);
+ input_report_abs(sensor->idev, ABS_Z, axis.z);
+ input_sync(sensor->idev);
+
+ if (sensor->use_poll)
+ schedule_delayed_work(&sensor->input_work,
+ msecs_to_jiffies(MPU3050_DEFAULT_POLL_INTERVAL));
+}
+
+/**
* mpu3050_hw_init - initialize hardware
* @sensor: the sensor
*
@@ -325,6 +443,7 @@
sensor->client = client;
sensor->dev = &client->dev;
sensor->idev = idev;
+ sensor->platform_data = client->dev.platform_data;
mpu3050_set_power_mode(client, 1);
msleep(10);
@@ -365,14 +484,22 @@
if (error)
goto err_pm_set_suspended;
- error = request_threaded_irq(client->irq,
+ if (client->irq == 0) {
+ INIT_DELAYED_WORK(&sensor->input_work, mpu3050_input_work_fn);
+ sensor->use_poll = 1;
+ } else {
+ sensor->use_poll = 0;
+
+ error = request_threaded_irq(client->irq,
NULL, mpu3050_interrupt_thread,
IRQF_TRIGGER_RISING,
"mpu3050", sensor);
- if (error) {
- dev_err(&client->dev,
- "can't get IRQ %d, error %d\n", client->irq, error);
- goto err_pm_set_suspended;
+ if (error) {
+ dev_err(&client->dev,
+ "can't get IRQ %d, error %d\n",
+ client->irq, error);
+ goto err_pm_set_suspended;
+ }
}
error = input_register_device(idev);
@@ -387,7 +514,8 @@
return 0;
err_free_irq:
- free_irq(client->irq, sensor);
+ if (client->irq > 0)
+ free_irq(client->irq, sensor);
err_pm_set_suspended:
pm_runtime_set_suspended(&client->dev);
err_free_mem:
diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
index 964218f..5ffc133 100644
--- a/drivers/media/video/msm/Kconfig
+++ b/drivers/media/video/msm/Kconfig
@@ -261,6 +261,16 @@
---help---
Enable support for Video Pre-processing Engine
+config MSM_CAM_IRQ_ROUTER
+ bool "Enable MSM CAM IRQ Router"
+ depends on MSM_CAMERA
+ ---help---
+ Enable IRQ Router for Camera. Depending on the
+ configuration, this module can handle the
+ interrupts from multiple camera hardware
+ cores and composite them into a single
+ interrupt to the MSM.
+
config QUP_EXCLUSIVE_TO_CAMERA
bool "QUP exclusive to camera"
depends on MSM_CAMERA
diff --git a/drivers/media/video/msm/Makefile b/drivers/media/video/msm/Makefile
index 9ed6cca..431da2e 100644
--- a/drivers/media/video/msm/Makefile
+++ b/drivers/media/video/msm/Makefile
@@ -14,6 +14,7 @@
obj-$(CONFIG_MSM_CAMERA) += msm_isp.o msm.o msm_mem.o msm_mctl.o msm_mctl_buf.o msm_mctl_pp.o
obj-$(CONFIG_MSM_CAMERA) += server/ eeprom/ sensors/ actuators/ csi/
obj-$(CONFIG_MSM_CAMERA) += msm_gesture.o
+ obj-$(CONFIG_MSM_CAM_IRQ_ROUTER) += msm_camirq_router.o
else
obj-$(CONFIG_MSM_CAMERA) += msm_camera.o
endif
diff --git a/drivers/media/video/msm/csi/msm_csic.c b/drivers/media/video/msm/csi/msm_csic.c
index a217f9d..dbb4f32 100644
--- a/drivers/media/video/msm/csi/msm_csic.c
+++ b/drivers/media/video/msm/csi/msm_csic.c
@@ -388,6 +388,8 @@
{
struct csic_device *new_csic_dev;
int rc = 0;
+ struct msm_cam_subdev_info sd_info;
+
CDBG("%s: device id = %d\n", __func__, pdev->id);
new_csic_dev = kzalloc(sizeof(struct csic_device), GFP_KERNEL);
if (!new_csic_dev) {
@@ -455,8 +457,11 @@
iounmap(new_csic_dev->base);
new_csic_dev->base = NULL;
+ sd_info.sdev_type = CSIC_DEV;
+ sd_info.sd_index = pdev->id;
+ sd_info.irq_num = new_csic_dev->irq->start;
msm_cam_register_subdev_node(
- &new_csic_dev->subdev, CSIC_DEV, pdev->id);
+ &new_csic_dev->subdev, &sd_info);
return 0;
diff --git a/drivers/media/video/msm/csi/msm_csid.c b/drivers/media/video/msm/csi/msm_csid.c
index bb2a1d4..1ab4e66 100644
--- a/drivers/media/video/msm/csi/msm_csid.c
+++ b/drivers/media/video/msm/csi/msm_csid.c
@@ -298,6 +298,8 @@
static int __devinit csid_probe(struct platform_device *pdev)
{
struct csid_device *new_csid_dev;
+ struct msm_cam_subdev_info sd_info;
+
int rc = 0;
CDBG("%s: device id = %d\n", __func__, pdev->id);
new_csid_dev = kzalloc(sizeof(struct csid_device), GFP_KERNEL);
@@ -338,7 +340,10 @@
}
new_csid_dev->pdev = pdev;
- msm_cam_register_subdev_node(&new_csid_dev->subdev, CSID_DEV, pdev->id);
+ sd_info.sdev_type = CSID_DEV;
+ sd_info.sd_index = pdev->id;
+ sd_info.irq_num = new_csid_dev->irq->start;
+ msm_cam_register_subdev_node(&new_csid_dev->subdev, &sd_info);
return 0;
csid_no_resource:
diff --git a/drivers/media/video/msm/csi/msm_csiphy.c b/drivers/media/video/msm/csi/msm_csiphy.c
index e25660b..4693a8a 100644
--- a/drivers/media/video/msm/csi/msm_csiphy.c
+++ b/drivers/media/video/msm/csi/msm_csiphy.c
@@ -282,6 +282,8 @@
{
struct csiphy_device *new_csiphy_dev;
int rc = 0;
+ struct msm_cam_subdev_info sd_info;
+
CDBG("%s: device id = %d\n", __func__, pdev->id);
new_csiphy_dev = kzalloc(sizeof(struct csiphy_device), GFP_KERNEL);
if (!new_csiphy_dev) {
@@ -333,8 +335,11 @@
disable_irq(new_csiphy_dev->irq->start);
new_csiphy_dev->pdev = pdev;
+ sd_info.sdev_type = CSIPHY_DEV;
+ sd_info.sd_index = pdev->id;
+ sd_info.irq_num = new_csiphy_dev->irq->start;
msm_cam_register_subdev_node(
- &new_csiphy_dev->subdev, CSIPHY_DEV, pdev->id);
+ &new_csiphy_dev->subdev, &sd_info);
return 0;
csiphy_no_resource:
diff --git a/drivers/media/video/msm/csi/msm_ispif.c b/drivers/media/video/msm/csi/msm_ispif.c
index be97091..0f16bbf 100644
--- a/drivers/media/video/msm/csi/msm_ispif.c
+++ b/drivers/media/video/msm/csi/msm_ispif.c
@@ -640,6 +640,8 @@
static int __devinit ispif_probe(struct platform_device *pdev)
{
int rc = 0;
+ struct msm_cam_subdev_info sd_info;
+
CDBG("%s\n", __func__);
ispif = kzalloc(sizeof(struct ispif_device), GFP_KERNEL);
if (!ispif) {
@@ -687,7 +689,10 @@
}
ispif->pdev = pdev;
- msm_cam_register_subdev_node(&ispif->subdev, ISPIF_DEV, pdev->id);
+ sd_info.sdev_type = ISPIF_DEV;
+ sd_info.sd_index = pdev->id;
+ sd_info.irq_num = ispif->irq->start;
+ msm_cam_register_subdev_node(&ispif->subdev, &sd_info);
return 0;
ispif_no_mem:
diff --git a/drivers/media/video/msm/io/msm_io_vfe31.c b/drivers/media/video/msm/io/msm_io_vfe31.c
index 0c0c450..8c733a0 100644
--- a/drivers/media/video/msm/io/msm_io_vfe31.c
+++ b/drivers/media/video/msm/io/msm_io_vfe31.c
@@ -247,7 +247,7 @@
}
if (!IS_ERR(clk))
- clk_enable(clk);
+ clk_prepare_enable(clk);
else
rc = -1;
return rc;
@@ -303,10 +303,11 @@
}
if (!IS_ERR(clk)) {
- clk_disable(clk);
+ clk_disable_unprepare(clk);
clk_put(clk);
- } else
+ } else {
rc = -1;
+ }
return rc;
}
diff --git a/drivers/media/video/msm/msm.c b/drivers/media/video/msm/msm.c
index c3c09d4..d34b8e1 100644
--- a/drivers/media/video/msm/msm.c
+++ b/drivers/media/video/msm/msm.c
@@ -1277,6 +1277,7 @@
struct msm_camera_sensor_info *sdata;
struct msm_cam_v4l2_device *pcam;
struct msm_sensor_ctrl_t *s_ctrl;
+ struct msm_cam_subdev_info sd_info;
D("%s for %s\n", __func__, sensor_sd->name);
@@ -1321,8 +1322,11 @@
}
msm_server_update_sensor_info(pcam, sdata);
+ sd_info.sdev_type = SENSOR_DEV;
+ sd_info.sd_index = vnode_count;
+ sd_info.irq_num = 0;
/* register the subdevice, must be done for callbacks */
- rc = msm_cam_register_subdev_node(sensor_sd, SENSOR_DEV, vnode_count);
+ rc = msm_cam_register_subdev_node(sensor_sd, &sd_info);
if (rc < 0) {
D("%s sensor sub device register failed\n",
__func__);
diff --git a/drivers/media/video/msm/msm.h b/drivers/media/video/msm/msm.h
index 7bc6cff..d0322d1 100644
--- a/drivers/media/video/msm/msm.h
+++ b/drivers/media/video/msm/msm.h
@@ -58,14 +58,16 @@
#define MSM_GEMINI_DRV_NAME "msm_gemini"
#define MSM_MERCURY_DRV_NAME "msm_mercury"
#define MSM_I2C_MUX_DRV_NAME "msm_cam_i2c_mux"
+#define MSM_IRQ_ROUTER_DRV_NAME "msm_cam_irq_router"
#define MAX_NUM_CSIPHY_DEV 3
-#define MAX_NUM_CSID_DEV 3
+#define MAX_NUM_CSID_DEV 4
#define MAX_NUM_CSIC_DEV 3
#define MAX_NUM_ISPIF_DEV 1
#define MAX_NUM_VFE_DEV 2
#define MAX_NUM_AXI_DEV 2
#define MAX_NUM_VPE_DEV 1
+#define MAX_NUM_JPEG_DEV 3
enum msm_cam_subdev_type {
CSIPHY_DEV,
@@ -79,6 +81,7 @@
ACTUATOR_DEV,
EEPROM_DEV,
GESTURE_DEV,
+ IRQ_ROUTER_DEV,
};
/* msm queue management APIs*/
@@ -129,6 +132,7 @@
struct msm_free_buf {
uint8_t num_planes;
+ int32_t image_mode;
uint32_t ch_paddr[VIDEO_MAX_PLANES];
uint32_t vb;
};
@@ -404,6 +408,15 @@
struct msm_mem_map_info mem_map;
};
+struct msm_cam_subdev_info {
+ uint8_t sdev_type;
+ /* Subdev index. For eg: CSIPHY0, CSIPHY1 etc */
+ uint8_t sd_index;
+ /* This device/subdev's interrupt number, assigned
+ * from the hardware document. */
+ uint8_t irq_num;
+};
+
/* 2 for camera, 1 for gesture */
#define MAX_NUM_ACTIVE_CAMERA 3
@@ -420,6 +433,68 @@
uint32_t handle;
};
+struct msm_cam_server_irqmap_entry {
+ int irq_num;
+ int irq_idx;
+ uint8_t cam_hw_idx;
+ uint8_t is_composite;
+};
+
+struct intr_table_entry {
+ /* irq_num as understood by msm.
+ * Unique for every camera hw core & target. Use a mapping function
+ * to map this irq number to its equivalent index in camera side. */
+ int irq_num;
+ /* Camera hw core idx, in case of non-composite IRQs*/
+ uint8_t cam_hw_idx;
+ /* Camera hw core mask, in case of composite IRQs. */
+ uint32_t cam_hw_mask;
+ /* Each interrupt is mapped to an index, which is used
+ * to add/delete entries into the lookup table. Both the information
+ * are needed in the lookup table to avoid another subdev call into
+ * the IRQ Router subdev to get the irq_idx in the interrupt context */
+ int irq_idx;
+ /* Is this irq composite? */
+ uint8_t is_composite;
+ /* IRQ Trigger type: TRIGGER_RAISING, TRIGGER_HIGH, etc. */
+ uint32_t irq_trigger_type;
+ /* If IRQ Router hw is present,
+ * this field holds the number of camera hw core
+ * which are bundled together in the above
+ * interrupt. > 1 in case of composite irqs.
+ * If IRQ Router hw is not present, this field should be set to 1. */
+ int num_hwcore;
+ /* Pointers to the subdevs composited in this
+ * irq. If not composite, the 0th index stores the subdev to which
+ * this irq needs to be dispatched to. */
+ struct v4l2_subdev *subdev_list[CAMERA_SS_IRQ_MAX];
+ /* Device requesting the irq. */
+ const char *dev_name;
+ /* subdev private data, if any */
+ void *data;
+};
+
+struct irqmgr_intr_lkup_table {
+ /* Individual(hw) interrupt lookup table:
+ * This table is populated during initialization and doesnt
+ * change, unless the IRQ Router has been configured
+ * for composite IRQs. If the IRQ Router has been configured
+ * for composite IRQs, the is_composite field of that IRQ will
+ * be set to 1(default 0). And when there is an interrupt on
+ * that line, the composite interrupt lookup table is used
+ * for handling the interrupt. */
+ struct intr_table_entry ind_intr_tbl[CAMERA_SS_IRQ_MAX];
+
+ /* Composite interrupt lookup table:
+ * This table can be dynamically modified based on the usecase.
+ * If the usecase requires two or more HW core IRQs to be bundled
+ * into a single composite IRQ, then this table is populated
+ * accordingly. Also when this is done, the composite field
+ * in the intr_lookup_table has to be updated to reflect that
+ * the irq 'irq_num' will now be triggered in composite mode. */
+ struct intr_table_entry comp_intr_tbl[CAMERA_SS_IRQ_MAX];
+};
+
/* abstract camera server device for all sensor successfully probed*/
struct msm_cam_server_dev {
@@ -465,9 +540,18 @@
struct v4l2_subdev *axi_device[MAX_NUM_AXI_DEV];
struct v4l2_subdev *vpe_device[MAX_NUM_VPE_DEV];
struct v4l2_subdev *gesture_device;
-};
+ struct v4l2_subdev *irqr_device;
-/* camera server related functions */
+ spinlock_t intr_table_lock;
+ struct irqmgr_intr_lkup_table irq_lkup_table;
+ /* Stores the pointer to the subdev when the individual
+ * subdevices register themselves with the server. This
+ * will be used while dispatching composite irqs. The
+ * cam_hw_idx will serve as the index into this array to
+ * dispatch the irq to the corresponding subdev. */
+ struct v4l2_subdev *subdev_table[MSM_CAM_HW_MAX];
+ struct msm_cam_server_irqmap_entry hw_irqmap[CAMERA_SS_IRQ_MAX];
+};
/* ISP related functions */
void msm_isp_vfe_dev_init(struct v4l2_subdev *vd);
@@ -582,7 +666,7 @@
void __user *arg);
void msm_release_ion_client(struct kref *ref);
int msm_cam_register_subdev_node(struct v4l2_subdev *sd,
- enum msm_cam_subdev_type sdev_type, uint8_t index);
+ struct msm_cam_subdev_info *sd_info);
int msm_server_open_client(int *p_qidx);
int msm_server_send_ctrl(struct msm_ctrl_cmd *out, int ctrl_id);
int msm_server_close_client(int idx);
diff --git a/drivers/media/video/msm/msm_camirq_router.c b/drivers/media/video/msm/msm_camirq_router.c
new file mode 100644
index 0000000..52dd175
--- /dev/null
+++ b/drivers/media/video/msm/msm_camirq_router.c
@@ -0,0 +1,266 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/module.h>
+#include <linux/interrupt.h>
+#include <mach/irqs.h>
+#include <media/msm_isp.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-subdev.h>
+#include "msm.h"
+#include "server/msm_cam_server.h"
+#include "msm_camirq_router.h"
+
+#ifdef CONFIG_MSM_CAMERA_DEBUG
+#define D(fmt, args...) pr_debug("msm: " fmt, ##args)
+#else
+#define D(fmt, args...) do {} while (0)
+#endif
+
+static void msm_irqrouter_update_irqmap_entry(
+ struct msm_cam_server_irqmap_entry *entry,
+ int is_composite, int irq_idx, int cam_hw_idx)
+{
+ int rc = 0;
+ entry->irq_idx = irq_idx;
+ entry->is_composite = is_composite;
+ entry->cam_hw_idx = cam_hw_idx;
+ rc = msm_cam_server_update_irqmap(entry);
+ if (rc < 0)
+ pr_err("%s Error updating irq %d information ",
+ __func__, irq_idx);
+}
+
+static void msm_irqrouter_send_default_irqmap(
+ struct irqrouter_ctrl_type *irqrouter_ctrl)
+{
+ struct msm_cam_server_irqmap_entry *irqmap =
+ &irqrouter_ctrl->def_hw_irqmap[0];
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_0],
+ 0, CAMERA_SS_IRQ_0, MSM_CAM_HW_MICRO);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_1],
+ 0, CAMERA_SS_IRQ_1, MSM_CAM_HW_CCI);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_2],
+ 0, CAMERA_SS_IRQ_2, MSM_CAM_HW_CSI0);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_3],
+ 0, CAMERA_SS_IRQ_3, MSM_CAM_HW_CSI1);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_4],
+ 0, CAMERA_SS_IRQ_4, MSM_CAM_HW_CSI2);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_5],
+ 0, CAMERA_SS_IRQ_5, MSM_CAM_HW_CSI3);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_6],
+ 0, CAMERA_SS_IRQ_6, MSM_CAM_HW_ISPIF);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_7],
+ 0, CAMERA_SS_IRQ_7, MSM_CAM_HW_CPP);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_8],
+ 0, CAMERA_SS_IRQ_8, MSM_CAM_HW_VFE0);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_9],
+ 0, CAMERA_SS_IRQ_9, MSM_CAM_HW_VFE1);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_10],
+ 0, CAMERA_SS_IRQ_10, MSM_CAM_HW_JPEG0);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_11],
+ 0, CAMERA_SS_IRQ_11, MSM_CAM_HW_JPEG1);
+
+ msm_irqrouter_update_irqmap_entry(&irqmap[CAMERA_SS_IRQ_12],
+ 0, CAMERA_SS_IRQ_12, MSM_CAM_HW_JPEG2);
+}
+
+static int msm_irqrouter_open(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct irqrouter_ctrl_type *irqrouter_ctrl = v4l2_get_subdevdata(sd);
+ /* Only one object of IRQ Router allowed. */
+ if (atomic_read(&irqrouter_ctrl->active) != 0) {
+ pr_err("%s IRQ router is already opened\n", __func__);
+ return -EINVAL;
+ }
+
+ D("%s E ", __func__);
+ atomic_inc(&irqrouter_ctrl->active);
+
+ return 0;
+}
+
+static int msm_irqrouter_close(struct v4l2_subdev *sd,
+ struct v4l2_subdev_fh *fh)
+{
+ struct irqrouter_ctrl_type *irqrouter_ctrl = v4l2_get_subdevdata(sd);
+ if (atomic_read(&irqrouter_ctrl->active) == 0) {
+ pr_err("%s IRQ router is already closed\n", __func__);
+ return -EINVAL;
+ }
+ D("%s E ", __func__);
+ atomic_dec(&irqrouter_ctrl->active);
+ return 0;
+}
+
+static const struct v4l2_subdev_internal_ops msm_irqrouter_internal_ops = {
+ .open = msm_irqrouter_open,
+ .close = msm_irqrouter_close,
+};
+
+long msm_irqrouter_subdev_ioctl(struct v4l2_subdev *sd,
+ unsigned int cmd, void *arg)
+{
+ struct irqrouter_ctrl_type *irqrouter_ctrl = v4l2_get_subdevdata(sd);
+ struct msm_camera_irq_cfg *irq_cfg;
+ struct intr_table_entry irq_req;
+ int rc = 0;
+
+ /* Handle all IRQ Router Subdev IOCTLs here.
+ * Userspace sends the composite irq configuration.
+ * IRQ Router subdev then configures the registers to group
+ * together individual core hw irqs into a composite IRQ
+ * to the MSM IRQ controller. It also registers them with
+ * the irq manager in the camera server. */
+ switch (cmd) {
+ case MSM_IRQROUTER_CFG_COMPIRQ:
+ COPY_FROM_USER(rc, &irq_cfg, (void __user *)arg,
+ sizeof(struct msm_camera_irq_cfg));
+ if (rc) {
+ ERR_COPY_FROM_USER();
+ break;
+ }
+
+ if (!irq_cfg ||
+ (irq_cfg->irq_idx < CAMERA_SS_IRQ_0) ||
+ (irq_cfg->irq_idx >= CAMERA_SS_IRQ_MAX)) {
+ pr_err("%s Invalid input", __func__);
+ return -EINVAL;
+ } else {
+ irq_req.cam_hw_mask = irq_cfg->cam_hw_mask;
+ irq_req.irq_idx = irq_cfg->irq_idx;
+ irq_req.irq_num =
+ irqrouter_ctrl->def_hw_irqmap[irq_cfg->irq_idx].irq_num;
+ irq_req.is_composite = 1;
+ irq_req.irq_trigger_type = IRQF_TRIGGER_RISING;
+ irq_req.num_hwcore = irq_cfg->num_hwcore;
+ irq_req.data = NULL;
+ rc = msm_cam_server_request_irq(&irq_req);
+ if (rc < 0) {
+ pr_err("%s Error requesting comp irq %d ",
+ __func__, irq_req.irq_idx);
+ return rc;
+ }
+ irqrouter_ctrl->def_hw_irqmap
+ [irq_cfg->irq_idx].is_composite = 1;
+ }
+ break;
+ default:
+ pr_err("%s Invalid cmd %d ", __func__, cmd);
+ break;
+ }
+
+ return rc;
+}
+
+static const struct v4l2_subdev_core_ops msm_irqrouter_subdev_core_ops = {
+ .ioctl = msm_irqrouter_subdev_ioctl,
+};
+
+static const struct v4l2_subdev_ops msm_irqrouter_subdev_ops = {
+ .core = &msm_irqrouter_subdev_core_ops,
+};
+
+static int __devinit irqrouter_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ struct irqrouter_ctrl_type *irqrouter_ctrl;
+ struct msm_cam_subdev_info sd_info;
+
+ D("%s: device id = %d\n", __func__, pdev->id);
+
+ irqrouter_ctrl = kzalloc(sizeof(struct irqrouter_ctrl_type),
+ GFP_KERNEL);
+ if (!irqrouter_ctrl) {
+ pr_err("%s: not enough memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ v4l2_subdev_init(&irqrouter_ctrl->subdev, &msm_irqrouter_subdev_ops);
+ irqrouter_ctrl->subdev.internal_ops = &msm_irqrouter_internal_ops;
+ irqrouter_ctrl->subdev.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
+ snprintf(irqrouter_ctrl->subdev.name,
+ sizeof(irqrouter_ctrl->subdev.name), "msm_irqrouter");
+ v4l2_set_subdevdata(&irqrouter_ctrl->subdev, irqrouter_ctrl);
+ irqrouter_ctrl->pdev = pdev;
+
+ msm_irqrouter_send_default_irqmap(irqrouter_ctrl);
+
+ media_entity_init(&irqrouter_ctrl->subdev.entity, 0, NULL, 0);
+ irqrouter_ctrl->subdev.entity.type = MEDIA_ENT_T_DEVNODE_V4L;
+ irqrouter_ctrl->subdev.entity.group_id = IRQ_ROUTER_DEV;
+ irqrouter_ctrl->subdev.entity.name = pdev->name;
+
+ sd_info.sdev_type = IRQ_ROUTER_DEV;
+ sd_info.sd_index = 0;
+ sd_info.irq_num = 0;
+ /* Now register this subdev with the camera server. */
+ rc = msm_cam_register_subdev_node(&irqrouter_ctrl->subdev, &sd_info);
+ if (rc < 0) {
+ pr_err("%s Error registering irqr subdev %d", __func__, rc);
+ goto error;
+ }
+ irqrouter_ctrl->subdev.entity.revision =
+ irqrouter_ctrl->subdev.devnode->num;
+ atomic_set(&irqrouter_ctrl->active, 0);
+
+ platform_set_drvdata(pdev, &irqrouter_ctrl->subdev);
+
+ return rc;
+error:
+ kfree(irqrouter_ctrl);
+ return rc;
+}
+
+static int __exit irqrouter_exit(struct platform_device *pdev)
+{
+ kfree(irqrouter_ctrl);
+ return 0;
+}
+
+static struct platform_driver msm_irqrouter_driver = {
+ .probe = irqrouter_probe,
+ .remove = irqrouter_exit,
+ .driver = {
+ .name = MSM_IRQ_ROUTER_DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_irqrouter_init_module(void)
+{
+ return platform_driver_register(&msm_irqrouter_driver);
+}
+
+static void __exit msm_irqrouter_exit_module(void)
+{
+ platform_driver_unregister(&msm_irqrouter_driver);
+}
+
+module_init(msm_irqrouter_init_module);
+module_exit(msm_irqrouter_exit_module);
+MODULE_DESCRIPTION("msm camera irq router");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/video/msm/msm_camirq_router.h b/drivers/media/video/msm/msm_camirq_router.h
new file mode 100644
index 0000000..2c9cb73
--- /dev/null
+++ b/drivers/media/video/msm/msm_camirq_router.h
@@ -0,0 +1,206 @@
+/* Copyright (c) 2012, Code Aurora Forum. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 __MSM_CAM_IRQROUTER_H__
+#define __MSM_CAM_IRQROUTER_H__
+
+#include <linux/bitops.h>
+
+/* Camera SS common registers defines - Start */
+/* These registers are not directly related to
+ * IRQ Router, but are common to the Camera SS.
+ * IRQ Router registers dont have a unique base address
+ * in the memory mapped address space. It is offset from
+ * the Camera SS base address. So keep the common Camera
+ * SS registers also in the IRQ Router subdev for now. */
+
+/* READ ONLY: Camera Subsystem HW version */
+#define CAMSS_HW_VERSION 0x00000000
+
+/* Bits 4:0 of this register can be used to select a desired
+ * camera core test bus to drive the Camera_SS test bus output */
+#define CAMSS_TESTBUS_SEL 0x00000004
+
+/* Bits 4:0 of this register is used to allow either Microcontroller
+ * or the CCI drive the corresponding GPIO output.
+ * For eg: Setting bit 0 of this register allows Microcontroller to
+ * drive GPIO #0. Clearing the bit allows CCI to drive GPIO #0. */
+#define CAMSS_GPIO_MUX_SEL 0x00000008
+
+/* Bit 0 of this register is used to set the default AHB master
+ * for the AHB Arbiter. 0 - AHB Master 0, 1 - AHB Master 1*/
+#define CAMSS_AHB_ARB_CTL 0x0000000C
+
+/* READ ONLY */
+#define CAMSS_XPU2_STATUS 0x00000010
+
+/* Select the appropriate CSI clock for CSID Pixel pipes */
+#define CAMSS_CSI_PIX_CLK_MUX_SEL 0x00000020
+#define CAMSS_CSI_PIX_CLK_CGC_EN 0x00000024
+
+/* Select the appropriate CSI clock for CSID RDI pipes */
+#define CAMSS_CSI_RDI_CLK_MUX_SEL 0x00000028
+#define CAMSS_CSI_RDI_CLK_CGC_EN 0x0000002C
+
+/* Select the appropriate CSI clock for CSI Phy0 */
+#define CAMSS_CSI_PHY_0_CLK_MUX_SEL 0x00000030
+#define CAMSS_CSI_PHY_0_CLK_CGC_EN 0x00000034
+
+/* Select the appropriate CSI clock for CSI Phy1 */
+#define CAMSS_CSI_PHY_1_CLK_MUX_SEL 0x00000038
+#define CAMSS_CSI_PHY_1_CLK_CGC_EN 0x0000003C
+
+/* Select the appropriate CSI clock for CSI Phy2 */
+#define CAMSS_CSI_PHY_2_CLK_MUX_SEL 0x00000040
+#define CAMSS_CSI_PHY_2_CLK_CGC_EN 0x00000044
+/* Camera SS common registers defines - End */
+
+/* IRQ Router registers defines - Start */
+/* This register is used to reset the composite
+ * IRQ outputs of the Camera_SS IRQ Router */
+#define CAMSS_IRQ_COMPOSITE_RESET_CTRL 0x00000060
+
+/* By default, this 'allows' the interrupts from
+ * Micro to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_0 0x00000064
+
+/* By default, this 'allows' the interrupts from
+ * CCI to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_1 0x00000068
+
+/* By default, this 'allows' the interrupts from
+ * CSI_0 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_2 0x0000006C
+
+/* By default, this 'allows' the interrupts from
+ * CSI_1 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_3 0x00000070
+
+/* By default, this 'allows' the interrupts from
+ * CSI_2 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_4 0x00000074
+
+/* By default, this 'allows' the interrupts from
+ * CSI_3 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_5 0x00000078
+
+/* By default, this 'allows' the interrupts from
+ * ISPIF to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_6 0x0000007C
+
+/* By default, this 'allows' the interrupts from
+ * CPP to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_7 0x00000080
+
+/* By default, this 'allows' the interrupts from
+ * VFE_0 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_8 0x00000084
+
+/* By default, this 'allows' the interrupts from
+ * VFE_1 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_9 0x00000088
+
+/* By default, this 'allows' the interrupts from
+ * JPEG_0 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_10 0x0000008C
+
+/* By default, this 'allows' the interrupts from
+ * JPEG_1 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_11 0x00000090
+
+/* By default, this 'allows' the interrupts from
+ * JPEG_2 to pass through, unless configured in
+ * composite mode. */
+#define CAMSS_IRQ_COMPOSITE_MASK_12 0x00000094
+
+/* The following IRQ_COMPOSITE_MICRO_MASK registers
+ * allow the interrupts from the individual hw
+ * cores to be composited into an IRQ for Micro. */
+#define CAMSS_IRQ_COMPOSITE_MICRO_MASK_0 0x000000A4
+#define CAMSS_IRQ_COMPOSITE_MICRO_MASK_1 0x000000A8
+#define CAMSS_IRQ_COMPOSITE_MICRO_MASK_2 0x000000AC
+#define CAMSS_IRQ_COMPOSITE_MICRO_MASK_3 0x000000B0
+#define CAMSS_IRQ_COMPOSITE_MICRO_MASK_4 0x000000B4
+#define CAMSS_IRQ_COMPOSITE_MICRO_MASK_5 0x000000B8
+/* IRQ Router register defines - End */
+
+/* Writing this mask will reset all the composite
+ * IRQs of the Camera_SS IRQ Router */
+#define CAMSS_IRQ_COMPOSITE_RESET_MASK 0x003F1FFF
+
+/* Use this to enable Micro IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_MICRO_IRQ_IN_COMPOSITE BIT(0)
+/* Use this to enable CCI IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_CCI_IRQ_IN_COMPOSITE BIT(1)
+/* Use this to enable CSI0 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_CSI0_IRQ_IN_COMPOSITE BIT(2)
+/* Use this to enable CSI1 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_CSI1_IRQ_IN_COMPOSITE BIT(3)
+/* Use this to enable CSI2 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_CSI2_IRQ_IN_COMPOSITE BIT(4)
+/* Use this to enable CSI3 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_CSI3_IRQ_IN_COMPOSITE BIT(5)
+/* Use this to enable ISPIF IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_ISPIF_IRQ_IN_COMPOSITE BIT(6)
+/* Use this to enable CPP IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_CPP_IRQ_IN_COMPOSITE BIT(7)
+/* Use this to enable VFE0 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_VFE0_IRQ_IN_COMPOSITE BIT(8)
+/* Use this to enable VFE1 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_VFE1_IRQ_IN_COMPOSITE BIT(9)
+/* Use this to enable JPEG0 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_JPEG0_IRQ_IN_COMPOSITE BIT(10)
+/* Use this to enable JPEG1 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_JPEG1_IRQ_IN_COMPOSITE BIT(11)
+/* Use this to enable JPEG2 IRQ from IRQ Router
+ * composite interrupt */
+#define ENABLE_JPEG2_IRQ_IN_COMPOSITE BIT(12)
+
+struct irqrouter_ctrl_type {
+ /* v4l2 subdev */
+ struct v4l2_subdev subdev;
+ struct platform_device *pdev;
+
+ void __iomem *irqr_dev_base;
+
+ struct resource *irqr_dev_mem;
+ struct resource *irqr_dev_io;
+ atomic_t active;
+ struct msm_cam_server_irqmap_entry def_hw_irqmap[CAMERA_SS_IRQ_MAX];
+};
+
+#endif /* __MSM_CAM_IRQROUTER_H__ */
diff --git a/drivers/media/video/msm/msm_gesture.c b/drivers/media/video/msm/msm_gesture.c
index 6b81d15..5777cb5 100644
--- a/drivers/media/video/msm/msm_gesture.c
+++ b/drivers/media/video/msm/msm_gesture.c
@@ -455,6 +455,8 @@
struct msm_gesture_ctrl *p_gesture_ctrl = &g_gesture_ctrl;
struct v4l2_subdev *gesture_subdev =
kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+ struct msm_cam_subdev_info sd_info;
+
D("%s\n", __func__);
if (!gesture_subdev) {
pr_err("%s: no enough memory\n", __func__);
@@ -475,7 +477,10 @@
/* events */
gesture_subdev->flags |= V4L2_SUBDEV_FL_HAS_EVENTS;
- msm_cam_register_subdev_node(gesture_subdev, GESTURE_DEV, 0);
+ sd_info.sdev_type = GESTURE_DEV;
+ sd_info.sd_index = 0;
+ sd_info.irq_num = 0;
+ msm_cam_register_subdev_node(gesture_subdev, &sd_info);
gesture_subdev->entity.revision = gesture_subdev->devnode->num;
diff --git a/drivers/media/video/msm/msm_isp.c b/drivers/media/video/msm/msm_isp.c
index fab0095..834c9b0 100644
--- a/drivers/media/video/msm/msm_isp.c
+++ b/drivers/media/video/msm/msm_isp.c
@@ -146,7 +146,7 @@
static int msm_isp_notify_VFE_BUF_EVT(struct v4l2_subdev *sd, void *arg)
{
- int rc = -EINVAL, image_mode;
+ int rc = -EINVAL;
struct msm_vfe_resp *vdata = (struct msm_vfe_resp *)arg;
struct msm_free_buf free_buf, temp_free_buf;
struct msm_camvfe_params vfe_params;
@@ -154,16 +154,22 @@
struct msm_cam_media_controller *pmctl =
(struct msm_cam_media_controller *)v4l2_get_subdev_hostdata(sd);
struct msm_cam_v4l2_device *pcam = pmctl->pcam_ptr;
-
- int vfe_id = vdata->evt_msg.msg_id;
+ struct msm_frame_info *frame_info =
+ (struct msm_frame_info *)vdata->evt_msg.data;
+ uint32_t vfe_id, image_mode;
if (!pcam) {
pr_debug("%s pcam is null. return\n", __func__);
msm_isp_sync_free(vdata);
return rc;
}
- /* Convert the vfe msg to the image mode */
- image_mode = msm_isp_vfe_msg_to_img_mode(pmctl, vfe_id);
- BUG_ON(image_mode < 0);
+ if (frame_info) {
+ vfe_id = frame_info->path;
+ image_mode = frame_info->image_mode;
+ } else {
+ vfe_id = vdata->evt_msg.msg_id;
+ image_mode = msm_isp_vfe_msg_to_img_mode(pmctl, vfe_id);
+ }
+
switch (vdata->type) {
case VFE_MSG_V32_START:
case VFE_MSG_V32_START_RECORDING:
@@ -302,45 +308,49 @@
}
case NOTIFY_VFE_MSG_OUT: {
uint8_t msgid;
+ int32_t image_mode = -1;
struct isp_msg_output *isp_output =
(struct isp_msg_output *)arg;
- switch (isp_output->output_id) {
- case MSG_ID_OUTPUT_P:
- msgid = VFE_MSG_OUTPUT_P;
- break;
- case MSG_ID_OUTPUT_V:
- msgid = VFE_MSG_OUTPUT_V;
- break;
- case MSG_ID_OUTPUT_T:
- msgid = VFE_MSG_OUTPUT_T;
- break;
- case MSG_ID_OUTPUT_S:
- msgid = VFE_MSG_OUTPUT_S;
- break;
- case MSG_ID_OUTPUT_PRIMARY:
- msgid = VFE_MSG_OUTPUT_PRIMARY;
- break;
- case MSG_ID_OUTPUT_SECONDARY:
- msgid = VFE_MSG_OUTPUT_SECONDARY;
- break;
- default:
- pr_err("%s: Invalid VFE output id: %d\n",
- __func__, isp_output->output_id);
- rc = -EINVAL;
- break;
+ if (isp_output->buf.image_mode < 0) {
+ switch (isp_output->output_id) {
+ case MSG_ID_OUTPUT_P:
+ msgid = VFE_MSG_OUTPUT_P;
+ break;
+ case MSG_ID_OUTPUT_V:
+ msgid = VFE_MSG_OUTPUT_V;
+ break;
+ case MSG_ID_OUTPUT_T:
+ msgid = VFE_MSG_OUTPUT_T;
+ break;
+ case MSG_ID_OUTPUT_S:
+ msgid = VFE_MSG_OUTPUT_S;
+ break;
+ case MSG_ID_OUTPUT_PRIMARY:
+ msgid = VFE_MSG_OUTPUT_PRIMARY;
+ break;
+ case MSG_ID_OUTPUT_SECONDARY:
+ msgid = VFE_MSG_OUTPUT_SECONDARY;
+ break;
+ default:
+ pr_err("%s: Invalid VFE output id: %d\n",
+ __func__, isp_output->output_id);
+ rc = -EINVAL;
+ break;
+ }
+ if (!rc)
+ image_mode =
+ msm_isp_vfe_msg_to_img_mode(pmctl, msgid);
+ } else {
+ image_mode = isp_output->buf.image_mode;
}
-
- if (!rc) {
- isp_event->isp_data.isp_msg.msg_id =
- isp_output->output_id;
- isp_event->isp_data.isp_msg.frame_id =
- isp_output->frameCounter;
- buf = isp_output->buf;
- msgid = msm_isp_vfe_msg_to_img_mode(pmctl, msgid);
- BUG_ON(msgid < 0);
- msm_mctl_buf_done(pmctl, msgid,
- &buf, isp_output->frameCounter);
- }
+ isp_event->isp_data.isp_msg.msg_id =
+ isp_output->output_id;
+ isp_event->isp_data.isp_msg.frame_id =
+ isp_output->frameCounter;
+ buf = isp_output->buf;
+ BUG_ON(image_mode < 0);
+ msm_mctl_buf_done(pmctl, image_mode,
+ &buf, isp_output->frameCounter);
}
break;
case NOTIFY_VFE_MSG_COMP_STATS: {
diff --git a/drivers/media/video/msm/msm_vfe31_v4l2.c b/drivers/media/video/msm/msm_vfe31_v4l2.c
index 7a2670c..885cd90 100644
--- a/drivers/media/video/msm/msm_vfe31_v4l2.c
+++ b/drivers/media/video/msm/msm_vfe31_v4l2.c
@@ -420,15 +420,18 @@
vfe31_ctrl->vfebase + VFE_GLOBAL_RESET);
}
-static void vfe31_subdev_notify(int id, int path)
+static void vfe31_subdev_notify(int id, int path, int image_mode)
{
struct msm_vfe_resp rp;
+ struct msm_frame_info frame_info;
unsigned long flags;
spin_lock_irqsave(&vfe31_ctrl->sd_notify_lock, flags);
memset(&rp, 0, sizeof(struct msm_vfe_resp));
CDBG("vfe31_subdev_notify : msgId = %d\n", id);
rp.evt_msg.type = MSM_CAMERA_MSG;
- rp.evt_msg.msg_id = path;
+ frame_info.image_mode = image_mode;
+ frame_info.path = path;
+ rp.evt_msg.data = &frame_info;
rp.type = id;
v4l2_subdev_notify(&vfe31_ctrl->subdev, NOTIFY_VFE_BUF_EVT, &rp);
spin_unlock_irqrestore(&vfe31_ctrl->sd_notify_lock, flags);
@@ -442,10 +445,12 @@
ch_info = axi_cfg + V31_AXI_CFG_LEN;
vfe31_ctrl->outpath.out0.ch0 = 0x0000FFFF & *ch_info;
vfe31_ctrl->outpath.out0.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
- vfe31_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info++;
+ vfe31_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info;
+ vfe31_ctrl->outpath.out0.image_mode = 0x0000FFFF & (*ch_info++ >> 16);
vfe31_ctrl->outpath.out1.ch0 = 0x0000FFFF & *ch_info;
vfe31_ctrl->outpath.out1.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
- vfe31_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info++;
+ vfe31_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info;
+ vfe31_ctrl->outpath.out1.image_mode = 0x0000FFFF & (*ch_info++ >> 16);
vfe31_ctrl->outpath.out2.ch0 = 0x0000FFFF & *ch_info;
vfe31_ctrl->outpath.out2.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
vfe31_ctrl->outpath.out2.ch2 = 0x0000FFFF & *ch_info++;
@@ -1168,7 +1173,14 @@
{
struct vfe31_output_ch *outch = NULL;
struct msm_free_buf *b = NULL;
- vfe31_subdev_notify(id, path);
+ uint32_t image_mode = 0;
+
+ if (path == VFE_MSG_OUTPUT_PRIMARY)
+ image_mode = vfe31_ctrl->outpath.out0.image_mode;
+ else
+ image_mode = vfe31_ctrl->outpath.out1.image_mode;
+
+ vfe31_subdev_notify(id, path, image_mode);
outch = vfe31_get_ch(path);
if (outch->free_buf.ch_paddr[0])
b = &outch->free_buf;
@@ -1178,7 +1190,14 @@
{
struct vfe31_output_ch *outch = NULL;
int rc = 0;
- vfe31_subdev_notify(id, path);
+ uint32_t image_mode = 0;
+
+ if (path == VFE_MSG_OUTPUT_PRIMARY)
+ image_mode = vfe31_ctrl->outpath.out0.image_mode;
+ else
+ image_mode = vfe31_ctrl->outpath.out1.image_mode;
+
+ vfe31_subdev_notify(id, path, image_mode);
outch = vfe31_get_ch(path);
if (outch->ping.ch_paddr[0] && outch->pong.ch_paddr[0]) {
/* Configure Preview Ping Pong */
@@ -2595,11 +2614,13 @@
}
}
static void vfe_send_outmsg(struct v4l2_subdev *sd, uint8_t msgid,
- uint32_t ch0_paddr, uint32_t ch1_paddr, uint32_t ch2_paddr)
+ uint32_t ch0_paddr, uint32_t ch1_paddr,
+ uint32_t ch2_paddr, uint32_t image_mode)
{
struct isp_msg_output msg;
msg.output_id = msgid;
+ msg.buf.image_mode = image_mode;
msg.buf.ch_paddr[0] = ch0_paddr;
msg.buf.ch_paddr[1] = ch1_paddr;
msg.buf.ch_paddr[2] = ch2_paddr;
@@ -2681,7 +2702,8 @@
vfe_send_outmsg(&vfe31_ctrl->subdev,
MSG_ID_OUTPUT_PRIMARY, ch0_paddr,
- ch1_paddr, ch2_paddr);
+ ch1_paddr, ch2_paddr,
+ vfe31_ctrl->outpath.out0.image_mode);
if (vfe31_ctrl->liveshot_state == VFE_STATE_STOPPED)
vfe31_ctrl->liveshot_state = VFE_STATE_IDLE;
@@ -2753,7 +2775,8 @@
vfe_send_outmsg(&vfe31_ctrl->subdev,
MSG_ID_OUTPUT_SECONDARY, ch0_paddr,
- ch1_paddr, ch2_paddr);
+ ch1_paddr, ch2_paddr,
+ vfe31_ctrl->outpath.out1.image_mode);
} else {
vfe31_ctrl->outpath.out1.frame_drop_cnt++;
CDBG("path_irq_1 - no free buffer!\n");
@@ -3821,7 +3844,10 @@
static int __devinit vfe31_probe(struct platform_device *pdev)
{
int rc = 0;
+ struct msm_cam_subdev_info sd_info;
+
CDBG("%s: device id = %d\n", __func__, pdev->id);
+
vfe31_ctrl = kzalloc(sizeof(struct vfe31_ctrl_type), GFP_KERNEL);
if (!vfe31_ctrl) {
pr_err("%s: no enough memory\n", __func__);
@@ -3893,7 +3919,10 @@
disable_irq(vfe31_ctrl->vfeirq->start);
vfe31_ctrl->pdev = pdev;
- msm_cam_register_subdev_node(&vfe31_ctrl->subdev, VFE_DEV, 0);
+ sd_info.sdev_type = VFE_DEV;
+ sd_info.sd_index = 0;
+ sd_info.irq_num = vfe31_ctrl->vfeirq->start;
+ msm_cam_register_subdev_node(&vfe31_ctrl->subdev, &sd_info);
return 0;
vfe31_no_resource:
diff --git a/drivers/media/video/msm/msm_vfe31_v4l2.h b/drivers/media/video/msm/msm_vfe31_v4l2.h
index e94f286..739d157 100644
--- a/drivers/media/video/msm/msm_vfe31_v4l2.h
+++ b/drivers/media/video/msm/msm_vfe31_v4l2.h
@@ -695,7 +695,7 @@
struct vfe31_output_ch {
struct list_head free_buf_queue;
spinlock_t free_buf_lock;
- uint16_t output_fmt;
+ uint16_t image_mode;
int8_t ch0;
int8_t ch1;
int8_t ch2;
diff --git a/drivers/media/video/msm/msm_vfe32.c b/drivers/media/video/msm/msm_vfe32.c
index 3611656..9382292 100644
--- a/drivers/media/video/msm/msm_vfe32.c
+++ b/drivers/media/video/msm/msm_vfe32.c
@@ -25,6 +25,7 @@
#include <media/msm_isp.h>
#include "msm.h"
+#include "msm_cam_server.h"
#include "msm_vfe32.h"
atomic_t irq_cnt;
@@ -389,16 +390,19 @@
vfe32_ctrl->share_ctrl->vfebase + VFE_CAMIF_COMMAND);
}
-static void vfe32_subdev_notify(int id, int path,
+static void vfe32_subdev_notify(int id, int path, int image_mode,
struct v4l2_subdev *sd, struct vfe_share_ctrl_t *share_ctrl)
{
struct msm_vfe_resp rp;
+ struct msm_frame_info frame_info;
unsigned long flags = 0;
spin_lock_irqsave(&share_ctrl->sd_notify_lock, flags);
CDBG("vfe32_subdev_notify : msgId = %d\n", id);
memset(&rp, 0, sizeof(struct msm_vfe_resp));
rp.evt_msg.type = MSM_CAMERA_MSG;
- rp.evt_msg.msg_id = path;
+ frame_info.image_mode = image_mode;
+ frame_info.path = path;
+ rp.evt_msg.data = &frame_info;
rp.type = id;
v4l2_subdev_notify(sd, NOTIFY_VFE_BUF_EVT, &rp);
spin_unlock_irqrestore(&share_ctrl->sd_notify_lock, flags);
@@ -415,11 +419,15 @@
axi_ctrl->share_ctrl->outpath.out0.ch0 = 0x0000FFFF & *ch_info;
axi_ctrl->share_ctrl->outpath.out0.ch1 =
0x0000FFFF & (*ch_info++ >> 16);
- axi_ctrl->share_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info++;
+ axi_ctrl->share_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info;
+ axi_ctrl->share_ctrl->outpath.out0.image_mode =
+ 0x0000FFFF & (*ch_info++ >> 16);
axi_ctrl->share_ctrl->outpath.out1.ch0 = 0x0000FFFF & *ch_info;
axi_ctrl->share_ctrl->outpath.out1.ch1 =
0x0000FFFF & (*ch_info++ >> 16);
- axi_ctrl->share_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info++;
+ axi_ctrl->share_ctrl->outpath.out1.ch2 = 0x0000FFFF & *ch_info;
+ axi_ctrl->share_ctrl->outpath.out1.image_mode =
+ 0x0000FFFF & (*ch_info++ >> 16);
axi_ctrl->share_ctrl->outpath.out2.ch0 = 0x0000FFFF & *ch_info;
axi_ctrl->share_ctrl->outpath.out2.ch1 =
0x0000FFFF & (*ch_info++ >> 16);
@@ -1239,7 +1247,14 @@
{
struct vfe32_output_ch *outch = NULL;
struct msm_free_buf *b = NULL;
- vfe32_subdev_notify(id, path,
+ uint32_t image_mode = 0;
+
+ if (path == VFE_MSG_OUTPUT_PRIMARY)
+ image_mode = axi_ctrl->share_ctrl->outpath.out0.image_mode;
+ else
+ image_mode = axi_ctrl->share_ctrl->outpath.out1.image_mode;
+
+ vfe32_subdev_notify(id, path, image_mode,
&axi_ctrl->subdev, axi_ctrl->share_ctrl);
outch = vfe32_get_ch(path, axi_ctrl->share_ctrl);
if (outch->free_buf.ch_paddr[0])
@@ -1251,7 +1266,13 @@
{
struct vfe32_output_ch *outch = NULL;
int rc = 0;
- vfe32_subdev_notify(id, path,
+ uint32_t image_mode = 0;
+ if (path == VFE_MSG_OUTPUT_PRIMARY)
+ image_mode = vfe32_ctrl->share_ctrl->outpath.out0.image_mode;
+ else
+ image_mode = vfe32_ctrl->share_ctrl->outpath.out1.image_mode;
+
+ vfe32_subdev_notify(id, path, image_mode,
&vfe32_ctrl->subdev, vfe32_ctrl->share_ctrl);
outch = vfe32_get_ch(path, vfe32_ctrl->share_ctrl);
if (outch->ping.ch_paddr[0] && outch->pong.ch_paddr[0]) {
@@ -3163,11 +3184,13 @@
static void vfe_send_outmsg(
struct axi_ctrl_t *axi_ctrl, uint8_t msgid,
- uint32_t ch0_paddr, uint32_t ch1_paddr, uint32_t ch2_paddr)
+ uint32_t ch0_paddr, uint32_t ch1_paddr,
+ uint32_t ch2_paddr, uint32_t image_mode)
{
struct isp_msg_output msg;
msg.output_id = msgid;
+ msg.buf.image_mode = image_mode;
msg.buf.ch_paddr[0] = ch0_paddr;
msg.buf.ch_paddr[1] = ch1_paddr;
msg.buf.ch_paddr[2] = ch2_paddr;
@@ -3268,7 +3291,8 @@
vfe_send_outmsg(axi_ctrl,
MSG_ID_OUTPUT_PRIMARY, ch0_paddr,
- ch1_paddr, ch2_paddr);
+ ch1_paddr, ch2_paddr,
+ axi_ctrl->share_ctrl->outpath.out0.image_mode);
if (axi_ctrl->share_ctrl->liveshot_state == VFE_STATE_STOPPED)
axi_ctrl->share_ctrl->liveshot_state = VFE_STATE_IDLE;
@@ -3348,7 +3372,9 @@
vfe_send_outmsg(axi_ctrl,
MSG_ID_OUTPUT_SECONDARY, ch0_paddr,
- ch1_paddr, ch2_paddr);
+ ch1_paddr, ch2_paddr,
+ axi_ctrl->share_ctrl->outpath.out1.image_mode);
+
} else {
axi_ctrl->share_ctrl->outpath.out1.frame_drop_cnt++;
CDBG("path_irq_1 - no free buffer!\n");
@@ -3954,6 +3980,17 @@
return IRQ_HANDLED;
}
+int msm_axi_subdev_isr_routine(struct v4l2_subdev *sd,
+ u32 status, bool *handled)
+{
+ struct axi_ctrl_t *axi_ctrl = v4l2_get_subdevdata(sd);
+ irqreturn_t ret;
+ pr_info("%s E ", __func__);
+ ret = vfe32_parse_irq(axi_ctrl->vfeirq->start, axi_ctrl);
+ *handled = TRUE;
+ return 0;
+}
+
static long msm_vfe_subdev_ioctl(struct v4l2_subdev *sd,
unsigned int subdev_cmd, void *arg)
{
@@ -4653,6 +4690,7 @@
static const struct v4l2_subdev_core_ops msm_axi_subdev_core_ops = {
.ioctl = msm_axi_subdev_ioctl,
+ .interrupt_service_routine = msm_axi_subdev_isr_routine,
};
static const struct v4l2_subdev_video_ops msm_axi_subdev_video_ops = {
@@ -4672,6 +4710,9 @@
struct axi_ctrl_t *axi_ctrl;
struct vfe32_ctrl_type *vfe32_ctrl;
struct vfe_share_ctrl_t *share_ctrl;
+ struct intr_table_entry irq_req;
+ struct msm_cam_subdev_info sd_info;
+
CDBG("%s: device id = %d\n", __func__, pdev->id);
share_ctrl = kzalloc(sizeof(struct vfe_share_ctrl_t), GFP_KERNEL);
@@ -4707,7 +4748,11 @@
sizeof(axi_ctrl->subdev.name), "axi");
v4l2_set_subdevdata(&axi_ctrl->subdev, axi_ctrl);
axi_ctrl->pdev = pdev;
- msm_cam_register_subdev_node(&axi_ctrl->subdev, AXI_DEV, 0);
+
+ sd_info.sdev_type = AXI_DEV;
+ sd_info.sd_index = 0;
+ sd_info.irq_num = 0;
+ msm_cam_register_subdev_node(&axi_ctrl->subdev, &sd_info);
v4l2_subdev_init(&vfe32_ctrl->subdev, &msm_vfe_subdev_ops);
vfe32_ctrl->subdev.internal_ops = &msm_vfe_internal_ops;
@@ -4740,23 +4785,49 @@
goto vfe32_no_resource;
}
- rc = request_irq(axi_ctrl->vfeirq->start, vfe32_parse_irq,
- IRQF_TRIGGER_RISING, "vfe", axi_ctrl);
- if (rc < 0) {
- release_mem_region(axi_ctrl->vfemem->start,
- resource_size(axi_ctrl->vfemem));
- pr_err("%s: irq request fail\n", __func__);
- rc = -EBUSY;
+ /* Request for this device irq from the camera server. If the
+ * IRQ Router is present on this target, the interrupt will be
+ * handled by the camera server and the interrupt service
+ * routine called. If the request_irq call returns ENXIO, then
+ * the IRQ Router hardware is not present on this target. We
+ * have to request for the irq ourselves and register the
+ * appropriate interrupt handler. */
+ irq_req.cam_hw_idx = MSM_CAM_HW_VFE0;
+ irq_req.dev_name = "vfe";
+ irq_req.irq_idx = CAMERA_SS_IRQ_8;
+ irq_req.irq_num = axi_ctrl->vfeirq->start;
+ irq_req.is_composite = 0;
+ irq_req.irq_trigger_type = IRQF_TRIGGER_RISING;
+ irq_req.num_hwcore = 1;
+ irq_req.subdev_list[0] = &axi_ctrl->subdev;
+ irq_req.data = (void *)axi_ctrl;
+ rc = msm_cam_server_request_irq(&irq_req);
+ if (rc == -ENXIO) {
+ /* IRQ Router hardware is not present on this hardware.
+ * Request for the IRQ and register the interrupt handler. */
+ rc = request_irq(axi_ctrl->vfeirq->start, vfe32_parse_irq,
+ IRQF_TRIGGER_RISING, "vfe", axi_ctrl);
+ if (rc < 0) {
+ release_mem_region(axi_ctrl->vfemem->start,
+ resource_size(axi_ctrl->vfemem));
+ pr_err("%s: irq request fail\n", __func__);
+ rc = -EBUSY;
+ goto vfe32_no_resource;
+ }
+ disable_irq(axi_ctrl->vfeirq->start);
+ } else if (rc < 0) {
+ pr_err("%s Error registering irq ", __func__);
goto vfe32_no_resource;
}
- disable_irq(axi_ctrl->vfeirq->start);
-
tasklet_init(&axi_ctrl->vfe32_tasklet,
axi32_do_tasklet, (unsigned long)axi_ctrl);
vfe32_ctrl->pdev = pdev;
- msm_cam_register_subdev_node(&vfe32_ctrl->subdev, VFE_DEV, 0);
+ sd_info.sdev_type = VFE_DEV;
+ sd_info.sd_index = 0;
+ sd_info.irq_num = axi_ctrl->vfeirq->start;
+ msm_cam_register_subdev_node(&vfe32_ctrl->subdev, &sd_info);
return 0;
vfe32_no_resource:
diff --git a/drivers/media/video/msm/msm_vfe32.h b/drivers/media/video/msm/msm_vfe32.h
index 086600a..d5da432 100644
--- a/drivers/media/video/msm/msm_vfe32.h
+++ b/drivers/media/video/msm/msm_vfe32.h
@@ -741,7 +741,7 @@
struct vfe32_output_ch {
struct list_head free_buf_queue;
spinlock_t free_buf_lock;
- uint16_t output_fmt;
+ uint16_t image_mode;
int8_t ch0;
int8_t ch1;
int8_t ch2;
diff --git a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
index 893fc06..398621f 100644
--- a/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
+++ b/drivers/media/video/msm/msm_vfe7x27a_v4l2.c
@@ -370,6 +370,7 @@
struct isp_msg_output msg;
msg.output_id = msgid;
+ msg.buf.image_mode = -1;
msg.buf.ch_paddr[0] = ch0_paddr;
msg.buf.ch_paddr[1] = ch1_paddr;
msg.frameCounter = vfe2x_ctrl->vfeFrameId;
@@ -859,6 +860,7 @@
CDBG("vfe2x_subdev_notify : msgId = %d\n", id);
rp.evt_msg.type = MSM_CAMERA_MSG;
rp.evt_msg.msg_id = path;
+ rp.evt_msg.data = NULL;
rp.type = id;
v4l2_subdev_notify(&vfe2x_ctrl->subdev, NOTIFY_VFE_BUF_EVT, &rp);
spin_unlock_irqrestore(&vfe2x_ctrl->sd_notify_lock, flags);
@@ -1793,6 +1795,8 @@
static int __devinit vfe2x_probe(struct platform_device *pdev)
{
+ struct msm_cam_subdev_info sd_info;
+
CDBG("%s: device id = %d\n", __func__, pdev->id);
vfe2x_ctrl = kzalloc(sizeof(struct vfe2x_ctrl_type), GFP_KERNEL);
if (!vfe2x_ctrl) {
@@ -1809,7 +1813,10 @@
platform_set_drvdata(pdev, &vfe2x_ctrl->subdev);
vfe2x_ctrl->pdev = pdev;
- msm_cam_register_subdev_node(&vfe2x_ctrl->subdev, VFE_DEV, 0);
+ sd_info.sdev_type = VFE_DEV;
+ sd_info.sd_index = 0;
+ sd_info.irq_num = 0;
+ msm_cam_register_subdev_node(&vfe2x_ctrl->subdev, &sd_info);
return 0;
}
diff --git a/drivers/media/video/msm/msm_vpe.c b/drivers/media/video/msm/msm_vpe.c
index 54e9582..fb22cf9 100644
--- a/drivers/media/video/msm/msm_vpe.c
+++ b/drivers/media/video/msm/msm_vpe.c
@@ -970,6 +970,8 @@
static int __devinit msm_vpe_probe(struct platform_device *pdev)
{
int rc = 0;
+ struct msm_cam_subdev_info sd_info;
+
D("%s: device id = %d\n", __func__, pdev->id);
vpe_ctrl = kzalloc(sizeof(struct vpe_ctrl_type), GFP_KERNEL);
if (!vpe_ctrl) {
@@ -1028,7 +1030,10 @@
atomic_set(&vpe_ctrl->active, 0);
vpe_ctrl->pdev = pdev;
- msm_cam_register_subdev_node(&vpe_ctrl->subdev, VPE_DEV, pdev->id);
+ sd_info.sdev_type = VPE_DEV;
+ sd_info.sd_index = pdev->id;
+ sd_info.irq_num = vpe_ctrl->vpeirq->start;
+ msm_cam_register_subdev_node(&vpe_ctrl->subdev, &sd_info);
vpe_ctrl->subdev.entity.revision = vpe_ctrl->subdev.devnode->num;
msm_queue_init(&vpe_ctrl->eventData_q, "ackevents");
diff --git a/drivers/media/video/msm/server/msm_cam_server.c b/drivers/media/video/msm/server/msm_cam_server.c
index fc1e713..dfa7fbe 100644
--- a/drivers/media/video/msm/server/msm_cam_server.c
+++ b/drivers/media/video/msm/server/msm_cam_server.c
@@ -1435,20 +1435,379 @@
}
return;
-}
-
-void msm_cam_release_subdev_node(struct video_device *vdev)
-{
- struct v4l2_subdev *sd = video_get_drvdata(vdev);
- sd->devnode = NULL;
- kfree(vdev);
+}
+
+void msm_cam_release_subdev_node(struct video_device *vdev)
+{
+ struct v4l2_subdev *sd = video_get_drvdata(vdev);
+ sd->devnode = NULL;
+ kfree(vdev);
+}
+
+/* Helper function to get the irq_idx corresponding
+ * to the irq_num. */
+int get_irq_idx_from_irq_num(int irq_num)
+{
+ int i;
+ for (i = 0; i < CAMERA_SS_IRQ_MAX; i++)
+ if (irq_num == g_server_dev.hw_irqmap[i].irq_num)
+ return g_server_dev.hw_irqmap[i].irq_idx;
+
+ return -EINVAL;
+}
+
+static irqreturn_t msm_camera_server_parse_irq(int irq_num, void *data)
+{
+ unsigned long flags;
+ int irq_idx, i, rc;
+ u32 status = 0;
+ struct intr_table_entry *ind_irq_tbl;
+ struct intr_table_entry *comp_irq_tbl;
+ bool subdev_handled = 0;
+
+ irq_idx = get_irq_idx_from_irq_num(irq_num);
+ if (irq_idx < 0) {
+ pr_err("server_parse_irq: no clients for irq #%d. returning ",
+ irq_num);
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&g_server_dev.intr_table_lock, flags);
+ ind_irq_tbl = &g_server_dev.irq_lkup_table.ind_intr_tbl[0];
+ comp_irq_tbl = &g_server_dev.irq_lkup_table.comp_intr_tbl[0];
+ if (ind_irq_tbl[irq_idx].is_composite) {
+ for (i = 0; i < comp_irq_tbl[irq_idx].num_hwcore; i++) {
+ if (comp_irq_tbl[irq_idx].subdev_list[i]) {
+ rc = v4l2_subdev_call(
+ comp_irq_tbl[irq_idx].subdev_list[i],
+ core, interrupt_service_routine,
+ status, &subdev_handled);
+ if ((rc < 0) || !subdev_handled) {
+ pr_err("server_parse_irq:Error\n"
+ "handling irq %d rc = %d",
+ irq_num, rc);
+ /* Dispatch the irq to the remaining
+ * subdevs in the list. */
+ continue;
+ }
+ }
+ }
+ } else {
+ rc = v4l2_subdev_call(ind_irq_tbl[irq_idx].subdev_list[0],
+ core, interrupt_service_routine,
+ status, &subdev_handled);
+ if ((rc < 0) || !subdev_handled) {
+ pr_err("server_parse_irq: Error handling irq %d rc = %d",
+ irq_num, rc);
+ spin_unlock_irqrestore(&g_server_dev.intr_table_lock,
+ flags);
+ return IRQ_HANDLED;
+ }
+ }
+ spin_unlock_irqrestore(&g_server_dev.intr_table_lock, flags);
+ return IRQ_HANDLED;
+}
+
+/* Helper function to get the irq_idx corresponding
+ * to the camera hwcore. This function should _only_
+ * be invoked when the IRQ Router is configured
+ * non-composite mode. */
+int get_irq_idx_from_camhw_idx(int cam_hw_idx)
+{
+ int i;
+ for (i = 0; i < MSM_CAM_HW_MAX; i++)
+ if (cam_hw_idx == g_server_dev.hw_irqmap[i].cam_hw_idx)
+ return g_server_dev.hw_irqmap[i].irq_idx;
+
+ return -EINVAL;
+}
+
+static inline void update_compirq_subdev_info(
+ struct intr_table_entry *irq_entry,
+ uint32_t cam_hw_mask, uint8_t cam_hw_id,
+ int *num_hwcore)
+{
+ if (cam_hw_mask & (0x1 << cam_hw_id)) {
+ /* If the mask has been set for this cam hwcore
+ * update the subdev ptr......*/
+ irq_entry->subdev_list[cam_hw_id] =
+ g_server_dev.subdev_table[cam_hw_id];
+ (*num_hwcore)++;
+ } else {
+ /*....else, just clear it, so that the irq will
+ * not be dispatched to this hw. */
+ irq_entry->subdev_list[cam_hw_id] = NULL;
+ }
+}
+
+static int msm_server_update_composite_irq_info(
+ struct intr_table_entry *irq_req)
+{
+ int num_hwcore = 0, rc = 0;
+ struct intr_table_entry *comp_irq_tbl =
+ &g_server_dev.irq_lkup_table.comp_intr_tbl[0];
+
+ comp_irq_tbl[irq_req->irq_idx].is_composite = 1;
+ comp_irq_tbl[irq_req->irq_idx].irq_trigger_type =
+ irq_req->irq_trigger_type;
+ comp_irq_tbl[irq_req->irq_idx].num_hwcore = irq_req->num_hwcore;
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_MICRO, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_CCI, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_CSI0, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_CSI1, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_CSI2, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_CSI3, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_ISPIF, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_CPP, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_VFE0, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_VFE1, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_JPEG0, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_JPEG1, &num_hwcore);
+
+ update_compirq_subdev_info(&comp_irq_tbl[irq_req->irq_idx],
+ irq_req->cam_hw_mask, MSM_CAM_HW_JPEG2, &num_hwcore);
+
+ if (num_hwcore != irq_req->num_hwcore) {
+ pr_warn("%s Mismatch!! requested cam hwcores: %d, Mask set %d",
+ __func__, irq_req->num_hwcore, num_hwcore);
+ rc = -EINVAL;
+ }
+ return rc;
+}
+
+int msm_cam_server_request_irq(void *arg)
+{
+ unsigned long flags;
+ int rc = 0;
+ struct intr_table_entry *irq_req = (struct intr_table_entry *)arg;
+ struct intr_table_entry *ind_irq_tbl =
+ &g_server_dev.irq_lkup_table.ind_intr_tbl[0];
+ struct intr_table_entry *comp_irq_tbl =
+ &g_server_dev.irq_lkup_table.comp_intr_tbl[0];
+
+ if (!irq_req || !irq_req->irq_num || !irq_req->num_hwcore) {
+ pr_err("%s Invalid input ", __func__);
+ return -EINVAL;
+ }
+
+ if (!g_server_dev.irqr_device) {
+ /* This either means, the current target does not
+ * have a IRQ Router hw or the IRQ Router device is
+ * not probed yet. The latter should not happen.
+ * In any case, just return back without updating
+ * the interrupt lookup table. */
+ pr_info("%s IRQ Router hw is not present. ", __func__);
+ return -ENXIO;
+ }
+
+ if (irq_req->is_composite) {
+ if (irq_req->irq_idx >= CAMERA_SS_IRQ_0 &&
+ irq_req->irq_idx < CAMERA_SS_IRQ_MAX) {
+ spin_lock_irqsave(&g_server_dev.intr_table_lock, flags);
+ /* Update the composite irq information into
+ * the composite irq lookup table.... */
+ if (msm_server_update_composite_irq_info(irq_req)) {
+ pr_err("%s Invalid configuration", __func__);
+ spin_unlock_irqrestore(
+ &g_server_dev.intr_table_lock, flags);
+ return -EINVAL;
+ }
+ spin_unlock_irqrestore(&g_server_dev.intr_table_lock,
+ flags);
+ /*...and then update the corresponding entry
+ * in the individual irq lookup table to indicate
+ * that this IRQ is a composite irq and needs to be
+ * sent to multiple subdevs. */
+ ind_irq_tbl[irq_req->irq_idx].is_composite = 1;
+ rc = request_irq(comp_irq_tbl[irq_req->irq_idx].irq_num,
+ msm_camera_server_parse_irq,
+ irq_req->irq_trigger_type,
+ ind_irq_tbl[irq_req->irq_idx].dev_name,
+ ind_irq_tbl[irq_req->irq_idx].data);
+ if (rc < 0) {
+ pr_err("%s: request_irq failed for %s\n",
+ __func__, irq_req->dev_name);
+ return -EBUSY;
+ }
+ } else {
+ pr_err("%s Invalid irq_idx %d ",
+ __func__, irq_req->irq_idx);
+ return -EINVAL;
+ }
+ } else {
+ if (irq_req->cam_hw_idx >= MSM_CAM_HW_MICRO &&
+ irq_req->cam_hw_idx < MSM_CAM_HW_MAX) {
+ /* Update the irq information into
+ * the individual irq lookup table.... */
+ irq_req->irq_idx =
+ get_irq_idx_from_camhw_idx(irq_req->cam_hw_idx);
+ if (irq_req->cam_hw_idx < 0) {
+ pr_err("%s Invalid hw index %d ", __func__,
+ irq_req->cam_hw_idx);
+ return -EINVAL;
+ }
+ spin_lock_irqsave(&g_server_dev.intr_table_lock, flags);
+ /* Make sure the composite irq is not configured for
+ * this IRQ already. */
+ BUG_ON(ind_irq_tbl[irq_req->irq_idx].is_composite);
+
+ ind_irq_tbl[irq_req->irq_idx] = *irq_req;
+ /* irq_num is stored inside the server's hw_irqmap
+ * during the device subdevice registration. */
+ ind_irq_tbl[irq_req->irq_idx].irq_num =
+ g_server_dev.hw_irqmap[irq_req->irq_idx].irq_num;
+
+ /*...and clear the corresponding entry in the
+ * compsoite irq lookup table to indicate that this
+ * IRQ will only be dispatched to single subdev. */
+ memset(&comp_irq_tbl[irq_req->irq_idx], 0,
+ sizeof(struct intr_table_entry));
+ D("%s Saving Entry %d %d %d %p",
+ __func__,
+ ind_irq_tbl[irq_req->cam_hw_idx].irq_num,
+ ind_irq_tbl[irq_req->cam_hw_idx].cam_hw_idx,
+ ind_irq_tbl[irq_req->cam_hw_idx].is_composite,
+ ind_irq_tbl[irq_req->cam_hw_idx].subdev_list[0]);
+
+ spin_unlock_irqrestore(&g_server_dev.intr_table_lock,
+ flags);
+
+ rc = request_irq(ind_irq_tbl[irq_req->irq_idx].irq_num,
+ msm_camera_server_parse_irq,
+ irq_req->irq_trigger_type,
+ ind_irq_tbl[irq_req->irq_idx].dev_name,
+ ind_irq_tbl[irq_req->irq_idx].data);
+ if (rc < 0) {
+ pr_err("%s: request_irq failed for %s\n",
+ __func__, irq_req->dev_name);
+ return -EBUSY;
+ }
+ } else {
+ pr_err("%s Invalid hw index %d ", __func__,
+ irq_req->cam_hw_idx);
+ return -EINVAL;
+ }
+ }
+ D("%s Successfully requested for IRQ for device %s ", __func__,
+ irq_req->dev_name);
+ return rc;
+}
+
+int msm_cam_server_update_irqmap(
+ struct msm_cam_server_irqmap_entry *irqmap_entry)
+{
+ if (!irqmap_entry || (irqmap_entry->irq_idx < CAMERA_SS_IRQ_0 ||
+ irqmap_entry->irq_idx >= CAMERA_SS_IRQ_MAX)) {
+ pr_err("%s Invalid irqmap entry ", __func__);
+ return -EINVAL;
+ }
+ g_server_dev.hw_irqmap[irqmap_entry->irq_idx] = *irqmap_entry;
+ return 0;
+}
+
+static int msm_cam_server_register_subdev(struct v4l2_device *v4l2_dev,
+ struct v4l2_subdev *sd)
+{
+ int rc = 0;
+ struct video_device *vdev;
+
+ if (v4l2_dev == NULL || sd == NULL || !sd->name[0]) {
+ pr_err("%s Invalid input ", __func__);
+ return -EINVAL;
+ }
+
+ rc = v4l2_device_register_subdev(v4l2_dev, sd);
+ if (rc < 0) {
+ pr_err("%s v4l2 subdev register failed for %s ret = %d",
+ __func__, sd->name, rc);
+ return rc;
+ }
+
+ /* Register a device node for every subdev marked with the
+ * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
+ */
+ if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
+ return rc;
+
+ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
+ if (!vdev) {
+ pr_err("%s Not enough memory ", __func__);
+ rc = -ENOMEM;
+ goto clean_up;
+ }
+
+ video_set_drvdata(vdev, sd);
+ strlcpy(vdev->name, sd->name, sizeof(vdev->name));
+ vdev->v4l2_dev = v4l2_dev;
+ vdev->fops = &v4l2_subdev_fops;
+ vdev->release = msm_cam_release_subdev_node;
+ rc = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
+ sd->owner);
+ if (rc < 0) {
+ pr_err("%s Error registering video device %s", __func__,
+ sd->name);
+ kfree(vdev);
+ goto clean_up;
+ }
+#if defined(CONFIG_MEDIA_CONTROLLER)
+ sd->entity.info.v4l.major = VIDEO_MAJOR;
+ sd->entity.info.v4l.minor = vdev->minor;
+#endif
+ sd->devnode = vdev;
+ return 0;
+
+clean_up:
+ if (sd->devnode)
+ video_unregister_device(sd->devnode);
+ return rc;
+}
+
+static int msm_cam_server_fill_sdev_irqnum(int cam_hw_idx,
+ int irq_num)
+{
+ int rc = 0, irq_idx;
+ irq_idx = get_irq_idx_from_camhw_idx(cam_hw_idx);
+ if (irq_idx < 0) {
+ pr_err("%s Invalid cam_hw_idx %d ", __func__, cam_hw_idx);
+ rc = -EINVAL;
+ } else {
+ g_server_dev.hw_irqmap[irq_idx].irq_num = irq_num;
+ }
+ return rc;
}
int msm_cam_register_subdev_node(struct v4l2_subdev *sd,
- enum msm_cam_subdev_type sdev_type, uint8_t index)
+ struct msm_cam_subdev_info *sd_info)
{
- struct video_device *vdev;
- int err = 0;
+ int err = 0, cam_hw_idx;
+ uint8_t sdev_type, index;
+
+ sdev_type = sd_info->sdev_type;
+ index = sd_info->sd_index;
switch (sdev_type) {
case CSIPHY_DEV:
@@ -1466,7 +1825,13 @@
err = -EINVAL;
break;
}
+ cam_hw_idx = MSM_CAM_HW_CSI0 + index;
g_server_dev.csid_device[index] = sd;
+ if (g_server_dev.irqr_device) {
+ g_server_dev.subdev_table[cam_hw_idx] = sd;
+ err = msm_cam_server_fill_sdev_irqnum(cam_hw_idx,
+ sd_info->irq_num);
+ }
break;
case CSIC_DEV:
@@ -1480,6 +1845,11 @@
case ISPIF_DEV:
g_server_dev.ispif_device = sd;
+ if (g_server_dev.irqr_device) {
+ g_server_dev.subdev_table[cam_hw_idx] = sd;
+ err = msm_cam_server_fill_sdev_irqnum(MSM_CAM_HW_ISPIF,
+ sd_info->irq_num);
+ }
break;
case VFE_DEV:
@@ -1488,7 +1858,13 @@
err = -EINVAL;
break;
}
+ cam_hw_idx = MSM_CAM_HW_VFE0 + index;
g_server_dev.vfe_device[index] = sd;
+ if (g_server_dev.irqr_device) {
+ g_server_dev.subdev_table[cam_hw_idx] = sd;
+ err = msm_cam_server_fill_sdev_irqnum(cam_hw_idx,
+ sd_info->irq_num);
+ }
break;
case VPE_DEV:
@@ -1512,6 +1888,9 @@
case GESTURE_DEV:
g_server_dev.gesture_device = sd;
break;
+ case IRQ_ROUTER_DEV:
+ g_server_dev.irqr_device = sd;
+ break;
default:
break;
}
@@ -1519,49 +1898,11 @@
if (err < 0)
return err;
- err = v4l2_device_register_subdev(&g_server_dev.v4l2_dev, sd);
- if (err < 0) {
- pr_err("%s v4l2 subdev register failed for %d ret = %d",
- __func__, sdev_type, err);
- return err;
- }
-
- /* Register a device node for every subdev marked with the
- * V4L2_SUBDEV_FL_HAS_DEVNODE flag.
- */
- if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
- return err;
-
- vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
- if (!vdev) {
- err = -ENOMEM;
- goto clean_up;
- }
-
- video_set_drvdata(vdev, sd);
- strlcpy(vdev->name, sd->name, sizeof(vdev->name));
- vdev->v4l2_dev = &g_server_dev.v4l2_dev;
- vdev->fops = &v4l2_subdev_fops;
- vdev->release = msm_cam_release_subdev_node;
- err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
- sd->owner);
- if (err < 0) {
- kfree(vdev);
- goto clean_up;
- }
-#if defined(CONFIG_MEDIA_CONTROLLER)
- sd->entity.info.v4l.major = VIDEO_MAJOR;
- sd->entity.info.v4l.minor = vdev->minor;
-#endif
- sd->devnode = vdev;
- return 0;
-
-clean_up:
- if (sd->devnode)
- video_unregister_device(sd->devnode);
- return err;
+ err = msm_cam_server_register_subdev(&g_server_dev.v4l2_dev, sd);
+ return err;
}
+
static int msm_setup_server_dev(struct platform_device *pdev)
{
int rc = -ENODEV, i;
@@ -1604,6 +1945,9 @@
mutex_init(&g_server_dev.server_lock);
mutex_init(&g_server_dev.server_queue_lock);
+ spin_lock_init(&g_server_dev.intr_table_lock);
+ memset(&g_server_dev.irq_lkup_table, 0,
+ sizeof(struct irqmgr_intr_lkup_table));
g_server_dev.pcam_active = NULL;
g_server_dev.camera_info.num_cameras = 0;
atomic_set(&g_server_dev.number_pcam_active, 0);
@@ -1760,16 +2104,20 @@
struct v4l2_event v4l2_evt;
struct msm_isp_event_ctrl *isp_event;
- isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl), GFP_KERNEL);
- if (!isp_event) {
- pr_err("%s Insufficient memory. return", __func__);
- return -ENOMEM;
- }
+ void *ctrlcmd_data;
+
event_qcmd = kzalloc(sizeof(struct msm_queue_cmd), GFP_KERNEL);
if (!event_qcmd) {
pr_err("%s Insufficient memory. return", __func__);
- kfree(isp_event);
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto event_qcmd_alloc_fail;
+ }
+
+ isp_event = kzalloc(sizeof(struct msm_isp_event_ctrl), GFP_KERNEL);
+ if (!isp_event) {
+ pr_err("%s Insufficient memory. return", __func__);
+ rc = -ENOMEM;
+ goto isp_event_alloc_fail;
}
D("%s\n", __func__);
@@ -1782,12 +2130,23 @@
server_dev->server_queue[out->queue_idx].evt_id =
server_dev->server_evt_id;
v4l2_evt.type = V4L2_EVENT_PRIVATE_START + ctrl_id;
+ v4l2_evt.id = 0;
v4l2_evt.u.data[0] = out->queue_idx;
/* setup event object to transfer the command; */
isp_event->resptype = MSM_CAM_RESP_V4L2;
isp_event->isp_data.ctrl = *out;
isp_event->isp_data.ctrl.evt_id = server_dev->server_evt_id;
+ if (out->value != NULL && out->length != 0) {
+ ctrlcmd_data = kzalloc(out->length, GFP_KERNEL);
+ if (!ctrlcmd_data) {
+ rc = -ENOMEM;
+ goto ctrlcmd_alloc_fail;
+ }
+ memcpy(ctrlcmd_data, out->value, out->length);
+ isp_event->isp_data.ctrl.value = ctrlcmd_data;
+ }
+
atomic_set(&event_qcmd->on_heap, 1);
event_qcmd->command = isp_event;
@@ -1811,7 +2170,8 @@
if (!rc)
rc = -ETIMEDOUT;
if (rc < 0) {
- kfree(isp_event);
+ if (++server_dev->server_evt_id == 0)
+ server_dev->server_evt_id++;
pr_err("%s: wait_event error %d\n", __func__, rc);
return rc;
}
@@ -1832,7 +2192,6 @@
kfree(ctrlcmd);
free_qcmd(rcmd);
- kfree(isp_event);
D("%s: rc %d\n", __func__, rc);
/* rc is the time elapsed. */
if (rc >= 0) {
@@ -1845,6 +2204,13 @@
rc = -EINVAL;
}
return rc;
+
+ctrlcmd_alloc_fail:
+ kfree(isp_event);
+isp_event_alloc_fail:
+ kfree(event_qcmd);
+event_qcmd_alloc_fail:
+ return rc;
}
int msm_server_close_client(int idx)
diff --git a/drivers/media/video/msm/server/msm_cam_server.h b/drivers/media/video/msm/server/msm_cam_server.h
index 677feaa..8a02d32 100644
--- a/drivers/media/video/msm/server/msm_cam_server.h
+++ b/drivers/media/video/msm/server/msm_cam_server.h
@@ -61,5 +61,7 @@
struct v4l2_event_subscription *sub);
int msm_server_get_crop(struct msm_cam_v4l2_device *pcam,
int idx, struct v4l2_crop *crop);
-
+int msm_cam_server_request_irq(void *arg);
+int msm_cam_server_update_irqmap(
+ struct msm_cam_server_irqmap_entry *entry);
#endif /* _MSM_CAM_SERVER_H */
diff --git a/drivers/media/video/msm_vidc/msm_smem.c b/drivers/media/video/msm_vidc/msm_smem.c
index 25b5c5c..bdace3c 100644
--- a/drivers/media/video/msm_vidc/msm_smem.c
+++ b/drivers/media/video/msm_vidc/msm_smem.c
@@ -69,6 +69,8 @@
struct ion_handle *hndl;
size_t len;
int rc = 0;
+ if (size == 0)
+ goto skip_mem_alloc;
flags = flags | ION_HEAP(ION_CP_MM_HEAP_ID);
hndl = ion_alloc(client->clnt, size, align, flags);
if (IS_ERR_OR_NULL(hndl)) {
@@ -96,6 +98,7 @@
fail_map:
ion_free(client->clnt, hndl);
fail_shared_mem_alloc:
+skip_mem_alloc:
return rc;
}
diff --git a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
index 1c646dd..6cd9e6b 100644
--- a/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_v4l2_vidc.c
@@ -28,6 +28,7 @@
#include "msm_smem.h"
#define BASE_DEVICE_NUMBER 32
+#define MAX_EVENTS 30
struct msm_vidc_drv *vidc_driver;
@@ -215,21 +216,20 @@
int rc;
struct buffer_info *bi;
struct v4l2_buffer buffer_info;
+ struct v4l2_plane plane;
v4l2_inst = get_v4l2_inst(file, NULL);
if (b->count == 0) {
list_for_each_safe(ptr, next, &v4l2_inst->registered_bufs) {
bi = list_entry(ptr, struct buffer_info, list);
if (bi->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
buffer_info.type = bi->type;
- buffer_info.m.planes[0].reserved[0] =
- bi->fd;
- buffer_info.m.planes[0].reserved[1] =
- bi->buff_off;
- buffer_info.m.planes[0].length = bi->size;
- buffer_info.m.planes[0].m.userptr =
- bi->uvaddr;
+ plane.reserved[0] = bi->fd;
+ plane.reserved[1] = bi->buff_off;
+ plane.length = bi->size;
+ plane.m.userptr = bi->uvaddr;
+ buffer_info.m.planes = &plane;
buffer_info.length = 1;
- pr_err("Releasing buffer: %d, %d, %d\n",
+ pr_info("Releasing buffer: %d, %d, %d\n",
buffer_info.m.planes[0].reserved[0],
buffer_info.m.planes[0].reserved[1],
buffer_info.m.planes[0].length);
@@ -359,9 +359,7 @@
struct v4l2_event_subscription *sub)
{
int rc = 0;
- if (sub->type == V4L2_EVENT_ALL)
- sub->type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- rc = v4l2_event_subscribe(fh, sub, 0);
+ rc = v4l2_event_subscribe(fh, sub, MAX_EVENTS);
return rc;
}
@@ -445,7 +443,7 @@
INIT_LIST_HEAD(&core->instances);
mutex_init(&core->sync_lock);
spin_lock_init(&core->lock);
- core->base_addr = 0x34f00000;
+ core->base_addr = 0x14f00000;
core->state = VIDC_CORE_UNINIT;
for (i = SYS_MSG_INDEX(SYS_MSG_START);
i <= SYS_MSG_INDEX(SYS_MSG_END); i++) {
diff --git a/drivers/media/video/msm_vidc/msm_venc.c b/drivers/media/video/msm_vidc/msm_venc.c
index ed99d35..ec93628 100644
--- a/drivers/media/video/msm_vidc/msm_venc.c
+++ b/drivers/media/video/msm_vidc/msm_venc.c
@@ -348,7 +348,7 @@
{
.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
.name = "H.264 Loop Filter Mode",
- .type = V4L2_CTRL_TYPE_INTEGER,
+ .type = V4L2_CTRL_TYPE_MENU,
.minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
.maximum = L_MODE,
.default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
@@ -375,7 +375,9 @@
static u32 get_frame_size_compressed(int plane, u32 height, u32 width)
{
- return ((height + 31) & (~31)) * ((width + 31) & (~31)) * 3/2;
+ int sz = ((height + 31) & (~31)) * ((width + 31) & (~31)) * 3/2;
+ sz = (sz + 4095) & (~4095);
+ return sz;
}
static struct hal_quantization
diff --git a/drivers/media/video/msm_vidc/msm_vidc.c b/drivers/media/video/msm_vidc/msm_vidc.c
index a11f817..11fbcf4 100644
--- a/drivers/media/video/msm_vidc/msm_vidc.c
+++ b/drivers/media/video/msm_vidc/msm_vidc.c
@@ -186,7 +186,7 @@
void *vidc_get_userptr(void *alloc_ctx, unsigned long vaddr,
unsigned long size, int write)
{
- return NULL;
+ return (void *)0xdeadbeef;
}
void vidc_put_userptr(void *buf_priv)
diff --git a/drivers/media/video/msm_vidc/msm_vidc_common.c b/drivers/media/video/msm_vidc/msm_vidc_common.c
index 52f1dca..9b617aa 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_common.c
+++ b/drivers/media/video/msm_vidc/msm_vidc_common.c
@@ -183,21 +183,9 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct video_device *vdev;
- struct v4l2_event dqevent;
- struct msm_vidc_core *core;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
signal_session_msg_receipt(cmd, inst);
- core = inst->core;
- if (inst->session_type == MSM_VIDC_ENCODER)
- vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
- else
- vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
- dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- dqevent.u.data[0] = (uint8_t)MSM_VIDC_OPEN_DONE;
- v4l2_event_queue(vdev, &dqevent);
- return;
} else {
pr_err("Failed to get valid response for session init\n");
}
@@ -207,24 +195,17 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct video_device *vdev;
struct v4l2_event dqevent;
struct msm_vidc_cb_event *event_notify;
- struct msm_vidc_core *core;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
- core = inst->core;
- if (inst->session_type == MSM_VIDC_ENCODER)
- vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
- else
- vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
- dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- dqevent.u.data[0] = (uint8_t)MSM_VIDC_DECODER_EVENT_CHANGE;
+ dqevent.type = V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED;
+ dqevent.id = 0;
event_notify = (struct msm_vidc_cb_event *) response->data;
inst->reconfig_height = event_notify->height;
inst->reconfig_width = event_notify->width;
inst->in_reconfig = true;
- v4l2_event_queue(vdev, &dqevent);
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
return;
} else {
pr_err("Failed to get valid response for event_change\n");
@@ -262,20 +243,9 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct video_device *vdev;
- struct v4l2_event dqevent;
- struct msm_vidc_core *core;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
signal_session_msg_receipt(cmd, inst);
- core = inst->core;
- if (inst->session_type == MSM_VIDC_ENCODER)
- vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
- else
- vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
- dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- dqevent.u.data[0] = (uint8_t)MSM_VIDC_START_DONE;
- v4l2_event_queue(vdev, &dqevent);
} else {
pr_err("Failed to get valid response for start\n");
}
@@ -285,20 +255,9 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct video_device *vdev;
- struct v4l2_event dqevent;
- struct msm_vidc_core *core;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
signal_session_msg_receipt(cmd, inst);
- core = inst->core;
- if (inst->session_type == MSM_VIDC_ENCODER)
- vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
- else
- vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
- dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- dqevent.u.data[0] = (uint8_t)MSM_VIDC_STOP_DONE;
- v4l2_event_queue(vdev, &dqevent);
} else {
pr_err("Failed to get valid response for stop\n");
}
@@ -320,19 +279,12 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct video_device *vdev;
struct v4l2_event dqevent;
- struct msm_vidc_core *core;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
- core = inst->core;
- if (inst->session_type == MSM_VIDC_ENCODER)
- vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
- else
- vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
- dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- dqevent.u.data[0] = (uint8_t)MSM_VIDC_DECODER_FLUSH_DONE;
- v4l2_event_queue(vdev, &dqevent);
+ dqevent.type = V4L2_EVENT_MSM_VIDC_FLUSH_DONE;
+ dqevent.id = 0;
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
} else {
pr_err("Failed to get valid response for flush\n");
}
@@ -343,20 +295,13 @@
{
struct msm_vidc_cb_cmd_done *response = data;
struct msm_vidc_inst *inst;
- struct video_device *vdev;
struct v4l2_event dqevent;
- struct msm_vidc_core *core;
if (response) {
inst = (struct msm_vidc_inst *)response->session_id;
signal_session_msg_receipt(cmd, inst);
- core = inst->core;
- if (inst->session_type == MSM_VIDC_ENCODER)
- vdev = &core->vdev[MSM_VIDC_ENCODER].vdev;
- else
- vdev = &core->vdev[MSM_VIDC_DECODER].vdev;
- dqevent.type = V4L2_EVENT_PRIVATE_START + V4L2_EVENT_VIDC_BASE;
- dqevent.u.data[0] = (uint8_t)MSM_VIDC_CLOSE_DONE;
- v4l2_event_queue(vdev, &dqevent);
+ dqevent.type = V4L2_EVENT_MSM_VIDC_CLOSE_DONE;
+ dqevent.id = 0;
+ v4l2_event_queue_fh(&inst->event_handler, &dqevent);
} else {
pr_err("Failed to get valid response for session close\n");
}
diff --git a/drivers/media/video/msm_vidc/msm_vidc_internal.h b/drivers/media/video/msm_vidc/msm_vidc_internal.h
index fb1ab58..fb8fbc4 100644
--- a/drivers/media/video/msm_vidc/msm_vidc_internal.h
+++ b/drivers/media/video/msm_vidc/msm_vidc_internal.h
@@ -75,11 +75,6 @@
MSM_VIDC_CORE_UNINIT,
};
-enum vidc_resposes_id {
- MSM_VIDC_DECODER_FLUSH_DONE = 0x11,
- MSM_VIDC_DECODER_EVENT_CHANGE,
-};
-
struct buf_info {
struct list_head list;
struct vb2_buffer *buf;
diff --git a/drivers/mfd/pm8038-core.c b/drivers/mfd/pm8038-core.c
index 4271a2a..8fef786 100644
--- a/drivers/mfd/pm8038-core.c
+++ b/drivers/mfd/pm8038-core.c
@@ -36,6 +36,9 @@
#define REG_SPK_BASE 0x253
#define REG_SPK_REGISTERS 3
+#define REG_TEMP_ALARM_CTRL 0x01B
+#define REG_TEMP_ALARM_PWM 0x09B
+
#define PM8038_VERSION_MASK 0xFFF0
#define PM8038_VERSION_VALUE 0x09F0
#define PM8038_REVISION_MASK 0x000F
@@ -300,6 +303,30 @@
.pdata_size = sizeof("pm8038-dbg"),
};
+static const struct resource thermal_alarm_cell_resources[] __devinitconst = {
+ SINGLE_IRQ_RESOURCE("pm8038_tempstat_irq", PM8038_TEMPSTAT_IRQ),
+ SINGLE_IRQ_RESOURCE("pm8038_overtemp_irq", PM8038_OVERTEMP_IRQ),
+};
+
+static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
+ .adc_channel = CHANNEL_DIE_TEMP,
+ .adc_type = PM8XXX_TM_ADC_PM8XXX_ADC,
+ .reg_addr_temp_alarm_ctrl = REG_TEMP_ALARM_CTRL,
+ .reg_addr_temp_alarm_pwm = REG_TEMP_ALARM_PWM,
+ .tm_name = "pm8038_tz",
+ .irq_name_temp_stat = "pm8038_tempstat_irq",
+ .irq_name_over_temp = "pm8038_overtemp_irq",
+};
+
+static struct mfd_cell thermal_alarm_cell __devinitdata = {
+ .name = PM8XXX_TM_DEV_NAME,
+ .id = -1,
+ .resources = thermal_alarm_cell_resources,
+ .num_resources = ARRAY_SIZE(thermal_alarm_cell_resources),
+ .platform_data = &thermal_alarm_cdata,
+ .pdata_size = sizeof(struct pm8xxx_tm_core_data),
+};
+
static struct pm8xxx_vreg regulator_data[] = {
/* name pc_name ctrl test hpm_min */
NLDO1200("8038_l1", 0x0AE, 0x0AF, LDO_1200),
@@ -606,6 +633,14 @@
goto bail;
}
}
+
+ ret = mfd_add_devices(pmic->dev, 0, &thermal_alarm_cell, 1, NULL,
+ irq_base);
+ if (ret) {
+ pr_err("Failed to add thermal alarm subdevice ret=%d\n", ret);
+ goto bail;
+ }
+
return 0;
bail:
if (pmic->irq_chip) {
diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
index a21b39d..b5ffe94 100644
--- a/drivers/mmc/core/mmc.c
+++ b/drivers/mmc/core/mmc.c
@@ -1455,7 +1455,15 @@
err = mmc_poweroff_notify(host, MMC_PW_OFF_NOTIFY_SHORT);
} else {
if (mmc_card_can_sleep(host))
- err = mmc_card_sleep(host);
+ /*
+ * If sleep command has error it doesn't mean host
+ * cannot suspend, but a deeper low power state
+ * transition for the card has failed. Ignore
+ * sleep errors so that the suspend is not aborted.
+ * In error case, mmc_resume() takes care of
+ * complete intialization of the card.
+ */
+ mmc_card_sleep(host);
else if (!mmc_host_is_spi(host))
mmc_deselect_cards(host);
}
@@ -1512,7 +1520,7 @@
if (card && card->ext_csd.rev >= 3) {
err = mmc_card_sleepawake(host, 1);
if (err < 0)
- pr_debug("%s: Error %d while putting card into sleep",
+ pr_warn("%s: Error %d while putting card into sleep",
mmc_hostname(host), err);
}
diff --git a/drivers/net/usb/rmnet_usb_data.c b/drivers/net/usb/rmnet_usb_data.c
index ee9737a..9e1e252 100644
--- a/drivers/net/usb/rmnet_usb_data.c
+++ b/drivers/net/usb/rmnet_usb_data.c
@@ -505,6 +505,7 @@
{
struct usbnet *unet;
struct driver_info *info;
+ struct usb_device *udev;
unsigned int iface_num;
static int first_rmnet_iface_num = -EINVAL;
int status = 0;
@@ -555,8 +556,17 @@
if (status)
dev_dbg(&iface->dev, "mode debugfs file is not available\n");
+ udev = unet->udev;
+
/* allow modem to wake up suspended system */
- device_set_wakeup_enable(&unet->udev->dev, 1);
+ device_set_wakeup_enable(&udev->dev, 1);
+
+ /* set default autosuspend timeout for modem and roothub */
+ if (udev->parent && !udev->parent->parent) {
+ pm_runtime_set_autosuspend_delay(&udev->dev, 1000);
+ pm_runtime_set_autosuspend_delay(&udev->parent->dev, 200);
+ }
+
out:
return status;
}
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index c983389..a1561f0 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -3308,6 +3308,9 @@
int rc;
int vdd_safe;
+ /* forcing 19p2mhz before accessing any charger registers */
+ pm8921_chg_force_19p2mhz_clk(chip);
+
rc = pm_chg_masked_write(chip, SYS_CONFIG_2,
BOOT_DONE_BIT, BOOT_DONE_BIT);
if (rc) {
@@ -3501,8 +3504,6 @@
/* Disable EOC FSM processing */
pm8xxx_writeb(chip->dev->parent, CHG_BUCK_CTRL_TEST3, 0x91);
- pm8921_chg_force_19p2mhz_clk(chip);
-
rc = pm_chg_masked_write(chip, CHG_CNTRL, VREF_BATT_THERM_FORCE_ON,
VREF_BATT_THERM_FORCE_ON);
if (rc)
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 742beef..882eb97 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -1,6 +1,6 @@
config USB_DWC3
tristate "DesignWare USB3 DRD Core Support"
- depends on (USB && USB_GADGET)
+ depends on (USB || USB_GADGET)
select USB_OTG_UTILS
select USB_GADGET_DUALSPEED
select USB_XHCI_PLATFORM
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 3227508..0b46082 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -1,11 +1,13 @@
ccflags-$(CONFIG_USB_DWC3_DEBUG) := -DDEBUG
ccflags-$(CONFIG_USB_DWC3_VERBOSE) += -DVERBOSE_DEBUG
+ccflags-y += -Idrivers/usb/host
obj-$(CONFIG_USB_DWC3) += dwc3.o
dwc3-y := core.o
dwc3-y += host.o
dwc3-y += gadget.o ep0.o
+dwc3-y += dwc3_otg.o
ifneq ($(CONFIG_DEBUG_FS),)
dwc3-y += debugfs.o
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 99b58d8..1fbfdd8 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -372,6 +372,32 @@
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ /*
+ * Currently, the default and the recommended value for GUSB3PIPECTL
+ * [21:19] in the RTL is 3'b100 or 32 consecutive errors. Based on
+ * analysis and experiments in the lab, it is found that there is a
+ * relatively low probability of getting 32 consecutive word errors
+ * in the presence of random recovered noise (during electrical idle).
+ * This can delay the entry to a low power state such that for
+ * applications where the link stays in a non-U0 state for a short
+ * duration (< 1 microsecond), the local PHY does not enter the low
+ * power state prior to receiving a potential LFPS wakeup. This causes
+ * the PHY CDR (Clock and Data Recovery) operation to be unstable for
+ * some Synopsys PHYs.
+ *
+ * The proposal now is to change the default and the recommended value
+ * for GUSB3PIPECTL[21:19] in the RTL from 3'b100 to a minimum of
+ * 3'b001. Perform the same in software for controllers prior to 2.30a
+ * revision.
+ */
+
+ if (dwc->revision < DWC3_REVISION_230A) {
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+ reg &= ~DWC3_GUSB3PIPECTL_DELAY_P1P2P3;
+ reg |= 1 << __ffs(DWC3_GUSB3PIPECTL_DELAY_P1P2P3);
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+ }
+
ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE);
if (ret) {
dev_err(dwc->dev, "failed to allocate event buffers\n");
@@ -410,7 +436,6 @@
struct device *dev = &pdev->dev;
int ret = -ENOMEM;
- int irq;
void __iomem *regs;
void *mem;
@@ -425,16 +450,30 @@
dwc = PTR_ALIGN(mem, DWC3_ALIGN_MASK + 1);
dwc->mem = mem;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
- dev_err(dev, "missing resource\n");
+ dev_err(dev, "missing IRQ\n");
return -ENODEV;
}
+ dwc->xhci_resources[1] = *res;
- dwc->res = res;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing memory resource\n");
+ return -ENODEV;
+ }
+ dwc->xhci_resources[0] = *res;
+ dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
+ DWC3_XHCI_REGS_END;
- res = devm_request_mem_region(dev, res->start, resource_size(res),
+ /*
+ * Request memory region but exclude xHCI regs,
+ * since it will be requested by the xhci-plat driver.
+ */
+ res = devm_request_mem_region(dev, res->start + DWC3_GLOBALS_REGS_START,
+ resource_size(res) - DWC3_GLOBALS_REGS_START,
dev_name(dev));
+
if (!res) {
dev_err(dev, "can't request mem region\n");
return -ENOMEM;
@@ -446,19 +485,12 @@
return -ENOMEM;
}
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(dev, "missing IRQ\n");
- return -ENODEV;
- }
-
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
dwc->regs = regs;
dwc->regs_size = resource_size(res);
dwc->dev = dev;
- dwc->irq = irq;
if (!strncmp("super", maximum_speed, 5))
dwc->maximum_speed = DWC3_DCFG_SUPERSPEED;
@@ -505,15 +537,24 @@
break;
case DWC3_MODE_DRD:
dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG);
+ ret = dwc3_otg_init(dwc);
+ if (ret) {
+ dev_err(dev, "failed to initialize otg\n");
+ goto err1;
+ }
+
ret = dwc3_host_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize host\n");
+ dwc3_otg_exit(dwc);
goto err1;
}
ret = dwc3_gadget_init(dwc);
if (ret) {
dev_err(dev, "failed to initialize gadget\n");
+ dwc3_host_exit(dwc);
+ dwc3_otg_exit(dwc);
goto err1;
}
break;
@@ -542,8 +583,9 @@
dwc3_host_exit(dwc);
break;
case DWC3_MODE_DRD:
- dwc3_host_exit(dwc);
dwc3_gadget_exit(dwc);
+ dwc3_host_exit(dwc);
+ dwc3_otg_exit(dwc);
break;
default:
/* do nothing */
@@ -576,8 +618,9 @@
dwc3_host_exit(dwc);
break;
case DWC3_MODE_DRD:
- dwc3_host_exit(dwc);
dwc3_gadget_exit(dwc);
+ dwc3_host_exit(dwc);
+ dwc3_otg_exit(dwc);
break;
default:
/* do nothing */
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 6c7945b..98adff7 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -50,10 +50,13 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include "dwc3_otg.h"
+
/* Global constants */
#define DWC3_ENDPOINTS_NUM 32
+#define DWC3_XHCI_RESOURCES_NUM 2
-#define DWC3_EVENT_BUFFERS_SIZE PAGE_SIZE
+#define DWC3_EVENT_BUFFERS_SIZE (2 * PAGE_SIZE)
#define DWC3_EVENT_TYPE_MASK 0xfe
#define DWC3_EVENT_TYPE_DEV 0
@@ -70,11 +73,22 @@
#define DWC3_DEVICE_EVENT_ERRATIC_ERROR 9
#define DWC3_DEVICE_EVENT_CMD_CMPL 10
#define DWC3_DEVICE_EVENT_OVERFLOW 11
+#define DWC3_DEVICE_EVENT_VENDOR_DEV_TEST_LMP 12
#define DWC3_GEVNTCOUNT_MASK 0xfffc
#define DWC3_GSNPSID_MASK 0xffff0000
#define DWC3_GSNPSREV_MASK 0xffff
+/* DWC3 registers memory space boundries */
+#define DWC3_XHCI_REGS_START 0x0
+#define DWC3_XHCI_REGS_END 0x7fff
+#define DWC3_GLOBALS_REGS_START 0xc100
+#define DWC3_GLOBALS_REGS_END 0xc6ff
+#define DWC3_DEVICE_REGS_START 0xc700
+#define DWC3_DEVICE_REGS_END 0xcbff
+#define DWC3_OTG_REGS_START 0xcc00
+#define DWC3_OTG_REGS_END 0xccff
+
/* Global Registers */
#define DWC3_GSBUSCFG0 0xc100
#define DWC3_GSBUSCFG1 0xc104
@@ -139,8 +153,9 @@
/* OTG Registers */
#define DWC3_OCFG 0xcc00
#define DWC3_OCTL 0xcc04
-#define DWC3_OEVTEN 0xcc08
-#define DWC3_OSTS 0xcc0C
+#define DWC3_OEVT 0xcc08
+#define DWC3_OEVTEN 0xcc0c
+#define DWC3_OSTS 0xcc10
/* Bit fields */
@@ -172,6 +187,7 @@
/* Global USB3 PIPE Control Register */
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
+#define DWC3_GUSB3PIPECTL_DELAY_P1P2P3 (7 << 19)
/* Global TX Fifo Size Register */
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
@@ -182,6 +198,9 @@
#define DWC3_GHWPARAMS1_EN_PWROPT_NO 0
#define DWC3_GHWPARAMS1_EN_PWROPT_CLK 1
+/* Global HWPARAMS6 Register */
+#define DWC3_GHWPARAMS6_SRP_SUPPORT (1 << 10)
+
/* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -300,6 +319,37 @@
#define DWC3_DEPCMD_TYPE_BULK 2
#define DWC3_DEPCMD_TYPE_INTR 3
+/* OTG Events Register */
+#define DWC3_OEVT_DEVICEMODE (1 << 31)
+#define DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT (1 << 24)
+#define DWC3_OEVTEN_OTGADEVBHOSTENDEVNT (1 << 20)
+#define DWC3_OEVTEN_OTGADEVHOSTEVNT (1 << 19)
+#define DWC3_OEVTEN_OTGADEVHNPCHNGEVNT (1 << 18)
+#define DWC3_OEVTEN_OTGADEVSRPDETEVNT (1 << 17)
+#define DWC3_OEVTEN_OTGADEVSESSENDDETEVNT (1 << 16)
+#define DWC3_OEVTEN_OTGBDEVBHOSTENDEVNT (1 << 11)
+#define DWC3_OEVTEN_OTGBDEVHNPCHNGEVNT (1 << 10)
+#define DWC3_OEVTEN_OTGBDEVSESSVLDDETEVNT (1 << 9)
+#define DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT (1 << 8)
+
+/* OTG OSTS register */
+#define DWC3_OTG_OSTS_OTGSTATE_SHIFT (8)
+#define DWC3_OTG_OSTS_OTGSTATE (0xF << DWC3_OTG_OSTS_OTGSTATE_SHIFT)
+#define DWC3_OTG_OSTS_PERIPHERALSTATE (1 << 4)
+#define DWC3_OTG_OSTS_XHCIPRTPOWER (1 << 3)
+#define DWC3_OTG_OSTS_BSESVALID (1 << 2)
+#define DWC3_OTG_OSTS_VBUSVALID (1 << 1)
+#define DWC3_OTG_OSTS_CONIDSTS (1 << 0)
+
+/* OTG OSTS register */
+#define DWC3_OTG_OCTL_PERIMODE (1 << 6)
+#define DWC3_OTG_OCTL_PRTPWRCTL (1 << 5)
+#define DWC3_OTG_OCTL_HNPREQ (1 << 4)
+#define DWC3_OTG_OCTL_SESREQ (1 << 3)
+#define DWC3_OTG_OCTL_TERMSELDLPULSE (1 << 2)
+#define DWC3_OTG_OCTL_DEVSETHNPEN (1 << 1)
+#define DWC3_OTG_OCTL_HSTSETHNPEN (1 << 0)
+
/* Structures */
struct dwc3_trb;
@@ -582,8 +632,9 @@
spinlock_t lock;
struct device *dev;
+ struct dwc3_otg *dotg;
struct platform_device *xhci;
- struct resource *res;
+ struct resource xhci_resources[DWC3_XHCI_RESOURCES_NUM];
struct dwc3_event_buffer **ev_buffs;
struct dwc3_ep *eps[DWC3_ENDPOINTS_NUM];
@@ -594,8 +645,6 @@
void __iomem *regs;
size_t regs_size;
- int irq;
-
u32 num_event_buffers;
u32 u1u2;
u32 maximum_speed;
@@ -609,6 +658,7 @@
#define DWC3_REVISION_185A 0x5533185a
#define DWC3_REVISION_188A 0x5533188a
#define DWC3_REVISION_190A 0x5533190a
+#define DWC3_REVISION_230A 0x5533230a
unsigned is_selfpowered:1;
unsigned three_stage_setup:1;
@@ -633,6 +683,12 @@
u8 test_mode;
u8 test_mode_nr;
+
+ /* Indicate if the gadget was powered by the otg driver */
+ bool vbus_active;
+
+ /* Indicate if software connect was issued by the usb_gadget_driver */
+ bool softconnect;
};
/* -------------------------------------------------------------------------- */
@@ -772,6 +828,9 @@
void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc);
+int dwc3_otg_init(struct dwc3 *dwc);
+void dwc3_otg_exit(struct dwc3 *dwc);
+
int dwc3_host_init(struct dwc3 *dwc);
void dwc3_host_exit(struct dwc3 *dwc);
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 5a79cae..d216f17 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -17,6 +17,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/ioport.h>
+#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/types.h>
@@ -30,6 +31,7 @@
#include <mach/rpm-regulator.h>
+#include "dwc3_otg.h"
#include "core.h"
#include "gadget.h"
@@ -87,6 +89,16 @@
#define DBM_TRB_DATA_SRC 0x40000000
#define DBM_TRB_DMA 0x20000000
#define DBM_TRB_EP_NUM(ep) (ep<<24)
+/**
+ * USB QSCRATCH Hardware registers
+ *
+ */
+#define QSCRATCH_REG_OFFSET (0x000F8800)
+#define CHARGING_DET_CTRL_REG (QSCRATCH_REG_OFFSET + 0x18)
+#define CHARGING_DET_OUTPUT_REG (QSCRATCH_REG_OFFSET + 0x1C)
+#define ALT_INTERRUPT_EN_REG (QSCRATCH_REG_OFFSET + 0x20)
+#define HS_PHY_IRQ_STAT_REG (QSCRATCH_REG_OFFSET + 0x24)
+
struct dwc3_msm_req_complete {
struct list_head list_item;
@@ -104,6 +116,7 @@
u8 ep_num_mapping[DBM_MAX_EPS];
const struct usb_ep_ops *original_ep_ops[DWC3_ENDPOINTS_NUM];
struct list_head req_complete_list;
+ struct clk *core_clk;
struct regulator *hsusb_3p3;
struct regulator *hsusb_1p8;
struct regulator *hsusb_vddcx;
@@ -111,6 +124,11 @@
struct regulator *ssusb_vddcx;
enum usb_vdd_type ss_vdd_type;
enum usb_vdd_type hs_vdd_type;
+ struct dwc3_charger charger;
+ struct usb_phy *otg_xceiv;
+ struct delayed_work chg_work;
+ enum usb_chg_state chg_state;
+ u8 dcd_retries;
};
#define USB_HSPHY_3P3_VOL_MIN 3050000 /* uV */
@@ -150,6 +168,7 @@
};
static struct dwc3_msm *context;
+static u64 dwc3_msm_dma_mask = DMA_BIT_MASK(64);
/**
*
@@ -221,6 +240,34 @@
}
/**
+ * Write register and read back masked value to confirm it is written
+ *
+ * @base - DWC3 base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask specifying what should be updated
+ * @val - value to write.
+ *
+ */
+static inline void dwc3_msm_write_readback(void *base, u32 offset,
+ const u32 mask, u32 val)
+{
+ u32 write_val, tmp = ioread32(base + offset);
+
+ tmp &= ~mask; /* retain other bits */
+ write_val = tmp | val;
+
+ iowrite32(write_val, base + offset);
+
+ /* Read back to see if val was written */
+ tmp = ioread32(base + offset);
+ tmp &= mask; /* clear other bits */
+
+ if (tmp != val)
+ dev_err(context->dev, "%s: write: %x to QSCRATCH: %x FAILED\n",
+ __func__, val, offset);
+}
+
+/**
* Return DBM EP number which is not already configured.
*
*/
@@ -996,6 +1043,184 @@
return rc < 0 ? rc : 0;
}
+static void dwc3_chg_enable_secondary_det(struct dwc3_msm *mdwc)
+{
+ u32 chg_ctrl;
+
+ /* Turn off VDP_SRC */
+ dwc3_msm_write_reg(mdwc->base, CHARGING_DET_CTRL_REG, 0x0);
+ msleep(20);
+
+ /* Before proceeding make sure VDP_SRC is OFF */
+ chg_ctrl = dwc3_msm_read_reg(mdwc->base, CHARGING_DET_CTRL_REG);
+ if (chg_ctrl & 0x3F)
+ dev_err(mdwc->dev, "%s Unable to reset chg_det block: %x\n",
+ __func__, chg_ctrl);
+ /*
+ * Configure DM as current source, DP as current sink
+ * and enable battery charging comparators.
+ */
+ dwc3_msm_write_readback(mdwc->base, CHARGING_DET_CTRL_REG, 0x3F, 0x34);
+}
+
+static bool dwc3_chg_det_check_output(struct dwc3_msm *mdwc)
+{
+ u32 chg_det;
+ bool ret = false;
+
+ chg_det = dwc3_msm_read_reg(mdwc->base, CHARGING_DET_OUTPUT_REG);
+ ret = chg_det & 1;
+
+ return ret;
+}
+
+static void dwc3_chg_enable_primary_det(struct dwc3_msm *mdwc)
+{
+ /*
+ * Configure DP as current source, DM as current sink
+ * and enable battery charging comparators.
+ */
+ dwc3_msm_write_readback(mdwc->base, CHARGING_DET_CTRL_REG, 0x3F, 0x30);
+}
+
+static inline bool dwc3_chg_check_dcd(struct dwc3_msm *mdwc)
+{
+ u32 chg_state;
+ bool ret = false;
+
+ chg_state = dwc3_msm_read_reg(mdwc->base, CHARGING_DET_OUTPUT_REG);
+ ret = chg_state & 2;
+
+ return ret;
+}
+
+static inline void dwc3_chg_disable_dcd(struct dwc3_msm *mdwc)
+{
+ dwc3_msm_write_readback(mdwc->base, CHARGING_DET_CTRL_REG, 0x3F, 0x0);
+}
+
+static inline void dwc3_chg_enable_dcd(struct dwc3_msm *mdwc)
+{
+ /* Data contact detection enable, DCDENB */
+ dwc3_msm_write_readback(mdwc->base, CHARGING_DET_CTRL_REG, 0x3F, 0x2);
+}
+
+static void dwc3_chg_block_reset(struct dwc3_msm *mdwc)
+{
+ u32 chg_ctrl;
+
+ /* Clear charger detecting control bits */
+ dwc3_msm_write_reg(mdwc->base, CHARGING_DET_CTRL_REG, 0x0);
+
+ /* Clear alt interrupt latch and enable bits */
+ dwc3_msm_write_reg(mdwc->base, HS_PHY_IRQ_STAT_REG, 0xFFF);
+ dwc3_msm_write_reg(mdwc->base, ALT_INTERRUPT_EN_REG, 0x0);
+
+ udelay(100);
+
+ /* Before proceeding make sure charger block is RESET */
+ chg_ctrl = dwc3_msm_read_reg(mdwc->base, CHARGING_DET_CTRL_REG);
+ if (chg_ctrl & 0x3F)
+ dev_err(mdwc->dev, "%s Unable to reset chg_det block: %x\n",
+ __func__, chg_ctrl);
+}
+
+static const char *chg_to_string(enum dwc3_chg_type chg_type)
+{
+ switch (chg_type) {
+ case USB_SDP_CHARGER: return "USB_SDP_CHARGER";
+ case USB_DCP_CHARGER: return "USB_DCP_CHARGER";
+ case USB_CDP_CHARGER: return "USB_CDP_CHARGER";
+ default: return "INVALID_CHARGER";
+ }
+}
+
+#define DWC3_CHG_DCD_POLL_TIME (100 * HZ/1000) /* 100 msec */
+#define DWC3_CHG_DCD_MAX_RETRIES 6 /* Tdcd_tmout = 6 * 100 msec */
+#define DWC3_CHG_PRIMARY_DET_TIME (50 * HZ/1000) /* TVDPSRC_ON */
+#define DWC3_CHG_SECONDARY_DET_TIME (50 * HZ/1000) /* TVDMSRC_ON */
+
+static void dwc3_chg_detect_work(struct work_struct *w)
+{
+ struct dwc3_msm *mdwc = container_of(w, struct dwc3_msm, chg_work.work);
+ bool is_dcd = false, tmout, vout;
+ unsigned long delay;
+
+ dev_dbg(mdwc->dev, "chg detection work\n");
+ switch (mdwc->chg_state) {
+ case USB_CHG_STATE_UNDEFINED:
+ dwc3_chg_block_reset(mdwc);
+ dwc3_chg_enable_dcd(mdwc);
+ mdwc->chg_state = USB_CHG_STATE_WAIT_FOR_DCD;
+ mdwc->dcd_retries = 0;
+ delay = DWC3_CHG_DCD_POLL_TIME;
+ break;
+ case USB_CHG_STATE_WAIT_FOR_DCD:
+ is_dcd = dwc3_chg_check_dcd(mdwc);
+ tmout = ++mdwc->dcd_retries == DWC3_CHG_DCD_MAX_RETRIES;
+ if (is_dcd || tmout) {
+ dwc3_chg_disable_dcd(mdwc);
+ dwc3_chg_enable_primary_det(mdwc);
+ delay = DWC3_CHG_PRIMARY_DET_TIME;
+ mdwc->chg_state = USB_CHG_STATE_DCD_DONE;
+ } else {
+ delay = DWC3_CHG_DCD_POLL_TIME;
+ }
+ break;
+ case USB_CHG_STATE_DCD_DONE:
+ vout = dwc3_chg_det_check_output(mdwc);
+ if (vout) {
+ dwc3_chg_enable_secondary_det(mdwc);
+ delay = DWC3_CHG_SECONDARY_DET_TIME;
+ mdwc->chg_state = USB_CHG_STATE_PRIMARY_DONE;
+ } else {
+ mdwc->charger.chg_type = USB_SDP_CHARGER;
+ mdwc->chg_state = USB_CHG_STATE_DETECTED;
+ delay = 0;
+ }
+ break;
+ case USB_CHG_STATE_PRIMARY_DONE:
+ vout = dwc3_chg_det_check_output(mdwc);
+ if (vout)
+ mdwc->charger.chg_type = USB_DCP_CHARGER;
+ else
+ mdwc->charger.chg_type = USB_CDP_CHARGER;
+ mdwc->chg_state = USB_CHG_STATE_SECONDARY_DONE;
+ /* fall through */
+ case USB_CHG_STATE_SECONDARY_DONE:
+ mdwc->chg_state = USB_CHG_STATE_DETECTED;
+ /* fall through */
+ case USB_CHG_STATE_DETECTED:
+ dwc3_chg_block_reset(mdwc);
+ dev_dbg(mdwc->dev, "chg_type = %s\n",
+ chg_to_string(mdwc->charger.chg_type));
+ mdwc->charger.notify_detection_complete(mdwc->otg_xceiv->otg,
+ &mdwc->charger);
+ return;
+ default:
+ return;
+ }
+
+ queue_delayed_work(system_nrt_wq, &mdwc->chg_work, delay);
+}
+
+static void dwc3_start_chg_det(struct dwc3_charger *charger, bool start)
+{
+ struct dwc3_msm *mdwc = context;
+
+ if (start == false) {
+ cancel_delayed_work_sync(&mdwc->chg_work);
+ mdwc->chg_state = USB_CHG_STATE_UNDEFINED;
+ charger->chg_type = DWC3_INVALID_CHARGER;
+ return;
+ }
+
+ mdwc->chg_state = USB_CHG_STATE_UNDEFINED;
+ charger->chg_type = DWC3_INVALID_CHARGER;
+ queue_delayed_work(system_nrt_wq, &mdwc->chg_work, 0);
+}
+
+
static int __devinit dwc3_msm_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -1015,6 +1240,19 @@
msm->dev = &pdev->dev;
INIT_LIST_HEAD(&msm->req_complete_list);
+ INIT_DELAYED_WORK(&msm->chg_work, dwc3_chg_detect_work);
+
+ /*
+ * DWC3 Core requires its CORE CLK (aka master / bus clk) to
+ * run at 125Mhz in SSUSB mode and >60MHZ for HSUSB mode.
+ */
+ msm->core_clk = devm_clk_get(&pdev->dev, "core_clk");
+ if (IS_ERR(msm->core_clk)) {
+ dev_err(&pdev->dev, "failed to get core_clk\n");
+ return PTR_ERR(msm->core_clk);
+ }
+ clk_set_rate(msm->core_clk, 125000000);
+ clk_prepare_enable(msm->core_clk);
/* SS PHY */
msm->ss_vdd_type = VDDCX_CORNER;
@@ -1024,7 +1262,8 @@
"SSUSB_VDDCX");
if (IS_ERR(msm->ssusb_vddcx)) {
dev_err(&pdev->dev, "unable to get ssusb vddcx\n");
- return PTR_ERR(msm->ssusb_vddcx);
+ ret = PTR_ERR(msm->ssusb_vddcx);
+ goto disable_core_clk;
}
msm->ss_vdd_type = VDDCX;
dev_dbg(&pdev->dev, "ss_vdd_type: VDDCX\n");
@@ -1033,7 +1272,7 @@
ret = dwc3_ssusb_config_vddcx(1);
if (ret) {
dev_err(&pdev->dev, "ssusb vddcx configuration failed\n");
- return ret;
+ goto disable_core_clk;
}
ret = regulator_enable(context->ssusb_vddcx);
@@ -1115,10 +1354,9 @@
goto disable_hs_ldo;
}
- dma_set_coherent_mask(&dwc3->dev, pdev->dev.coherent_dma_mask);
-
dwc3->dev.parent = &pdev->dev;
- dwc3->dev.dma_mask = pdev->dev.dma_mask;
+ dwc3->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+ dwc3->dev.dma_mask = &dwc3_msm_dma_mask;
dwc3->dev.dma_parms = pdev->dev.dma_parms;
msm->resource_size = resource_size(res);
msm->dwc3 = dwc3;
@@ -1155,8 +1393,24 @@
/* Reset the DBM */
dwc3_msm_dbm_soft_reset();
+ msm->otg_xceiv = usb_get_transceiver();
+ if (msm->otg_xceiv) {
+ msm->charger.start_detection = dwc3_start_chg_det;
+ ret = dwc3_set_charger(msm->otg_xceiv->otg, &msm->charger);
+ if (ret || !msm->charger.notify_detection_complete) {
+ dev_err(&pdev->dev, "failed to register charger: %d\n",
+ ret);
+ goto put_xcvr;
+ }
+ } else {
+ dev_err(&pdev->dev, "%s: No OTG transceiver found\n", __func__);
+ }
+
return 0;
+put_xcvr:
+ usb_put_transceiver(msm->otg_xceiv);
+ platform_device_del(dwc3);
put_pdev:
platform_device_put(dwc3);
disable_hs_ldo:
@@ -1175,6 +1429,8 @@
regulator_disable(context->ssusb_vddcx);
unconfig_ss_vddcx:
dwc3_ssusb_config_vddcx(0);
+disable_core_clk:
+ clk_disable_unprepare(msm->core_clk);
return ret;
}
@@ -1183,6 +1439,10 @@
{
struct dwc3_msm *msm = platform_get_drvdata(pdev);
+ if (msm->otg_xceiv) {
+ dwc3_start_chg_det(&msm->charger, false);
+ usb_put_transceiver(msm->otg_xceiv);
+ }
platform_device_unregister(msm->dwc3);
dwc3_hsusb_ldo_enable(0);
@@ -1193,6 +1453,7 @@
dwc3_ssusb_ldo_init(0);
regulator_disable(msm->ssusb_vddcx);
dwc3_ssusb_config_vddcx(0);
+ clk_disable_unprepare(msm->core_clk);
return 0;
}
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index d7d9c0e..a5c77ad 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -49,7 +49,6 @@
#include <linux/of.h>
#include "core.h"
-#include "io.h"
/*
* All these registers belong to OMAP's Wrapper around the
@@ -143,6 +142,17 @@
u32 dma_status:1;
};
+static inline u32 dwc3_omap_readl(void __iomem *base, u32 offset)
+{
+ return readl_relaxed(base + offset);
+}
+
+static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
+{
+ writel_relaxed(value, base + offset);
+}
+
+
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
{
struct dwc3_omap *omap = _omap;
@@ -150,7 +160,7 @@
spin_lock(&omap->lock);
- reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_1);
+ reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_1);
if (reg & USBOTGSS_IRQ1_DMADISABLECLR) {
dev_dbg(omap->dev, "DMA Disable was Cleared\n");
@@ -184,10 +194,10 @@
if (reg & USBOTGSS_IRQ1_IDPULLUP_FALL)
dev_dbg(omap->dev, "IDPULLUP Fall\n");
- dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg);
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_1, reg);
- reg = dwc3_readl(omap->base, USBOTGSS_IRQSTATUS_0);
- dwc3_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg);
+ reg = dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0);
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQSTATUS_0, reg);
spin_unlock(&omap->lock);
@@ -270,7 +280,7 @@
omap->base = base;
omap->dwc3 = dwc3;
- reg = dwc3_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
+ reg = dwc3_omap_readl(omap->base, USBOTGSS_UTMI_OTG_STATUS);
utmi_mode = of_get_property(node, "utmi-mode", &size);
if (utmi_mode && size == sizeof(*utmi_mode)) {
@@ -293,10 +303,10 @@
}
}
- dwc3_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
+ dwc3_omap_writel(omap->base, USBOTGSS_UTMI_OTG_STATUS, reg);
/* check the DMA Status */
- reg = dwc3_readl(omap->base, USBOTGSS_SYSCONFIG);
+ reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
omap->dma_status = !!(reg & USBOTGSS_SYSCONFIG_DMADISABLE);
/* Set No-Idle and No-Standby */
@@ -306,7 +316,7 @@
reg |= (USBOTGSS_SYSCONFIG_STANDBYMODE(USBOTGSS_STANDBYMODE_NO_STANDBY)
| USBOTGSS_SYSCONFIG_IDLEMODE(USBOTGSS_IDLEMODE_NO_IDLE));
- dwc3_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
+ dwc3_omap_writel(omap->base, USBOTGSS_SYSCONFIG, reg);
ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0,
"dwc3-omap", omap);
@@ -318,7 +328,7 @@
/* enable all IRQs */
reg = USBOTGSS_IRQO_COREIRQ_ST;
- dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_0, reg);
reg = (USBOTGSS_IRQ1_OEVT |
USBOTGSS_IRQ1_DRVVBUS_RISE |
@@ -330,7 +340,7 @@
USBOTGSS_IRQ1_DISCHRGVBUS_FALL |
USBOTGSS_IRQ1_IDPULLUP_FALL);
- dwc3_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
+ dwc3_omap_writel(omap->base, USBOTGSS_IRQENABLE_SET_1, reg);
ret = platform_device_add_resources(dwc3, pdev->resource,
pdev->num_resources);
diff --git a/drivers/usb/dwc3/dwc3_otg.c b/drivers/usb/dwc3/dwc3_otg.c
new file mode 100644
index 0000000..5df030a
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3_otg.c
@@ -0,0 +1,664 @@
+/**
+ * dwc3_otg.c - DesignWare USB3 DRD Controller OTG
+ *
+ * Copyright (c) 2012, Code Aurora Forum. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/platform_device.h>
+
+#include "core.h"
+#include "dwc3_otg.h"
+#include "io.h"
+#include "xhci.h"
+
+
+/**
+ * dwc3_otg_set_host_regs - reset dwc3 otg registers to host operation.
+ *
+ * This function sets the OTG registers to work in A-Device host mode.
+ * This function should be called just before entering to A-Device mode.
+ *
+ * @w: Pointer to the dwc3 otg workqueue.
+ */
+static void dwc3_otg_set_host_regs(struct dwc3_otg *dotg)
+{
+ u32 octl;
+
+ /* Set OCTL[6](PeriMode) to 0 (host) */
+ octl = dwc3_readl(dotg->regs, DWC3_OCTL);
+ octl &= ~DWC3_OTG_OCTL_PERIMODE;
+ dwc3_writel(dotg->regs, DWC3_OCTL, octl);
+
+ /*
+ * TODO: add more OTG registers writes for HOST mode here,
+ * see figure 12-10 A-device flow in dwc3 Synopsis spec
+ */
+}
+
+/**
+ * dwc3_otg_set_peripheral_regs - reset dwc3 otg registers to peripheral operation.
+ *
+ * This function sets the OTG registers to work in B-Device peripheral mode.
+ * This function should be called just before entering to B-Device mode.
+ *
+ * @w: Pointer to the dwc3 otg workqueue.
+ */
+static void dwc3_otg_set_peripheral_regs(struct dwc3_otg *dotg)
+{
+ u32 octl;
+
+ /* Set OCTL[6](PeriMode) to 1 (peripheral) */
+ octl = dwc3_readl(dotg->regs, DWC3_OCTL);
+ octl |= DWC3_OTG_OCTL_PERIMODE;
+ dwc3_writel(dotg->regs, DWC3_OCTL, octl);
+
+ /*
+ * TODO: add more OTG registers writes for PERIPHERAL mode here,
+ * see figure 12-19 B-device flow in dwc3 Synopsis spec
+ */
+}
+
+/**
+ * dwc3_otg_start_host - helper function for starting/stoping the host controller driver.
+ *
+ * @otg: Pointer to the otg_transceiver structure.
+ * @on: start / stop the host controller driver.
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int dwc3_otg_start_host(struct usb_otg *otg, int on)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+ struct usb_hcd *hcd;
+ struct xhci_hcd *xhci;
+ int ret = 0;
+
+ if (!otg->host)
+ return -EINVAL;
+
+ hcd = bus_to_hcd(otg->host);
+ xhci = hcd_to_xhci(hcd);
+ if (on) {
+ dev_dbg(otg->phy->dev, "%s: turn on host %s\n",
+ __func__, otg->host->bus_name);
+ dwc3_otg_set_host_regs(dotg);
+
+ /*
+ * This should be revisited for more testing post-silicon.
+ * In worst case we may need to disconnect the root hub
+ * before stopping the controller so that it does not
+ * interfere with runtime pm/system pm.
+ * We can also consider registering and unregistering xhci
+ * platform device. It is almost similar to add_hcd and
+ * remove_hcd, But we may not use standard set_host method
+ * anymore.
+ */
+ ret = hcd->driver->start(hcd);
+ if (ret) {
+ dev_err(otg->phy->dev,
+ "%s: failed to start primary hcd, ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = xhci->shared_hcd->driver->start(xhci->shared_hcd);
+ if (ret) {
+ dev_err(otg->phy->dev,
+ "%s: failed to start secondary hcd, ret=%d\n",
+ __func__, ret);
+ return ret;
+ }
+ } else {
+ dev_dbg(otg->phy->dev, "%s: turn off host %s\n",
+ __func__, otg->host->bus_name);
+ hcd->driver->stop(hcd);
+ }
+
+ return 0;
+}
+
+/**
+ * dwc3_otg_set_host - bind/unbind the host controller driver.
+ *
+ * @otg: Pointer to the otg_transceiver structure.
+ * @host: Pointer to the usb_bus structure.
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int dwc3_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ if (host) {
+ dev_dbg(otg->phy->dev, "%s: set host %s\n",
+ __func__, host->bus_name);
+ otg->host = host;
+
+ /*
+ * Only after both peripheral and host are set then check
+ * OTG sm. This prevents unnecessary activation of the sm
+ * in case the ID is high.
+ */
+ if (otg->gadget)
+ schedule_work(&dotg->sm_work);
+ } else {
+ if (otg->phy->state == OTG_STATE_A_HOST) {
+ dwc3_otg_start_host(otg, 0);
+ otg->host = NULL;
+ otg->phy->state = OTG_STATE_UNDEFINED;
+ schedule_work(&dotg->sm_work);
+ } else {
+ otg->host = NULL;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * dwc3_otg_start_peripheral - bind/unbind the peripheral controller.
+ *
+ * @otg: Pointer to the otg_transceiver structure.
+ * @gadget: pointer to the usb_gadget structure.
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int dwc3_otg_start_peripheral(struct usb_otg *otg, int on)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ if (!otg->gadget)
+ return -EINVAL;
+
+ if (on) {
+ dev_dbg(otg->phy->dev, "%s: turn on gadget %s\n",
+ __func__, otg->gadget->name);
+ dwc3_otg_set_peripheral_regs(dotg);
+ usb_gadget_vbus_connect(otg->gadget);
+ } else {
+ dev_dbg(otg->phy->dev, "%s: turn off gadget %s\n",
+ __func__, otg->gadget->name);
+ usb_gadget_vbus_disconnect(otg->gadget);
+ }
+
+ return 0;
+}
+
+/**
+ * dwc3_otg_set_peripheral - bind/unbind the peripheral controller driver.
+ *
+ * @otg: Pointer to the otg_transceiver structure.
+ * @gadget: pointer to the usb_gadget structure.
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+static int dwc3_otg_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *gadget)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ if (gadget) {
+ dev_dbg(otg->phy->dev, "%s: set gadget %s\n",
+ __func__, gadget->name);
+ otg->gadget = gadget;
+
+ /*
+ * Only after both peripheral and host are set then check
+ * OTG sm. This prevents unnecessary activation of the sm
+ * in case the ID is grounded.
+ */
+ if (otg->host)
+ schedule_work(&dotg->sm_work);
+ } else {
+ if (otg->phy->state == OTG_STATE_B_PERIPHERAL) {
+ dwc3_otg_start_peripheral(otg, 0);
+ otg->gadget = NULL;
+ otg->phy->state = OTG_STATE_UNDEFINED;
+ schedule_work(&dotg->sm_work);
+ } else {
+ otg->gadget = NULL;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * dwc3_ext_chg_det_done - callback to handle charger detection completion
+ * @otg: Pointer to the otg transceiver structure
+ * @charger: Pointer to the external charger structure
+ *
+ * Returns 0 on success
+ */
+static void dwc3_ext_chg_det_done(struct usb_otg *otg, struct dwc3_charger *chg)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ /*
+ * Ignore chg_detection notification if BSV has gone off by this time.
+ * STOP chg_det as part of !BSV handling would reset the chg_det flags
+ */
+ if (test_bit(B_SESS_VLD, &dotg->inputs))
+ schedule_work(&dotg->sm_work);
+}
+
+/**
+ * dwc3_set_charger - bind/unbind external charger driver
+ * @otg: Pointer to the otg transceiver structure
+ * @charger: Pointer to the external charger structure
+ *
+ * Returns 0 on success
+ */
+int dwc3_set_charger(struct usb_otg *otg, struct dwc3_charger *charger)
+{
+ struct dwc3_otg *dotg = container_of(otg, struct dwc3_otg, otg);
+
+ dotg->charger = charger;
+ if (charger)
+ charger->notify_detection_complete = dwc3_ext_chg_det_done;
+
+ return 0;
+}
+
+/* IRQs which OTG driver is interested in handling */
+#define DWC3_OEVT_MASK (DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT | \
+ DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT)
+
+/**
+ * dwc3_otg_interrupt - interrupt handler for dwc3 otg events.
+ * @_dotg: Pointer to out controller context structure
+ *
+ * Returns IRQ_HANDLED on success otherwise IRQ_NONE.
+ */
+static irqreturn_t dwc3_otg_interrupt(int irq, void *_dotg)
+{
+ struct dwc3_otg *dotg = (struct dwc3_otg *)_dotg;
+ u32 osts, oevt_reg;
+ int ret = IRQ_NONE;
+ int handled_irqs = 0;
+
+ oevt_reg = dwc3_readl(dotg->regs, DWC3_OEVT);
+
+ if (!(oevt_reg & DWC3_OEVT_MASK))
+ return IRQ_NONE;
+
+ osts = dwc3_readl(dotg->regs, DWC3_OSTS);
+
+ if ((oevt_reg & DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT) ||
+ (oevt_reg & DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT)) {
+ /*
+ * ID sts has changed, set inputs later, in the workqueue
+ * function, switch from A to B or from B to A.
+ */
+
+ if (osts & DWC3_OTG_OSTS_CONIDSTS)
+ set_bit(ID, &dotg->inputs);
+ else
+ clear_bit(ID, &dotg->inputs);
+
+ if (osts & DWC3_OTG_OSTS_BSESVALID)
+ set_bit(B_SESS_VLD, &dotg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &dotg->inputs);
+
+ schedule_work(&dotg->sm_work);
+
+ handled_irqs |= (oevt_reg & DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT) ?
+ DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT : 0;
+ handled_irqs |= (oevt_reg & DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT) ?
+ DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT : 0;
+
+ ret = IRQ_HANDLED;
+
+ /* Clear the interrupts we handled */
+ dwc3_writel(dotg->regs, DWC3_OEVT, handled_irqs);
+ }
+
+ return ret;
+}
+
+/**
+ * dwc3_otg_init_sm - initialize OTG statemachine input
+ * @dotg: Pointer to the dwc3_otg structure
+ *
+ */
+void dwc3_otg_init_sm(struct dwc3_otg *dotg)
+{
+ u32 osts = dwc3_readl(dotg->regs, DWC3_OSTS);
+ struct usb_phy *phy = dotg->otg.phy;
+
+ /*
+ * TODO: If using external notifications then wait here till initial
+ * state is reported
+ */
+
+ dev_dbg(phy->dev, "Initialize OTG inputs, osts: 0x%x\n", osts);
+
+ if (osts & DWC3_OTG_OSTS_CONIDSTS)
+ set_bit(ID, &dotg->inputs);
+ else
+ clear_bit(ID, &dotg->inputs);
+
+ if (osts & DWC3_OTG_OSTS_BSESVALID)
+ set_bit(B_SESS_VLD, &dotg->inputs);
+ else
+ clear_bit(B_SESS_VLD, &dotg->inputs);
+}
+
+/**
+ * dwc3_otg_sm_work - workqueue function.
+ *
+ * @w: Pointer to the dwc3 otg workqueue
+ *
+ * NOTE: After any change in phy->state,
+ * we must reschdule the state machine.
+ */
+static void dwc3_otg_sm_work(struct work_struct *w)
+{
+ struct dwc3_otg *dotg = container_of(w, struct dwc3_otg, sm_work);
+ struct usb_phy *phy = dotg->otg.phy;
+ struct dwc3_charger *charger = dotg->charger;
+ bool work = 0;
+
+ dev_dbg(phy->dev, "%s state\n", otg_state_string(phy->state));
+
+ /* Check OTG state */
+ switch (phy->state) {
+ case OTG_STATE_UNDEFINED:
+ dwc3_otg_init_sm(dotg);
+ /* Switch to A or B-Device according to ID / BSV */
+ if (!test_bit(ID, &dotg->inputs) && phy->otg->host) {
+ dev_dbg(phy->dev, "!id\n");
+ phy->state = OTG_STATE_A_IDLE;
+ work = 1;
+ } else if (test_bit(B_SESS_VLD, &dotg->inputs)) {
+ dev_dbg(phy->dev, "b_sess_vld\n");
+ phy->state = OTG_STATE_B_IDLE;
+ work = 1;
+ } else {
+ phy->state = OTG_STATE_B_IDLE;
+ /* TODO: Enter low power state */
+ }
+ break;
+
+ case OTG_STATE_B_IDLE:
+ if (!test_bit(ID, &dotg->inputs) && phy->otg->host) {
+ dev_dbg(phy->dev, "!id\n");
+ phy->state = OTG_STATE_A_IDLE;
+ work = 1;
+ if (charger) {
+ if (charger->chg_type == DWC3_INVALID_CHARGER)
+ charger->start_detection(dotg->charger,
+ false);
+ else
+ charger->chg_type =
+ DWC3_INVALID_CHARGER;
+ }
+ } else if (test_bit(B_SESS_VLD, &dotg->inputs)) {
+ dev_dbg(phy->dev, "b_sess_vld\n");
+ if (charger) {
+ /* Has charger been detected? If no detect it */
+ switch (charger->chg_type) {
+ case DWC3_DCP_CHARGER:
+ /* TODO: initiate LPM */
+ break;
+ case DWC3_CDP_CHARGER:
+ dwc3_otg_start_peripheral(&dotg->otg,
+ 1);
+ phy->state = OTG_STATE_B_PERIPHERAL;
+ work = 1;
+ break;
+ case DWC3_SDP_CHARGER:
+ dwc3_otg_start_peripheral(&dotg->otg,
+ 1);
+ phy->state = OTG_STATE_B_PERIPHERAL;
+ work = 1;
+ break;
+ default:
+ dev_dbg(phy->dev, "chg_det started\n");
+ charger->start_detection(charger, true);
+ break;
+ }
+ } else {
+ /* no charger registered, start peripheral */
+ if (dwc3_otg_start_peripheral(&dotg->otg, 1)) {
+ /*
+ * Probably set_peripheral not called
+ * yet. We will re-try as soon as it
+ * will be called
+ */
+ dev_err(phy->dev,
+ "unable to start B-device\n");
+ phy->state = OTG_STATE_UNDEFINED;
+ return;
+ }
+ }
+ } else {
+ if (charger) {
+ if (charger->chg_type == DWC3_INVALID_CHARGER)
+ charger->start_detection(dotg->charger,
+ false);
+ else
+ charger->chg_type =
+ DWC3_INVALID_CHARGER;
+ }
+ /* TODO: Enter low power state */
+ }
+ break;
+
+ case OTG_STATE_B_PERIPHERAL:
+ if (!test_bit(B_SESS_VLD, &dotg->inputs) ||
+ !test_bit(ID, &dotg->inputs)) {
+ dev_dbg(phy->dev, "!id || !bsv\n");
+ dwc3_otg_start_peripheral(&dotg->otg, 0);
+ phy->state = OTG_STATE_B_IDLE;
+ if (charger)
+ charger->chg_type = DWC3_INVALID_CHARGER;
+ work = 1;
+ }
+ break;
+
+ case OTG_STATE_A_IDLE:
+ /* Switch to A-Device*/
+ if (test_bit(ID, &dotg->inputs)) {
+ dev_dbg(phy->dev, "id\n");
+ phy->state = OTG_STATE_B_IDLE;
+ work = 1;
+ } else {
+ if (dwc3_otg_start_host(&dotg->otg, 1)) {
+ /*
+ * Probably set_host was not called yet.
+ * We will re-try as soon as it will be called
+ */
+ dev_dbg(phy->dev,
+ "unable to start A-device\n");
+ phy->state = OTG_STATE_UNDEFINED;
+ return;
+ }
+ phy->state = OTG_STATE_A_HOST;
+ }
+ break;
+
+ case OTG_STATE_A_HOST:
+ if (test_bit(ID, &dotg->inputs)) {
+ dev_dbg(phy->dev, "id\n");
+ dwc3_otg_start_host(&dotg->otg, 0);
+ phy->state = OTG_STATE_B_IDLE;
+ work = 1;
+ }
+ break;
+
+ default:
+ dev_err(phy->dev, "%s: invalid otg-state\n", __func__);
+
+ }
+
+ if (work)
+ schedule_work(&dotg->sm_work);
+}
+
+
+/**
+ * dwc3_otg_reset - reset dwc3 otg registers.
+ *
+ * @w: Pointer to the dwc3 otg workqueue
+ */
+static void dwc3_otg_reset(struct dwc3_otg *dotg)
+{
+ /*
+ * OCFG[2] - OTG-Version = 1
+ * OCFG[1] - HNPCap = 0
+ * OCFG[0] - SRPCap = 0
+ */
+ dwc3_writel(dotg->regs, DWC3_OCFG, 0x4);
+
+ /*
+ * OCTL[6] - PeriMode = 1
+ * OCTL[5] - PrtPwrCtl = 0
+ * OCTL[4] - HNPReq = 0
+ * OCTL[3] - SesReq = 0
+ * OCTL[2] - TermSelDLPulse = 0
+ * OCTL[1] - DevSetHNPEn = 0
+ * OCTL[0] - HstSetHNPEn = 0
+ */
+ dwc3_writel(dotg->regs, DWC3_OCTL, 0x40);
+
+ /* Clear all otg events (interrupts) indications */
+ dwc3_writel(dotg->regs, DWC3_OEVT, 0xFFFF);
+
+ /* Enable ID/BSV StsChngEn event*/
+ dwc3_writel(dotg->regs, DWC3_OEVTEN,
+ DWC3_OEVTEN_OTGCONIDSTSCHNGEVNT |
+ DWC3_OEVTEN_OTGBDEVVBUSCHNGEVNT);
+}
+
+/**
+ * dwc3_otg_init - Initializes otg related registers
+ * @dwc: Pointer to out controller context structure
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+int dwc3_otg_init(struct dwc3 *dwc)
+{
+ u32 reg;
+ int ret = 0;
+ struct dwc3_otg *dotg;
+
+ dev_dbg(dwc->dev, "dwc3_otg_init\n");
+
+ /*
+ * GHWPARAMS6[10] bit is SRPSupport.
+ * This bit also reflects DWC_USB3_EN_OTG
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_GHWPARAMS6);
+ if (!(reg & DWC3_GHWPARAMS6_SRP_SUPPORT)) {
+ /*
+ * No OTG support in the HW core.
+ * We return 0 to indicate no error, since this is acceptable
+ * situation, just continue probe the dwc3 driver without otg.
+ */
+ dev_dbg(dwc->dev, "dwc3_otg address space is not supported\n");
+ return 0;
+ }
+
+ /* Allocate and init otg instance */
+ dotg = kzalloc(sizeof(struct dwc3_otg), GFP_KERNEL);
+ if (!dotg) {
+ dev_err(dwc->dev, "unable to allocate dwc3_otg\n");
+ return -ENOMEM;
+ }
+
+ dotg->irq = platform_get_irq(to_platform_device(dwc->dev), 0);
+ if (dotg->irq < 0) {
+ dev_err(dwc->dev, "%s: missing IRQ\n", __func__);
+ ret = -ENODEV;
+ goto err1;
+ }
+
+ dotg->regs = dwc->regs;
+
+ dotg->otg.set_peripheral = dwc3_otg_set_peripheral;
+ dotg->otg.set_host = dwc3_otg_set_host;
+
+ /* This reference is used by dwc3 modules for checking otg existance */
+ dwc->dotg = dotg;
+
+ dotg->otg.phy = kzalloc(sizeof(struct usb_phy), GFP_KERNEL);
+ if (!dotg->otg.phy) {
+ dev_err(dwc->dev, "unable to allocate dwc3_otg.phy\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ dotg->otg.phy->otg = &dotg->otg;
+ dotg->otg.phy->dev = dwc->dev;
+
+ ret = usb_set_transceiver(dotg->otg.phy);
+ if (ret) {
+ dev_err(dotg->otg.phy->dev,
+ "%s: failed to set transceiver, already exists\n",
+ __func__);
+ goto err2;
+ }
+
+ dwc3_otg_reset(dotg);
+
+ dotg->otg.phy->state = OTG_STATE_UNDEFINED;
+
+ INIT_WORK(&dotg->sm_work, dwc3_otg_sm_work);
+
+ ret = request_irq(dotg->irq, dwc3_otg_interrupt, IRQF_SHARED,
+ "dwc3_otg", dotg);
+ if (ret) {
+ dev_err(dotg->otg.phy->dev, "failed to request irq #%d --> %d\n",
+ dotg->irq, ret);
+ goto err3;
+ }
+
+ return 0;
+
+err3:
+ cancel_work_sync(&dotg->sm_work);
+ usb_set_transceiver(NULL);
+err2:
+ kfree(dotg->otg.phy);
+err1:
+ dwc->dotg = NULL;
+ kfree(dotg);
+
+ return ret;
+}
+
+/**
+ * dwc3_otg_exit
+ * @dwc: Pointer to out controller context structure
+ *
+ * Returns 0 on success otherwise negative errno.
+ */
+void dwc3_otg_exit(struct dwc3 *dwc)
+{
+ struct dwc3_otg *dotg = dwc->dotg;
+
+ /* dotg is null when GHWPARAMS6[10]=SRPSupport=0, see dwc3_otg_init */
+ if (dotg) {
+ if (dotg->charger)
+ dotg->charger->start_detection(dotg->charger, false);
+ cancel_work_sync(&dotg->sm_work);
+ usb_set_transceiver(NULL);
+ free_irq(dotg->irq, dotg);
+ kfree(dotg->otg.phy);
+ kfree(dotg);
+ dwc->dotg = NULL;
+ }
+}
diff --git a/drivers/usb/dwc3/dwc3_otg.h b/drivers/usb/dwc3/dwc3_otg.h
new file mode 100644
index 0000000..0d8b61b
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3_otg.h
@@ -0,0 +1,76 @@
+/**
+ * dwc3_otg.h - DesignWare USB3 DRD Controller OTG
+ *
+ * Copyright (c) 2012, Code Aurora Forum. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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_USB_DWC3_OTG_H
+#define __LINUX_USB_DWC3_OTG_H
+
+#include <linux/workqueue.h>
+
+#include <linux/usb/otg.h>
+
+struct dwc3_charger;
+
+/**
+ * struct dwc3_otg: OTG driver data. Shared by HCD and DCD.
+ * @otg: USB OTG Transceiver structure.
+ * @irq: IRQ number assigned for HSUSB controller.
+ * @regs: ioremapped register base address.
+ * @sm_work: OTG state machine work.
+ * @charger: DWC3 external charger detector
+ * @inputs: OTG state machine inputs
+ */
+struct dwc3_otg {
+ struct usb_otg otg;
+ int irq;
+ void __iomem *regs;
+ struct work_struct sm_work;
+ struct dwc3_charger *charger;
+#define ID 0
+#define B_SESS_VLD 1
+ unsigned long inputs;
+};
+
+/**
+ * USB charger types
+ *
+ * DWC3_INVALID_CHARGER Invalid USB charger.
+ * DWC3_SDP_CHARGER Standard downstream port. Refers to a downstream port
+ * on USB compliant host/hub.
+ * DWC3_DCP_CHARGER Dedicated charger port (AC charger/ Wall charger).
+ * DWC3_CDP_CHARGER Charging downstream port. Enumeration can happen and
+ * IDEV_CHG_MAX can be drawn irrespective of USB state.
+ */
+enum dwc3_chg_type {
+ DWC3_INVALID_CHARGER = 0,
+ DWC3_SDP_CHARGER,
+ DWC3_DCP_CHARGER,
+ DWC3_CDP_CHARGER,
+};
+
+struct dwc3_charger {
+ enum dwc3_chg_type chg_type;
+
+ /* start/stop charger detection, provided by external charger module */
+ void (*start_detection)(struct dwc3_charger *charger, bool start);
+
+ /* to notify OTG about charger detection completion, provided by OTG */
+ void (*notify_detection_complete)(struct usb_otg *otg,
+ struct dwc3_charger *charger);
+};
+
+/* for external charger driver */
+extern int dwc3_set_charger(struct usb_otg *otg, struct dwc3_charger *charger);
+
+#endif /* __LINUX_USB_DWC3_OTG_H */
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a988c43..060144f 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -49,6 +49,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
#include "core.h"
#include "gadget.h"
@@ -1326,7 +1327,59 @@
is_on = !!is_on;
spin_lock_irqsave(&dwc->lock, flags);
+
+ dwc->softconnect = is_on;
+
+ if ((dwc->dotg && !dwc->vbus_active) ||
+ !dwc->gadget_driver) {
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ /*
+ * Need to wait for vbus_session(on) from otg driver or to
+ * the udc_start.
+ */
+ return 0;
+ }
+
dwc3_gadget_run_stop(dwc, is_on);
+
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return 0;
+}
+
+static int dwc3_gadget_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+ struct dwc3 *dwc = gadget_to_dwc(_gadget);
+ unsigned long flags;
+
+ if (!dwc->dotg)
+ return -EPERM;
+
+ is_active = !!is_active;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+
+ /* Mark that the vbus was powered */
+ dwc->vbus_active = is_active;
+
+ /*
+ * Check if upper level usb_gadget_driver was already registerd with
+ * this udc controller driver (if dwc3_gadget_start was called)
+ */
+ if (dwc->gadget_driver && dwc->softconnect) {
+ if (dwc->vbus_active) {
+ /*
+ * Both vbus was activated by otg and pullup was
+ * signaled by the gadget driver.
+ */
+ dwc3_gadget_run_stop(dwc, 1);
+ } else {
+ dwc3_gadget_run_stop(dwc, 0);
+ }
+ }
+
spin_unlock_irqrestore(&dwc->lock, flags);
return 0;
@@ -1417,6 +1470,7 @@
.get_frame = dwc3_gadget_get_frame,
.wakeup = dwc3_gadget_wakeup,
.set_selfpowered = dwc3_gadget_set_selfpowered,
+ .vbus_session = dwc3_gadget_vbus_session,
.pullup = dwc3_gadget_pullup,
.udc_start = dwc3_gadget_start,
.udc_stop = dwc3_gadget_stop,
@@ -2153,6 +2207,33 @@
break;
case DWC3_DEVICE_EVENT_OVERFLOW:
dev_vdbg(dwc->dev, "Overflow\n");
+ /*
+ * Controllers prior to 2.30a revision has a bug where
+ * Overflow Event may overwrite an unacknowledged event
+ * in the event buffer. The severity of the issue depends
+ * on the overwritten event type. Add a warning message
+ * saying that an event is overwritten.
+ *
+ * TODO: In future we may need to see if we can re-enumerate
+ * with host.
+ */
+ if (dwc->revision < DWC3_REVISION_230A)
+ dev_warn(dwc->dev, "Unacknowledged event overwritten\n");
+ break;
+ case DWC3_DEVICE_EVENT_VENDOR_DEV_TEST_LMP:
+ /*
+ * Controllers prior to 2.30a revision has a bug, due to which
+ * a vendor device test LMP event can not be filtered. But
+ * this event is not handled in the current code. This is a
+ * special event and 8 bytes of data will follow the event.
+ * Handling this event is tricky when event buffer is almost
+ * full. Moreover this event will not occur in normal scenario
+ * and can only happen with special hosts in testing scenarios.
+ * Add a warning message to indicate that this event is received
+ * which means that event buffer might have corrupted.
+ */
+ if (dwc->revision < DWC3_REVISION_230A)
+ dev_warn(dwc->dev, "Vendor Device Test LMP Received\n");
break;
default:
dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
@@ -2314,8 +2395,7 @@
}
/* Enable all but Start and End of Frame IRQs */
- reg = (DWC3_DEVTEN_VNDRDEVTSTRCVEDEN |
- DWC3_DEVTEN_EVNTOVERFLOWEN |
+ reg = (DWC3_DEVTEN_EVNTOVERFLOWEN |
DWC3_DEVTEN_CMDCMPLTEN |
DWC3_DEVTEN_ERRTICERREN |
DWC3_DEVTEN_WKUPEVTEN |
@@ -2338,6 +2418,15 @@
goto err7;
}
+ if (dwc->dotg) {
+ /* dwc3 otg driver is active (DRD mode + SRPSupport=1) */
+ ret = otg_set_peripheral(&dwc->dotg->otg, &dwc->gadget);
+ if (ret) {
+ dev_err(dwc->dev, "failed to set peripheral to otg\n");
+ goto err7;
+ }
+ }
+
return 0;
err7:
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index b108d18..099708b 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -38,20 +38,13 @@
#include <linux/platform_device.h>
#include "core.h"
-
-static struct resource generic_resources[] = {
- {
- .flags = IORESOURCE_IRQ,
- },
- {
- .flags = IORESOURCE_MEM,
- },
-};
+#include "xhci.h"
int dwc3_host_init(struct dwc3 *dwc)
{
struct platform_device *xhci;
int ret;
+ struct xhci_plat_data pdata;
xhci = platform_device_alloc("xhci-hcd", -1);
if (!xhci) {
@@ -67,15 +60,19 @@
xhci->dev.dma_parms = dwc->dev->dma_parms;
dwc->xhci = xhci;
+ pdata.vendor = ((dwc->revision & DWC3_GSNPSID_MASK) >>
+ __ffs(DWC3_GSNPSID_MASK) & DWC3_GSNPSREV_MASK);
+ pdata.revision = dwc->revision & DWC3_GSNPSREV_MASK;
- /* setup resources */
- generic_resources[0].start = dwc->irq;
+ ret = platform_device_add_data(xhci, (const void *) &pdata,
+ sizeof(struct xhci_plat_data));
+ if (ret) {
+ dev_err(dwc->dev, "couldn't add pdata to xHCI device\n");
+ goto err1;
+ }
- generic_resources[1].start = dwc->res->start;
- generic_resources[1].end = dwc->res->start + 0x7fff;
-
- ret = platform_device_add_resources(xhci, generic_resources,
- ARRAY_SIZE(generic_resources));
+ ret = platform_device_add_resources(xhci, dwc->xhci_resources,
+ DWC3_XHCI_RESOURCES_NUM);
if (ret) {
dev_err(dwc->dev, "couldn't add resources to xHCI device\n");
goto err1;
diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h
index 071d561..90de7a4 100644
--- a/drivers/usb/dwc3/io.h
+++ b/drivers/usb/dwc3/io.h
@@ -41,14 +41,26 @@
#include <linux/io.h>
+#include "core.h"
+
static inline u32 dwc3_readl(void __iomem *base, u32 offset)
{
- return readl(base + offset);
+ /*
+ * We requested the mem region starting from the Globals address
+ * space, see dwc3_probe in core.c.
+ * However, the offsets are given starting from xHCI address space.
+ */
+ return readl_relaxed(base + (offset - DWC3_GLOBALS_REGS_START));
}
static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value)
{
- writel(value, base + offset);
+ /*
+ * We requested the mem region starting from the Globals address
+ * space, see dwc3_probe in core.c.
+ * However, the offsets are given starting from xHCI address space.
+ */
+ writel_relaxed(value, base + (offset - DWC3_GLOBALS_REGS_START));
}
#endif /* __DRIVERS_USB_DWC3_IO_H */
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 90a3b1e..be8e6aa 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -1546,7 +1546,8 @@
{ \
if (size >= sizeof(buffer)) \
return -EINVAL; \
- strlcpy(buffer, strim((char *) buf), sizeof(buffer)); \
+ strlcpy(buffer, buf, sizeof(buffer)); \
+ strim(buffer); \
return size; \
} \
static DEVICE_ATTR(field, S_IRUGO | S_IWUSR, field ## _show, field ## _store);
diff --git a/drivers/usb/gadget/ci13xxx_msm.c b/drivers/usb/gadget/ci13xxx_msm.c
index 1cbaa8e..e3c1216 100644
--- a/drivers/usb/gadget/ci13xxx_msm.c
+++ b/drivers/usb/gadget/ci13xxx_msm.c
@@ -56,7 +56,7 @@
if (_udc_ctxt.wake_irq && _udc_ctxt.wake_irq_state) {
disable_irq_wake(_udc_ctxt.wake_irq);
- disable_irq(_udc_ctxt.wake_irq);
+ disable_irq_nosync(_udc_ctxt.wake_irq);
_udc_ctxt.wake_irq_state = false;
}
}
@@ -134,7 +134,7 @@
dev_dbg(&pdev->dev, "_udc_ctxt.gpio_irq = %d and irq = %d\n",
_udc_ctxt.wake_gpio, wake_irq);
ret = request_irq(wake_irq, ci13xxx_msm_resume_irq,
- IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "usb resume", NULL);
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT, "usb resume", NULL);
if (ret < 0) {
dev_err(&pdev->dev, "could not register USB_RESUME IRQ.\n");
goto gpio_free;
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 673ad12..78ece8d 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -343,6 +343,8 @@
/* Write 1 to disable the port */
xhci_writel(xhci, port_status | PORT_PE, addr);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
port_status = xhci_readl(xhci, addr);
xhci_dbg(xhci, "disable port, actual port %d status = 0x%x\n",
wIndex, port_status);
@@ -389,6 +391,8 @@
}
/* Change bits are all write 1 to clear */
xhci_writel(xhci, port_status | status, addr);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
port_status = xhci_readl(xhci, addr);
xhci_dbg(xhci, "clear port %s change, actual port %d status = 0x%x\n",
port_change_bit, wIndex, port_status);
@@ -420,6 +424,8 @@
temp &= ~PORT_PLS_MASK;
temp |= PORT_LINK_STROBE | link_state;
xhci_writel(xhci, temp, port_array[port_id]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
}
void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
@@ -446,6 +452,8 @@
temp &= ~PORT_WKOC_E;
xhci_writel(xhci, temp, port_array[port_id]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
}
/* Test and clear port RWC bit */
@@ -459,6 +467,8 @@
temp = xhci_port_state_to_neutral(temp);
temp |= port_bit;
xhci_writel(xhci, temp, port_array[port_id]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
}
}
@@ -721,6 +731,8 @@
*/
xhci_writel(xhci, temp | PORT_POWER,
port_array[wIndex]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
temp = xhci_readl(xhci, port_array[wIndex]);
xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp);
@@ -728,6 +740,8 @@
case USB_PORT_FEAT_RESET:
temp = (temp | PORT_RESET);
xhci_writel(xhci, temp, port_array[wIndex]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
temp = xhci_readl(xhci, port_array[wIndex]);
xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp);
@@ -743,6 +757,8 @@
case USB_PORT_FEAT_BH_PORT_RESET:
temp |= PORT_WR;
xhci_writel(xhci, temp, port_array[wIndex]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
temp = xhci_readl(xhci, port_array[wIndex]);
break;
@@ -936,8 +952,11 @@
t2 &= ~PORT_WAKE_BITS;
t1 = xhci_port_state_to_neutral(t1);
- if (t1 != t2)
+ if (t1 != t2) {
xhci_writel(xhci, t2, port_array[port_index]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
+ }
if (hcd->speed != HCD_USB3) {
/* enable remote wake up for USB 2.0 */
@@ -951,6 +970,8 @@
tmp = xhci_readl(xhci, addr);
tmp |= PORT_RWE;
xhci_writel(xhci, tmp, addr);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
}
}
hcd->state = HC_STATE_SUSPENDED;
@@ -1028,8 +1049,11 @@
xhci, port_index + 1);
if (slot_id)
xhci_ring_device(xhci, slot_id);
- } else
+ } else {
xhci_writel(xhci, temp, port_array[port_index]);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
+ }
if (hcd->speed != HCD_USB3) {
/* disable remote wake up for USB 2.0 */
@@ -1043,6 +1067,8 @@
tmp = xhci_readl(xhci, addr);
tmp &= ~PORT_RWE;
xhci_writel(xhci, tmp, addr);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
}
}
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index 689bc18..8467dc0 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -14,17 +14,30 @@
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/usb/otg.h>
#include "xhci.h"
+#define SYNOPSIS_DWC3_VENDOR 0x5533
+
+static struct usb_phy *phy;
+
static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci)
{
+ struct xhci_plat_data *pdata = dev->platform_data;
+
/*
* As of now platform drivers don't provide MSI support so we ensure
* here that the generic code does not try to make a pci_dev from our
* dev struct in order to setup MSI
*/
xhci->quirks |= XHCI_BROKEN_MSI;
+
+ if (!pdata)
+ return;
+ else if (pdata->vendor == SYNOPSIS_DWC3_VENDOR &&
+ pdata->revision < 0x230A)
+ xhci->quirks |= XHCI_PORTSC_DELAY;
}
/* called during probe() after chip reset completes */
@@ -149,6 +162,19 @@
if (ret)
goto put_usb3_hcd;
+ phy = usb_get_transceiver();
+ if (phy && phy->otg) {
+ dev_dbg(&pdev->dev, "%s otg support available\n", __func__);
+ hcd->driver->stop(hcd);
+ ret = otg_set_host(phy->otg, &hcd->self);
+ if (ret) {
+ dev_err(&pdev->dev, "%s otg_set_host failed\n",
+ __func__);
+ usb_put_transceiver(phy);
+ goto put_usb3_hcd;
+ }
+ }
+
return 0;
put_usb3_hcd:
@@ -182,6 +208,11 @@
usb_put_hcd(hcd);
kfree(xhci);
+ if (phy && phy->otg) {
+ otg_set_host(phy->otg, NULL);
+ usb_put_transceiver(phy);
+ }
+
return 0;
}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 36641a7..2c26998 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -318,16 +318,6 @@
return;
}
-static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
-{
- int i;
-
- if (xhci->msix_entries) {
- for (i = 0; i < xhci->msix_count; i++)
- synchronize_irq(xhci->msix_entries[i].vector);
- }
-}
-
static int xhci_try_enable_msi(struct usb_hcd *hcd)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
@@ -383,11 +373,7 @@
{
}
-static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
-{
-}
-
-#endif
+#endif /* CONFIG_PCI */
/*
* Initialize memory for HCD and xHC (one-time init).
@@ -513,6 +499,13 @@
xhci_dbg(xhci, "xhci_run\n");
+ xhci_dbg(xhci, "Calling HCD init\n");
+ /* Initialize HCD and host controller data structures. */
+ ret = xhci_init(hcd);
+ if (ret)
+ return ret;
+ xhci_dbg(xhci, "Called HCD init\n");
+
ret = xhci_try_enable_msi(hcd);
if (ret)
return ret;
@@ -661,6 +654,23 @@
}
#ifdef CONFIG_PM
+
+#ifdef CONFIG_PCI
+static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
+{
+ int i;
+
+ if (xhci->msix_entries) {
+ for (i = 0; i < xhci->msix_count; i++)
+ synchronize_irq(xhci->msix_entries[i].vector);
+ }
+}
+#else
+static void xhci_msix_sync_irqs(struct xhci_hcd *xhci)
+{
+}
+#endif /* CONFIG_PCI */
+
static void xhci_save_registers(struct xhci_hcd *xhci)
{
xhci->s3.command = xhci_readl(xhci, &xhci->op_regs->command);
@@ -3712,6 +3722,8 @@
hird = xhci_calculate_hird_besl(xhci, udev);
temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird);
xhci_writel(xhci, temp, pm_addr);
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ ndelay(100);
/* Set port link state to U2(L1) */
addr = port_array[port_num];
@@ -3789,6 +3801,7 @@
unsigned int port_num;
unsigned long flags;
int hird;
+ bool delay;
if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support ||
!udev->lpm_capable)
@@ -3801,6 +3814,9 @@
if (udev->usb2_hw_lpm_capable != 1)
return -EPERM;
+ if (xhci->quirks & XHCI_PORTSC_DELAY)
+ delay = true;
+
spin_lock_irqsave(&xhci->lock, flags);
port_array = xhci->usb2_ports;
@@ -3817,12 +3833,18 @@
temp &= ~PORT_HIRD_MASK;
temp |= PORT_HIRD(hird) | PORT_RWE;
xhci_writel(xhci, temp, pm_addr);
+ if (delay)
+ ndelay(100);
temp = xhci_readl(xhci, pm_addr);
temp |= PORT_HLE;
xhci_writel(xhci, temp, pm_addr);
+ if (delay)
+ ndelay(100);
} else {
temp &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK);
xhci_writel(xhci, temp, pm_addr);
+ if (delay)
+ ndelay(100);
}
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -4043,12 +4065,6 @@
dma_set_mask(hcd->self.controller, DMA_BIT_MASK(32));
}
- xhci_dbg(xhci, "Calling HCD init\n");
- /* Initialize HCD and host controller data structures. */
- retval = xhci_init(hcd);
- if (retval)
- goto error;
- xhci_dbg(xhci, "Called HCD init\n");
return 0;
error:
kfree(xhci);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 3d69c4b..127b0e9 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1479,6 +1479,21 @@
#define XHCI_RESET_ON_RESUME (1 << 7)
#define XHCI_SW_BW_CHECKING (1 << 8)
#define XHCI_AMD_0x96_HOST (1 << 9)
+/*
+ * In Synopsis DWC3 controller, PORTSC register access involves multiple clock
+ * domains. When the software does a PORTSC write, handshakes are needed
+ * across these clock domains. This results in long access times, especially
+ * for USB 2.0 ports. In order to solve this issue, when the PORTSC write
+ * operations happen on the system bus, the command is latched and system bus
+ * is released immediately. However, the real PORTSC write access will take
+ * some time internally to complete. If the software quickly does a read to the
+ * PORTSC, some fields (port status change related fields like OCC, etc.) may
+ * not have correct value due to the current way of handling these bits.
+ *
+ * The workaround is to give some delay (5 mac2_clk -> UTMI clock = 60 MHz ->
+ * (16.66 ns x 5 = 84ns) ~100ns after writing to the PORTSC register.
+ */
+#define XHCI_PORTSC_DELAY (1 << 10)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1667,6 +1682,11 @@
static inline void xhci_unregister_pci(void) {}
#endif
+struct xhci_plat_data {
+ unsigned vendor;
+ unsigned revision;
+};
+
#if defined(CONFIG_USB_XHCI_PLATFORM) \
|| defined(CONFIG_USB_XHCI_PLATFORM_MODULE)
int xhci_register_plat(void);
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 55d2b3e..cad6e02 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -914,6 +914,7 @@
goto error;
}
+ mutex_lock(&mgmt->mdp_do_hist_mutex);
mutex_lock(&mgmt->mdp_hist_mutex);
if (mgmt->mdp_is_hist_start == TRUE) {
pr_err("%s histogram already started\n", __func__);
@@ -933,6 +934,7 @@
error_lock:
mutex_unlock(&mgmt->mdp_hist_mutex);
+ mutex_unlock(&mgmt->mdp_do_hist_mutex);
error:
return ret;
}
@@ -949,6 +951,7 @@
goto error;
}
+ mutex_lock(&mgmt->mdp_do_hist_mutex);
mutex_lock(&mgmt->mdp_hist_mutex);
if (mgmt->mdp_is_hist_start == FALSE) {
pr_err("%s histogram already stopped\n", __func__);
@@ -969,10 +972,12 @@
mutex_unlock(&mgmt->mdp_hist_mutex);
cancel_work_sync(&mgmt->mdp_histogram_worker);
+ mutex_unlock(&mgmt->mdp_do_hist_mutex);
return ret;
error_lock:
mutex_unlock(&mgmt->mdp_hist_mutex);
+ mutex_unlock(&mgmt->mdp_do_hist_mutex);
error:
return ret;
}
@@ -1178,11 +1183,13 @@
return ret;
}
+#define MDP_HISTOGRAM_TIMEOUT_MS 84 /*5 Frames*/
static int mdp_do_histogram(struct fb_info *info,
struct mdp_histogram_data *hist)
{
struct mdp_hist_mgmt *mgmt = NULL;
int ret = 0;
+ unsigned long timeout = (MDP_HISTOGRAM_TIMEOUT_MS * HZ) / 1000;
ret = mdp_histogram_block2mgmt(hist->block, &mgmt);
if (ret) {
@@ -1227,9 +1234,17 @@
mgmt->hist = hist;
mutex_unlock(&mgmt->mdp_hist_mutex);
- if (wait_for_completion_killable(&mgmt->mdp_hist_comp)) {
- pr_err("%s(): histogram bin collection killed", __func__);
- ret = -EINVAL;
+ ret = wait_for_completion_killable_timeout(&mgmt->mdp_hist_comp,
+ timeout);
+ if (ret <= 0) {
+ if (!ret) {
+ mgmt->hist = NULL;
+ ret = -ETIMEDOUT;
+ pr_debug("%s: bin collection timedout", __func__);
+ } else {
+ mgmt->hist = NULL;
+ pr_debug("%s: bin collection interrupted", __func__);
+ }
goto error;
}
diff --git a/drivers/video/msm/mdp4_overlay_dtv.c b/drivers/video/msm/mdp4_overlay_dtv.c
index fe31f93..9174bc5 100644
--- a/drivers/video/msm/mdp4_overlay_dtv.c
+++ b/drivers/video/msm/mdp4_overlay_dtv.c
@@ -239,15 +239,8 @@
mdp_footswitch_ctrl(TRUE);
mdp4_overlay_panel_mode(MDP4_MIXER1, MDP4_PANEL_DTV);
-
- /* Allocate dtv_pipe at dtv_on*/
- if (dtv_pipe == NULL) {
- if (mdp4_overlay_dtv_set(mfd, NULL)) {
- pr_warn("%s: dtv_pipe is NULL, dtv_set failed\n",
- __func__);
- return -EINVAL;
- }
- }
+ if (dtv_pipe != NULL)
+ ret = mdp4_dtv_start(mfd);
ret = panel_next_on(pdev);
if (ret != 0)
@@ -708,6 +701,14 @@
return;
}
mutex_lock(&mfd->dma->ov_mutex);
+ if (dtv_pipe == NULL) {
+ if (mdp4_overlay_dtv_set(mfd, NULL)) {
+ pr_warn("%s: dtv_pipe == NULL\n", __func__);
+ mutex_unlock(&mfd->dma->ov_mutex);
+ return;
+ }
+ }
+
pipe = dtv_pipe;
if (hdmi_prim_display && (pipe->pipe_used == 0 ||
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
index 45430e8..a5171f0 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.c
@@ -590,6 +590,12 @@
__func__, dev_ctxt);
return false;
}
+ if (dev_ctxt->turbo_mode_set &&
+ (req_perf_lvl < RESTRK_1080P_TURBO_PERF_LEVEL)) {
+ VCDRES_MSG_MED("%s(): TURBO MODE!!\n", __func__);
+ return true;
+ }
+
VCDRES_MSG_LOW("%s(), req_perf_lvl = %d", __func__, req_perf_lvl);
if (resource_context.vidc_platform_data->disable_turbo
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
index 2b534f1..96e729d 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_device_sm.c
@@ -759,6 +759,7 @@
client = dev_ctxt->cctxt_list_head;
dev_ctxt->cctxt_list_head = cctxt;
cctxt->next = client;
+ dev_ctxt->turbo_mode_set = 0;
*clnt_cctxt = cctxt;
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_power_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_power_sm.c
index 80e03ba..33b2300 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_power_sm.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_power_sm.c
@@ -312,6 +312,7 @@
return false;
}
dev_ctxt->curr_perf_lvl = RESTRK_1080P_TURBO_PERF_LEVEL;
+ vcd_update_decoder_perf_level(dev_ctxt, RESTRK_1080P_TURBO_PERF_LEVEL);
#endif
return rc;
}
diff --git a/include/linux/input/mpu3050.h b/include/linux/input/mpu3050.h
new file mode 100644
index 0000000..6006abb
--- /dev/null
+++ b/include/linux/input/mpu3050.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012, Code Aurora Forum. 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 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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 __MPU3050_H__
+#define __MPU3050_H__
+
+struct mpu3050_gyro_platform_data {
+ int poll_interval;
+ int min_interval;
+
+ int (*init)(void);
+ void (*exit)(void);
+ int (*power_on)(void);
+ int (*power_off)(void);
+
+ int gpio_int;
+ int gpio_fsync;
+};
+
+#endif /* __MPU3050_H__ */
diff --git a/include/linux/ion.h b/include/linux/ion.h
index d9443ff..fca5517 100644
--- a/include/linux/ion.h
+++ b/include/linux/ion.h
@@ -86,6 +86,14 @@
FIXED_HIGH,
};
+enum cp_mem_usage {
+ VIDEO_BITSTREAM = 0x1,
+ VIDEO_PIXEL = 0x2,
+ VIDEO_NONPIXEL = 0x3,
+ MAX_USAGE = 0x4,
+ UNKNOWN = 0x7FFFFFFF,
+};
+
/**
* Flag to use when allocating to indicate that a heap is secure.
*/
@@ -482,22 +490,28 @@
*
* @client - a client that has allocated from the heap heap_id
* @heap_id - heap id to secure.
+ * @version - version of content protection
+ * @data - extra data needed for protection
*
* Secure a heap
* Returns 0 on success
*/
-int ion_secure_heap(struct ion_device *dev, int heap_id);
+int ion_secure_heap(struct ion_device *dev, int heap_id, int version,
+ void *data);
/**
* ion_unsecure_heap - un-secure a heap
*
* @client - a client that has allocated from the heap heap_id
* @heap_id - heap id to un-secure.
+ * @version - version of content protection
+ * @data - extra data needed for protection
*
* Un-secure a heap
* Returns 0 on success
*/
-int ion_unsecure_heap(struct ion_device *dev, int heap_id);
+int ion_unsecure_heap(struct ion_device *dev, int heap_id, int version,
+ void *data);
/**
* msm_ion_secure_heap - secure a heap. Wrapper around ion_secure_heap.
@@ -520,6 +534,30 @@
int msm_ion_unsecure_heap(int heap_id);
/**
+ * msm_ion_secure_heap_2_0 - secure a heap using 2.0 APIs
+ * Wrapper around ion_secure_heap.
+ *
+ * @heap_id - heap id to secure.
+ * @usage - usage hint to TZ
+ *
+ * Secure a heap
+ * Returns 0 on success
+ */
+int msm_ion_secure_heap_2_0(int heap_id, enum cp_mem_usage usage);
+
+/**
+ * msm_ion_unsecure_heap - unsecure a heap secured with 3.0 APIs.
+ * Wrapper around ion_unsecure_heap.
+ *
+ * @heap_id - heap id to secure.
+ * @usage - usage hint to TZ
+ *
+ * Un-secure a heap
+ * Returns 0 on success
+ */
+int msm_ion_unsecure_heap_2_0(int heap_id, enum cp_mem_usage usage);
+
+/**
* msm_ion_do_cache_op - do cache operations.
*
* @client - pointer to ION client.
@@ -627,13 +665,15 @@
return;
}
-static inline int ion_secure_heap(struct ion_device *dev, int heap_id)
+static inline int ion_secure_heap(struct ion_device *dev, int heap_id,
+ int version, void *data)
{
return -ENODEV;
}
-static inline int ion_unsecure_heap(struct ion_device *dev, int heap_id)
+static inline int ion_unsecure_heap(struct ion_device *dev, int heap_id,
+ int version, void *data)
{
return -ENODEV;
}
@@ -649,6 +689,17 @@
return -ENODEV;
}
+static inline int msm_ion_secure_heap_2_0(int heap_id, enum cp_mem_usage usage)
+{
+ return -ENODEV;
+}
+
+static inline int msm_ion_unsecure_heap_2_0(int heap_id,
+ enum cp_mem_usage usage)
+{
+ return -ENODEV;
+}
+
static inline int msm_ion_do_cache_op(struct ion_client *client,
struct ion_handle *handle, void *vaddr,
unsigned long len, unsigned int cmd)
diff --git a/include/linux/mfd/pm8xxx/pm8038.h b/include/linux/mfd/pm8xxx/pm8038.h
index 90557b9..682abc8 100644
--- a/include/linux/mfd/pm8xxx/pm8038.h
+++ b/include/linux/mfd/pm8xxx/pm8038.h
@@ -33,6 +33,7 @@
#include <linux/leds-pm8xxx.h>
#include <linux/mfd/pm8xxx/ccadc.h>
#include <linux/mfd/pm8xxx/spk.h>
+#include <linux/mfd/pm8xxx/tm.h>
#define PM8038_CORE_DEV_NAME "pm8038-core"
@@ -64,6 +65,9 @@
#define PM8038_RESOUT_IRQ PM8038_IRQ_BLOCK_BIT(6, 4)
+#define PM8038_OVERTEMP_IRQ PM8038_IRQ_BLOCK_BIT(4, 2)
+#define PM8038_TEMPSTAT_IRQ PM8038_IRQ_BLOCK_BIT(6, 7)
+
struct pm8038_platform_data {
int irq_base;
struct pm8xxx_gpio_platform_data *gpio_pdata;
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 22d4997..5eb1845 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -24,6 +24,7 @@
#include <linux/usb/otg.h>
#include <linux/wakelock.h>
#include <linux/pm_qos.h>
+#include <linux/hrtimer.h>
/*
* The following are bit fields describing the usb_request.udc_priv word.
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index f18fafd..63ebdea 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -2295,11 +2295,11 @@
#define V4L2_EVENT_FRAME_SYNC 4
#define V4L2_EVENT_PRIVATE_START 0x08000000
-#define V4L2_EVENT_MSM_VIDC_START { V4L2_EVENT_PRIVATE_START + 0x00001000}
-#define V4L2_EVENT_MSM_VIDC_FLUSH_DONE {V4L2_EVENT_PRIVATE_START + 1}
+#define V4L2_EVENT_MSM_VIDC_START (V4L2_EVENT_PRIVATE_START + 0x00001000)
+#define V4L2_EVENT_MSM_VIDC_FLUSH_DONE (V4L2_EVENT_PRIVATE_START + 1)
#define V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED \
- {V4L2_EVENT_PRIVATE_START + 2}
-#define V4L2_EVENT_MSM_VIDC_CLOSE_DONE {V4L2_EVENT_PRIVATE_START + 3}
+ (V4L2_EVENT_PRIVATE_START + 2)
+#define V4L2_EVENT_MSM_VIDC_CLOSE_DONE (V4L2_EVENT_PRIVATE_START + 3)
/* Payload for V4L2_EVENT_VSYNC */
struct v4l2_event_vsync {
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index 0c9b274..320ac8b 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -1594,4 +1594,66 @@
uint32_t len;
};
+enum msm_camss_irq_idx {
+ CAMERA_SS_IRQ_0,
+ CAMERA_SS_IRQ_1,
+ CAMERA_SS_IRQ_2,
+ CAMERA_SS_IRQ_3,
+ CAMERA_SS_IRQ_4,
+ CAMERA_SS_IRQ_5,
+ CAMERA_SS_IRQ_6,
+ CAMERA_SS_IRQ_7,
+ CAMERA_SS_IRQ_8,
+ CAMERA_SS_IRQ_9,
+ CAMERA_SS_IRQ_10,
+ CAMERA_SS_IRQ_11,
+ CAMERA_SS_IRQ_12,
+ CAMERA_SS_IRQ_MAX
+};
+
+enum msm_cam_hw_idx {
+ MSM_CAM_HW_MICRO,
+ MSM_CAM_HW_CCI,
+ MSM_CAM_HW_CSI0,
+ MSM_CAM_HW_CSI1,
+ MSM_CAM_HW_CSI2,
+ MSM_CAM_HW_CSI3,
+ MSM_CAM_HW_ISPIF,
+ MSM_CAM_HW_CPP,
+ MSM_CAM_HW_VFE0,
+ MSM_CAM_HW_VFE1,
+ MSM_CAM_HW_JPEG0,
+ MSM_CAM_HW_JPEG1,
+ MSM_CAM_HW_JPEG2,
+ MSM_CAM_HW_MAX
+};
+
+struct msm_camera_irq_cfg {
+ /* Bit mask of all the camera hardwares that needs to
+ * be composited into a single IRQ to the MSM.
+ * Current usage: (may be updated based on hw changes)
+ * Bits 31:13 - Reserved.
+ * Bits 12:0
+ * 12 - MSM_CAM_HW_JPEG2
+ * 11 - MSM_CAM_HW_JPEG1
+ * 10 - MSM_CAM_HW_JPEG0
+ * 9 - MSM_CAM_HW_VFE1
+ * 8 - MSM_CAM_HW_VFE0
+ * 7 - MSM_CAM_HW_CPP
+ * 6 - MSM_CAM_HW_ISPIF
+ * 5 - MSM_CAM_HW_CSI3
+ * 4 - MSM_CAM_HW_CSI2
+ * 3 - MSM_CAM_HW_CSI1
+ * 2 - MSM_CAM_HW_CSI0
+ * 1 - MSM_CAM_HW_CCI
+ * 0 - MSM_CAM_HW_MICRO
+ */
+ uint32_t cam_hw_mask;
+ uint8_t irq_idx;
+ uint8_t num_hwcore;
+};
+
+#define MSM_IRQROUTER_CFG_COMPIRQ \
+ _IOWR('V', BASE_VIDIOC_PRIVATE, void __user *)
+
#endif /* __LINUX_MSM_CAMERA_H */
diff --git a/include/media/msm_isp.h b/include/media/msm_isp.h
index cb728a0..333d0df 100644
--- a/include/media/msm_isp.h
+++ b/include/media/msm_isp.h
@@ -327,5 +327,10 @@
#define VFE_OUTPUTS_JPEG_AND_THUMB BIT(9)
#define VFE_OUTPUTS_THUMB_AND_JPEG BIT(10)
+struct msm_frame_info {
+ uint32_t image_mode;
+ uint32_t path;
+};
+
#endif /*__MSM_ISP_H__*/
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index 4676a02..17fef15 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -241,7 +241,7 @@
struct snd_soc_dai_driver *driver;
/* DAI runtime info */
- unsigned int capture_active:1; /* stream is in use */
+ unsigned int capture_active; /* stream is in use */
unsigned int playback_active:1; /* stream is in use */
unsigned int symmetric_rates:1;
struct snd_pcm_runtime *runtime;
diff --git a/sound/soc/codecs/wcd9304.c b/sound/soc/codecs/wcd9304.c
index 4193e12..9ade172 100644
--- a/sound/soc/codecs/wcd9304.c
+++ b/sound/soc/codecs/wcd9304.c
@@ -4642,7 +4642,7 @@
/* Initialize current threshold to 350MA
* number of wait and run cycles to 4096
*/
- {SITAR_A_RX_HPH_OCP_CTL, 0xF8, 0x60},
+ {SITAR_A_RX_HPH_OCP_CTL, 0xE0, 0x60},
{SITAR_A_RX_COM_OCP_COUNT, 0xFF, 0xFF},
{SITAR_A_QFUSE_CTL, 0xFF, 0x03},
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 7eefe97..e85e9f5 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -4256,7 +4256,8 @@
continue;
if (!strncmp(w->sname,
tabla_dai[j].playback.stream_name, 13)) {
- --tabla_p->dai[j].ch_act;
+ if (tabla_p->dai[j].ch_act)
+ --tabla_p->dai[j].ch_act;
break;
}
}
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
index a7fd38b..2da5c6a 100644
--- a/sound/soc/msm/apq8064.c
+++ b/sound/soc/msm/apq8064.c
@@ -18,6 +18,8 @@
#include <linux/gpio.h>
#include <linux/mfd/pm8xxx/pm8921.h>
#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/slimbus/slimbus.h>
#include <sound/core.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
@@ -1417,6 +1419,19 @@
return 0;
}
+static int msm_slimbus_1_startup(struct snd_pcm_substream *substream)
+{
+ struct slim_controller *slim = slim_busnum_to_ctrl(1);
+
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ if (slim != NULL)
+ pm_runtime_get_sync(slim->dev.parent);
+
+ return 0;
+}
+
static void msm_auxpcm_shutdown(struct snd_pcm_substream *substream)
{
@@ -1433,6 +1448,19 @@
rtd->dai_link->cpu_dai_name, rtd->dai_link->codec_dai_name);
}
+static void msm_slimbus_1_shutdown(struct snd_pcm_substream *substream)
+{
+ struct slim_controller *slim = slim_busnum_to_ctrl(1);
+
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+
+ if (slim != NULL) {
+ pm_runtime_mark_last_busy(slim->dev.parent);
+ pm_runtime_put(slim->dev.parent);
+ }
+}
+
static struct snd_soc_ops msm_be_ops = {
.startup = msm_startup,
.hw_params = msm_hw_params,
@@ -1445,9 +1473,9 @@
};
static struct snd_soc_ops msm_slimbus_1_be_ops = {
- .startup = msm_startup,
+ .startup = msm_slimbus_1_startup,
.hw_params = msm_slimbus_1_hw_params,
- .shutdown = msm_shutdown,
+ .shutdown = msm_slimbus_1_shutdown,
};
static struct snd_soc_ops msm_slimbus_3_be_ops = {
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
index 6e190b2..56e83d5 100644
--- a/sound/soc/msm/msm-dai-fe.c
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -426,6 +426,30 @@
.ops = &msm_fe_dai_ops,
.name = "SEC_I2S_RX_HOSTLESS",
},
+ {
+ .playback = {
+ .stream_name = "SGLTE Playback",
+ .aif_name = "SGLTE_DL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .capture = {
+ .stream_name = "SGLTE Capture",
+ .aif_name = "SGLTE_UL",
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_fe_dai_ops,
+ .name = "SGLTE",
+ },
};
static __devinit int msm_fe_dai_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 4e1ce52..8051c92 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -48,6 +48,7 @@
static int fm_switch_enable;
static int fm_pcmrx_switch_enable;
+static int srs_alsa_ctrl_ever_called;
#define INT_RX_VOL_MAX_STEPS 0x2000
#define INT_RX_VOL_GAIN 0x2000
@@ -120,6 +121,12 @@
static void srs_send_params(int port_id, unsigned int techs,
int param_block_idx) {
+
+ /* only send commands to dsp if srs alsa ctrl was used
+ at least one time */
+ if (!srs_alsa_ctrl_ever_called)
+ return;
+
pr_debug("SRS %s: called, port_id = %d, techs flags = %u,"
" paramblockidx %d", __func__, port_id, techs,
param_block_idx);
@@ -328,6 +335,8 @@
payload.copp_ids[payload.num_copps++] =
msm_bedais[i].port_id;
+ srs_port_id = msm_bedais[i].port_id;
+ srs_send_params(srs_port_id, 1, 0);
}
}
if (payload.num_copps)
@@ -436,6 +445,8 @@
msm_pcm_routing_build_matrix(val,
fe_dai_map[val][session_type], path_type);
+ srs_port_id = msm_bedais[reg].port_id;
+ srs_send_params(srs_port_id, 1, 0);
}
} else {
if (test_bit(val, &msm_bedais[reg].fe_sessions) &&
@@ -505,6 +516,8 @@
session_id = voc_get_session_id(VOICE_SESSION_NAME);
else if (val == MSM_FRONTEND_DAI_VOLTE)
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+ else if (val == MSM_FRONTEND_DAI_SGLTE)
+ session_id = voc_get_session_id(SGLTE_SESSION_NAME);
else
session_id = voc_get_session_id(VOIP_SESSION_NAME);
@@ -811,6 +824,8 @@
unsigned int techs = 0;
unsigned short offset, value, max, index;
+ srs_alsa_ctrl_ever_called = 1;
+
max = sizeof(msm_srs_trumedia_params) >> 1;
index = (unsigned short)((ucontrol->value.integer.value[0] &
SRS_PARAM_INDEX_MASK) >> 31);
@@ -1303,6 +1318,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_PRI_I2S_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_PRI_I2S_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new sec_i2s_rx_voice_mixer_controls[] = {
@@ -1315,6 +1333,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SEC_I2S_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_SEC_I2S_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
@@ -1327,6 +1348,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_SLIMBUS_0_RX ,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
@@ -1342,6 +1366,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_INT_BT_SCO_RX ,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new mi2s_rx_voice_mixer_controls[] = {
@@ -1354,6 +1381,9 @@
SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_MI2S_RX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_MI2S_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = {
@@ -1369,6 +1399,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_AFE_PCM_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_AFE_PCM_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = {
@@ -1384,6 +1417,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_AUXPCM_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_AUXPCM_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new sec_aux_pcm_rx_voice_mixer_controls[] = {
@@ -1396,6 +1432,9 @@
SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = {
@@ -1411,6 +1450,9 @@
SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_HDMI_RX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
msm_routing_put_voice_stub_mixer),
+ SOC_SINGLE_EXT("SGLTE", MSM_BACKEND_DAI_HDMI_RX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new stub_rx_mixer_controls[] = {
@@ -1476,6 +1518,29 @@
msm_routing_put_voice_mixer),
};
+static const struct snd_kcontrol_new tx_sglte_mixer_controls[] = {
+ SOC_SINGLE_EXT("PRI_TX_SGLTE", MSM_BACKEND_DAI_PRI_I2S_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("MI2S_TX_SGLTE", MSM_BACKEND_DAI_MI2S_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SLIM_0_TX_SGLTE", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_SGLTE",
+ MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_SGLTE, 1, 0,
+ msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AFE_PCM_TX_SGLTE", MSM_BACKEND_DAI_AFE_PCM_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("AUX_PCM_TX_SGLTE", MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("SEC_AUX_PCM_TX_SGLTE", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+ MSM_FRONTEND_DAI_SGLTE, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
SOC_SINGLE_EXT("PRI_TX_Voip", MSM_BACKEND_DAI_PRI_I2S_TX,
MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
@@ -1878,6 +1943,8 @@
SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("VoLTE_DL", "VoLTE Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("VoLTE_UL", "VoLTE Capture", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SGLTE_DL", "SGLTE Playback", 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("SGLTE_UL", "SGLTE Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
0, 0, 0, 0),
@@ -2025,6 +2092,9 @@
SND_SOC_DAPM_MIXER("VoLTE_Tx Mixer",
SND_SOC_NOPM, 0, 0, tx_volte_mixer_controls,
ARRAY_SIZE(tx_volte_mixer_controls)),
+ SND_SOC_DAPM_MIXER("SGLTE_Tx Mixer",
+ SND_SOC_NOPM, 0, 0, tx_sglte_mixer_controls,
+ ARRAY_SIZE(tx_sglte_mixer_controls)),
SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
@@ -2175,41 +2245,49 @@
{"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"PRI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"PRI_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
{"SEC_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SEC_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SEC_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"SEC_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"SEC_I2S_RX", NULL, "SEC_RX_Voice Mixer"},
{"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SLIM_0_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SLIM_0_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"INTERNAL_BT_SCO_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
{"AFE_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"AFE_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"AFE_PCM_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"PCM_RX", NULL, "AFE_PCM_RX_Voice Mixer"},
{"AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"AUX_PCM_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"},
{"SEC_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"SEC_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"SEC_AUX_PCM_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"SEC_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX_Voice Mixer"},
{"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
{"HDMI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+ {"HDMI_RX_Voice Mixer", "SGLTE", "SGLTE_DL"},
{"HDMI_RX_Voice Mixer", "Voip", "VOIP_DL"},
{"HDMI", NULL, "HDMI_RX_Voice Mixer"},
{"HDMI", NULL, "HDMI_DL_HL"},
@@ -2229,6 +2307,14 @@
{"VoLTE_Tx Mixer", "AUX_PCM_TX_VoLTE", "AUX_PCM_TX"},
{"VoLTE_Tx Mixer", "SEC_AUX_PCM_TX_VoLTE", "SEC_AUX_PCM_TX"},
{"VoLTE_UL", NULL, "VoLTE_Tx Mixer"},
+ {"SGLTE_Tx Mixer", "PRI_TX_SGLTE", "PRI_I2S_TX"},
+ {"SGLTE_Tx Mixer", "MI2S_TX_SGLTE", "MI2S_TX"},
+ {"SGLTE_Tx Mixer", "SLIM_0_TX_SGLTE", "SLIMBUS_0_TX"},
+ {"SGLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_SGLTE", "INT_BT_SCO_TX"},
+ {"SGLTE_Tx Mixer", "AFE_PCM_TX_SGLTE", "PCM_TX"},
+ {"SGLTE_Tx Mixer", "AUX_PCM_TX_SGLTE", "AUX_PCM_TX"},
+ {"SGLTE_Tx Mixer", "SEC_AUX_PCM_TX_SGLTE", "SEC_AUX_PCM_TX"},
+ {"SGLTE_UL", NULL, "SGLTE_Tx Mixer"},
{"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
{"Voip_Tx Mixer", "MI2S_TX_Voip", "MI2S_TX"},
{"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index d1230ad..2f213e7 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -66,6 +66,7 @@
MSM_FRONTEND_DAI_AFE_TX,
MSM_FRONTEND_DAI_VOICE_STUB,
MSM_FRONTEND_DAI_VOLTE,
+ MSM_FRONTEND_DAI_SGLTE,
MSM_FRONTEND_DAI_MAX,
};
diff --git a/sound/soc/msm/msm-pcm-voice.c b/sound/soc/msm/msm-pcm-voice.c
index 7bdb4f0..633973e 100644
--- a/sound/soc/msm/msm-pcm-voice.c
+++ b/sound/soc/msm/msm-pcm-voice.c
@@ -59,6 +59,14 @@
return false;
}
+static int is_sglte(struct msm_voice *psglte)
+{
+ if (psglte == &voice_info[SGLTE_SESSION_INDEX])
+ return true;
+ else
+ return false;
+}
+
static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
@@ -93,6 +101,10 @@
voice = &voice_info[VOLTE_SESSION_INDEX];
pr_debug("%s: Open VoLTE Substream Id=%s\n",
__func__, substream->pcm->id);
+ } else if (!strncmp("SGLTE", substream->pcm->id, 5)) {
+ voice = &voice_info[SGLTE_SESSION_INDEX];
+ pr_debug("%s: Open SGLTE Substream Id=%s\n",
+ __func__, substream->pcm->id);
} else {
voice = &voice_info[VOICE_SESSION_INDEX];
pr_debug("%s: Open VOICE Substream Id=%s\n",
@@ -162,6 +174,8 @@
pr_debug("end voice call\n");
if (is_volte(prtd))
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+ else if (is_sglte(prtd))
+ session_id = voc_get_session_id(SGLTE_SESSION_NAME);
else
session_id = voc_get_session_id(VOICE_SESSION_NAME);
voc_end_voice_call(session_id);
@@ -187,6 +201,8 @@
if (prtd->playback_start && prtd->capture_start) {
if (is_volte(prtd))
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+ else if (is_sglte(prtd))
+ session_id = voc_get_session_id(SGLTE_SESSION_NAME);
else
session_id = voc_get_session_id(VOICE_SESSION_NAME);
voc_start_voice_call(session_id);
@@ -217,6 +233,8 @@
pr_debug("%s: cmd = %d\n", __func__, cmd);
if (is_volte(prtd))
session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+ else if (is_sglte(prtd))
+ session_id = voc_get_session_id(SGLTE_SESSION_NAME);
else
session_id = voc_get_session_id(VOICE_SESSION_NAME);
@@ -290,6 +308,23 @@
return 0;
}
+static int msm_sglte_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_sglte_volume_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int volume = ucontrol->value.integer.value[0];
+ pr_debug("%s: volume: %d\n", __func__, volume);
+ voc_set_rx_vol_index(voc_get_session_id(SGLTE_SESSION_NAME),
+ RX_PATH, volume);
+ return 0;
+}
+
static int msm_voice_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -328,6 +363,25 @@
return 0;
}
+static int msm_sglte_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = 0;
+ return 0;
+}
+
+static int msm_sglte_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mute = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: mute=%d\n", __func__, mute);
+
+ voc_set_tx_mute(voc_get_session_id(SGLTE_SESSION_NAME), TX_PATH, mute);
+
+ return 0;
+}
+
static int msm_voice_rx_device_mute_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -368,6 +422,26 @@
return 0;
}
+static int msm_sglte_rx_device_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] =
+ voc_get_rx_device_mute(voc_get_session_id(SGLTE_SESSION_NAME));
+ return 0;
+}
+
+static int msm_sglte_rx_device_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int mute = ucontrol->value.integer.value[0];
+
+ pr_debug("%s: mute=%d\n", __func__, mute);
+
+ voc_set_rx_device_mute(voc_get_session_id(SGLTE_SESSION_NAME), mute);
+
+ return 0;
+}
+
static const char const *tty_mode[] = {"OFF", "HCO", "VCO", "FULL"};
static const struct soc_enum msm_tty_mode_enum[] = {
SOC_ENUM_SINGLE_EXT(4, tty_mode),
@@ -481,6 +555,13 @@
msm_volte_mute_get, msm_volte_mute_put),
SOC_SINGLE_EXT("VoLTE Rx Volume", SND_SOC_NOPM, 0, 5, 0,
msm_volte_volume_get, msm_volte_volume_put),
+ SOC_SINGLE_EXT("SGLTE Rx Device Mute", SND_SOC_NOPM, 0, 1, 0,
+ msm_sglte_rx_device_mute_get,
+ msm_sglte_rx_device_mute_put),
+ SOC_SINGLE_EXT("SGLTE Tx Mute", SND_SOC_NOPM, 0, 1, 0,
+ msm_sglte_mute_get, msm_sglte_mute_put),
+ SOC_SINGLE_EXT("SGLTE Rx Volume", SND_SOC_NOPM, 0, 5, 0,
+ msm_sglte_volume_get, msm_sglte_volume_put),
};
static struct snd_pcm_ops msm_pcm_ops = {
@@ -543,6 +624,7 @@
memset(&voice_info, 0, sizeof(voice_info));
mutex_init(&voice_info[VOICE_SESSION_INDEX].lock);
mutex_init(&voice_info[VOLTE_SESSION_INDEX].lock);
+ mutex_init(&voice_info[SGLTE_SESSION_INDEX].lock);
return platform_driver_register(&msm_pcm_driver);
}
diff --git a/sound/soc/msm/msm-pcm-voice.h b/sound/soc/msm/msm-pcm-voice.h
index aa00577..41aca89 100644
--- a/sound/soc/msm/msm-pcm-voice.h
+++ b/sound/soc/msm/msm-pcm-voice.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011,2012 Code Aurora Forum. 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 and
@@ -16,6 +16,7 @@
enum {
VOICE_SESSION_INDEX,
VOLTE_SESSION_INDEX,
+ SGLTE_SESSION_INDEX,
VOICE_SESSION_INDEX_MAX,
};
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index 98cfa6d..2c44b46 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -1321,6 +1321,21 @@
.codec_name = "snd-soc-dummy",
.be_id = MSM_FRONTEND_DAI_VOLTE,
},
+ {
+ .name = "SGLTE",
+ .stream_name = "SGLTE",
+ .cpu_dai_name = "SGLTE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,/* this dainlink has playback support */
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .be_id = MSM_FRONTEND_DAI_SGLTE,
+ },
/* Backend BT/FM DAI Links */
{
.name = LPASS_BE_INT_BT_SCO_RX,
diff --git a/sound/soc/msm/qdsp6/q6voice.c b/sound/soc/msm/qdsp6/q6voice.c
index 0c30dc9..f66a01c 100644
--- a/sound/soc/msm/qdsp6/q6voice.c
+++ b/sound/soc/msm/qdsp6/q6voice.c
@@ -34,6 +34,7 @@
#define VOC_PATH_PASSIVE 0
#define VOC_PATH_FULL 1
#define VOC_PATH_VOLTE_PASSIVE 2
+#define VOC_PATH_SGLTE_PASSIVE 3
/* CVP CAL Size: 245760 = 240 * 1024 */
#define CVP_CAL_SIZE 245760
@@ -149,6 +150,9 @@
else if (!strncmp(name, "VoLTE session", 13))
session_id =
common.voice[VOC_PATH_VOLTE_PASSIVE].session_id;
+ else if (!strncmp(name, "SGLTE session", 13))
+ session_id =
+ common.voice[VOC_PATH_SGLTE_PASSIVE].session_id;
else
session_id = common.voice[VOC_PATH_FULL].session_id;
@@ -189,6 +193,11 @@
return (session_id == common.voice[VOC_PATH_VOLTE_PASSIVE].session_id);
}
+static bool is_sglte_session(u16 session_id)
+{
+ return (session_id == common.voice[VOC_PATH_SGLTE_PASSIVE].session_id);
+}
+
static int voice_apr_register(void)
{
pr_debug("%s\n", __func__);
@@ -275,8 +284,10 @@
pr_err("%s: apr_mvm is NULL.\n", __func__);
return -EINVAL;
}
- pr_debug("%s: VoLTE command to MVM\n", __func__);
- if (is_volte_session(v->session_id)) {
+ pr_debug("%s: VoLTE/SGLTE command to MVM\n", __func__);
+ if (is_volte_session(v->session_id) ||
+ is_sglte_session(v->session_id)) {
+
mvm_handle = voice_get_mvm_handle(v);
mvm_voice_ctl_cmd.hdr.hdr_field = APR_HDR_FIELD(
APR_MSG_TYPE_SEQ_CMD,
@@ -350,7 +361,8 @@
if (!mvm_handle) {
if (is_voice_session(v->session_id) ||
- is_volte_session(v->session_id)) {
+ is_volte_session(v->session_id) ||
+ is_sglte_session(v->session_id)) {
mvm_session_cmd.hdr.hdr_field = APR_HDR_FIELD(
APR_MSG_TYPE_SEQ_CMD,
APR_HDR_LEN(APR_HDR_SIZE),
@@ -369,11 +381,15 @@
if (is_volte_session(v->session_id)) {
strlcpy(mvm_session_cmd.mvm_session.name,
"default volte voice",
+ sizeof(mvm_session_cmd.mvm_session.name) - 1);
+ } else if (is_sglte_session(v->session_id)) {
+ strlcpy(mvm_session_cmd.mvm_session.name,
+ "default modem voice2",
sizeof(mvm_session_cmd.mvm_session.name));
} else {
- strlcpy(mvm_session_cmd.mvm_session.name,
+ strlcpy(mvm_session_cmd.mvm_session.name,
"default modem voice",
- sizeof(mvm_session_cmd.mvm_session.name));
+ sizeof(mvm_session_cmd.mvm_session.name) - 1);
}
v->mvm_state = CMD_STATUS_FAIL;
@@ -432,7 +448,8 @@
/* send cmd to create cvs session */
if (!cvs_handle) {
if (is_voice_session(v->session_id) ||
- is_volte_session(v->session_id)) {
+ is_volte_session(v->session_id) ||
+ is_sglte_session(v->session_id)) {
pr_debug("%s: creating CVS passive session\n",
__func__);
@@ -452,11 +469,15 @@
if (is_volte_session(v->session_id)) {
strlcpy(mvm_session_cmd.mvm_session.name,
"default volte voice",
- sizeof(mvm_session_cmd.mvm_session.name));
- } else {
- strlcpy(cvs_session_cmd.cvs_session.name,
- "default modem voice",
+ sizeof(mvm_session_cmd.mvm_session.name) - 1);
+ } else if (is_sglte_session(v->session_id)) {
+ strlcpy(cvs_session_cmd.cvs_session.name,
+ "default modem voice2",
sizeof(cvs_session_cmd.cvs_session.name));
+ } else {
+ strlcpy(cvs_session_cmd.cvs_session.name,
+ "default modem voice",
+ sizeof(cvs_session_cmd.cvs_session.name) - 1);
}
v->cvs_state = CMD_STATUS_FAIL;
diff --git a/sound/soc/msm/qdsp6/q6voice.h b/sound/soc/msm/qdsp6/q6voice.h
index 88ab0d5..468aba8 100644
--- a/sound/soc/msm/qdsp6/q6voice.h
+++ b/sound/soc/msm/qdsp6/q6voice.h
@@ -16,7 +16,7 @@
#include <linux/ion.h>
#define MAX_VOC_PKT_SIZE 642
-#define SESSION_NAME_LEN 20
+#define SESSION_NAME_LEN 21
#define VOC_REC_UPLINK 0x00
#define VOC_REC_DOWNLINK 0x01
@@ -918,7 +918,7 @@
void *buf;
};
-#define MAX_VOC_SESSIONS 3
+#define MAX_VOC_SESSIONS 4
#define SESSION_ID_BASE 0xFFF0
struct common_data {
@@ -990,6 +990,7 @@
#define VOICE_SESSION_NAME "Voice session"
#define VOIP_SESSION_NAME "VoIP session"
#define VOLTE_SESSION_NAME "VoLTE session"
+#define SGLTE_SESSION_NAME "SGLTE session"
uint16_t voc_get_session_id(char *name);
int voc_start_playback(uint32_t set);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 3a9fbe1..fd3fe6a 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -236,11 +236,24 @@
}
if (codec_dai->driver->ops->startup) {
- ret = codec_dai->driver->ops->startup(substream, codec_dai);
- if (ret < 0) {
- printk(KERN_ERR "asoc: can't open codec %s\n",
- codec_dai->name);
- goto codec_dai_err;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = codec_dai->driver->ops->startup(substream,
+ codec_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "asoc: can't open codec %s\n",
+ codec_dai->name);
+ goto codec_dai_err;
+ }
+ } else {
+ if (!codec_dai->capture_active) {
+ ret = codec_dai->driver->ops->startup(substream,
+ codec_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "can't open codec %s\n",
+ codec_dai->name);
+ goto codec_dai_err;
+ }
+ }
}
}
@@ -456,8 +469,15 @@
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dai_digital_mute(codec_dai, 1);
- if (cpu_dai->driver->ops->shutdown)
- cpu_dai->driver->ops->shutdown(substream, cpu_dai);
+ if (cpu_dai->driver->ops->shutdown) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ codec_dai->driver->ops->shutdown(substream, codec_dai);
+ } else {
+ if (!codec_dai->capture_active)
+ codec_dai->driver->ops->shutdown(substream,
+ codec_dai);
+ }
+ }
if (codec_dai->driver->ops->shutdown)
codec_dai->driver->ops->shutdown(substream, codec_dai);
@@ -485,7 +505,8 @@
}
} else {
/* capture streams can be powered down now */
- snd_soc_dapm_stream_event(rtd,
+ if (!codec_dai->capture_active)
+ snd_soc_dapm_stream_event(rtd,
codec_dai->driver->capture.stream_name,
SND_SOC_DAPM_STREAM_STOP);
}
@@ -557,11 +578,12 @@
snd_soc_dapm_stream_event(rtd,
codec_dai->driver->playback.stream_name,
SND_SOC_DAPM_STREAM_START);
- else
- snd_soc_dapm_stream_event(rtd,
+ else {
+ if (codec_dai->capture_active == 1)
+ snd_soc_dapm_stream_event(rtd,
codec_dai->driver->capture.stream_name,
SND_SOC_DAPM_STREAM_START);
-
+ }
snd_soc_dai_digital_mute(codec_dai, 0);
out:
@@ -594,11 +616,24 @@
}
if (codec_dai->driver->ops->hw_params) {
- ret = codec_dai->driver->ops->hw_params(substream, params, codec_dai);
- if (ret < 0) {
- printk(KERN_ERR "asoc: can't set codec %s hw params\n",
- codec_dai->name);
- goto codec_err;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = codec_dai->driver->ops->hw_params(substream,
+ params, codec_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "not set codec %s hw params\n",
+ codec_dai->name);
+ goto codec_err;
+ }
+ } else {
+ if (codec_dai->capture_active == 1) {
+ ret = codec_dai->driver->ops->hw_params(
+ substream, params, codec_dai);
+ if (ret < 0) {
+ printk(KERN_ERR "fail: %s hw params\n",
+ codec_dai->name);
+ goto codec_err;
+ }
+ }
}
}
@@ -706,9 +741,19 @@
int ret;
if (codec_dai->driver->ops->trigger) {
- ret = codec_dai->driver->ops->trigger(substream, cmd, codec_dai);
- if (ret < 0)
- return ret;
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = codec_dai->driver->ops->trigger(substream,
+ cmd, codec_dai);
+ if (ret < 0)
+ return ret;
+ } else {
+ if (codec_dai->capture_active == 1) {
+ ret = codec_dai->driver->ops->trigger(
+ substream, cmd, codec_dai);
+ if (ret < 0)
+ return ret;
+ }
+ }
}
if (platform->driver->ops && platform->driver->ops->trigger) {
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 1fcf1bb..28e5548 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -30,6 +30,17 @@
return __value(parse_events_text + 1, 16, PE_RAW);
}
+static int sh_raw(void)
+{
+ return __value(parse_events_text + 2, 16, PE_SH_RAW);
+}
+
+static int fab_raw(void)
+{
+ return __value(parse_events_text + 2, 16, PE_FAB_RAW);
+}
+
+
static int str(int token)
{
parse_events_lval.str = strdup(parse_events_text);
@@ -107,6 +118,8 @@
mem: { return PE_PREFIX_MEM; }
r{num_raw_hex} { return raw(); }
+rs{num_raw_hex} { return sh_raw(); }
+rm{num_raw_hex} { return fab_raw(); }
{num_dec} { return value(10); }
{num_hex} { return value(16); }
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index d9637da..07b292d 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -24,7 +24,7 @@
%}
-%token PE_VALUE PE_VALUE_SYM PE_RAW PE_TERM
+%token PE_VALUE PE_VALUE_SYM PE_RAW PE_SH_RAW PE_FAB_RAW PE_TERM
%token PE_NAME
%token PE_MODIFIER_EVENT PE_MODIFIER_BP
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
@@ -33,6 +33,8 @@
%type <num> PE_VALUE
%type <num> PE_VALUE_SYM
%type <num> PE_RAW
+%type <num> PE_SH_RAW
+%type <num> PE_FAB_RAW
%type <num> PE_TERM
%type <str> PE_NAME
%type <str> PE_NAME_CACHE_TYPE
@@ -77,7 +79,9 @@
event_legacy_mem |
event_legacy_tracepoint sep_dc |
event_legacy_numeric sep_dc |
- event_legacy_raw sep_dc
+ event_legacy_raw sep_dc |
+ event_legacy_shared_raw sep_dc |
+ event_legacy_fabric_raw sep_dc
event_pmu:
PE_NAME '/' event_config '/'
@@ -149,6 +153,18 @@
ABORT_ON(parse_events_add_numeric(list_event, idx, PERF_TYPE_RAW, $1, NULL));
}
+event_legacy_shared_raw:
+PE_SH_RAW
+{
+ ABORT_ON(parse_events_add_numeric(list_event, idx, 6, $1, NULL));
+}
+
+event_legacy_fabric_raw:
+PE_FAB_RAW
+{
+ ABORT_ON(parse_events_add_numeric(list_event, idx, 7, $1, NULL));
+}
+
event_config:
event_config ',' event_term
{