Merge "msm: bam_dmux: log state changes" into msm-3.0
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 2097e0a..0a7fe5f 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -222,6 +222,10 @@
to allow physical memory down to a theoretical minimum of 64K
boundaries.
+config GENERIC_BUG
+ def_bool y
+ depends on BUG
+
source "init/Kconfig"
source "kernel/Kconfig.freezer"
diff --git a/arch/arm/configs/msm-copper_defconfig b/arch/arm/configs/msm-copper_defconfig
index 4d4c27f..8492aa3 100644
--- a/arch/arm/configs/msm-copper_defconfig
+++ b/arch/arm/configs/msm-copper_defconfig
@@ -133,6 +133,7 @@
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_ASCII=y
CONFIG_NLS_ISO8859_1=y
diff --git a/arch/arm/configs/msm7627-perf_defconfig b/arch/arm/configs/msm7627-perf_defconfig
index 0f2ffc1..82160ce 100644
--- a/arch/arm/configs/msm7627-perf_defconfig
+++ b/arch/arm/configs/msm7627-perf_defconfig
@@ -151,6 +151,8 @@
# CONFIG_WIRELESS_EXT_SYSFS is not set
CONFIG_RFKILL=y
# CONFIG_RFKILL_PM is not set
+CONFIG_GENLOCK=y
+CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_MTD=y
CONFIG_MTD_TESTS=m
CONFIG_MTD_CMDLINE_PARTS=y
diff --git a/arch/arm/configs/msm7627_defconfig b/arch/arm/configs/msm7627_defconfig
index d1d0e66..c5f1b30 100644
--- a/arch/arm/configs/msm7627_defconfig
+++ b/arch/arm/configs/msm7627_defconfig
@@ -151,6 +151,8 @@
# CONFIG_WIRELESS_EXT_SYSFS is not set
CONFIG_RFKILL=y
# CONFIG_RFKILL_PM is not set
+CONFIG_GENLOCK=y
+CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_MTD=y
CONFIG_MTD_TESTS=m
CONFIG_MTD_CMDLINE_PARTS=y
diff --git a/arch/arm/configs/msm7627a-perf_defconfig b/arch/arm/configs/msm7627a-perf_defconfig
index 342aad6..f3979f0 100644
--- a/arch/arm/configs/msm7627a-perf_defconfig
+++ b/arch/arm/configs/msm7627a-perf_defconfig
@@ -249,6 +249,9 @@
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_SOC=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_SUSPEND=y
@@ -303,6 +306,7 @@
CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
CONFIG_EXT4_FS=y
+CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_YAFFS_FS=y
diff --git a/arch/arm/configs/msm7627a_defconfig b/arch/arm/configs/msm7627a_defconfig
index c524759..4d4bbbc 100644
--- a/arch/arm/configs/msm7627a_defconfig
+++ b/arch/arm/configs/msm7627a_defconfig
@@ -248,6 +248,9 @@
CONFIG_SND=y
CONFIG_SND_SOC=y
CONFIG_SND_MSM_SOC=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_SUSPEND=y
@@ -298,6 +301,7 @@
CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
CONFIG_EXT4_FS=y
+CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_YAFFS_FS=y
diff --git a/arch/arm/configs/msm7630-perf_defconfig b/arch/arm/configs/msm7630-perf_defconfig
index e5fec35..aa8c7ee 100644
--- a/arch/arm/configs/msm7630-perf_defconfig
+++ b/arch/arm/configs/msm7630-perf_defconfig
@@ -352,6 +352,7 @@
CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
CONFIG_EXT4_FS=y
+CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_YAFFS_FS=y
diff --git a/arch/arm/configs/msm7630_defconfig b/arch/arm/configs/msm7630_defconfig
index 6e3290b..d8bffa1 100644
--- a/arch/arm/configs/msm7630_defconfig
+++ b/arch/arm/configs/msm7630_defconfig
@@ -337,6 +337,7 @@
CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
CONFIG_EXT4_FS=y
+CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_YAFFS_FS=y
diff --git a/arch/arm/configs/msm8660-perf_defconfig b/arch/arm/configs/msm8660-perf_defconfig
index 255d06e..cffa904 100644
--- a/arch/arm/configs/msm8660-perf_defconfig
+++ b/arch/arm/configs/msm8660-perf_defconfig
@@ -62,6 +62,7 @@
CONFIG_MSM_RMT_STORAGE_CLIENT=y
CONFIG_MSM_SDIO_SMEM=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_PIL_QDSP6V3=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
@@ -344,6 +345,8 @@
CONFIG_SND_USB_AUDIO=y
CONFIG_SND_SOC=y
CONFIG_HID_APPLE=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_SUSPEND=y
@@ -415,6 +418,7 @@
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_EXT4_FS_SECURITY=y
+CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_NFS_FS=y
@@ -433,6 +437,7 @@
CONFIG_TIMER_STATS=y
# CONFIG_DEBUG_PREEMPT is not set
CONFIG_DEBUG_INFO=y
+CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
CONFIG_CRYPTO_SHA256=y
diff --git a/arch/arm/configs/msm8660_defconfig b/arch/arm/configs/msm8660_defconfig
index 0a88039..2382ed1 100644
--- a/arch/arm/configs/msm8660_defconfig
+++ b/arch/arm/configs/msm8660_defconfig
@@ -54,6 +54,7 @@
# CONFIG_MSM_RPCSERVER_HANDSET is not set
CONFIG_MSM_RMT_STORAGE_CLIENT=y
# CONFIG_MSM_HW3D is not set
+CONFIG_MSM_PIL_QDSP6V3=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_RPM_LOG=y
CONFIG_MSM_RPM_STATS_LOG=y
@@ -332,6 +333,8 @@
CONFIG_SND_SOC=y
CONFIG_MSM_8x60_VOIP=y
CONFIG_HID_APPLE=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_SUSPEND=y
@@ -400,6 +403,7 @@
CONFIG_EXT4_FS=y
CONFIG_EXT4_FS_POSIX_ACL=y
CONFIG_EXT4_FS_SECURITY=y
+CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_NFS_FS=y
@@ -428,6 +432,7 @@
CONFIG_DEBUG_LIST=y
CONFIG_DEBUG_SG=y
CONFIG_DEBUG_PAGEALLOC=y
+CONFIG_ENABLE_DEFAULT_TRACERS=y
CONFIG_DYNAMIC_DEBUG=y
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_LL=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index bb94d2a..a55b57e 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -61,6 +61,7 @@
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
# CONFIG_MSM_HW3D is not set
CONFIG_MSM_PIL_QDSP6V4=y
+CONFIG_MSM_PIL_RIVA=y
CONFIG_MSM_SUBSYSTEM_RESTART=y
CONFIG_MSM_MODEM_8960=y
CONFIG_MSM_LPASS_8960=y
@@ -75,7 +76,6 @@
CONFIG_MSM_QDSS=y
CONFIG_MSM_SLEEP_STATS=y
CONFIG_STRICT_MEMORY_RWX=y
-CONFIG_MSM_SHOW_RESUME_IRQ=y
CONFIG_NO_HZ=y
CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
@@ -84,6 +84,7 @@
CONFIG_AEABI=y
CONFIG_HIGHMEM=y
CONFIG_VMALLOC_RESERVE=0x19000000
+CONFIG_CC_STACKPROTECTOR=y
CONFIG_CP_ACCESS=y
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
@@ -242,6 +243,8 @@
CONFIG_SLIP_MODE_SLIP6=y
# CONFIG_MSM_RMNET is not set
CONFIG_MSM_RMNET_BAM=y
+CONFIG_USB_USBNET=y
+CONFIG_MSM_RMNET_USB=y
CONFIG_INPUT_EVDEV=y
CONFIG_INPUT_EVBUG=m
CONFIG_KEYBOARD_PMIC8XXX=y
@@ -292,6 +295,7 @@
CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
CONFIG_USB_VIDEO_CLASS=y
CONFIG_MSM_CAMERA_V4L2=y
+CONFIG_MT9M114=y
CONFIG_MSM_CAMERA_FLASH_SC628A=y
CONFIG_MSM_CAMERA_SENSOR=y
CONFIG_MSM_ACTUATOR=y
@@ -322,6 +326,9 @@
CONFIG_SND_USB_AUDIO=y
CONFIG_SND_SOC=y
CONFIG_SND_SOC_MSM8960=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
CONFIG_USB=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
CONFIG_USB_SUSPEND=y
@@ -390,6 +397,7 @@
CONFIG_EXT3_FS=y
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
CONFIG_EXT4_FS=y
+CONFIG_FUSE_FS=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
CONFIG_NFS_FS=y
diff --git a/arch/arm/configs/msm9615_defconfig b/arch/arm/configs/msm9615_defconfig
index 003f9c1..ed9a45b 100644
--- a/arch/arm/configs/msm9615_defconfig
+++ b/arch/arm/configs/msm9615_defconfig
@@ -187,6 +187,7 @@
CONFIG_RTC_DRV_PM8XXX=y
CONFIG_MSM_SSBI=y
CONFIG_SPS=y
+CONFIG_USB_BAM=y
CONFIG_SPS_SUPPORT_BAMDMA=y
CONFIG_VFAT_FS=y
CONFIG_TMPFS=y
diff --git a/arch/arm/include/asm/bug.h b/arch/arm/include/asm/bug.h
index 4d88425..9abe7a0 100644
--- a/arch/arm/include/asm/bug.h
+++ b/arch/arm/include/asm/bug.h
@@ -3,21 +3,58 @@
#ifdef CONFIG_BUG
-#ifdef CONFIG_DEBUG_BUGVERBOSE
-extern void __bug(const char *file, int line) __attribute__((noreturn));
-/* give file/line information */
-#define BUG() __bug(__FILE__, __LINE__)
-
+/*
+ * Use a suitable undefined instruction to use for ARM/Thumb2 bug handling.
+ * We need to be careful not to conflict with those used by other modules and
+ * the register_undef_hook() system.
+ */
+#ifdef CONFIG_THUMB2_KERNEL
+#define BUG_INSTR_VALUE 0xde02
+#define BUG_INSTR_TYPE ".hword "
#else
-
-/* this just causes an oops */
-#define BUG() do { *(int *)0 = 0; } while (1)
-
+#define BUG_INSTR_VALUE 0xe7f001f2
+#define BUG_INSTR_TYPE ".word "
#endif
+
+#define BUG() _BUG(__FILE__, __LINE__, BUG_INSTR_VALUE)
+#define _BUG(file, line, value) __BUG(file, line, value)
+
+#ifdef CONFIG_DEBUG_BUGVERBOSE
+
+/*
+ * The extra indirection is to ensure that the __FILE__ string comes through
+ * OK. Many version of gcc do not support the asm %c parameter which would be
+ * preferable to this unpleasantness. We use mergeable string sections to
+ * avoid multiple copies of the string appearing in the kernel image.
+ */
+
+#define __BUG(__file, __line, __value) \
+do { \
+ BUILD_BUG_ON(sizeof(struct bug_entry) != 12); \
+ asm volatile("1:\t" BUG_INSTR_TYPE #__value "\n" \
+ ".pushsection .rodata.str, \"aMS\", %progbits, 1\n" \
+ "2:\t.asciz " #__file "\n" \
+ ".popsection\n" \
+ ".pushsection __bug_table,\"a\"\n" \
+ "3:\t.word 1b, 2b\n" \
+ "\t.hword " #__line ", 0\n" \
+ ".popsection"); \
+ unreachable(); \
+} while (0)
+
+#else /* not CONFIG_DEBUG_BUGVERBOSE */
+
+#define __BUG(__file, __line, __value) \
+do { \
+ asm volatile(BUG_INSTR_TYPE #__value); \
+ unreachable(); \
+} while (0)
+#endif /* CONFIG_DEBUG_BUGVERBOSE */
+
#define HAVE_ARCH_BUG
-#endif
+#endif /* CONFIG_BUG */
#include <asm-generic/bug.h>
diff --git a/arch/arm/include/asm/mach/mmc.h b/arch/arm/include/asm/mach/mmc.h
index c86fad9..75ec143 100644
--- a/arch/arm/include/asm/mach/mmc.h
+++ b/arch/arm/include/asm/mach/mmc.h
@@ -130,6 +130,8 @@
void (*sdio_lpm_gpio_setup)(struct device *, unsigned int);
unsigned int status_irq;
unsigned int status_gpio;
+ /* Indicates the polarity of the GPIO line when card is inserted */
+ bool is_status_gpio_active_low;
unsigned int sdiowakeup_irq;
unsigned long irq_flags;
unsigned long mmc_bus_width;
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 2f8bf62..b0c57a9 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -398,6 +398,15 @@
mcr p15, 0, r0, c1, c0, 0 @ write control reg
mrc p15, 0, r3, c0, c0, 0 @ read id reg
mov r3, r3
+#ifdef CONFIG_ARCH_MSM_KRAIT
+ ldr r3, =0xff00fc00
+ and r3, r9, r3
+ ldr r4, =0x51000400
+ cmp r3, r4
+ mrceq p15, 7, r3, c15, c0, 2
+ biceq r3, r3, #0x400
+ mcreq p15, 7, r3, c15, c0, 2
+#endif
mov r3, r13
mov pc, r3
__enable_mmu_end:
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index b59ad93..6a7158b 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -247,7 +247,6 @@
/* Don't read disabled counters! */
if (hwc->idx < 0)
return;
-
armpmu_event_update(event, hwc, hwc->idx, 0);
}
@@ -632,6 +631,24 @@
armpmu->stop();
}
+static void armpmu_update_counters(void)
+{
+ int idx;
+ struct cpu_hw_events *cpuc = &__get_cpu_var(cpu_hw_events);
+
+ if (!armpmu)
+ return;
+
+ for (idx = 0; idx <= armpmu->num_events; ++idx) {
+ struct perf_event *event = cpuc->events[idx];
+
+ if (!event)
+ continue;
+
+ armpmu_read(event);
+ }
+}
+
static struct pmu pmu = {
.pmu_enable = armpmu_enable,
.pmu_disable = armpmu_disable,
@@ -657,6 +674,7 @@
{
switch (cmd) {
case CPU_PM_ENTER:
+ armpmu_update_counters();
perf_pmu_disable(&pmu);
break;
diff --git a/arch/arm/kernel/perf_event_msm.c b/arch/arm/kernel/perf_event_msm.c
index b40a226..8fa2f30 100644
--- a/arch/arm/kernel/perf_event_msm.c
+++ b/arch/arm/kernel/perf_event_msm.c
@@ -610,6 +610,7 @@
u32 gr;
unsigned long event;
struct scorpion_evt evtinfo;
+ unsigned long long prev_count = local64_read(&hwc->prev_count);
/*
* Enable counter and interrupt, and set the counter to count
@@ -652,6 +653,9 @@
/* Enable interrupt for this counter */
armv7_pmnc_enable_intens(idx);
+ /* Restore prev val */
+ armv7pmu_write_counter(idx, prev_count & COUNT_MASK);
+
/* Enable counter */
armv7_pmnc_enable_counter(idx);
diff --git a/arch/arm/kernel/perf_event_msm_krait.c b/arch/arm/kernel/perf_event_msm_krait.c
index cf1f1ee..3c8fb84 100644
--- a/arch/arm/kernel/perf_event_msm_krait.c
+++ b/arch/arm/kernel/perf_event_msm_krait.c
@@ -419,6 +419,7 @@
u32 gr;
unsigned long event;
struct krait_evt evtinfo;
+ unsigned long long prev_count = local64_read(&hwc->prev_count);
/*
* Enable counter and interrupt, and set the counter to count
@@ -465,6 +466,9 @@
/* Enable interrupt for this counter */
armv7_pmnc_enable_intens(idx);
+ /* Restore prev val */
+ armv7pmu_write_counter(idx, prev_count & COUNT_MASK);
+
/* Enable counter */
armv7_pmnc_enable_counter(idx);
diff --git a/arch/arm/kernel/perf_event_msm_krait_l2.c b/arch/arm/kernel/perf_event_msm_krait_l2.c
index c8b48a8..335ccaf 100644
--- a/arch/arm/kernel/perf_event_msm_krait_l2.c
+++ b/arch/arm/kernel/perf_event_msm_krait_l2.c
@@ -117,7 +117,7 @@
set_l2_indirect_reg(group_reg, resr_val);
}
-static void set_evfilter(int ctr)
+static void set_evfilter_task_mode(int ctr)
{
u32 filter_reg = (ctr * 16) + IA_L2PMXEVFILTER_BASE;
u32 filter_val = 0x000f0030 | 1 << smp_processor_id();
@@ -125,6 +125,14 @@
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)
@@ -298,7 +306,10 @@
set_evres(evdesc.event_groupsel, evdesc.event_reg,
evdesc.event_group_code);
- set_evfilter(idx);
+ if (event->cpu < 0)
+ set_evfilter_task_mode(idx);
+ else
+ set_evfilter_sys_mode(idx);
out:
enable_intenset(idx);
diff --git a/arch/arm/kernel/perf_event_msm_l2.c b/arch/arm/kernel/perf_event_msm_l2.c
index 33fb5bf..c242754 100644
--- a/arch/arm/kernel/perf_event_msm_l2.c
+++ b/arch/arm/kernel/perf_event_msm_l2.c
@@ -427,13 +427,20 @@
asm volatile ("mcr p15, 3, %0, c15, c6, 7" : : "r" (val));
}
-static void bb_l2_set_evfilter(void)
+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) {
@@ -633,7 +640,10 @@
bb_l2_set_evcntcr();
- bb_l2_set_evfilter();
+ 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);
diff --git a/arch/arm/kernel/perf_event_v7.c b/arch/arm/kernel/perf_event_v7.c
index 277f1ce..3689e07 100644
--- a/arch/arm/kernel/perf_event_v7.c
+++ b/arch/arm/kernel/perf_event_v7.c
@@ -738,6 +738,10 @@
#define ARMV7_EXCLUDE_USER (1 << 30)
#define ARMV7_INCLUDE_HYP (1 << 27)
+
+/* Mask for restoring h/w counter val */
+#define COUNT_MASK 0xffffffff
+
/*
* Add an event filter to a given event. This will only work for PMUv2 PMUs.
*/
diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c
index c063c56..ef37557 100644
--- a/arch/arm/kernel/traps.c
+++ b/arch/arm/kernel/traps.c
@@ -21,6 +21,7 @@
#include <linux/kdebug.h>
#include <linux/module.h>
#include <linux/kexec.h>
+#include <linux/bug.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/sched.h>
@@ -270,6 +271,8 @@
raw_spin_lock_irq(&die_lock);
console_verbose();
bust_spinlocks(1);
+ if (!user_mode(regs))
+ report_bug(regs->ARM_pc, regs);
ret = __die(str, err, thread, regs);
if (regs && kexec_should_crash(thread->task))
@@ -301,6 +304,24 @@
}
}
+#ifdef CONFIG_GENERIC_BUG
+
+int is_valid_bugaddr(unsigned long pc)
+{
+#ifdef CONFIG_THUMB2_KERNEL
+ unsigned short bkpt;
+#else
+ unsigned long bkpt;
+#endif
+
+ if (probe_kernel_address((unsigned *)pc, bkpt))
+ return 0;
+
+ return bkpt == BUG_INSTR_VALUE;
+}
+
+#endif
+
static LIST_HEAD(undef_hook);
static DEFINE_RAW_SPINLOCK(undef_lock);
@@ -697,16 +718,6 @@
arm_notify_die("unknown data abort code", regs, &info, instr, 0);
}
-void __attribute__((noreturn)) __bug(const char *file, int line)
-{
- printk(KERN_CRIT"kernel BUG at %s:%d!\n", file, line);
- *(int *)0 = 0;
-
- /* Avoid "noreturn function does return" */
- for (;;);
-}
-EXPORT_SYMBOL(__bug);
-
void __readwrite_bug(const char *fn)
{
printk("%s called, but not implemented\n", fn);
diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S
index 8652d45..6530fa3 100644
--- a/arch/arm/kernel/vmlinux.lds.S
+++ b/arch/arm/kernel/vmlinux.lds.S
@@ -24,7 +24,8 @@
#define ARM_CPU_KEEP(x)
#endif
-#if defined(CONFIG_SMP_ON_UP) && !defined(CONFIG_DEBUG_SPINLOCK)
+#if (defined(CONFIG_SMP_ON_UP) && !defined(CONFIG_DEBUG_SPINLOCK)) || \
+ defined(CONFIG_GENERIC_BUG)
#define ARM_EXIT_KEEP(x) x
#else
#define ARM_EXIT_KEEP(x)
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 0078ebc..d80586e 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -41,6 +41,8 @@
select ARCH_HAS_HOLES_MEMORYMODEL
select MEMORY_HOTPLUG
select MEMORY_HOTREMOVE
+ select ARCH_ENABLE_MEMORY_HOTPLUG
+ select ARCH_ENABLE_MEMORY_HOTREMOVE
select MIGRATION
select ARCH_MEMORY_PROBE
select ARCH_MEMORY_REMOVE
@@ -73,6 +75,7 @@
select ARCH_REQUIRE_GPIOLIB
select MSM_ADM3
select REGULATOR
+ select MSM_RPM_REGULATOR
select MSM_V2_TLMM
select MSM_PIL
select MSM_SCM
@@ -126,6 +129,7 @@
select MSM_SCM if SMP
select MSM_DIRECT_SCLK_ACCESS
select REGULATOR
+ select MSM_RPM_REGULATOR
select MSM_RPM
select MSM_XO
select MSM_QDSP6_APR
@@ -143,10 +147,15 @@
select ARCH_HAS_HOLES_MEMORYMODEL
select MEMORY_HOTPLUG
select MEMORY_HOTREMOVE
+ select ARCH_ENABLE_MEMORY_HOTPLUG
+ select ARCH_ENABLE_MEMORY_HOTREMOVE
select MIGRATION
select ARCH_MEMORY_PROBE
select ARCH_MEMORY_REMOVE
select FIX_MOVABLE_ZONE
+ select CLEANCACHE
+ select QCACHE
+ select MSM_MULTIMEDIA_USE_ION
config ARCH_MSM8930
bool "MSM8930"
@@ -174,6 +183,8 @@
select ARCH_HAS_HOLES_MEMORYMODEL
select MEMORY_HOTPLUG
select MEMORY_HOTREMOVE
+ select ARCH_ENABLE_MEMORY_HOTPLUG
+ select ARCH_ENABLE_MEMORY_HOTREMOVE
select MIGRATION
select ARCH_MEMORY_PROBE
select ARCH_MEMORY_REMOVE
@@ -221,6 +232,9 @@
select MSM_RPM
select MSM_SPM_V2
select MSM_NATIVE_RESTART
+ select REGULATOR
+ select MSM_RPM_REGULATOR
+
endmenu
choice
@@ -1563,6 +1577,14 @@
for idle power collapse, wait the number of microseconds in case
Modem becomes ready soon.
+config MSM_RPM_REGULATOR
+ bool "RPM regulator driver"
+ depends on RPM && REGULATOR
+ help
+ Compile in support for the RPM regulator driver, used for setting
+ voltages and other parameters of the various power rails supplied
+ by some Qualcomm PMICs.
+
config MSM_PIL
bool "Peripheral image loading"
select FW_LOADER
@@ -1574,6 +1596,13 @@
Say yes to support these devices.
+config MSM_PIL_QDSP6V3
+ tristate "QDSP6v3 (Hexagon) Boot Support"
+ depends on MSM_PIL
+ help
+ Support for booting and shutting down QDSP6v3 processors (hexagon).
+ The QDSP6 is a low power DSP used in audio software applications.
+
config MSM_PIL_QDSP6V4
tristate "QDSP6v4 (Hexagon) Boot Support"
depends on MSM_PIL
@@ -1582,6 +1611,14 @@
The QDSP6 is a low power DSP used in audio, modem firmware, and modem
software applications.
+config MSM_PIL_RIVA
+ tristate "RIVA (WCNSS) Boot Support"
+ depends on MSM_PIL
+ help
+ Support for booting and shutting down the RIVA processor (WCNSS).
+ Riva is the wireless subsystem processor used in bluetooth, wireless
+ LAN, and FM software applications.
+
config MSM_SCM
bool "Secure Channel Manager (SCM) support"
default n
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 05f4ab4..e53f91d 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -71,7 +71,9 @@
obj-$(CONFIG_ARCH_MSM8X60) += peripheral-reset.o
obj-$(CONFIG_ARCH_MSM8960) += peripheral-reset-8960.o
endif
+obj-$(CONFIG_MSM_PIL_QDSP6V3) += pil-q6v3.o
obj-$(CONFIG_MSM_PIL_QDSP6V4) += pil-q6v4.o
+obj-$(CONFIG_MSM_PIL_RIVA) += pil-riva.o
obj-$(CONFIG_ARCH_QSD8X50) += sirc.o
obj-$(CONFIG_ARCH_FSM9XXX) += sirc-fsm9xxx.o
obj-$(CONFIG_MSM_FIQ_SUPPORT) += fiq_glue.o
@@ -173,9 +175,15 @@
obj-$(CONFIG_ARCH_MSM8X60) += devices-msm8x60.o clock-local.o clock-8x60.o acpuclock-8x60.o
obj-$(CONFIG_ARCH_MSM8X60) += clock-rpm.o
obj-$(CONFIG_ARCH_MSM8X60) += saw-regulator.o
-obj-$(CONFIG_ARCH_MSM8X60) += rpm-regulator.o rpm-regulator-8660.o
obj-$(CONFIG_ARCH_MSM8X60) += footswitch-8x60.o
+ifdef CONFIG_MSM_RPM_REGULATOR
+obj-y += rpm-regulator.o
+obj-$(CONFIG_ARCH_MSM8X60) += rpm-regulator-8660.o
+obj-$(CONFIG_ARCH_MSM8960) += rpm-regulator-8960.o
+obj-$(CONFIG_ARCH_MSM9615) += rpm-regulator-9615.o
+endif
+
ifdef CONFIG_MSM_SUBSYSTEM_RESTART
obj-y += subsystem_notif.o
obj-y += subsystem_restart.o
@@ -214,10 +222,10 @@
obj-$(CONFIG_MACH_MSM7X27_SURF) += board-msm7x27.o devices-msm7x27.o
obj-$(CONFIG_MACH_MSM7X27_FFA) += board-msm7x27.o devices-msm7x27.o
obj-$(CONFIG_ARCH_MSM7X27A) += clock-pcom-lookup.o devices-msm7x27a.o
-obj-$(CONFIG_MACH_MSM7X27A_RUMI3) += board-msm7x27a.o
-obj-$(CONFIG_MACH_MSM7X27A_SURF) += board-msm7x27a.o
-obj-$(CONFIG_MACH_MSM7X27A_FFA) += board-msm7x27a.o
-obj-$(CONFIG_MACH_MSM7627A_QRD1) += board-qrd7627a.o
+obj-$(CONFIG_MACH_MSM7X27A_RUMI3) += board-msm7x27a.o board-msm7627a-storage.o
+obj-$(CONFIG_MACH_MSM7X27A_SURF) += board-msm7x27a.o board-msm7627a-storage.o
+obj-$(CONFIG_MACH_MSM7X27A_FFA) += board-msm7x27a.o board-msm7627a-storage.o
+obj-$(CONFIG_MACH_MSM7627A_QRD1) += board-qrd7627a.o board-msm7627a-storage.o
obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o memory_topology.o
obj-$(CONFIG_ARCH_MSM7X30) += clock-local.o clock-7x30.o acpuclock-7x30.o
obj-$(CONFIG_MACH_MSM7X25_SURF) += board-msm7x27.o devices-msm7x25.o
@@ -226,12 +234,12 @@
obj-$(CONFIG_ARCH_MSM8960) += footswitch-8x60.o
obj-$(CONFIG_ARCH_MSM8960) += acpuclock-8960.o
obj-$(CONFIG_ARCH_MSM8960) += memory_topology.o
-obj-$(CONFIG_ARCH_MSM8960) += saw-regulator.o rpm-regulator.o rpm-regulator-8960.o
+obj-$(CONFIG_ARCH_MSM8960) += saw-regulator.o
obj-$(CONFIG_ARCH_MSM8960) += devices-8960.o
obj-$(CONFIG_ARCH_APQ8064) += devices-8960.o devices-8064.o
board-8960-all-objs += board-8960.o board-8960-camera.o board-8960-display.o board-8960-pmic.o board-8960-storage.o board-8960-gpiomux.o
board-8930-all-objs += board-8930.o board-8930-camera.o board-8930-display.o board-8930-pmic.o board-8930-storage.o board-8930-gpiomux.o
-board-8064-all-objs += board-8064.o board-8064-storage.o
+board-8064-all-objs += board-8064.o board-8064-pmic.o board-8064-storage.o board-8064-gpiomux.o
obj-$(CONFIG_MACH_MSM8960_SIM) += board-8960-all.o board-8960-regulator.o
obj-$(CONFIG_MACH_MSM8960_RUMI3) += board-8960-all.o board-8960-regulator.o
obj-$(CONFIG_MACH_MSM8960_CDP) += board-8960-all.o board-8960-regulator.o
@@ -243,9 +251,8 @@
obj-$(CONFIG_ARCH_MSM8960) += bms-batterydata.o
obj-$(CONFIG_MACH_APQ8064_SIM) += board-8064-all.o board-8064-regulator.o
obj-$(CONFIG_MACH_APQ8064_RUMI3) += board-8064-all.o board-8064-regulator.o
-obj-$(CONFIG_ARCH_MSM9615) += board-9615.o devices-9615.o board-9615-regulator.o
+obj-$(CONFIG_ARCH_MSM9615) += board-9615.o devices-9615.o board-9615-regulator.o board-9615-gpiomux.o
obj-$(CONFIG_ARCH_MSM9615) += clock-local.o clock-9615.o acpuclock-9615.o clock-rpm.o
-obj-$(CONFIG_ARCH_MSM9615) += rpm-regulator.o rpm-regulator-9615.o
obj-$(CONFIG_ARCH_MSMCOPPER) += board-copper.o board-dt.o
obj-$(CONFIG_MACH_SAPPHIRE) += board-sapphire.o board-sapphire-gpio.o
diff --git a/arch/arm/mach-msm/acpuclock-7201.c b/arch/arm/mach-msm/acpuclock-7201.c
index 0338d53..9fbe364 100644
--- a/arch/arm/mach-msm/acpuclock-7201.c
+++ b/arch/arm/mach-msm/acpuclock-7201.c
@@ -33,7 +33,6 @@
#include <asm/mach-types.h>
#include <mach/socinfo.h>
-#include "proc_comm.h"
#include "smd_private.h"
#include "acpuclock.h"
@@ -76,7 +75,6 @@
struct clkctl_acpu_speed *current_speed;
struct mutex lock;
uint32_t max_speed_delta_khz;
- unsigned long max_axi_khz;
struct clk *ebi1_clk;
};
@@ -127,62 +125,6 @@
* largest frequency jump that's less than max_speed_delta_khz on each PLL.
*/
-/* 7x01/7x25 normal with GSM capable modem */
-static struct clkctl_acpu_speed pll0_245_pll1_768_pll2_1056_pll4_0[] = {
- { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 30720 },
- { 1, 122880, ACPU_PLL_0, 4, 1, 61440, 1, 3, 61440 },
- { 0, 128000, ACPU_PLL_1, 1, 5, 64000, 1, 3, 61440 },
- { 0, 176000, ACPU_PLL_2, 2, 5, 88000, 1, 3, 61440 },
- { 1, 245760, ACPU_PLL_0, 4, 0, 81920, 2, 4, 61440 },
- { 1, 256000, ACPU_PLL_1, 1, 2, 128000, 1, 5, 128000 },
- { 0, 352000, ACPU_PLL_2, 2, 2, 88000, 3, 5, 128000 },
- { 1, 384000, ACPU_PLL_1, 1, 1, 128000, 2, 6, 128000 },
- { 1, 528000, ACPU_PLL_2, 2, 1, 132000, 3, 7, 128000 },
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
-};
-
-/* 7x01/7x25 normal with CDMA-only modem */
-static struct clkctl_acpu_speed pll0_196_pll1_768_pll2_1056_pll4_0[] = {
- { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 24576 },
- { 1, 98304, ACPU_PLL_0, 4, 1, 49152, 1, 3, 24576 },
- { 0, 128000, ACPU_PLL_1, 1, 5, 64000, 1, 3, 24576 },
- { 0, 176000, ACPU_PLL_2, 2, 5, 88000, 1, 3, 24576 },
- { 1, 196608, ACPU_PLL_0, 4, 0, 65536, 2, 4, 24576 },
- { 1, 256000, ACPU_PLL_1, 1, 2, 128000, 1, 5, 128000 },
- { 0, 352000, ACPU_PLL_2, 2, 2, 88000, 3, 5, 128000 },
- { 1, 384000, ACPU_PLL_1, 1, 1, 128000, 2, 6, 128000 },
- { 1, 528000, ACPU_PLL_2, 2, 1, 132000, 3, 7, 128000 },
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
-};
-
-/* 7x01/7x25 turbo with GSM capable modem */
-static struct clkctl_acpu_speed pll0_245_pll1_960_pll2_1056_pll4_0[] = {
- { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 30720 },
- { 0, 120000, ACPU_PLL_1, 1, 7, 60000, 1, 3, 61440 },
- { 1, 122880, ACPU_PLL_0, 4, 1, 61440, 1, 3, 61440 },
- { 0, 176000, ACPU_PLL_2, 2, 5, 88000, 1, 3, 61440 },
- { 1, 245760, ACPU_PLL_0, 4, 0, 81920, 2, 4, 61440 },
- { 1, 320000, ACPU_PLL_1, 1, 2, 107000, 2, 5, 120000 },
- { 0, 352000, ACPU_PLL_2, 2, 2, 88000, 3, 5, 120000 },
- { 1, 480000, ACPU_PLL_1, 1, 1, 120000, 3, 6, 120000 },
- { 1, 528000, ACPU_PLL_2, 2, 1, 132000, 3, 7, 122880 },
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
-};
-
-/* 7x01/7x25 turbo with CDMA-only modem */
-static struct clkctl_acpu_speed pll0_196_pll1_960_pll2_1056_pll4_0[] = {
- { 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 24576 },
- { 1, 98304, ACPU_PLL_0, 4, 1, 49152, 1, 3, 24576 },
- { 0, 120000, ACPU_PLL_1, 1, 7, 60000, 1, 3, 24576 },
- { 0, 176000, ACPU_PLL_2, 2, 5, 88000, 1, 3, 24576 },
- { 1, 196608, ACPU_PLL_0, 4, 0, 65536, 2, 4, 24576 },
- { 1, 320000, ACPU_PLL_1, 1, 2, 107000, 2, 5, 120000 },
- { 0, 352000, ACPU_PLL_2, 2, 2, 88000, 3, 5, 120000 },
- { 1, 480000, ACPU_PLL_1, 1, 1, 120000, 3, 6, 120000 },
- { 1, 528000, ACPU_PLL_2, 2, 1, 132000, 3, 7, 120000 },
- { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
-};
-
/* 7x27 normal with GSM capable modem */
static struct clkctl_acpu_speed pll0_245_pll1_960_pll2_1200_pll4_0[] = {
{ 0, 19200, ACPU_PLL_TCXO, 0, 0, 19200, 0, 0, 30720 },
@@ -190,10 +132,10 @@
{ 1, 122880, ACPU_PLL_0, 4, 1, 61440, 1, 3, 61440 },
{ 0, 200000, ACPU_PLL_2, 2, 5, 66667, 2, 4, 61440 },
{ 1, 245760, ACPU_PLL_0, 4, 0, 122880, 1, 4, 61440 },
- { 1, 320000, ACPU_PLL_1, 1, 2, 160000, 1, 5, 122880 },
- { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 122880 },
- { 1, 480000, ACPU_PLL_1, 1, 1, 160000, 2, 6, 122880 },
- { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 122880 },
+ { 1, 320000, ACPU_PLL_1, 1, 2, 160000, 1, 5, 160000 },
+ { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 160000 },
+ { 1, 480000, ACPU_PLL_1, 1, 1, 160000, 2, 6, 160000 },
+ { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 200000 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
};
@@ -204,10 +146,10 @@
{ 0, 120000, ACPU_PLL_1, 1, 7, 60000, 1, 3, 49152 },
{ 1, 196608, ACPU_PLL_0, 4, 0, 65536, 2, 4, 98304 },
{ 0, 200000, ACPU_PLL_2, 2, 5, 66667, 2, 4, 98304 },
- { 1, 320000, ACPU_PLL_1, 1, 2, 160000, 1, 5, 120000 },
- { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 120000 },
- { 1, 480000, ACPU_PLL_1, 1, 1, 160000, 2, 6, 120000 },
- { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 120000 },
+ { 1, 320000, ACPU_PLL_1, 1, 2, 160000, 1, 5, 160000 },
+ { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 160000 },
+ { 1, 480000, ACPU_PLL_1, 1, 1, 160000, 2, 6, 160000 },
+ { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 200000 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
};
@@ -218,10 +160,10 @@
{ 1, 122880, ACPU_PLL_1, 1, 1, 61440, 1, 3, 61440 },
{ 0, 200000, ACPU_PLL_2, 2, 5, 66667, 2, 4, 61440 },
{ 1, 245760, ACPU_PLL_1, 1, 0, 122880, 1, 4, 61440 },
- { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 122880 },
- { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 122880 },
- { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 122880 },
- { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 122880 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 160000 },
+ { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 160000 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 160000 },
+ { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 200000 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
};
@@ -232,10 +174,10 @@
{ 0, 120000, ACPU_PLL_0, 4, 7, 60000, 1, 3, 49152 },
{ 1, 196608, ACPU_PLL_1, 1, 0, 65536, 2, 4, 98304 },
{ 0, 200000, ACPU_PLL_2, 2, 5, 66667, 2, 4, 98304 },
- { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 120000 },
- { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 120000 },
- { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 120000 },
- { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 120000 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 160000 },
+ { 0, 400000, ACPU_PLL_2, 2, 2, 133333, 2, 5, 160000 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 160000 },
+ { 1, 600000, ACPU_PLL_2, 2, 1, 200000, 2, 7, 200000 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
};
@@ -246,10 +188,10 @@
{ 1, 122880, ACPU_PLL_1, 1, 1, 61440, 1, 3, 61440 },
{ 0, 200000, ACPU_PLL_2, 2, 3, 66667, 2, 4, 61440 },
{ 1, 245760, ACPU_PLL_1, 1, 0, 122880, 1, 4, 61440 },
- { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 122880 },
- { 0, 400000, ACPU_PLL_2, 2, 1, 133333, 2, 5, 122880 },
- { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 122880 },
- { 1, 800000, ACPU_PLL_2, 2, 0, 200000, 3, 7, 122880 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 160000 },
+ { 0, 400000, ACPU_PLL_2, 2, 1, 133333, 2, 5, 160000 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 160000 },
+ { 1, 800000, ACPU_PLL_2, 2, 0, 200000, 3, 7, 200000 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
};
@@ -260,10 +202,10 @@
{ 0, 120000, ACPU_PLL_0, 4, 7, 60000, 1, 3, 49152 },
{ 1, 196608, ACPU_PLL_1, 1, 0, 65536, 2, 4, 98304 },
{ 0, 200000, ACPU_PLL_2, 2, 3, 66667, 2, 4, 98304 },
- { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 120000 },
- { 0, 400000, ACPU_PLL_2, 2, 1, 133333, 2, 5, 120000 },
- { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 120000 },
- { 1, 800000, ACPU_PLL_2, 2, 0, 200000, 3, 7, 120000 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 160000, 1, 5, 160000 },
+ { 0, 400000, ACPU_PLL_2, 2, 1, 133333, 2, 5, 160000 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 160000, 2, 6, 160000 },
+ { 1, 800000, ACPU_PLL_2, 2, 0, 200000, 3, 7, 200000 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
};
@@ -418,14 +360,11 @@
#define PLL_0_MHZ 0
#define PLL_196_MHZ 10
#define PLL_245_MHZ 12
-#define PLL_491_MHZ 25
#define PLL_589_MHZ 30
#define PLL_737_MHZ 38
-#define PLL_768_MHZ 40
#define PLL_800_MHZ 41
#define PLL_960_MHZ 50
#define PLL_1008_MHZ 52
-#define PLL_1056_MHZ 55
#define PLL_1200_MHZ 62
#define PLL_CONFIG(m0, m1, m2, m4) { \
@@ -442,10 +381,6 @@
};
static struct pll_freq_tbl_map acpu_freq_tbl_list[] = {
- PLL_CONFIG(196, 768, 1056, 0),
- PLL_CONFIG(245, 768, 1056, 0),
- PLL_CONFIG(196, 960, 1056, 0),
- PLL_CONFIG(245, 960, 1056, 0),
PLL_CONFIG(196, 960, 1200, 0),
PLL_CONFIG(245, 960, 1200, 0),
PLL_CONFIG(960, 196, 1200, 0),
@@ -523,30 +458,22 @@
if (id >= ACPU_PLL_END)
return -EINVAL;
- if (pll_control) {
- remote_spin_lock(&pll_lock);
- if (on) {
- pll_control->pll[PLL_BASE + id].votes |= 2;
- if (!pll_control->pll[PLL_BASE + id].on) {
- pll_enable(soc_pll[id].mod_reg, 1);
- pll_control->pll[PLL_BASE + id].on = 1;
- }
- } else {
- pll_control->pll[PLL_BASE + id].votes &= ~2;
- if (pll_control->pll[PLL_BASE + id].on
- && !pll_control->pll[PLL_BASE + id].votes) {
- pll_enable(soc_pll[id].mod_reg, 0);
- pll_control->pll[PLL_BASE + id].on = 0;
- }
+ remote_spin_lock(&pll_lock);
+ if (on) {
+ pll_control->pll[PLL_BASE + id].votes |= 2;
+ if (!pll_control->pll[PLL_BASE + id].on) {
+ pll_enable(soc_pll[id].mod_reg, 1);
+ pll_control->pll[PLL_BASE + id].on = 1;
}
- remote_spin_unlock(&pll_lock);
} else {
- res = msm_proc_comm(PCOM_CLKCTL_RPC_PLL_REQUEST, &id, &on);
- if (res < 0)
- return res;
- else if ((int) id < 0)
- return -EINVAL;
+ pll_control->pll[PLL_BASE + id].votes &= ~2;
+ if (pll_control->pll[PLL_BASE + id].on
+ && !pll_control->pll[PLL_BASE + id].votes) {
+ pll_enable(soc_pll[id].mod_reg, 0);
+ pll_control->pll[PLL_BASE + id].on = 0;
+ }
}
+ remote_spin_unlock(&pll_lock);
if (on)
pr_debug("PLL enabled\n");
@@ -556,23 +483,10 @@
return res;
}
-
-/*----------------------------------------------------------------------------
- * ARM11 'owned' clock control
- *---------------------------------------------------------------------------*/
-
static int acpuclk_set_vdd_level(int vdd)
{
uint32_t current_vdd;
- /*
- * NOTE: v1.0 of 7x27a/7x25a chip doesn't have working
- * VDD switching support.
- */
- if ((cpu_is_msm7x27a() || cpu_is_msm7x25a()) &&
- (SOCINFO_VERSION_MINOR(socinfo_get_version()) < 1))
- return 0;
-
current_vdd = readl_relaxed(A11S_VDD_SVS_PLEVEL_ADDR) & 0x07;
pr_debug("Switching VDD from %u mV -> %d mV\n",
@@ -635,7 +549,7 @@
}
}
-static int acpuclk_7201_set_rate(int cpu, unsigned long rate,
+static int acpuclk_7627_set_rate(int cpu, unsigned long rate,
enum setrate_reason reason)
{
uint32_t reg_clkctl;
@@ -712,9 +626,9 @@
while (cur_s != tgt_s) {
/*
- * Always jump to target freq if within 256mhz, regulardless of
- * PLL. If differnece is greater, use the predefinied
- * steppings in the table.
+ * Always jump to target freq if within max_speed_delta_khz,
+ * regardless of PLL. If differnece is greater, use the
+ * predefined steppings in the table.
*/
int d = abs((int)(cur_s->a11clk_khz - tgt_s->a11clk_khz));
if (d > drv_state.max_speed_delta_khz) {
@@ -834,11 +748,6 @@
div = readl_relaxed(A11S_CLK_CNTL_ADDR) & 0x0f;
}
- /* Accomodate bootloaders that might not be implementing the
- * workaround for the h/w bug in 7x25. */
- if (cpu_is_msm7x25() && sel == 2)
- sel = 3;
-
for (speed = acpu_freq_tbl; speed->a11clk_khz != 0; speed++) {
if (speed->a11clk_src_sel == sel
&& (speed->a11clk_src_div == div))
@@ -872,7 +781,7 @@
pr_info("ACPU running at %d KHz\n", speed->a11clk_khz);
}
-static unsigned long acpuclk_7201_get_rate(int cpu)
+static unsigned long acpuclk_7627_get_rate(int cpu)
{
WARN_ONCE(drv_state.current_speed == NULL,
"%s: not initialized\n", __func__);
@@ -886,17 +795,10 @@
* Clock driver initialization
*---------------------------------------------------------------------------*/
-#define DIV2REG(n) ((n)-1)
-#define REG2DIV(n) ((n)+1)
-#define SLOWER_BY(div, factor) div = DIV2REG(REG2DIV(div) * factor)
-
static void __init acpu_freq_tbl_fixup(void)
{
unsigned long pll0_l, pll1_l, pll2_l, pll4_l;
- int axi_160mhz = 0, axi_200mhz = 0;
struct pll_freq_tbl_map *lst;
- struct clkctl_acpu_speed *t;
- unsigned int pll0_needs_fixup = 0;
/* Wait for the PLLs to be initialized and then read their frequency.
*/
@@ -934,15 +836,6 @@
pll4_l = 0;
}
- /* Some configurations run PLL0 twice as fast. Instead of having
- * separate tables for this case, we simply fix up the ACPU clock
- * source divider since it's a simple fix up.
- */
- if (pll0_l == PLL_491_MHZ) {
- pll0_l = PLL_245_MHZ;
- pll0_needs_fixup = 1;
- }
-
/* Fix the tables for 7x25a variant to not conflict with 7x27 ones */
if (cpu_is_msm7x25a()) {
if (pll1_l == PLL_245_MHZ) {
@@ -969,38 +862,11 @@
BUG();
}
- /* Fix up PLL0 source divider if necessary. Also, fix up the AXI to
- * the max that's supported by the board (RAM used in board).
- */
- axi_160mhz = (pll0_l == PLL_960_MHZ || pll1_l == PLL_960_MHZ);
- axi_200mhz = (pll2_l == PLL_1200_MHZ || pll2_l == PLL_800_MHZ);
- for (t = &acpu_freq_tbl[0]; t->a11clk_khz != 0; t++) {
-
- if (pll0_needs_fixup && t->pll == ACPU_PLL_0)
- SLOWER_BY(t->a11clk_src_div, 2);
- if (axi_160mhz && drv_state.max_axi_khz >= 160000
- && t->ahbclk_khz > 128000)
- t->axiclk_khz = 160000;
- if (axi_200mhz && drv_state.max_axi_khz >= 200000
- && t->ahbclk_khz > 160000)
- t->axiclk_khz = 200000;
- }
-
- t--;
- drv_state.max_axi_khz = t->axiclk_khz;
-
/* The default 7x27 ACPU clock plan supports running the AXI bus at
* 200 MHz. So we don't classify it as Turbo mode.
*/
if (cpu_is_msm7x27())
return;
-
- if (!axi_160mhz)
- pr_info("Turbo mode not supported.\n");
- else if (t->axiclk_khz == 160000)
- pr_info("Turbo mode supported and enabled.\n");
- else
- pr_info("Turbo mode supported but not enabled.\n");
}
/*
@@ -1102,51 +968,42 @@
}
}
-static void msm7x25_acpu_pll_hw_bug_fix(void)
-{
- unsigned int n;
-
- /* The 7625 has a hardware bug and in order to select PLL2 we
- * must program PLL3. Use the same table, and just fix up the
- * numbers on this target. */
- for (n = 0; acpu_freq_tbl[n].a11clk_khz != 0; n++)
- if (acpu_freq_tbl[n].pll == ACPU_PLL_2)
- acpu_freq_tbl[n].a11clk_src_sel = 3;
-}
-
static void shared_pll_control_init(void)
{
#define PLL_REMOTE_SPINLOCK_ID "S:7"
unsigned smem_size;
+
remote_spin_lock_init(&pll_lock, PLL_REMOTE_SPINLOCK_ID);
pll_control = smem_get_entry(SMEM_CLKREGIM_SOURCES, &smem_size);
- if (!pll_control)
- pr_warning("Can't find shared PLL control data structure!\n");
+ if (!pll_control) {
+ pr_err("Can't find shared PLL control data structure!\n");
+ BUG();
/* There might be more PLLs than what the application processor knows
* about. But the index used for each PLL is guaranteed to remain the
* same. */
- else if (smem_size < sizeof(struct shared_pll_control))
- pr_warning("Shared PLL control data structure too small!\n");
- else if (pll_control->version != 0xCCEE0001)
- pr_warning("Shared PLL control version mismatch!\n");
- else {
+ } else if (smem_size < sizeof(struct shared_pll_control)) {
+ pr_err("Shared PLL control data"
+ "structure too small!\n");
+ BUG();
+ } else if (pll_control->version != 0xCCEE0001) {
+ pr_err("Shared PLL control version mismatch!\n");
+ BUG();
+ } else {
pr_info("Shared PLL control available.\n");
return;
}
- pll_control = NULL;
- pr_warning("Falling back to proc_comm PLL control.\n");
}
-static struct acpuclk_data acpuclk_7201_data = {
- .set_rate = acpuclk_7201_set_rate,
- .get_rate = acpuclk_7201_get_rate,
+static struct acpuclk_data acpuclk_7627_data = {
+ .set_rate = acpuclk_7627_set_rate,
+ .get_rate = acpuclk_7627_get_rate,
.power_collapse_khz = POWER_COLLAPSE_KHZ,
.switch_time_us = 50,
};
-static int __init acpuclk_7201_init(struct acpuclk_soc_data *soc_data)
+static int __init acpuclk_7627_init(struct acpuclk_soc_data *soc_data)
{
pr_info("%s()\n", __func__);
@@ -1156,16 +1013,13 @@
mutex_init(&drv_state.lock);
shared_pll_control_init();
drv_state.max_speed_delta_khz = soc_data->max_speed_delta_khz;
- drv_state.max_axi_khz = soc_data->max_axi_khz;
acpu_freq_tbl_fixup();
- acpuclk_7201_data.wait_for_irq_khz = find_wait_for_irq_khz();
+ acpuclk_7627_data.wait_for_irq_khz = find_wait_for_irq_khz();
precompute_stepping();
- if (cpu_is_msm7x25())
- msm7x25_acpu_pll_hw_bug_fix();
acpuclk_hw_init();
lpj_init();
print_acpu_freq_tbl();
- acpuclk_register(&acpuclk_7201_data);
+ acpuclk_register(&acpuclk_7627_data);
#ifdef CONFIG_CPU_FREQ_MSM
cpufreq_table_init();
@@ -1174,26 +1028,17 @@
return 0;
}
-struct acpuclk_soc_data acpuclk_7201_soc_data __initdata = {
- .max_speed_delta_khz = 400000,
- .max_axi_khz = 160000,
- .init = acpuclk_7201_init,
-};
-
struct acpuclk_soc_data acpuclk_7x27_soc_data __initdata = {
.max_speed_delta_khz = 400000,
- .max_axi_khz = 200000,
- .init = acpuclk_7201_init,
+ .init = acpuclk_7627_init,
};
struct acpuclk_soc_data acpuclk_7x27a_soc_data __initdata = {
.max_speed_delta_khz = 400000,
- .max_axi_khz = 200000,
- .init = acpuclk_7201_init,
+ .init = acpuclk_7627_init,
};
struct acpuclk_soc_data acpuclk_7x27aa_soc_data __initdata = {
.max_speed_delta_khz = 504000,
- .max_axi_khz = 200000,
- .init = acpuclk_7201_init,
+ .init = acpuclk_7627_init,
};
diff --git a/arch/arm/mach-msm/acpuclock-8960.c b/arch/arm/mach-msm/acpuclock-8960.c
index be8c31c..2792e2a 100644
--- a/arch/arm/mach-msm/acpuclock-8960.c
+++ b/arch/arm/mach-msm/acpuclock-8960.c
@@ -68,7 +68,7 @@
#define STBY_KHZ 1
#define HFPLL_NOMINAL_VDD 1050000
-#define HFPLL_LOW_VDD 945000
+#define HFPLL_LOW_VDD 850000
#define HFPLL_LOW_VDD_PLL_L_MAX 0x28
#define SECCLKAGD BIT(4)
@@ -147,7 +147,7 @@
.vreg[VREG_DIG] = { "krait0_dig", 1150000,
RPM_VREG_VOTER1,
RPM_VREG_ID_PM8921_S3 },
- .vreg[VREG_HFPLL_A] = { "hfpll", 2200000,
+ .vreg[VREG_HFPLL_A] = { "hfpll", 2100000,
RPM_VREG_VOTER1,
RPM_VREG_ID_PM8921_S8 },
.vreg[VREG_HFPLL_B] = { "hfpll", 1800000,
@@ -165,7 +165,7 @@
.vreg[VREG_DIG] = { "krait0_dig", 1150000,
RPM_VREG_VOTER2,
RPM_VREG_ID_PM8921_S3 },
- .vreg[VREG_HFPLL_A] = { "hfpll", 2200000,
+ .vreg[VREG_HFPLL_A] = { "hfpll", 2100000,
RPM_VREG_VOTER2,
RPM_VREG_ID_PM8921_S8 },
.vreg[VREG_HFPLL_B] = { "hfpll", 1800000,
@@ -176,7 +176,7 @@
.hfpll_base = MSM_HFPLL_BASE + 0x400,
.aux_clk_sel = MSM_APCS_GCC_BASE + 0x028,
.l2cpmr_iaddr = L2CPMR_IADDR,
- .vreg[VREG_HFPLL_A] = { "hfpll", 2200000,
+ .vreg[VREG_HFPLL_A] = { "hfpll", 2100000,
RPM_VREG_VOTER6,
RPM_VREG_ID_PM8921_S8 },
.vreg[VREG_HFPLL_B] = { "hfpll", 1800000,
@@ -244,6 +244,57 @@
},
};
+/*TODO: Update the rpm vreg id when the rpm driver is ready */
+static struct scalable scalable_8930[] = {
+ [CPU0] = {
+ .hfpll_base = MSM_HFPLL_BASE + 0x200,
+ .aux_clk_sel = MSM_ACC0_BASE + 0x014,
+ .l2cpmr_iaddr = L2CPUCPMR_IADDR,
+ .vreg[VREG_CORE] = { "krait0", 1300000 },
+ .vreg[VREG_MEM] = { "krait0_mem", 1150000,
+ RPM_VREG_VOTER1,
+ RPM_VREG_ID_PM8921_L24 },
+ .vreg[VREG_DIG] = { "krait0_dig", 1150000,
+ RPM_VREG_VOTER1,
+ RPM_VREG_ID_PM8921_S3 },
+ .vreg[VREG_HFPLL_A] = { "hfpll", 2100000,
+ RPM_VREG_VOTER1,
+ RPM_VREG_ID_PM8921_S8 },
+ .vreg[VREG_HFPLL_B] = { "hfpll", 1800000,
+ RPM_VREG_VOTER1,
+ RPM_VREG_ID_PM8921_L23 },
+ },
+ [CPU1] = {
+ .hfpll_base = MSM_HFPLL_BASE + 0x300,
+ .aux_clk_sel = MSM_ACC1_BASE + 0x014,
+ .l2cpmr_iaddr = L2CPUCPMR_IADDR,
+ .vreg[VREG_CORE] = { "krait1", 1300000 },
+ .vreg[VREG_MEM] = { "krait0_mem", 1150000,
+ RPM_VREG_VOTER2,
+ RPM_VREG_ID_PM8921_L24 },
+ .vreg[VREG_DIG] = { "krait0_dig", 1150000,
+ RPM_VREG_VOTER2,
+ RPM_VREG_ID_PM8921_S3 },
+ .vreg[VREG_HFPLL_A] = { "hfpll", 2100000,
+ RPM_VREG_VOTER2,
+ RPM_VREG_ID_PM8921_S8 },
+ .vreg[VREG_HFPLL_B] = { "hfpll", 1800000,
+ RPM_VREG_VOTER2,
+ RPM_VREG_ID_PM8921_L23 },
+ },
+ [L2] = {
+ .hfpll_base = MSM_HFPLL_BASE + 0x400,
+ .aux_clk_sel = MSM_APCS_GCC_BASE + 0x028,
+ .l2cpmr_iaddr = L2CPMR_IADDR,
+ .vreg[VREG_HFPLL_A] = { "hfpll", 2100000,
+ RPM_VREG_VOTER6,
+ RPM_VREG_ID_PM8921_S8 },
+ .vreg[VREG_HFPLL_B] = { "hfpll", 1800000,
+ RPM_VREG_VOTER6,
+ RPM_VREG_ID_PM8921_L23 },
+ },
+};
+
static struct scalable *scalable;
static struct l2_level *l2_freq_tbl;
static struct acpu_level *acpu_freq_tbl;
@@ -269,12 +320,13 @@
.num_paths = 2, \
}
static struct msm_bus_paths bw_level_tbl[] = {
- [0] = BW_MBPS(616), /* At least 77 MHz on bus. */
- [1] = BW_MBPS(1024), /* At least 128 MHz on bus. */
- [2] = BW_MBPS(1536), /* At least 192 MHz on bus. */
- [3] = BW_MBPS(2048), /* At least 256 MHz on bus. */
- [4] = BW_MBPS(3080), /* At least 385 MHz on bus. */
- [5] = BW_MBPS(3968), /* At least 496 MHz on bus. */
+ [0] = BW_MBPS(640), /* At least 80 MHz on bus. */
+ [1] = BW_MBPS(1064), /* At least 133 MHz on bus. */
+ [2] = BW_MBPS(1600), /* At least 200 MHz on bus. */
+ [3] = BW_MBPS(2128), /* At least 266 MHz on bus. */
+ [4] = BW_MBPS(3200), /* At least 400 MHz on bus. */
+ [5] = BW_MBPS(3600), /* At least 450 MHz on bus. */
+ [6] = BW_MBPS(3936), /* At least 492 MHz on bus. */
};
static struct msm_bus_scale_pdata bus_client_pdata = {
@@ -303,19 +355,35 @@
[11] = { { 918000, HFPLL, 1, 0, 0x22 }, 1150000, 1150000, 3 },
};
-static struct acpu_level acpu_freq_tbl_8960_kraitv1[] = {
- { 0, {STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 950000 },
- { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 950000 },
- { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 950000 },
- { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 962500 },
- { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 962500 },
- { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 987500 },
- { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 1000000 },
- { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 1025000 },
- { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 1050000 },
- { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1087500 },
- { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1125000 },
- { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1137500 },
+static struct acpu_level acpu_freq_tbl_8960_kraitv1_slow[] = {
+ { 0, {STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 900000 },
+ { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 925000 },
+ { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 925000 },
+ { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 937500 },
+ { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 962500 },
+ { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 987500 },
+ { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 1000000 },
+ { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 1025000 },
+ { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1062500 },
+ { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1062500 },
+ { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1087500 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level acpu_freq_tbl_8960_kraitv1_nom_fast[] = {
+ { 0, {STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 862500 },
+ { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 862500 },
+ { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 862500 },
+ { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 887500 },
+ { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 925000 },
+ { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 925000 },
+ { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 937500 },
+ { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 962500 },
+ { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1012500 },
+ { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1025000 },
+ { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1025000 },
{ 0, { 0 } }
};
@@ -324,47 +392,74 @@
static struct l2_level l2_freq_tbl_8960_kraitv2[] = {
[0] = { {STBY_KHZ, QSB, 0, 0, 0x00 }, 1050000, 1050000, 0 },
[1] = { { 384000, PLL_8, 0, 2, 0x00 }, 1050000, 1050000, 1 },
- [2] = { { 432000, HFPLL, 2, 0, 0x20 }, 1050000, 1050000, 1 },
- [3] = { { 486000, HFPLL, 2, 0, 0x24 }, 1050000, 1050000, 1 },
- [4] = { { 540000, HFPLL, 2, 0, 0x28 }, 1050000, 1050000, 1 },
+ [2] = { { 432000, HFPLL, 2, 0, 0x20 }, 1050000, 1050000, 2 },
+ [3] = { { 486000, HFPLL, 2, 0, 0x24 }, 1050000, 1050000, 2 },
+ [4] = { { 540000, HFPLL, 2, 0, 0x28 }, 1050000, 1050000, 2 },
[5] = { { 594000, HFPLL, 1, 0, 0x16 }, 1050000, 1050000, 2 },
- [6] = { { 648000, HFPLL, 1, 0, 0x18 }, 1050000, 1050000, 2 },
- [7] = { { 702000, HFPLL, 1, 0, 0x1A }, 1050000, 1050000, 2 },
- [8] = { { 756000, HFPLL, 1, 0, 0x1C }, 1150000, 1150000, 2 },
- [9] = { { 810000, HFPLL, 1, 0, 0x1E }, 1150000, 1150000, 3 },
- [10] = { { 864000, HFPLL, 1, 0, 0x20 }, 1150000, 1150000, 3 },
- [11] = { { 918000, HFPLL, 1, 0, 0x22 }, 1150000, 1150000, 3 },
- [12] = { { 972000, HFPLL, 1, 0, 0x24 }, 1150000, 1150000, 4 },
- [13] = { { 1026000, HFPLL, 1, 0, 0x26 }, 1150000, 1150000, 4 },
- [14] = { { 1080000, HFPLL, 1, 0, 0x28 }, 1150000, 1150000, 4 },
- [15] = { { 1134000, HFPLL, 1, 0, 0x2A }, 1150000, 1150000, 4 },
- [16] = { { 1188000, HFPLL, 1, 0, 0x2C }, 1150000, 1150000, 4 },
+ [6] = { { 648000, HFPLL, 1, 0, 0x18 }, 1050000, 1050000, 4 },
+ [7] = { { 702000, HFPLL, 1, 0, 0x1A }, 1050000, 1050000, 4 },
+ [8] = { { 756000, HFPLL, 1, 0, 0x1C }, 1150000, 1150000, 4 },
+ [9] = { { 810000, HFPLL, 1, 0, 0x1E }, 1150000, 1150000, 4 },
+ [10] = { { 864000, HFPLL, 1, 0, 0x20 }, 1150000, 1150000, 4 },
+ [11] = { { 918000, HFPLL, 1, 0, 0x22 }, 1150000, 1150000, 6 },
+ [12] = { { 972000, HFPLL, 1, 0, 0x24 }, 1150000, 1150000, 6 },
+ [13] = { { 1026000, HFPLL, 1, 0, 0x26 }, 1150000, 1150000, 6 },
+ [14] = { { 1080000, HFPLL, 1, 0, 0x28 }, 1150000, 1150000, 6 },
+ [15] = { { 1134000, HFPLL, 1, 0, 0x2A }, 1150000, 1150000, 6 },
+ [16] = { { 1188000, HFPLL, 1, 0, 0x2C }, 1150000, 1150000, 6 },
};
-static struct acpu_level acpu_freq_tbl_8960_kraitv2[] = {
- { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 950000 },
- { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 950000 },
- { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 950000 },
- { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 962500 },
- { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 962500 },
- { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 987500 },
- { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 1000000 },
- { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 1025000 },
- { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 1050000 },
- { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1087500 },
- { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1125000 },
- { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1137500 },
- { 1, { 972000, HFPLL, 1, 0, 0x24 }, L2(16), 1300000 },
- { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(16), 1300000 },
- { 1, { 1080000, HFPLL, 1, 0, 0x28 }, L2(16), 1300000 },
- { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(16), 1300000 },
- { 1, { 1188000, HFPLL, 1, 0, 0x2C }, L2(16), 1300000 },
- { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(16), 1300000 },
- { 1, { 1296000, HFPLL, 1, 0, 0x30 }, L2(16), 1300000 },
- { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(16), 1300000 },
- { 1, { 1404000, HFPLL, 1, 0, 0x34 }, L2(16), 1300000 },
- { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(16), 1300000 },
- { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(16), 1300000 },
+static struct acpu_level acpu_freq_tbl_8960_kraitv2_slow[] = {
+ { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 900000 },
+ { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 925000 },
+ { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 925000 },
+ { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 937500 },
+ { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 962500 },
+ { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 987500 },
+ { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 1000000 },
+ { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 1025000 },
+ { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1062500 },
+ { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1062500 },
+ { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1087500 },
+ { 1, { 972000, HFPLL, 1, 0, 0x24 }, L2(16), 1100000 },
+ { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(16), 1100000 },
+ { 1, { 1080000, HFPLL, 1, 0, 0x28 }, L2(16), 1100000 },
+ { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(16), 1100000 },
+ { 1, { 1188000, HFPLL, 1, 0, 0x2C }, L2(16), 1125000 },
+ { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(16), 1137500 },
+ { 1, { 1296000, HFPLL, 1, 0, 0x30 }, L2(16), 1150000 },
+ { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(16), 1175000 },
+ { 1, { 1404000, HFPLL, 1, 0, 0x34 }, L2(16), 1187500 },
+ { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(16), 1225000 },
+ { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(16), 1250000 },
+ { 0, { 0 } }
+};
+
+static struct acpu_level acpu_freq_tbl_8960_kraitv2_nom_fast[] = {
+ { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 862500 },
+ { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 862500 },
+ { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 862500 },
+ { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 887500 },
+ { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 900000 },
+ { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 925000 },
+ { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 925000 },
+ { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 937500 },
+ { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 962500 },
+ { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1012500 },
+ { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1025000 },
+ { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1025000 },
+ { 1, { 972000, HFPLL, 1, 0, 0x24 }, L2(16), 1100000 },
+ { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(16), 1100000 },
+ { 1, { 1080000, HFPLL, 1, 0, 0x28 }, L2(16), 1100000 },
+ { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(16), 1100000 },
+ { 1, { 1188000, HFPLL, 1, 0, 0x2C }, L2(16), 1125000 },
+ { 1, { 1242000, HFPLL, 1, 0, 0x2E }, L2(16), 1137500 },
+ { 1, { 1296000, HFPLL, 1, 0, 0x30 }, L2(16), 1150000 },
+ { 1, { 1350000, HFPLL, 1, 0, 0x32 }, L2(16), 1175000 },
+ { 1, { 1404000, HFPLL, 1, 0, 0x34 }, L2(16), 1187500 },
+ { 1, { 1458000, HFPLL, 1, 0, 0x36 }, L2(16), 1225000 },
+ { 1, { 1512000, HFPLL, 1, 0, 0x38 }, L2(16), 1250000 },
{ 0, { 0 } }
};
@@ -417,6 +512,51 @@
{ 0, { 0 } }
};
+/* TODO: Update vdd_dig, vdd_mem and bw when data is available. */
+#undef L2
+#define L2(x) (&l2_freq_tbl_8930[(x)])
+static struct l2_level l2_freq_tbl_8930[] = {
+ [0] = { {STBY_KHZ, QSB, 0, 0, 0x00 }, 1050000, 1050000, 0 },
+ [1] = { { 384000, PLL_8, 0, 2, 0x00 }, 1050000, 1050000, 1 },
+ [2] = { { 432000, HFPLL, 2, 0, 0x20 }, 1050000, 1050000, 1 },
+ [3] = { { 486000, HFPLL, 2, 0, 0x24 }, 1050000, 1050000, 1 },
+ [4] = { { 540000, HFPLL, 2, 0, 0x28 }, 1050000, 1050000, 1 },
+ [5] = { { 594000, HFPLL, 1, 0, 0x16 }, 1050000, 1050000, 2 },
+ [6] = { { 648000, HFPLL, 1, 0, 0x18 }, 1050000, 1050000, 2 },
+ [7] = { { 702000, HFPLL, 1, 0, 0x1A }, 1050000, 1050000, 2 },
+ [8] = { { 756000, HFPLL, 1, 0, 0x1C }, 1150000, 1150000, 2 },
+ [9] = { { 810000, HFPLL, 1, 0, 0x1E }, 1150000, 1150000, 3 },
+ [10] = { { 864000, HFPLL, 1, 0, 0x20 }, 1150000, 1150000, 3 },
+ [11] = { { 918000, HFPLL, 1, 0, 0x22 }, 1150000, 1150000, 3 },
+ [12] = { { 972000, HFPLL, 1, 0, 0x24 }, 1150000, 1150000, 3 },
+ [13] = { { 1026000, HFPLL, 1, 0, 0x26 }, 1150000, 1150000, 4 },
+ [14] = { { 1080000, HFPLL, 1, 0, 0x28 }, 1150000, 1150000, 4 },
+ [15] = { { 1134000, HFPLL, 1, 0, 0x2A }, 1150000, 1150000, 4 },
+ [16] = { { 1188000, HFPLL, 1, 0, 0x2C }, 1150000, 1150000, 4 },
+};
+
+/* TODO: Update core voltages when data is available. */
+static struct acpu_level acpu_freq_tbl_8930[] = {
+ { 0, { STBY_KHZ, QSB, 0, 0, 0x00 }, L2(0), 900000 },
+ { 1, { 384000, PLL_8, 0, 2, 0x00 }, L2(1), 900000 },
+ { 1, { 432000, HFPLL, 2, 0, 0x20 }, L2(6), 925000 },
+ { 1, { 486000, HFPLL, 2, 0, 0x24 }, L2(6), 925000 },
+ { 1, { 540000, HFPLL, 2, 0, 0x28 }, L2(6), 937500 },
+ { 1, { 594000, HFPLL, 1, 0, 0x16 }, L2(6), 962500 },
+ { 1, { 648000, HFPLL, 1, 0, 0x18 }, L2(6), 987500 },
+ { 1, { 702000, HFPLL, 1, 0, 0x1A }, L2(6), 1000000 },
+ { 1, { 756000, HFPLL, 1, 0, 0x1C }, L2(11), 1025000 },
+ { 1, { 810000, HFPLL, 1, 0, 0x1E }, L2(11), 1062500 },
+ { 1, { 864000, HFPLL, 1, 0, 0x20 }, L2(11), 1062500 },
+ { 1, { 918000, HFPLL, 1, 0, 0x22 }, L2(11), 1087500 },
+ { 1, { 972000, HFPLL, 1, 0, 0x24 }, L2(16), 1100000 },
+ { 1, { 1026000, HFPLL, 1, 0, 0x26 }, L2(16), 1100000 },
+ { 1, { 1080000, HFPLL, 1, 0, 0x28 }, L2(16), 1100000 },
+ { 1, { 1134000, HFPLL, 1, 0, 0x2A }, L2(16), 1100000 },
+ { 1, { 1188000, HFPLL, 1, 0, 0x2C }, L2(16), 1125000 },
+ { 0, { 0 } }
+};
+
static unsigned long acpuclk_8960_get_rate(int cpu)
{
return scalable[cpu].current_speed->khz;
@@ -483,16 +623,16 @@
{
int rc;
- if (cpu_is_msm8960()) {
+ if (cpu_is_msm8960() || cpu_is_msm8930()) {
rc = rpm_vreg_set_voltage(sc->vreg[VREG_HFPLL_A].rpm_vreg_id,
- sc->vreg[VREG_HFPLL_A].rpm_vreg_voter, 2200000,
- 2200000, 0);
+ sc->vreg[VREG_HFPLL_A].rpm_vreg_voter, 2100000,
+ sc->vreg[VREG_HFPLL_A].max_vdd, 0);
if (rc)
pr_err("%s regulator enable failed (%d)\n",
sc->vreg[VREG_HFPLL_A].name, rc);
rc = rpm_vreg_set_voltage(sc->vreg[VREG_HFPLL_B].rpm_vreg_id,
sc->vreg[VREG_HFPLL_B].rpm_vreg_voter, 1800000,
- 1800000, 0);
+ sc->vreg[VREG_HFPLL_B].max_vdd, 0);
if (rc)
pr_err("%s regulator enable failed (%d)\n",
sc->vreg[VREG_HFPLL_B].name, rc);
@@ -530,7 +670,7 @@
*/
writel_relaxed(0, sc->hfpll_base + HFPLL_MODE);
- if (cpu_is_msm8960()) {
+ if (cpu_is_msm8960() || cpu_is_msm8930()) {
rc = rpm_vreg_set_voltage(sc->vreg[VREG_HFPLL_B].rpm_vreg_id,
sc->vreg[VREG_HFPLL_B].rpm_vreg_voter, 0,
0, 0);
@@ -1052,7 +1192,7 @@
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
if (WARN_ON(!prev_khz[cpu]))
- prev_khz[cpu] = acpu_freq_tbl->speed.khz;
+ return NOTIFY_BAD;
acpuclk_8960_set_rate(cpu, prev_khz[cpu], SETRATE_HOTPLUG);
break;
case CPU_STARTING:
@@ -1078,8 +1218,9 @@
struct acpu_level *l, *max_acpu_level = NULL;
/* Select frequency tables. */
- if (cpu_is_msm8960() || cpu_is_msm8930()) {
+ if (cpu_is_msm8960()) {
uint32_t pte_efuse, pvs;
+ struct acpu_level *v1, *v2;
pte_efuse = readl_relaxed(QFPROM_PTE_EFUSE_ADDR);
pvs = (pte_efuse >> 10) & 0x7;
@@ -1090,26 +1231,33 @@
case 0x0:
case 0x7:
pr_info("ACPU PVS: Slow\n");
+ v1 = acpu_freq_tbl_8960_kraitv1_slow;
+ v2 = acpu_freq_tbl_8960_kraitv2_slow;
break;
case 0x1:
pr_info("ACPU PVS: Nominal\n");
+ v1 = acpu_freq_tbl_8960_kraitv1_nom_fast;
+ v2 = acpu_freq_tbl_8960_kraitv2_nom_fast;
break;
case 0x3:
pr_info("ACPU PVS: Fast\n");
+ v1 = acpu_freq_tbl_8960_kraitv1_nom_fast;
+ v2 = acpu_freq_tbl_8960_kraitv2_nom_fast;
break;
default:
pr_warn("ACPU PVS: Unknown. Defaulting to slow.\n");
+ v1 = acpu_freq_tbl_8960_kraitv1_slow;
+ v2 = acpu_freq_tbl_8960_kraitv2_slow;
break;
}
- /* TODO: Select tables based on PVS data. */
scalable = scalable_8960;
if (cpu_is_krait_v1()) {
- acpu_freq_tbl = acpu_freq_tbl_8960_kraitv1;
+ acpu_freq_tbl = v1;
l2_freq_tbl = l2_freq_tbl_8960_kraitv1;
l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl_8960_kraitv1);
} else {
- acpu_freq_tbl = acpu_freq_tbl_8960_kraitv2;
+ acpu_freq_tbl = v2;
l2_freq_tbl = l2_freq_tbl_8960_kraitv2;
l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl_8960_kraitv2);
}
@@ -1118,6 +1266,11 @@
acpu_freq_tbl = acpu_freq_tbl_8064;
l2_freq_tbl = l2_freq_tbl_8064;
l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl_8064);
+ } else if (cpu_is_msm8930()) {
+ scalable = scalable_8930;
+ acpu_freq_tbl = acpu_freq_tbl_8930;
+ l2_freq_tbl = l2_freq_tbl_8930;
+ l2_freq_tbl_size = ARRAY_SIZE(l2_freq_tbl_8930);
} else {
BUG();
}
@@ -1158,3 +1311,7 @@
struct acpuclk_soc_data acpuclk_8960_soc_data __initdata = {
.init = acpuclk_8960_init,
};
+
+struct acpuclk_soc_data acpuclk_8930_soc_data __initdata = {
+ .init = acpuclk_8960_init,
+};
diff --git a/arch/arm/mach-msm/acpuclock-8x60.c b/arch/arm/mach-msm/acpuclock-8x60.c
index 6bc841b..7339a4c 100644
--- a/arch/arm/mach-msm/acpuclock-8x60.c
+++ b/arch/arm/mach-msm/acpuclock-8x60.c
@@ -845,7 +845,7 @@
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
if (WARN_ON(!prev_khz[cpu]))
- prev_khz[cpu] = acpu_freq_tbl->acpuclk_khz;
+ return NOTIFY_BAD;
acpuclk_8x60_set_rate(cpu, prev_khz[cpu], SETRATE_HOTPLUG);
break;
default:
diff --git a/arch/arm/mach-msm/acpuclock.h b/arch/arm/mach-msm/acpuclock.h
index 2b188d8..6ea691b 100644
--- a/arch/arm/mach-msm/acpuclock.h
+++ b/arch/arm/mach-msm/acpuclock.h
@@ -102,12 +102,12 @@
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_7201_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;
#endif
diff --git a/arch/arm/mach-msm/board-8064-gpiomux.c b/arch/arm/mach-msm/board-8064-gpiomux.c
new file mode 100644
index 0000000..50673b4
--- /dev/null
+++ b/arch/arm/mach-msm/board-8064-gpiomux.c
@@ -0,0 +1,154 @@
+/* Copyright (c) 2011, 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/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/bootmem.h>
+#include <asm/mach-types.h>
+#include <asm/mach/mmc.h>
+#include <mach/msm_bus_board.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/gpiomux.h>
+#include "devices.h"
+#include "board-8064.h"
+
+#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
+static struct gpiomux_setting gpio_eth_config = {
+ .pull = GPIOMUX_PULL_NONE,
+ .drv = GPIOMUX_DRV_8MA,
+ .func = GPIOMUX_FUNC_GPIO,
+};
+
+/* The SPI configurations apply to GSBI 5*/
+static struct gpiomux_setting gpio_spi_config = {
+ .func = GPIOMUX_FUNC_2,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+/* The SPI configurations apply to GSBI 5 chip select 2*/
+static struct gpiomux_setting gpio_spi_cs2_config = {
+ .func = GPIOMUX_FUNC_3,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+struct msm_gpiomux_config apq8064_ethernet_configs[] = {
+ {
+ .gpio = 43,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_eth_config,
+ [GPIOMUX_ACTIVE] = &gpio_eth_config,
+ }
+ },
+};
+#endif
+
+static struct gpiomux_setting audio_auxpcm[] = {
+ /* Suspended state */
+ {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ },
+ /* Active state */
+ {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_NONE,
+ },
+};
+
+static struct msm_gpiomux_config apq8064_gsbi_configs[] __initdata = {
+#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
+ {
+ .gpio = 51, /* GSBI5 QUP SPI_DATA_MOSI */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 52, /* GSBI5 QUP SPI_DATA_MISO */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+ {
+ .gpio = 31, /* GSBI5 QUP SPI_CS2_N */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_cs2_config,
+ },
+ },
+ {
+ .gpio = 54, /* GSBI5 QUP SPI_CLK */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gpio_spi_config,
+ },
+ },
+#endif
+};
+
+static struct msm_gpiomux_config apq8064_audio_auxpcm_configs[] __initdata = {
+ {
+ .gpio = 43,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
+ [GPIOMUX_ACTIVE] = &audio_auxpcm[1],
+ },
+ },
+ {
+ .gpio = 44,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
+ [GPIOMUX_ACTIVE] = &audio_auxpcm[1],
+ },
+ },
+ {
+ .gpio = 45,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
+ [GPIOMUX_ACTIVE] = &audio_auxpcm[1],
+ },
+ },
+ {
+ .gpio = 46,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
+ [GPIOMUX_ACTIVE] = &audio_auxpcm[1],
+ },
+ },
+};
+
+void __init apq8064_init_gpiomux(void)
+{
+ int rc;
+
+ rc = msm_gpiomux_init(NR_GPIO_IRQS);
+ if (rc) {
+ pr_err(KERN_ERR "msm_gpiomux_init failed %d\n", rc);
+ return;
+ }
+
+#if defined(CONFIG_KS8851) || defined(CONFIG_KS8851_MODULE)
+ msm_gpiomux_install(apq8064_ethernet_configs,
+ ARRAY_SIZE(apq8064_ethernet_configs));
+#endif
+
+ msm_gpiomux_install(apq8064_gsbi_configs,
+ ARRAY_SIZE(apq8064_gsbi_configs));
+
+ msm_gpiomux_install(apq8064_audio_auxpcm_configs,
+ ARRAY_SIZE(apq8064_audio_auxpcm_configs));
+}
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
new file mode 100644
index 0000000..032d8da
--- /dev/null
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -0,0 +1,102 @@
+/* Copyright (c) 2011, 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/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/bootmem.h>
+#include <asm/mach-types.h>
+#include <asm/mach/mmc.h>
+#include <mach/msm_bus_board.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include <mach/gpiomux.h>
+#include "devices.h"
+#include "board-8064.h"
+
+
+static struct pm8xxx_mpp_platform_data
+apq8064_pm8921_mpp_pdata __devinitdata = {
+ .mpp_base = PM8921_MPP_PM_TO_SYS(1),
+};
+
+static struct pm8xxx_gpio_platform_data
+apq8064_pm8921_gpio_pdata __devinitdata = {
+ .gpio_base = PM8921_GPIO_PM_TO_SYS(1),
+};
+
+static struct pm8xxx_irq_platform_data
+apq8064_pm8921_irq_pdata __devinitdata = {
+ .irq_base = PM8921_IRQ_BASE,
+ .devirq = PM8921_USR_IRQ_N,
+ .irq_trigger_flag = IRQF_TRIGGER_HIGH,
+ .dev_id = 0,
+};
+
+static struct pm8921_platform_data
+apq8064_pm8921_platform_data __devinitdata = {
+ .regulator_pdatas = msm8064_pm8921_regulator_pdata,
+ .irq_pdata = &apq8064_pm8921_irq_pdata,
+ .gpio_pdata = &apq8064_pm8921_gpio_pdata,
+ .mpp_pdata = &apq8064_pm8921_mpp_pdata,
+};
+
+static struct pm8xxx_irq_platform_data
+apq8064_pm8821_irq_pdata __devinitdata = {
+ .irq_base = PM8821_IRQ_BASE,
+ .devirq = PM8821_USR_IRQ_N,
+ .irq_trigger_flag = IRQF_TRIGGER_HIGH,
+ .dev_id = 1,
+};
+
+static struct pm8xxx_mpp_platform_data
+apq8064_pm8821_mpp_pdata __devinitdata = {
+ .mpp_base = PM8821_MPP_PM_TO_SYS(1),
+};
+
+static struct pm8821_platform_data
+apq8064_pm8821_platform_data __devinitdata = {
+ .irq_pdata = &apq8064_pm8821_irq_pdata,
+ .mpp_pdata = &apq8064_pm8821_mpp_pdata,
+};
+
+static struct msm_ssbi_platform_data apq8064_ssbi_pm8921_pdata __devinitdata = {
+ .controller_type = MSM_SBI_CTRL_PMIC_ARBITER,
+ .slave = {
+ .name = "pm8921-core",
+ .platform_data = &apq8064_pm8921_platform_data,
+ },
+};
+
+static struct msm_ssbi_platform_data apq8064_ssbi_pm8821_pdata __devinitdata = {
+ .controller_type = MSM_SBI_CTRL_PMIC_ARBITER,
+ .slave = {
+ .name = "pm8821-core",
+ .platform_data = &apq8064_pm8821_platform_data,
+ },
+};
+
+void __init apq8064_init_pmic(void)
+{
+ apq8064_device_ssbi_pmic1.dev.platform_data =
+ &apq8064_ssbi_pm8921_pdata;
+ apq8064_device_ssbi_pmic2.dev.platform_data =
+ &apq8064_ssbi_pm8821_pdata;
+ apq8064_pm8921_platform_data.num_regulators =
+ msm8064_pm8921_regulator_pdata_len;
+
+ if (machine_is_apq8064_rumi3()) {
+ apq8064_pm8921_irq_pdata.devirq = 0;
+ apq8064_pm8821_irq_pdata.devirq = 0;
+ }
+}
diff --git a/arch/arm/mach-msm/board-8064.c b/arch/arm/mach-msm/board-8064.c
index 6e2c044..370d8ab7 100644
--- a/arch/arm/mach-msm/board-8064.c
+++ b/arch/arm/mach-msm/board-8064.c
@@ -48,7 +48,7 @@
#define MSM_PMEM_KERNEL_EBI1_SIZE 0x600000
#define MSM_PMEM_ADSP_SIZE 0x3800000
-#define MSM_PMEM_AUDIO_SIZE 0x28B000
+#define MSM_PMEM_AUDIO_SIZE 0x2B4000
#define MSM_PMEM_SIZE 0x1800000 /* 24 Mbytes */
static struct memtype_reserve apq8064_reserve_table[] __initdata = {
@@ -152,7 +152,6 @@
apq8064_reserve_table[p->memory_type].size += p->size;
}
-
static void __init reserve_pmem_memory(void)
{
reserve_memory_for(&android_pmem_adsp_pdata);
@@ -334,6 +333,7 @@
.shared_ce_resource = QCE_SHARE_CE_RESOURCE,
.hw_key_support = QCE_HW_KEY_SUPPORT,
.sha_hmac = QCE_SHA_HMAC_SUPPORT,
+ .bus_scale_table = NULL,
};
static struct platform_device qcrypto_device = {
@@ -356,6 +356,7 @@
.shared_ce_resource = QCE_SHARE_CE_RESOURCE,
.hw_key_support = QCE_HW_KEY_SUPPORT,
.sha_hmac = QCE_SHA_HMAC_SUPPORT,
+ .bus_scale_table = NULL,
};
static struct platform_device qcedev_device = {
@@ -467,27 +468,28 @@
#ifdef CONFIG_HW_RANDOM_MSM
&apq8064_device_rng,
#endif
- &msm_pcm,
- &msm_pcm_routing,
- &msm_cpudai0,
- &msm_cpudai1,
- &msm_cpudai_hdmi_rx,
- &msm_cpudai_bt_rx,
- &msm_cpudai_bt_tx,
- &msm_cpudai_fm_rx,
- &msm_cpudai_fm_tx,
- &msm_cpu_fe,
- &msm_stub_codec,
- &msm_voice,
- &msm_voip,
- &msm_lpa_pcm,
- &msm_cpudai_afe_01_rx,
- &msm_cpudai_afe_01_tx,
- &msm_cpudai_afe_02_rx,
- &msm_cpudai_afe_02_tx,
- &msm_pcm_afe,
- &msm_cpudai_auxpcm_rx,
- &msm_cpudai_auxpcm_tx,
+ &apq_pcm,
+ &apq_pcm_routing,
+ &apq_cpudai0,
+ &apq_cpudai1,
+ &apq_cpudai_hdmi_rx,
+ &apq_cpudai_bt_rx,
+ &apq_cpudai_bt_tx,
+ &apq_cpudai_fm_rx,
+ &apq_cpudai_fm_tx,
+ &apq_cpu_fe,
+ &apq_stub_codec,
+ &apq_voice,
+ &apq_voip,
+ &apq_lpa_pcm,
+ &apq_pcm_hostless,
+ &apq_cpudai_afe_01_rx,
+ &apq_cpudai_afe_01_tx,
+ &apq_cpudai_afe_02_rx,
+ &apq_cpudai_afe_02_tx,
+ &apq_pcm_afe,
+ &apq_cpudai_auxpcm_rx,
+ &apq_cpudai_auxpcm_tx,
};
static struct platform_device *sim_devices[] __initdata = {
@@ -521,130 +523,6 @@
},
};
-#ifdef CONFIG_KS8851
-static struct gpiomux_setting gpio_eth_config = {
- .pull = GPIOMUX_PULL_NONE,
- .drv = GPIOMUX_DRV_8MA,
- .func = GPIOMUX_FUNC_GPIO,
-};
-
-/* The SPI configurations apply to GSBI 5*/
-static struct gpiomux_setting gpio_spi_config = {
- .func = GPIOMUX_FUNC_2,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_NONE,
-};
-
-/* The SPI configurations apply to GSBI 5 chip select 2*/
-static struct gpiomux_setting gpio_spi_cs2_config = {
- .func = GPIOMUX_FUNC_3,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_NONE,
-};
-#endif
-
-struct msm_gpiomux_config apq8064_ethernet_configs[NR_GPIO_IRQS] = {
-#ifdef CONFIG_KS8851
- {
- .gpio = KS8851_IRQ_GPIO,
- .settings = {
- [GPIOMUX_SUSPENDED] = &gpio_eth_config,
- [GPIOMUX_ACTIVE] = &gpio_eth_config,
- }
- },
-#endif
-};
-
-static struct msm_gpiomux_config apq8064_gsbi_configs[] __initdata = {
-#ifdef CONFIG_KS8851
- {
- .gpio = 51, /* GSBI5 QUP SPI_DATA_MOSI */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gpio_spi_config,
- },
- },
- {
- .gpio = 52, /* GSBI5 QUP SPI_DATA_MISO */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gpio_spi_config,
- },
- },
- {
- .gpio = 31, /* GSBI5 QUP SPI_CS2_N */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gpio_spi_cs2_config,
- },
- },
- {
- .gpio = 54, /* GSBI5 QUP SPI_CLK */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gpio_spi_config,
- },
- },
-#endif
-};
-
-static struct pm8xxx_mpp_platform_data
-apq8064_pm8921_mpp_pdata __devinitdata = {
- .mpp_base = PM8921_MPP_PM_TO_SYS(1),
-};
-
-static struct pm8xxx_gpio_platform_data
-apq8064_pm8921_gpio_pdata __devinitdata = {
- .gpio_base = PM8921_GPIO_PM_TO_SYS(1),
-};
-
-static struct pm8xxx_irq_platform_data
-apq8064_pm8921_irq_pdata __devinitdata = {
- .irq_base = PM8921_IRQ_BASE,
- .devirq = PM8921_USR_IRQ_N,
- .irq_trigger_flag = IRQF_TRIGGER_HIGH,
- .dev_id = 0,
-};
-
-static struct pm8921_platform_data
-apq8064_pm8921_platform_data __devinitdata = {
- .regulator_pdatas = msm8064_pm8921_regulator_pdata,
- .irq_pdata = &apq8064_pm8921_irq_pdata,
- .gpio_pdata = &apq8064_pm8921_gpio_pdata,
- .mpp_pdata = &apq8064_pm8921_mpp_pdata,
-};
-
-static struct pm8xxx_irq_platform_data
-apq8064_pm8821_irq_pdata __devinitdata = {
- .irq_base = PM8821_IRQ_BASE,
- .devirq = PM8821_USR_IRQ_N,
- .irq_trigger_flag = IRQF_TRIGGER_HIGH,
- .dev_id = 1,
-};
-
-static struct pm8xxx_mpp_platform_data
-apq8064_pm8821_mpp_pdata __devinitdata = {
- .mpp_base = PM8821_MPP_PM_TO_SYS(1),
-};
-
-static struct pm8821_platform_data
-apq8064_pm8821_platform_data __devinitdata = {
- .irq_pdata = &apq8064_pm8821_irq_pdata,
- .mpp_pdata = &apq8064_pm8821_mpp_pdata,
-};
-
-static struct msm_ssbi_platform_data apq8064_ssbi_pm8921_pdata __devinitdata = {
- .controller_type = MSM_SBI_CTRL_PMIC_ARBITER,
- .slave = {
- .name = "pm8921-core",
- .platform_data = &apq8064_pm8921_platform_data,
- },
-};
-
-static struct msm_ssbi_platform_data apq8064_ssbi_pm8821_pdata __devinitdata = {
- .controller_type = MSM_SBI_CTRL_PMIC_ARBITER,
- .slave = {
- .name = "pm8821-core",
- .platform_data = &apq8064_pm8821_platform_data,
- },
-};
-
static struct slim_boardinfo apq8064_slim_devices[] = {
{
.bus_num = 1,
@@ -658,77 +536,12 @@
.src_clk_rate = 24000000,
};
-
-static struct gpiomux_setting audio_auxpcm[] = {
- /* Suspended state */
- {
- .func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_NONE,
- },
- /* Active state */
- {
- .func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_NONE,
- },
-};
-static struct msm_gpiomux_config apq8064_audio_auxpcm_configs[] __initdata = {
- {
- .gpio = 43,
- .settings = {
- [GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
- [GPIOMUX_ACTIVE] = &audio_auxpcm[1],
- },
- },
- {
- .gpio = 44,
- .settings = {
- [GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
- [GPIOMUX_ACTIVE] = &audio_auxpcm[1],
- },
- },
- {
- .gpio = 45,
- .settings = {
- [GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
- [GPIOMUX_ACTIVE] = &audio_auxpcm[1],
- },
- },
- {
- .gpio = 46,
- .settings = {
- [GPIOMUX_SUSPENDED] = &audio_auxpcm[0],
- [GPIOMUX_ACTIVE] = &audio_auxpcm[1],
- },
- },
-};
-
static void __init apq8064_i2c_init(void)
{
apq8064_device_qup_i2c_gsbi4.dev.platform_data =
&apq8064_i2c_qup_gsbi4_pdata;
}
-static int __init gpiomux_init(void)
-{
- int rc;
-
- rc = msm_gpiomux_init(NR_GPIO_IRQS);
- if (rc) {
- pr_err(KERN_ERR "msm_gpiomux_init failed %d\n", rc);
- return rc;
- }
- msm_gpiomux_install(apq8064_ethernet_configs,
- ARRAY_SIZE(apq8064_ethernet_configs));
-
- msm_gpiomux_install(apq8064_gsbi_configs,
- ARRAY_SIZE(apq8064_gsbi_configs));
- msm_gpiomux_install(apq8064_audio_auxpcm_configs,
- ARRAY_SIZE(apq8064_audio_auxpcm_configs));
- return 0;
-}
-
#ifdef CONFIG_KS8851
static int ethernet_init(void)
{
@@ -763,18 +576,13 @@
if (socinfo_init() < 0)
pr_err("socinfo_init() failed!\n");
apq8064_clock_init();
- gpiomux_init();
+ apq8064_init_gpiomux();
apq8064_i2c_init();
apq8064_device_qup_spi_gsbi5.dev.platform_data =
&apq8064_qup_spi_gsbi5_pdata;
- apq8064_device_ssbi_pmic1.dev.platform_data =
- &apq8064_ssbi_pm8921_pdata;
- apq8064_device_ssbi_pmic2.dev.platform_data =
- &apq8064_ssbi_pm8821_pdata;
+ apq8064_init_pmic();
apq8064_device_otg.dev.platform_data = &msm_otg_pdata;
- apq8064_pm8921_platform_data.num_regulators =
- msm8064_pm8921_regulator_pdata_len;
platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
apq8064_init_mmc();
slim_register_board_info(apq8064_slim_devices,
@@ -793,8 +601,6 @@
static void __init apq8064_rumi3_init(void)
{
- apq8064_pm8921_irq_pdata.devirq = 0;
- apq8064_pm8821_irq_pdata.devirq = 0;
apq8064_common_init();
ethernet_init();
platform_add_devices(rumi3_devices, ARRAY_SIZE(rumi3_devices));
diff --git a/arch/arm/mach-msm/board-8064.h b/arch/arm/mach-msm/board-8064.h
index d9da00a..4a48897 100644
--- a/arch/arm/mach-msm/board-8064.h
+++ b/arch/arm/mach-msm/board-8064.h
@@ -42,4 +42,6 @@
struct mmc_platform_data *plat);
void apq8064_init_mmc(void);
+void apq8064_init_gpiomux(void);
+void apq8064_init_pmic(void);
#endif
diff --git a/arch/arm/mach-msm/board-8930-display.c b/arch/arm/mach-msm/board-8930-display.c
index f1103cb..c1b880c 100644
--- a/arch/arm/mach-msm/board-8930-display.c
+++ b/arch/arm/mach-msm/board-8930-display.c
@@ -17,6 +17,7 @@
#include <linux/bootmem.h>
#include <asm/mach-types.h>
#include <mach/msm_bus_board.h>
+#include <mach/msm_memtypes.h>
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/gpiomux.h>
@@ -46,25 +47,26 @@
#define MSM_FB_EXT_BUF_SIZE 0
#endif
-#ifdef CONFIG_FB_MSM_OVERLAY_WRITEBACK
-/* width x height x 3 bpp x 2 frame buffer */
-#define MSM_FB_WRITEBACK_SIZE (1376 * 768 * 3 * 2)
-#define MSM_FB_WRITEBACK_OFFSET \
- (MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE)
-#else
-#define MSM_FB_WRITEBACK_SIZE 0
-#define MSM_FB_WRITEBACK_OFFSET 0
-#endif
-
#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
/* 4 bpp x 2 page HDMI case */
#define MSM_FB_SIZE roundup((1920 * 1088 * 4 * 2), 4096)
#else
/* Note: must be multiple of 4096 */
-#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE + \
- MSM_FB_WRITEBACK_SIZE, 4096)
+#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE, 4096)
#endif
+#ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK
+#define MSM_FB_OVERLAY0_WRITEBACK_SIZE roundup((1376 * 768 * 3 * 2), 4096)
+#else
+#define MSM_FB_OVERLAY0_WRITEBACK_SIZE (0)
+#endif /* CONFIG_FB_MSM_OVERLAY0_WRITEBACK */
+
+#ifdef CONFIG_FB_MSM_OVERLAY1_WRITEBACK
+#define MSM_FB_OVERLAY1_WRITEBACK_SIZE roundup((1920 * 1088 * 3 * 2), 4096)
+#else
+#define MSM_FB_OVERLAY1_WRITEBACK_SIZE (0)
+#endif /* CONFIG_FB_MSM_OVERLAY1_WRITEBACK */
+
#define MDP_VSYNC_GPIO 0
#define PANEL_NAME_MAX_LEN 30
@@ -77,11 +79,6 @@
#define HDMI_PANEL_NAME "hdmi_msm"
#define TVOUT_PANEL_NAME "tvout_msm"
-static int writeback_offset(void)
-{
- return MSM_FB_WRITEBACK_OFFSET;
-}
-
static struct resource msm_fb_resources[] = {
{
.flags = IORESOURCE_DMA,
@@ -438,9 +435,21 @@
.mdp_bus_scale_table = &mdp_bus_scale_pdata,
#endif
.mdp_rev = MDP_REV_42,
- .writeback_offset = writeback_offset,
+ .mdp_writeback_memtype = MEMTYPE_EBI1,
+ .mdp_writeback_phys = NULL,
};
+void __init msm8930_mdp_writeback(struct memtype_reserve* reserve_table)
+{
+ mdp_pdata.mdp_writeback_size_ov0 = MSM_FB_OVERLAY0_WRITEBACK_SIZE;
+ mdp_pdata.mdp_writeback_size_ov1 = MSM_FB_OVERLAY1_WRITEBACK_SIZE;
+
+ reserve_table[mdp_pdata.mdp_writeback_memtype].size +=
+ mdp_pdata.mdp_writeback_size_ov0;
+ reserve_table[mdp_pdata.mdp_writeback_memtype].size +=
+ mdp_pdata.mdp_writeback_size_ov1;
+}
+
#define LPM_CHANNEL0 0
static int toshiba_gpio[] = {LPM_CHANNEL0};
@@ -781,7 +790,7 @@
platform_device_register(&mipi_dsi_novatek_panel_device);
#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL
- if (!cpu_is_msm8627())
+ if (!cpu_is_msm8930())
platform_device_register(&hdmi_msm_device);
#endif
diff --git a/arch/arm/mach-msm/board-8930.c b/arch/arm/mach-msm/board-8930.c
index d6c6ec8..3b89a32 100644
--- a/arch/arm/mach-msm/board-8930.c
+++ b/arch/arm/mach-msm/board-8930.c
@@ -117,7 +117,7 @@
#endif
#define MSM_PMEM_ADSP_SIZE 0x3800000
-#define MSM_PMEM_AUDIO_SIZE 0x28B000
+#define MSM_PMEM_AUDIO_SIZE 0x2B4000
#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
#define MSM_PMEM_SIZE 0x4000000 /* 64 Mbytes */
#else
@@ -326,11 +326,18 @@
msm8930_reserve_table[MEMTYPE_EBI1].size += MSM_ION_ADSP_SIZE;
#endif
}
+
+static void __init reserve_mdp_memory(void)
+{
+ msm8930_mdp_writeback(msm8930_reserve_table);
+}
+
static void __init msm8930_calculate_reserve_sizes(void)
{
size_pmem_devices();
reserve_pmem_memory();
reserve_ion_memory();
+ reserve_mdp_memory();
}
static struct reserve_info msm8930_reserve_info __initdata = {
@@ -776,10 +783,8 @@
#ifdef CONFIG_USB_MSM_OTG_72K
static struct msm_otg_platform_data msm_otg_pdata;
#else
-#define USB_5V_EN 42
static void msm_hsusb_vbus_power(bool on)
{
- int rc;
static bool vbus_is_on;
static struct regulator *mvs_otg_switch;
@@ -793,42 +798,16 @@
pr_err("Unable to get mvs_otg_switch\n");
return;
}
- /* TODO: Replace this with appropriate PM8038 alternative */
-#ifndef MSM8930_PHASE_2
- rc = gpio_request(PM8921_GPIO_PM_TO_SYS(USB_5V_EN),
- "usb_5v_en");
-#endif
- if (rc < 0) {
- pr_err("failed to request usb_5v_en gpio\n");
- goto put_mvs_otg;
- }
- /* TODO: Replace this with appropriate PM8038 alternative */
-#ifndef MSM8930_PHASE_2
- rc = gpio_direction_output(PM8921_GPIO_PM_TO_SYS(USB_5V_EN), 1);
- if (rc) {
- pr_err("%s: unable to set_direction for gpio [%d]\n",
- __func__, PM8921_GPIO_PM_TO_SYS(USB_5V_EN));
- goto free_usb_5v_en;
- }
-#endif
if (regulator_enable(mvs_otg_switch)) {
pr_err("unable to enable mvs_otg_switch\n");
- goto err_ldo_gpio_set_dir;
+ goto put_mvs_otg;
}
vbus_is_on = true;
return;
}
regulator_disable(mvs_otg_switch);
-
-/* TODO: Replace this with appropriate PM8038 alternative */
-#ifndef MSM8930_PHASE_2
-err_ldo_gpio_set_dir:
- gpio_set_value(PM8921_GPIO_PM_TO_SYS(USB_5V_EN), 0);
-free_usb_5v_en:
- gpio_free(PM8921_GPIO_PM_TO_SYS(USB_5V_EN));
-#endif
put_mvs_otg:
regulator_put(mvs_otg_switch);
vbus_is_on = false;
@@ -1556,6 +1535,10 @@
&msm8930_device_ext_5v_vreg,
&msm8930_device_ext_l2_vreg,
&msm8960_device_ssbi_pmic,
+ &msm_8960_q6_lpass,
+ &msm_8960_q6_mss_fw,
+ &msm_8960_q6_mss_sw,
+ &msm_8960_riva,
&msm8960_device_qup_spi_gsbi1,
&msm8960_device_qup_i2c_gsbi3,
&msm8960_device_qup_i2c_gsbi4,
@@ -1806,6 +1789,10 @@
},
};
+static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = {
+ .mode = MSM_PM_BOOT_CONFIG_TZ,
+};
+
#ifdef CONFIG_I2C
#define I2C_SURF 1
#define I2C_FFA (1 << 1)
@@ -1996,7 +1983,7 @@
msm8930_init_hsic();
msm8930_init_cam();
msm8930_init_mmc();
- acpuclk_init(&acpuclk_8960_soc_data);
+ acpuclk_init(&acpuclk_8930_soc_data);
register_i2c_devices();
msm8930_init_fb();
slim_register_board_info(msm_slim_devices,
@@ -2006,7 +1993,7 @@
msm_cpuidle_set_states(msm_cstates, ARRAY_SIZE(msm_cstates),
msm_pm_data);
change_memory_power = &msm8930_change_memory_power;
- BUG_ON(msm_pm_boot_init(MSM_PM_BOOT_CONFIG_TZ, NULL));
+ BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
if (PLATFORM_IS_CHARM25())
platform_add_devices(mdm_devices, ARRAY_SIZE(mdm_devices));
diff --git a/arch/arm/mach-msm/board-8930.h b/arch/arm/mach-msm/board-8930.h
index ec0b867..61eb408 100644
--- a/arch/arm/mach-msm/board-8930.h
+++ b/arch/arm/mach-msm/board-8930.h
@@ -18,6 +18,7 @@
#include <linux/i2c/sx150x.h>
#include <mach/irqs.h>
#include <mach/rpm-regulator.h>
+#include <mach/msm_memtypes.h>
/*
* TODO: When physical 8930/PM8038 hardware becomes
@@ -106,6 +107,7 @@
int msm8930_init_gpiomux(void);
void msm8930_allocate_fb_region(void);
void msm8930_pm8038_gpio_mpp_init(void);
+void msm8930_mdp_writeback(struct memtype_reserve *reserve_table);
#define PLATFORM_IS_CHARM25() \
(machine_is_msm8930_cdp() && \
diff --git a/arch/arm/mach-msm/board-8960-camera.c b/arch/arm/mach-msm/board-8960-camera.c
index e827b2b..f84944b 100644
--- a/arch/arm/mach-msm/board-8960-camera.c
+++ b/arch/arm/mach-msm/board-8960-camera.c
@@ -131,7 +131,7 @@
{
.gpio = 5,
.settings = {
- [GPIOMUX_ACTIVE] = &cam_settings[1],
+ [GPIOMUX_ACTIVE] = &cam_settings[3],
[GPIOMUX_SUSPENDED] = &cam_settings[0],
},
},
@@ -262,8 +262,8 @@
{
.src = MSM_BUS_MASTER_VFE,
.dst = MSM_BUS_SLAVE_EBI_CH0,
- .ab = 140451840,
- .ib = 561807360,
+ .ab = 154275840,
+ .ib = 617103360,
},
{
.src = MSM_BUS_MASTER_VPE,
@@ -415,6 +415,35 @@
},
};
#endif
+
+#ifdef CONFIG_MT9M114
+static struct msm_camera_sensor_flash_data flash_mt9m114 = {
+ .flash_type = MSM_CAMERA_FLASH_NONE
+};
+
+static struct msm_camera_sensor_platform_info sensor_board_info_mt9m114 = {
+ .mount_angle = 90,
+ .sensor_reset = 107,
+};
+
+static struct msm_camera_sensor_info msm_camera_sensor_mt9m114_data = {
+ .sensor_name = "mt9m114",
+ .pdata = &msm_camera_csi_device_data[0],
+ .flash_data = &flash_mt9m114,
+ .sensor_platform_info = &sensor_board_info_mt9m114,
+ .gpio_conf = &gpio_conf,
+ .csi_if = 1,
+ .camera_type = BACK_CAMERA_2D,
+};
+
+struct platform_device msm8960_camera_sensor_mt9m114 = {
+ .name = "msm_camera_mt9m114",
+ .dev = {
+ .platform_data = &msm_camera_sensor_mt9m114_data,
+ },
+};
+#endif
+
#ifdef CONFIG_OV2720
static struct msm_camera_sensor_flash_data flash_ov2720 = {
.flash_type = MSM_CAMERA_FLASH_NONE,
@@ -455,6 +484,9 @@
int i;
struct platform_device *cam_dev[] = {
&msm8960_camera_sensor_imx074,
+#ifdef CONFIG_MT9M114
+ &msm8960_camera_sensor_mt9m114,
+#endif
&msm8960_camera_sensor_ov2720,
};
diff --git a/arch/arm/mach-msm/board-8960-display.c b/arch/arm/mach-msm/board-8960-display.c
index 63c51ea..df351d2 100644
--- a/arch/arm/mach-msm/board-8960-display.c
+++ b/arch/arm/mach-msm/board-8960-display.c
@@ -17,6 +17,7 @@
#include <linux/bootmem.h>
#include <asm/mach-types.h>
#include <mach/msm_bus_board.h>
+#include <mach/msm_memtypes.h>
#include <mach/board.h>
#include <mach/gpio.h>
#include <mach/gpiomux.h>
@@ -37,25 +38,26 @@
#define MSM_FB_EXT_BUF_SIZE 0
#endif
-#ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK
-/* width x height x 3 bpp x 2 frame buffer */
-#define MSM_FB_WRITEBACK_SIZE (1376 * 768 * 3 * 2)
-#define MSM_FB_WRITEBACK_OFFSET \
- (MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE)
-#else
-#define MSM_FB_WRITEBACK_SIZE 0
-#define MSM_FB_WRITEBACK_OFFSET 0
-#endif
-
#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
/* 4 bpp x 2 page HDMI case */
#define MSM_FB_SIZE roundup((1920 * 1088 * 4 * 2), 4096)
#else
/* Note: must be multiple of 4096 */
-#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE + \
- MSM_FB_WRITEBACK_SIZE, 4096)
+#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE, 4096)
#endif
+#ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK
+#define MSM_FB_OVERLAY0_WRITEBACK_SIZE roundup((1376 * 768 * 3 * 2), 4096)
+#else
+#define MSM_FB_OVERLAY0_WRITEBACK_SIZE (0)
+#endif /* CONFIG_FB_MSM_OVERLAY0_WRITEBACK */
+
+#ifdef CONFIG_FB_MSM_OVERLAY1_WRITEBACK
+#define MSM_FB_OVERLAY1_WRITEBACK_SIZE roundup((1920 * 1088 * 3 * 2), 4096)
+#else
+#define MSM_FB_OVERLAY1_WRITEBACK_SIZE (0)
+#endif /* CONFIG_FB_MSM_OVERLAY1_WRITEBACK */
+
#define MDP_VSYNC_GPIO 0
#define PANEL_NAME_MAX_LEN 30
@@ -68,11 +70,6 @@
#define HDMI_PANEL_NAME "hdmi_msm"
#define TVOUT_PANEL_NAME "tvout_msm"
-static int writeback_offset(void)
-{
- return MSM_FB_WRITEBACK_OFFSET;
-}
-
static struct resource msm_fb_resources[] = {
{
.flags = IORESOURCE_DMA,
@@ -553,9 +550,21 @@
.mdp_bus_scale_table = &mdp_bus_scale_pdata,
#endif
.mdp_rev = MDP_REV_42,
- .writeback_offset = writeback_offset,
+ .mdp_writeback_memtype = MEMTYPE_EBI1,
+ .mdp_writeback_phys = NULL,
};
+void __init msm8960_mdp_writeback(struct memtype_reserve* reserve_table)
+{
+ mdp_pdata.mdp_writeback_size_ov0 = MSM_FB_OVERLAY0_WRITEBACK_SIZE;
+ mdp_pdata.mdp_writeback_size_ov1 = MSM_FB_OVERLAY1_WRITEBACK_SIZE;
+
+ reserve_table[mdp_pdata.mdp_writeback_memtype].size +=
+ mdp_pdata.mdp_writeback_size_ov0;
+ reserve_table[mdp_pdata.mdp_writeback_memtype].size +=
+ mdp_pdata.mdp_writeback_size_ov1;
+}
+
static struct platform_device mipi_dsi_renesas_panel_device = {
.name = "mipi_renesas",
.id = 0,
diff --git a/arch/arm/mach-msm/board-8960-gpiomux.c b/arch/arm/mach-msm/board-8960-gpiomux.c
index ce260e1..71713829 100644
--- a/arch/arm/mach-msm/board-8960-gpiomux.c
+++ b/arch/arm/mach-msm/board-8960-gpiomux.c
@@ -247,7 +247,7 @@
static struct gpiomux_setting hdmi_active_3_cfg = {
.func = GPIOMUX_FUNC_GPIO,
.drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
+ .pull = GPIOMUX_PULL_UP,
.dir = GPIOMUX_IN,
};
diff --git a/arch/arm/mach-msm/board-8960-pmic.c b/arch/arm/mach-msm/board-8960-pmic.c
index 5e1ea0d..154073f 100644
--- a/arch/arm/mach-msm/board-8960-pmic.c
+++ b/arch/arm/mach-msm/board-8960-pmic.c
@@ -90,7 +90,7 @@
/* Initial PM8921 GPIO configurations */
static struct pm8xxx_gpio_init pm8921_gpios[] __initdata = {
- PM8XXX_GPIO_DISABLE(6), /* Disable unused */
+ PM8XXX_GPIO_OUTPUT_VIN(6, 1, PM_GPIO_VIN_VPH), /* MHL power EN_N */
PM8XXX_GPIO_DISABLE(7), /* Disable NFC */
PM8XXX_GPIO_INPUT(16, PM_GPIO_PULL_UP_30), /* SD_CARD_WP */
/* External regulator shared by display and touchscreen on LiQUID */
@@ -403,10 +403,11 @@
325,
};
+#define MAX_VOLTAGE_MV 4200
static struct pm8921_charger_platform_data pm8921_chg_pdata __devinitdata = {
.safety_time = 180,
.update_time = 60000,
- .max_voltage = 4200,
+ .max_voltage = MAX_VOLTAGE_MV,
.min_voltage = 3200,
.resume_voltage_delta = 100,
.term_current = 100,
@@ -431,6 +432,7 @@
.i_test = 2500,
.v_failure = 3000,
.calib_delay_ms = 600000,
+ .max_voltage_uv = MAX_VOLTAGE_MV * 1000,
};
#define PM8921_LC_LED_MAX_CURRENT 4 /* I = 4mA */
diff --git a/arch/arm/mach-msm/board-8960-regulator.c b/arch/arm/mach-msm/board-8960-regulator.c
index 7bc3ca5..5049791 100644
--- a/arch/arm/mach-msm/board-8960-regulator.c
+++ b/arch/arm/mach-msm/board-8960-regulator.c
@@ -32,6 +32,7 @@
REGULATOR_SUPPLY("dsi_vdda", "mipi_dsi.1"),
REGULATOR_SUPPLY("mipi_csi_vdd", "msm_camera_imx074.0"),
REGULATOR_SUPPLY("mipi_csi_vdd", "msm_camera_ov2720.0"),
+ REGULATOR_SUPPLY("mipi_csi_vdd", "msm_camera_mt9m114.0"),
};
VREG_CONSUMERS(L3) = {
REGULATOR_SUPPLY("8921_l3", NULL),
@@ -71,11 +72,13 @@
REGULATOR_SUPPLY("8921_l11", NULL),
REGULATOR_SUPPLY("cam_vana", "msm_camera_imx074.0"),
REGULATOR_SUPPLY("cam_vana", "msm_camera_ov2720.0"),
+ REGULATOR_SUPPLY("cam_vana", "msm_camera_mt9m114.0"),
};
VREG_CONSUMERS(L12) = {
REGULATOR_SUPPLY("8921_l12", NULL),
REGULATOR_SUPPLY("cam_vdig", "msm_camera_imx074.0"),
REGULATOR_SUPPLY("cam_vdig", "msm_camera_ov2720.0"),
+ REGULATOR_SUPPLY("cam_vdig", "msm_camera_mt9m114.0"),
};
VREG_CONSUMERS(L14) = {
REGULATOR_SUPPLY("8921_l14", NULL),
@@ -88,6 +91,7 @@
REGULATOR_SUPPLY("8921_l16", NULL),
REGULATOR_SUPPLY("cam_vaf", "msm_camera_imx074.0"),
REGULATOR_SUPPLY("cam_vaf", "msm_camera_ov2720.0"),
+ REGULATOR_SUPPLY("cam_vaf", "msm_camera_mt9m114.0"),
};
VREG_CONSUMERS(L17) = {
REGULATOR_SUPPLY("8921_l17", NULL),
@@ -197,6 +201,7 @@
REGULATOR_SUPPLY("8921_lvs5", NULL),
REGULATOR_SUPPLY("cam_vio", "msm_camera_imx074.0"),
REGULATOR_SUPPLY("cam_vio", "msm_camera_ov2720.0"),
+ REGULATOR_SUPPLY("cam_vio", "msm_camera_mt9m114.0"),
};
VREG_CONSUMERS(LVS6) = {
REGULATOR_SUPPLY("8921_lvs6", NULL),
@@ -207,7 +212,6 @@
};
VREG_CONSUMERS(USB_OTG) = {
REGULATOR_SUPPLY("8921_usb_otg", NULL),
- REGULATOR_SUPPLY("vbus_otg", "msm_otg"),
};
VREG_CONSUMERS(HDMI_MVS) = {
REGULATOR_SUPPLY("8921_hdmi_mvs", NULL),
@@ -229,6 +233,10 @@
REGULATOR_SUPPLY("vdd_lvds_3p3v", "mipi_dsi.1"),
REGULATOR_SUPPLY("mhl_ext_3p3v", "msm_otg"),
};
+VREG_CONSUMERS(EXT_OTG_SW) = {
+ REGULATOR_SUPPLY("ext_otg_sw", NULL),
+ REGULATOR_SUPPLY("vbus_otg", "msm_otg"),
+};
#define PM8921_VREG_INIT(_id, _min_uV, _max_uV, _modes, _ops, _apply_uV, \
_pull_down, _always_on, _supply_regulator, \
@@ -320,7 +328,7 @@
.pin_ctrl = _pin_ctrl, \
}
-#define GPIO_VREG_INIT(_id, _reg_name, _gpio_label, _gpio) \
+#define GPIO_VREG(_id, _reg_name, _gpio_label, _gpio, _supply_regulator) \
[GPIO_VREG_ID_##_id] = { \
.init_data = { \
.constraints = { \
@@ -329,6 +337,7 @@
.num_consumer_supplies = \
ARRAY_SIZE(vreg_consumers_##_id), \
.consumer_supplies = vreg_consumers_##_id, \
+ .supply_regulator = _supply_regulator, \
}, \
.regulator_name = _reg_name, \
.gpio_label = _gpio_label, \
@@ -440,18 +449,21 @@
/* GPIO regulator constraints */
struct gpio_regulator_platform_data msm_gpio_regulator_pdata[] __devinitdata = {
- GPIO_VREG_INIT(EXT_5V, "ext_5v", "ext_5v_en", PM8921_MPP_PM_TO_SYS(7)),
- GPIO_VREG_INIT(EXT_L2, "ext_l2", "ext_l2_en", 91),
- GPIO_VREG_INIT(EXT_3P3V, "ext_3p3v", "ext_3p3v_en",
- PM8921_GPIO_PM_TO_SYS(17)),
+ /* ID vreg_name gpio_label gpio supply */
+ GPIO_VREG(EXT_5V, "ext_5v", "ext_5v_en", PM8921_MPP_PM_TO_SYS(7), NULL),
+ GPIO_VREG(EXT_L2, "ext_l2", "ext_l2_en", 91, NULL),
+ GPIO_VREG(EXT_3P3V, "ext_3p3v", "ext_3p3v_en",
+ PM8921_GPIO_PM_TO_SYS(17), NULL),
+ GPIO_VREG(EXT_OTG_SW, "ext_otg_sw", "ext_otg_sw_en",
+ PM8921_GPIO_PM_TO_SYS(42), "8921_usb_otg"),
};
/* SAW regulator constraints */
struct regulator_init_data msm_saw_regulator_pdata_s5 =
/* ID vreg_name min_uV max_uV */
- SAW_VREG_INIT(S5, "8921_s5", 950000, 1300000);
+ SAW_VREG_INIT(S5, "8921_s5", 850000, 1300000);
struct regulator_init_data msm_saw_regulator_pdata_s6 =
- SAW_VREG_INIT(S6, "8921_s6", 950000, 1300000);
+ SAW_VREG_INIT(S6, "8921_s6", 850000, 1300000);
/* PM8921 regulator constraints */
struct pm8921_regulator_platform_data
@@ -482,7 +494,7 @@
RPM_SMPS(S3, 0, 1, 1, 500000, 1150000, NULL, 100000, 4p80),
RPM_SMPS(S4, 1, 1, 0, 1800000, 1800000, NULL, 100000, 3p20),
RPM_SMPS(S7, 0, 1, 0, 1150000, 1150000, NULL, 100000, 3p20),
- RPM_SMPS(S8, 1, 1, 1, 2200000, 2200000, NULL, 100000, 1p60),
+ RPM_SMPS(S8, 1, 1, 1, 2100000, 2100000, NULL, 100000, 1p60),
/* ID a_on pd ss min_uV max_uV supply sys_uA init_ip */
RPM_LDO(L1, 1, 1, 0, 1050000, 1050000, "8921_s4", 0, 10000),
diff --git a/arch/arm/mach-msm/board-8960-storage.c b/arch/arm/mach-msm/board-8960-storage.c
index dfcafd4..8521717 100644
--- a/arch/arm/mach-msm/board-8960-storage.c
+++ b/arch/arm/mach-msm/board-8960-storage.c
@@ -247,6 +247,7 @@
.status_gpio = PM8921_GPIO_PM_TO_SYS(26),
.status_irq = PM8921_GPIO_IRQ(PM8921_IRQ_BASE, 26),
.irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ .is_status_gpio_active_low = true,
#endif
.xpc_cap = 1,
.uhs_caps = (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
diff --git a/arch/arm/mach-msm/board-8960.c b/arch/arm/mach-msm/board-8960.c
index fa607bf..e6ca8e0c 100644
--- a/arch/arm/mach-msm/board-8960.c
+++ b/arch/arm/mach-msm/board-8960.c
@@ -79,6 +79,8 @@
#include <mach/ion.h>
#include <mach/mdm2.h>
+#include <linux/fmem.h>
+
#include "timer.h"
#include "devices.h"
#include "devices-msm8x60.h"
@@ -114,24 +116,35 @@
.io_open_drain_ena = 0x0,
.irq_summary = -1,
},
+ [SX150X_LIQUID] = {
+ .gpio_base = GPIO_LIQUID_EXPANDER_BASE,
+ .oscio_is_gpo = false,
+ .io_pullup_ena = 0x0c08,
+ .io_pulldn_ena = 0x4060,
+ .io_open_drain_ena = 0x000c,
+ .io_polarity = 0,
+ .irq_summary = -1,
+ },
};
#endif
-#define MSM_PMEM_ADSP_SIZE 0x3800000
-#define MSM_PMEM_AUDIO_SIZE 0x28B000
+#define MSM_PMEM_ADSP_SIZE 0x4200000
+#define MSM_PMEM_AUDIO_SIZE 0x2B4000
#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
#define MSM_PMEM_SIZE 0x4000000 /* 64 Mbytes */
#else
#define MSM_PMEM_SIZE 0x1C00000 /* 28 Mbytes */
#endif
-
+#define MSM_LIQUID_PMEM_SIZE 0x4000000 /* 64 Mbytes */
#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
#define MSM_PMEM_KERNEL_EBI1_SIZE 0xB0C000
#define MSM_ION_EBI_SIZE (MSM_PMEM_SIZE + 0x600000)
#define MSM_ION_ADSP_SIZE MSM_PMEM_ADSP_SIZE
#define MSM_ION_HEAP_NUM 5
+#define MSM_LIQUID_ION_EBI_SIZE (MSM_LIQUID_PMEM_SIZE + 0x600000)
+static unsigned msm_ion_ebi_size = MSM_ION_EBI_SIZE;
#else
#define MSM_PMEM_KERNEL_EBI1_SIZE 0x110C000
#define MSM_ION_HEAP_NUM 2
@@ -149,9 +162,11 @@
#ifdef CONFIG_ANDROID_PMEM
static unsigned pmem_size = MSM_PMEM_SIZE;
+static unsigned pmem_param_set;
static int __init pmem_size_setup(char *p)
{
pmem_size = memparse(p, NULL);
+ pmem_param_set = 1;
return 0;
}
early_param("pmem_size", pmem_size_setup);
@@ -217,6 +232,9 @@
};
#endif
+struct fmem_platform_data fmem_pdata = {
+};
+
#define DSP_RAM_BASE_8960 0x8da00000
#define DSP_RAM_SIZE_8960 0x1800000
static int dspcrashd_pdata_8960 = 0xDEADDEAD;
@@ -253,6 +271,9 @@
#ifdef CONFIG_ANDROID_PMEM
#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
android_pmem_adsp_pdata.size = pmem_adsp_size;
+
+ if (!pmem_param_set && machine_is_msm8960_liquid())
+ pmem_size = MSM_LIQUID_PMEM_SIZE;
android_pmem_pdata.size = pmem_size;
#endif
android_pmem_audio_pdata.size = MSM_PMEM_AUDIO_SIZE;
@@ -276,6 +297,10 @@
#endif
}
+static void __init reserve_fmem_memory(void)
+{
+}
+
static int msm8960_paddr_to_memtype(unsigned int paddr)
{
return MEMTYPE_EBI1;
@@ -326,18 +351,45 @@
};
#endif
+struct platform_device fmem_device = {
+ .name = "fmem",
+ .id = 1,
+ .dev = { .platform_data = &fmem_pdata },
+};
+
static void reserve_ion_memory(void)
{
#if defined(CONFIG_ION_MSM) && defined(CONFIG_MSM_MULTIMEDIA_USE_ION)
- msm8960_reserve_table[MEMTYPE_EBI1].size += MSM_ION_EBI_SIZE;
+ unsigned int i;
+
+ if (!pmem_param_set && machine_is_msm8960_liquid()) {
+ msm_ion_ebi_size = MSM_LIQUID_ION_EBI_SIZE;
+ for (i = 0; i < ion_pdata.nr; i++) {
+ if (ion_pdata.heaps[i].id == ION_HEAP_EBI_ID) {
+ ion_pdata.heaps[i].size = msm_ion_ebi_size;
+ pr_debug("msm_ion_ebi_size 0x%x\n",
+ msm_ion_ebi_size);
+ break;
+ }
+ }
+ }
+ msm8960_reserve_table[MEMTYPE_EBI1].size += msm_ion_ebi_size;
msm8960_reserve_table[MEMTYPE_EBI1].size += MSM_ION_ADSP_SIZE;
#endif
}
+
+static void __init reserve_mdp_memory(void)
+{
+ msm8960_mdp_writeback(msm8960_reserve_table);
+}
+
static void __init msm8960_calculate_reserve_sizes(void)
{
size_pmem_devices();
reserve_pmem_memory();
reserve_ion_memory();
+ reserve_fmem_memory();
+ reserve_mdp_memory();
}
static struct reserve_info msm8960_reserve_info __initdata = {
@@ -398,6 +450,7 @@
static void __init msm8960_reserve(void)
{
msm_reserve();
+ fmem_pdata.phys = reserve_memory_for_fmem(fmem_pdata.size);
}
static int msm8960_change_memory_power(u64 start, u64 size,
@@ -552,6 +605,55 @@
#define QCE_SHARE_CE_RESOURCE 1
#define QCE_CE_SHARED 0
+/* Begin Bus scaling definitions */
+static struct msm_bus_vectors crypto_hw_init_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_ADM_PORT0,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = 0,
+ },
+ {
+ .src = MSM_BUS_MASTER_ADM_PORT1,
+ .dst = MSM_BUS_SLAVE_GSBI1_UART,
+ .ab = 0,
+ .ib = 0,
+ },
+};
+
+static struct msm_bus_vectors crypto_hw_active_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_ADM_PORT0,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 70000000UL,
+ .ib = 70000000UL,
+ },
+ {
+ .src = MSM_BUS_MASTER_ADM_PORT1,
+ .dst = MSM_BUS_SLAVE_GSBI1_UART,
+ .ab = 2480000000UL,
+ .ib = 2480000000UL,
+ },
+};
+
+static struct msm_bus_paths crypto_hw_bus_scale_usecases[] = {
+ {
+ ARRAY_SIZE(crypto_hw_init_vectors),
+ crypto_hw_init_vectors,
+ },
+ {
+ ARRAY_SIZE(crypto_hw_active_vectors),
+ crypto_hw_active_vectors,
+ },
+};
+
+static struct msm_bus_scale_pdata crypto_hw_bus_scale_pdata = {
+ crypto_hw_bus_scale_usecases,
+ ARRAY_SIZE(crypto_hw_bus_scale_usecases),
+ .name = "cryptohw",
+};
+/* End Bus Scaling Definitions*/
+
static struct resource qcrypto_resources[] = {
[0] = {
.start = QCE_0_BASE,
@@ -614,6 +716,7 @@
.shared_ce_resource = QCE_SHARE_CE_RESOURCE,
.hw_key_support = QCE_HW_KEY_SUPPORT,
.sha_hmac = QCE_SHA_HMAC_SUPPORT,
+ .bus_scale_table = &crypto_hw_bus_scale_pdata,
};
static struct platform_device qcrypto_device = {
@@ -636,6 +739,7 @@
.shared_ce_resource = QCE_SHARE_CE_RESOURCE,
.hw_key_support = QCE_HW_KEY_SUPPORT,
.sha_hmac = QCE_SHA_HMAC_SUPPORT,
+ .bus_scale_table = &crypto_hw_bus_scale_pdata,
};
static struct platform_device qcedev_device = {
@@ -772,10 +876,8 @@
#ifdef CONFIG_USB_MSM_OTG_72K
static struct msm_otg_platform_data msm_otg_pdata;
#else
-#define USB_5V_EN 42
static void msm_hsusb_vbus_power(bool on)
{
- int rc;
static bool vbus_is_on;
static struct regulator *mvs_otg_switch;
@@ -790,33 +892,15 @@
return;
}
- rc = gpio_request(PM8921_GPIO_PM_TO_SYS(USB_5V_EN),
- "usb_5v_en");
- if (rc < 0) {
- pr_err("failed to request usb_5v_en gpio\n");
- goto put_mvs_otg;
- }
-
- rc = gpio_direction_output(PM8921_GPIO_PM_TO_SYS(USB_5V_EN), 1);
- if (rc) {
- pr_err("%s: unable to set_direction for gpio [%d]\n",
- __func__, PM8921_GPIO_PM_TO_SYS(USB_5V_EN));
- goto free_usb_5v_en;
- }
-
if (regulator_enable(mvs_otg_switch)) {
pr_err("unable to enable mvs_otg_switch\n");
- goto err_ldo_gpio_set_dir;
+ goto put_mvs_otg;
}
vbus_is_on = true;
return;
}
regulator_disable(mvs_otg_switch);
-err_ldo_gpio_set_dir:
- gpio_set_value_cansleep(PM8921_GPIO_PM_TO_SYS(USB_5V_EN), 0);
-free_usb_5v_en:
- gpio_free(PM8921_GPIO_PM_TO_SYS(USB_5V_EN));
put_mvs_otg:
regulator_put(mvs_otg_switch);
vbus_is_on = false;
@@ -1167,7 +1251,6 @@
static struct i2c_board_info msm_isa1200_board_info[] __initdata = {
{
I2C_BOARD_INFO("isa1200_1", 0x90>>1),
- .platform_data = &isa1200_1_pdata,
},
};
@@ -1554,6 +1637,15 @@
},
};
+static struct platform_device msm8960_device_ext_otg_sw_vreg __devinitdata = {
+ .name = GPIO_REGULATOR_DEV_NAME,
+ .id = PM8921_GPIO_PM_TO_SYS(42),
+ .dev = {
+ .platform_data =
+ &msm_gpio_regulator_pdata[GPIO_VREG_ID_EXT_OTG_SW],
+ },
+};
+
static struct platform_device msm8960_device_rpm_regulator __devinitdata = {
.name = "rpm-regulator",
.id = -1,
@@ -1589,8 +1681,8 @@
&msm_device_saw_core0,
&msm_device_saw_core1,
&msm8960_device_ext_5v_vreg,
- &msm8960_device_ext_l2_vreg,
&msm8960_device_ssbi_pmic,
+ &msm8960_device_ext_otg_sw_vreg,
&msm8960_device_qup_spi_gsbi1,
&msm8960_device_qup_i2c_gsbi3,
&msm8960_device_qup_i2c_gsbi4,
@@ -1616,6 +1708,7 @@
#ifdef CONFIG_MSM_FAKE_BATTERY
&fish_battery_device,
#endif
+ &fmem_device,
#ifdef CONFIG_ANDROID_PMEM
#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
&android_pmem_device,
@@ -1678,6 +1771,9 @@
&msm_voip,
&msm_lpa_pcm,
&msm_compr_dsp,
+ &msm_cpudai_incall_music_rx,
+ &msm_cpudai_incall_record_rx,
+ &msm_cpudai_incall_record_tx,
#if defined(CONFIG_CRYPTO_DEV_QCRYPTO) || \
defined(CONFIG_CRYPTO_DEV_QCRYPTO_MODULE)
@@ -1703,6 +1799,7 @@
&msm_8960_q6_lpass,
&msm_8960_q6_mss_fw,
&msm_8960_q6_mss_sw,
+ &msm_8960_riva,
&msm8960_device_otg,
&msm8960_device_gadget_peripheral,
&msm_device_hsusb_host,
@@ -1737,6 +1834,9 @@
&msm_cpudai_afe_02_tx,
&msm_pcm_afe,
&msm_compr_dsp,
+ &msm_cpudai_incall_music_rx,
+ &msm_cpudai_incall_record_rx,
+ &msm_cpudai_incall_record_tx,
&msm_pcm_hostless,
&msm_bus_apps_fabric,
&msm_bus_sys_fabric,
@@ -1897,6 +1997,10 @@
},
};
+static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = {
+ .mode = MSM_PM_BOOT_CONFIG_TZ,
+};
+
#ifdef CONFIG_I2C
#define I2C_SURF 1
#define I2C_FFA (1 << 1)
@@ -1924,6 +2028,11 @@
I2C_BOARD_INFO("ov2720", 0x6C),
},
#endif
+#ifdef CONFIG_MT9M114
+ {
+ I2C_BOARD_INFO("mt9m114", 0x48),
+ },
+#endif
#ifdef CONFIG_MSM_CAMERA_FLASH_SC628A
{
I2C_BOARD_INFO("sc628a", 0x6E),
@@ -1983,6 +2092,13 @@
};
#endif /* CONFIG_ISL9519_CHARGER */
+static struct i2c_board_info liquid_io_expander_i2c_info[] __initdata = {
+ {
+ I2C_BOARD_INFO("sx1508q", 0x20),
+ .platform_data = &msm8960_sx150x_data[SX150X_LIQUID]
+ },
+};
+
static struct i2c_registry msm8960_i2c_devices[] __initdata = {
#ifdef CONFIG_MSM_CAMERA
{
@@ -2013,7 +2129,7 @@
ARRAY_SIZE(mxt_device_info),
},
{
- I2C_LIQUID,
+ I2C_FFA | I2C_LIQUID,
MSM_8960_GSBI10_QUP_I2C_BUS_ID,
sii_device_info,
ARRAY_SIZE(sii_device_info),
@@ -2024,6 +2140,12 @@
msm_isa1200_board_info,
ARRAY_SIZE(msm_isa1200_board_info),
},
+ {
+ I2C_LIQUID,
+ MSM_8960_GSBI10_QUP_I2C_BUS_ID,
+ liquid_io_expander_i2c_info,
+ ARRAY_SIZE(liquid_io_expander_i2c_info),
+ },
};
#endif /* CONFIG_I2C */
@@ -2096,7 +2218,7 @@
msm_pm_set_rpm_wakeup_irq(RPM_APCC_CPU0_WAKE_UP_IRQ);
msm_cpuidle_set_states(msm_cstates, ARRAY_SIZE(msm_cstates),
msm_pm_data);
- BUG_ON(msm_pm_boot_init(MSM_PM_BOOT_CONFIG_TZ, NULL));
+ BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
}
static void __init msm8960_rumi3_init(void)
@@ -2127,7 +2249,7 @@
msm_pm_set_rpm_wakeup_irq(RPM_APCC_CPU0_WAKE_UP_IRQ);
msm_cpuidle_set_states(msm_cstates, ARRAY_SIZE(msm_cstates),
msm_pm_data);
- BUG_ON(msm_pm_boot_init(MSM_PM_BOOT_CONFIG_TZ, NULL));
+ BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
}
static void __init msm8960_cdp_init(void)
@@ -2169,6 +2291,9 @@
spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info));
msm8960_init_pmic();
+ if ((SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2 &&
+ (machine_is_msm8960_mtp())) || machine_is_msm8960_liquid())
+ msm_isa1200_board_info[0].platform_data = &isa1200_1_pdata;
msm8960_i2c_init();
msm8960_gfx_init();
msm_spm_init(msm_spm_data, ARRAY_SIZE(msm_spm_data));
@@ -2178,6 +2303,8 @@
msm_num_footswitch_devices);
if (machine_is_msm8960_liquid())
platform_device_register(&msm8960_device_ext_3p3v_vreg);
+ if (machine_is_msm8960_cdp())
+ platform_device_register(&msm8960_device_ext_l2_vreg);
platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
msm8960_pm8921_gpio_mpp_init();
platform_add_devices(cdp_devices, ARRAY_SIZE(cdp_devices));
@@ -2197,8 +2324,7 @@
msm_cpuidle_set_states(msm_cstates, ARRAY_SIZE(msm_cstates),
msm_pm_data);
change_memory_power = &msm8960_change_memory_power;
- BUG_ON(msm_pm_boot_init(MSM_PM_BOOT_CONFIG_TZ, NULL));
-
+ BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
if (PLATFORM_IS_CHARM25())
platform_add_devices(mdm_devices, ARRAY_SIZE(mdm_devices));
}
diff --git a/arch/arm/mach-msm/board-8960.h b/arch/arm/mach-msm/board-8960.h
index 56fa3ca..125ae4d 100644
--- a/arch/arm/mach-msm/board-8960.h
+++ b/arch/arm/mach-msm/board-8960.h
@@ -18,6 +18,7 @@
#include <linux/i2c/sx150x.h>
#include <mach/irqs.h>
#include <mach/rpm-regulator.h>
+#include <mach/msm_memtypes.h>
/* Macros assume PMIC GPIOs and MPPs start at 1 */
#define PM8921_GPIO_BASE NR_GPIO_IRQS
@@ -34,6 +35,7 @@
#define GPIO_VREG_ID_EXT_5V 0
#define GPIO_VREG_ID_EXT_L2 1
#define GPIO_VREG_ID_EXT_3P3V 2
+#define GPIO_VREG_ID_EXT_OTG_SW 3
extern struct gpio_regulator_platform_data
msm_gpio_regulator_pdata[] __devinitdata;
@@ -57,12 +59,13 @@
GPIO_CAM_GP_XMT_FLASH_INT,
GPIO_CAM_GP_LED_EN1,
GPIO_CAM_GP_LED_EN2,
-
+ GPIO_LIQUID_EXPANDER_BASE = GPIO_CAM_EXPANDER_BASE + 8,
};
#endif
enum {
SX150X_CAM,
+ SX150X_LIQUID,
};
#endif
@@ -75,7 +78,7 @@
int msm8960_init_gpiomux(void);
void msm8960_allocate_fb_region(void);
void msm8960_pm8921_gpio_mpp_init(void);
-
+void msm8960_mdp_writeback(struct memtype_reserve *reserve_table);
#define PLATFORM_IS_CHARM25() \
(machine_is_msm8960_cdp() && \
(socinfo_get_platform_subtype() == 1) \
diff --git a/arch/arm/mach-msm/board-9615-gpiomux.c b/arch/arm/mach-msm/board-9615-gpiomux.c
new file mode 100644
index 0000000..4baa851
--- /dev/null
+++ b/arch/arm/mach-msm/board-9615-gpiomux.c
@@ -0,0 +1,252 @@
+/* Copyright (c) 2011, 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/init.h>
+#include <mach/gpiomux.h>
+#include <mach/board.h>
+#include <mach/gpio.h>
+#include "board-9615.h"
+
+static struct gpiomux_setting ps_hold = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting gsbi4 = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting gsbi5 = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting gsbi3 = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting gsbi3_cs1_config = {
+ .func = GPIOMUX_FUNC_4,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+#ifdef CONFIG_LTC4088_CHARGER
+static struct gpiomux_setting ltc4088_chg_cfg = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+#endif
+
+static struct gpiomux_setting sdcc2_clk_actv_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_16MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
+static struct gpiomux_setting sdcc2_cmd_data_0_3_actv_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_UP,
+};
+
+static struct gpiomux_setting sdcc2_suspend_cfg = {
+ .func = GPIOMUX_FUNC_1,
+ .drv = GPIOMUX_DRV_2MA,
+ .pull = GPIOMUX_PULL_DOWN,
+};
+
+static struct msm_gpiomux_config msm9615_sdcc2_configs[] __initdata = {
+ {
+ /* SDC2_DATA_0 */
+ .gpio = 25,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg,
+ [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg,
+ },
+ },
+ {
+ /* SDC2_DATA_1 */
+ .gpio = 26,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg,
+ [GPIOMUX_SUSPENDED] = &sdcc2_cmd_data_0_3_actv_cfg,
+ },
+ },
+ {
+ /* SDC2_DATA_2 */
+ .gpio = 27,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg,
+ [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg,
+ },
+ },
+ {
+ /* SDC2_DATA_3 */
+ .gpio = 28,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg,
+ [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg,
+ },
+ },
+ {
+ /* SDC2_CMD */
+ .gpio = 29,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg,
+ [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg,
+ },
+ },
+ {
+ /* SDC2_CLK */
+ .gpio = 30,
+ .settings = {
+ [GPIOMUX_ACTIVE] = &sdcc2_clk_actv_cfg,
+ [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg,
+ },
+ },
+};
+
+struct msm_gpiomux_config msm9615_ps_hold_config[] __initdata = {
+ {
+ .gpio = 83,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &ps_hold,
+ },
+ },
+};
+
+#ifdef CONFIG_LTC4088_CHARGER
+static struct msm_gpiomux_config
+ msm9615_ltc4088_charger_config[] __initdata = {
+ {
+ .gpio = 4,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = <c4088_chg_cfg,
+ },
+ },
+ {
+ .gpio = 6,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = <c4088_chg_cfg,
+ },
+ },
+ {
+ .gpio = 7,
+ .settings = {
+ [GPIOMUX_SUSPENDED] = <c4088_chg_cfg,
+ },
+ },
+};
+#endif
+
+struct msm_gpiomux_config msm9615_gsbi_configs[] __initdata = {
+ {
+ .gpio = 8, /* GSBI3 QUP SPI_CLK */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi3,
+ },
+ },
+ {
+ .gpio = 9, /* GSBI3 QUP SPI_CS_N */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi3,
+ },
+ },
+ {
+ .gpio = 10, /* GSBI3 QUP SPI_DATA_MISO */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi3,
+ },
+ },
+ {
+ .gpio = 11, /* GSBI3 QUP SPI_DATA_MOSI */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi3,
+ },
+ },
+ {
+ .gpio = 12, /* GSBI4 UART */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi4,
+ },
+ },
+ {
+ .gpio = 13, /* GSBI4 UART */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi4,
+ },
+ },
+ {
+ .gpio = 14, /* GSBI4 UART */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi4,
+ },
+ },
+ {
+ .gpio = 15, /* GSBI4 UART */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi4,
+ },
+ },
+ {
+ .gpio = 16, /* GSBI5 I2C QUP SCL */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi5,
+ },
+ },
+ {
+ .gpio = 17, /* GSBI5 I2C QUP SDA */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi5,
+ },
+ },
+ {
+ /* GPIO 19 can be used for I2C/UART on GSBI5 */
+ .gpio = 19, /* GSBI3 QUP SPI_CS_1 */
+ .settings = {
+ [GPIOMUX_SUSPENDED] = &gsbi3_cs1_config,
+ },
+ },
+};
+
+int __init msm9615_init_gpiomux(void)
+{
+ int rc;
+
+ rc = msm_gpiomux_init(NR_GPIO_IRQS);
+ if (rc) {
+ pr_err(KERN_ERR "msm_gpiomux_init failed %d\n", rc);
+ return rc;
+ }
+ msm_gpiomux_install(msm9615_gsbi_configs,
+ ARRAY_SIZE(msm9615_gsbi_configs));
+
+ msm_gpiomux_install(msm9615_ps_hold_config,
+ ARRAY_SIZE(msm9615_ps_hold_config));
+ msm_gpiomux_install(msm9615_sdcc2_configs,
+ ARRAY_SIZE(msm9615_sdcc2_configs));
+#ifdef CONFIG_LTC4088_CHARGER
+ msm_gpiomux_install(msm9615_ltc4088_charger_config,
+ ARRAY_SIZE(msm9615_ltc4088_charger_config));
+#endif
+
+ return 0;
+}
diff --git a/arch/arm/mach-msm/board-9615-regulator.c b/arch/arm/mach-msm/board-9615-regulator.c
index c4b7c5a..7cf4a8c 100644
--- a/arch/arm/mach-msm/board-9615-regulator.c
+++ b/arch/arm/mach-msm/board-9615-regulator.c
@@ -75,6 +75,7 @@
};
VREG_CONSUMERS(S3) = {
REGULATOR_SUPPLY("8018_s3", NULL),
+ REGULATOR_SUPPLY("wlan_vreg", "wlan_ar6000_pm_dev"),
};
VREG_CONSUMERS(S4) = {
REGULATOR_SUPPLY("8018_s4", NULL),
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index 5efafae..bfd65fb 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -14,19 +14,20 @@
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/msm_ssbi.h>
+#include <linux/memblock.h>
+#include <linux/usb/android.h>
+#include <linux/usb/msm_hsusb.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
+#include <linux/leds.h>
+#include <linux/leds-pm8xxx.h>
+#include <linux/power/ltc4088-charger.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
#include <asm/mach/mmc.h>
#include <mach/board.h>
#include <mach/msm_iomap.h>
#include <mach/gpio.h>
-#include <mach/gpiomux.h>
#include <mach/msm_spi.h>
-#include <linux/usb/android.h>
-#include <linux/usb/msm_hsusb.h>
-#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
-#include <linux/leds.h>
-#include <linux/leds-pm8xxx.h>
#include <mach/msm_bus_board.h>
#include "timer.h"
#include "devices.h"
@@ -34,7 +35,7 @@
#include "cpuidle.h"
#include "pm.h"
#include "acpuclock.h"
-#include <linux/power/ltc4088-charger.h>
+#include "pm-boot.h"
static struct pm8xxx_adc_amux pm8018_adc_channels_data[] = {
{"vcoin", CHANNEL_VCOIN, CHAN_PATH_SCALING2, AMUX_RSV1,
@@ -179,147 +180,6 @@
},
};
-static struct gpiomux_setting ps_hold = {
- .func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_NONE,
-};
-
-static struct gpiomux_setting gsbi4 = {
- .func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_NONE,
-};
-
-static struct gpiomux_setting gsbi5 = {
- .func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_NONE,
-};
-
-static struct gpiomux_setting gsbi3 = {
- .func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_NONE,
-};
-
-static struct gpiomux_setting gsbi3_cs1_config = {
- .func = GPIOMUX_FUNC_4,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_NONE,
-};
-
-#ifdef CONFIG_LTC4088_CHARGER
-static struct gpiomux_setting ltc4088_chg_cfg = {
- .func = GPIOMUX_FUNC_GPIO,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_NONE,
-};
-#endif
-
-struct msm_gpiomux_config msm9615_ps_hold_config[] __initdata = {
- {
- .gpio = 83,
- .settings = {
- [GPIOMUX_SUSPENDED] = &ps_hold,
- },
- },
-};
-
-#ifdef CONFIG_LTC4088_CHARGER
-static struct msm_gpiomux_config
- msm9615_ltc4088_charger_config[] __initdata = {
- {
- .gpio = 4,
- .settings = {
- [GPIOMUX_SUSPENDED] = <c4088_chg_cfg,
- },
- },
- {
- .gpio = 6,
- .settings = {
- [GPIOMUX_SUSPENDED] = <c4088_chg_cfg,
- },
- },
- {
- .gpio = 7,
- .settings = {
- [GPIOMUX_SUSPENDED] = <c4088_chg_cfg,
- },
- },
-};
-#endif
-
-struct msm_gpiomux_config msm9615_gsbi_configs[] __initdata = {
- {
- .gpio = 8, /* GSBI3 QUP SPI_CLK */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gsbi3,
- },
- },
- {
- .gpio = 9, /* GSBI3 QUP SPI_CS_N */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gsbi3,
- },
- },
- {
- .gpio = 10, /* GSBI3 QUP SPI_DATA_MISO */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gsbi3,
- },
- },
- {
- .gpio = 11, /* GSBI3 QUP SPI_DATA_MOSI */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gsbi3,
- },
- },
- {
- .gpio = 12, /* GSBI4 UART */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gsbi4,
- },
- },
- {
- .gpio = 13, /* GSBI4 UART */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gsbi4,
- },
- },
- {
- .gpio = 14, /* GSBI4 UART */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gsbi4,
- },
- },
- {
- .gpio = 15, /* GSBI4 UART */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gsbi4,
- },
- },
- {
- .gpio = 16, /* GSBI5 I2C QUP SCL */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gsbi5,
- },
- },
- {
- .gpio = 17, /* GSBI5 I2C QUP SDA */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gsbi5,
- },
- },
- {
- /* GPIO 19 can be used for I2C/UART on GSBI5 */
- .gpio = 19, /* GSBI3 QUP SPI_CS_1 */
- .settings = {
- [GPIOMUX_SUSPENDED] = &gsbi3_cs1_config,
- },
- },
-};
-
#if (defined(CONFIG_MMC_MSM_SDC1_SUPPORT)\
|| defined(CONFIG_MMC_MSM_SDC2_SUPPORT))
@@ -427,75 +287,6 @@
#endif
#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
-static struct gpiomux_setting sdcc2_clk_actv_cfg = {
- .func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_16MA,
- .pull = GPIOMUX_PULL_NONE,
-};
-
-static struct gpiomux_setting sdcc2_cmd_data_0_3_actv_cfg = {
- .func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_8MA,
- .pull = GPIOMUX_PULL_UP,
-};
-
-static struct gpiomux_setting sdcc2_suspend_cfg = {
- .func = GPIOMUX_FUNC_1,
- .drv = GPIOMUX_DRV_2MA,
- .pull = GPIOMUX_PULL_DOWN,
-};
-
-static struct msm_gpiomux_config msm9615_sdcc2_configs[] __initdata = {
- {
- /* SDC2_DATA_0 */
- .gpio = 25,
- .settings = {
- [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg,
- [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg,
- },
- },
- {
- /* SDC2_DATA_1 */
- .gpio = 26,
- .settings = {
- [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg,
- [GPIOMUX_SUSPENDED] = &sdcc2_cmd_data_0_3_actv_cfg,
- },
- },
- {
- /* SDC2_DATA_2 */
- .gpio = 27,
- .settings = {
- [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg,
- [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg,
- },
- },
- {
- /* SDC2_DATA_3 */
- .gpio = 28,
- .settings = {
- [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg,
- [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg,
- },
- },
- {
- /* SDC2_CMD */
- .gpio = 29,
- .settings = {
- [GPIOMUX_ACTIVE] = &sdcc2_cmd_data_0_3_actv_cfg,
- [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg,
- },
- },
- {
- /* SDC2_CLK */
- .gpio = 30,
- .settings = {
- [GPIOMUX_ACTIVE] = &sdcc2_clk_actv_cfg,
- [GPIOMUX_SUSPENDED] = &sdcc2_suspend_cfg,
- },
- },
-};
-
static struct msm_mmc_gpio sdc2_gpio_cfg[] = {
{25, "sdc2_dat_0"},
{26, "sdc2_dat_1"},
@@ -511,8 +302,6 @@
.size = ARRAY_SIZE(sdc2_gpio_cfg),
},
};
-#else
-static struct msm_gpiomux_config msm9615_sdcc2_configs[0];
#endif
static struct msm_mmc_pin_data mmc_slot_pin_data[MAX_SDCC_CONTROLLER] = {
@@ -532,12 +321,12 @@
#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
static unsigned int sdc1_sup_clk_rates[] = {
- 400000, 24000000, 48000000
+ 400000, 24000000, 48000000,
};
static struct mmc_platform_data sdc1_data = {
- .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29,
- .mmc_bus_width = MMC_CAP_4_BIT_DATA,
+ .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29,
+ .mmc_bus_width = MMC_CAP_4_BIT_DATA,
.sup_clk_table = sdc1_sup_clk_rates,
.sup_clk_cnt = ARRAY_SIZE(sdc1_sup_clk_rates),
.pclk_src_dfab = true,
@@ -550,7 +339,7 @@
#endif
.xpc_cap = 1,
.uhs_caps = (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 |
- MMC_CAP_MAX_CURRENT_400)
+ MMC_CAP_MAX_CURRENT_400),
};
static struct mmc_platform_data *msm9615_sdc1_pdata = &sdc1_data;
#else
@@ -559,7 +348,7 @@
#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
static unsigned int sdc2_sup_clk_rates[] = {
- 400000, 24000000, 48000000
+ 400000, 24000000, 48000000,
};
static struct mmc_platform_data sdc2_data = {
@@ -586,9 +375,6 @@
}
if (msm9615_sdc2_pdata) {
- msm_gpiomux_install(msm9615_sdcc2_configs,
- ARRAY_SIZE(msm9615_sdcc2_configs));
-
/* SDC2: External card slot used for WLAN */
msm_add_sdcc(2, msm9615_sdc2_pdata);
}
@@ -627,26 +413,10 @@
},
};
-static int __init gpiomux_init(void)
-{
- int rc;
-
- rc = msm_gpiomux_init(NR_GPIO_IRQS);
- if (rc) {
- pr_err(KERN_ERR "msm_gpiomux_init failed %d\n", rc);
- return rc;
- }
- msm_gpiomux_install(msm9615_gsbi_configs,
- ARRAY_SIZE(msm9615_gsbi_configs));
-
- msm_gpiomux_install(msm9615_ps_hold_config,
- ARRAY_SIZE(msm9615_ps_hold_config));
-#ifdef CONFIG_LTC4088_CHARGER
- msm_gpiomux_install(msm9615_ltc4088_charger_config,
- ARRAY_SIZE(msm9615_ltc4088_charger_config));
-#endif
- return 0;
-}
+static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = {
+ .mode = MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR,
+ .v_addr = MSM_APCS_GLB_BASE + 0x24,
+};
static void __init msm9615_init_buses(void)
{
@@ -727,6 +497,39 @@
0x13, 0x83,/* set source impedance adjustment */
-1};
+#define USB_BAM_PHY_BASE 0x12502000
+#define USB_BAM_PHY_SIZE 0x10000
+#define A2_BAM_PHY_BASE 0x124C2000
+static struct usb_bam_pipe_connect msm_usb_bam_connections[4][2] = {
+ [0][USB_TO_PEER_PERIPHERAL] = {
+ .src_phy_addr = USB_BAM_PHY_BASE,
+ .src_pipe_index = 11,
+ .dst_phy_addr = A2_BAM_PHY_BASE,
+ .dst_pipe_index = 0,
+ .data_fifo_base_offset = 0x1100,
+ .data_fifo_size = 0x600,
+ .desc_fifo_base_offset = 0x1700,
+ .desc_fifo_size = 0x300,
+ },
+ [0][PEER_PERIPHERAL_TO_USB] = {
+ .src_phy_addr = A2_BAM_PHY_BASE,
+ .src_pipe_index = 1,
+ .dst_phy_addr = USB_BAM_PHY_BASE,
+ .dst_pipe_index = 10,
+ .data_fifo_base_offset = 0xa00,
+ .data_fifo_size = 0x600,
+ .desc_fifo_base_offset = 0x1000,
+ .desc_fifo_size = 0x100,
+ },
+};
+
+static struct msm_usb_bam_platform_data msm_usb_bam_pdata = {
+ .connections = &msm_usb_bam_connections[0][0],
+ .usb_bam_phy_base = USB_BAM_PHY_BASE,
+ .usb_bam_phy_size = USB_BAM_PHY_SIZE,
+ .usb_bam_num_pipes = 32,
+};
+
static struct msm_otg_platform_data msm_otg_pdata = {
.mode = USB_OTG,
.otg_control = OTG_PHY_CONTROL,
@@ -781,6 +584,7 @@
&msm_device_otg,
&msm_device_gadget_peripheral,
&msm_device_hsusb_host,
+ &msm_device_usb_bam,
&android_usb_device,
&msm9615_device_uart_gsbi4,
&msm9615_device_ext_2p95v_vreg,
@@ -816,10 +620,15 @@
&msm9615_i2c_qup_gsbi5_pdata;
}
+static void __init msm9615_reserve(void)
+{
+ msm_pm_boot_pdata.p_addr = memblock_alloc(SZ_8, SZ_64K);
+}
+
static void __init msm9615_common_init(void)
{
msm9615_device_init();
- gpiomux_init();
+ msm9615_init_gpiomux();
msm9615_i2c_init();
regulator_suppress_info_printing();
platform_device_register(&msm9615_device_rpm_regulator);
@@ -833,6 +642,7 @@
msm_device_otg.dev.platform_data = &msm_otg_pdata;
msm_otg_pdata.phy_init_seq = shelby_phy_init_seq;
+ msm_device_usb_bam.dev.platform_data = &msm_usb_bam_pdata;
platform_add_devices(common_devices, ARRAY_SIZE(common_devices));
acpuclk_init(&acpuclk_9615_soc_data);
@@ -845,6 +655,7 @@
msm_pm_set_rpm_wakeup_irq(RPM_APCC_CPU0_WAKE_UP_IRQ);
msm_cpuidle_set_states(msm_cstates, ARRAY_SIZE(msm_cstates),
msm_pm_data);
+ BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
}
static void __init msm9615_cdp_init(void)
@@ -862,6 +673,7 @@
.init_irq = msm9615_init_irq,
.timer = &msm_timer,
.init_machine = msm9615_cdp_init,
+ .reserve = msm9615_reserve,
MACHINE_END
MACHINE_START(MSM9615_MTP, "QCT MSM9615 MTP")
@@ -869,4 +681,5 @@
.init_irq = msm9615_init_irq,
.timer = &msm_timer,
.init_machine = msm9615_mtp_init,
+ .reserve = msm9615_reserve,
MACHINE_END
diff --git a/arch/arm/mach-msm/board-9615.h b/arch/arm/mach-msm/board-9615.h
index 0f5adf0..044315a8 100644
--- a/arch/arm/mach-msm/board-9615.h
+++ b/arch/arm/mach-msm/board-9615.h
@@ -37,4 +37,5 @@
extern struct gpio_regulator_platform_data msm_gpio_regulator_pdata[];
+int msm9615_init_gpiomux(void);
#endif
diff --git a/arch/arm/mach-msm/board-fsm9xxx.c b/arch/arm/mach-msm/board-fsm9xxx.c
index 333d366..5d8d15b 100644
--- a/arch/arm/mach-msm/board-fsm9xxx.c
+++ b/arch/arm/mach-msm/board-fsm9xxx.c
@@ -51,7 +51,14 @@
#define PMIC_GPIO_SD_DET 165
#define GPIO_EPHY_RST_N 37
-
+#define GPIO_MAC_TXD_3 119
+#define GPIO_MAC_TXD_2 120
+#define GPIO_MAC_TXD_1 121
+#define GPIO_MAC_TXD_0 122
+#define GPIO_MAC_TX_EN 123
+#define GPIO_MAC_MDIO 127
+#define GPIO_MAC_MDC 128
+#define GPIO_MAC_TX_CLK 133
#define GPIO_GRFC_FTR0_0 136 /* GRFC 20 */
#define GPIO_GRFC_FTR0_1 137 /* GRFC 21 */
#define GPIO_GRFC_FTR1_0 145 /* GRFC 22 */
@@ -472,7 +479,23 @@
static struct msm_gpio phy_config_data[] = {
{ GPIO_CFG(GPIO_EPHY_RST_N, 0, GPIO_CFG_OUTPUT,
- GPIO_CFG_NO_PULL, GPIO_CFG_2MA), "MAC_RST_N" },
+ GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_RST_N" },
+ { GPIO_CFG(GPIO_MAC_TXD_3, 0, GPIO_CFG_OUTPUT,
+ GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TXD_3"},
+ { GPIO_CFG(GPIO_MAC_TXD_2, 0, GPIO_CFG_OUTPUT,
+ GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TXD_2"},
+ { GPIO_CFG(GPIO_MAC_TXD_1, 0, GPIO_CFG_OUTPUT,
+ GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TXD_1"},
+ { GPIO_CFG(GPIO_MAC_TXD_0, 0, GPIO_CFG_OUTPUT,
+ GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TXD_0"},
+ { GPIO_CFG(GPIO_MAC_TX_EN, 0, GPIO_CFG_OUTPUT,
+ GPIO_CFG_NO_PULL, GPIO_CFG_8MA), "MAC_TX_EN"},
+ { GPIO_CFG(GPIO_MAC_TX_CLK, 0, GPIO_CFG_OUTPUT,
+ GPIO_CFG_NO_PULL, GPIO_CFG_10MA), "MAC_TX_CLK"},
+ { GPIO_CFG(GPIO_MAC_MDIO, 0, GPIO_CFG_OUTPUT,
+ GPIO_CFG_NO_PULL, GPIO_CFG_6MA), "MDIO_MAC_MDIO"},
+ { GPIO_CFG(GPIO_MAC_MDC, 0, GPIO_CFG_OUTPUT,
+ GPIO_CFG_NO_PULL, GPIO_CFG_6MA), "MDC_MAC_MDC"},
};
static int __init phy_init(void)
@@ -673,6 +696,7 @@
.shared_ce_resource = QCE_NO_SHARE_CE_RESOURCE,
.hw_key_support = QCE_NO_HW_KEY_SUPPORT,
.sha_hmac = QCE_NO_SHA_HMAC_SUPPORT,
+ .bus_scale_table = NULL,
};
struct platform_device qcrypto_device = {
@@ -723,6 +747,7 @@
.shared_ce_resource = QCE_NO_SHARE_CE_RESOURCE,
.hw_key_support = QCE_NO_HW_KEY_SUPPORT,
.sha_hmac = QCE_NO_SHA_HMAC_SUPPORT,
+ .bus_scale_table = NULL,
};
static struct platform_device qcedev_device = {
diff --git a/arch/arm/mach-msm/board-msm7627a-storage.c b/arch/arm/mach-msm/board-msm7627a-storage.c
new file mode 100644
index 0000000..c3657b5
--- /dev/null
+++ b/arch/arm/mach-msm/board-msm7627a-storage.c
@@ -0,0 +1,391 @@
+/* Copyright (c) 2011, 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 <asm/mach-types.h>
+#include <asm/mach/mmc.h>
+#include <linux/regulator/consumer.h>
+#include <mach/gpio.h>
+#include <mach/gpiomux.h>
+#include <mach/board.h>
+
+#include "devices.h"
+#include "board-msm7627a.h"
+
+#if (defined(CONFIG_MMC_MSM_SDC1_SUPPORT)\
+ || defined(CONFIG_MMC_MSM_SDC2_SUPPORT)\
+ || defined(CONFIG_MMC_MSM_SDC3_SUPPORT)\
+ || defined(CONFIG_MMC_MSM_SDC4_SUPPORT))
+
+static unsigned long vreg_sts, gpio_sts;
+
+struct sdcc_gpio {
+ struct msm_gpio *cfg_data;
+ uint32_t size;
+ struct msm_gpio *sleep_cfg_data;
+};
+
+/**
+ * Due to insufficient drive strengths for SDC GPIO lines some old versioned
+ * SD/MMC cards may cause data CRC errors. Hence, set optimal values
+ * for SDC slots based on timing closure and marginality. SDC1 slot
+ * require higher value since it should handle bad signal quality due
+ * to size of T-flash adapters.
+ */
+static struct msm_gpio sdc1_cfg_data[] = {
+ {GPIO_CFG(51, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
+ "sdc1_dat_3"},
+ {GPIO_CFG(52, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
+ "sdc1_dat_2"},
+ {GPIO_CFG(53, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
+ "sdc1_dat_1"},
+ {GPIO_CFG(54, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
+ "sdc1_dat_0"},
+ {GPIO_CFG(55, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
+ "sdc1_cmd"},
+ {GPIO_CFG(56, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_14MA),
+ "sdc1_clk"},
+};
+
+static struct msm_gpio sdc2_cfg_data[] = {
+ {GPIO_CFG(62, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ "sdc2_clk"},
+ {GPIO_CFG(63, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc2_cmd"},
+ {GPIO_CFG(64, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc2_dat_3"},
+ {GPIO_CFG(65, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc2_dat_2"},
+ {GPIO_CFG(66, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc2_dat_1"},
+ {GPIO_CFG(67, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc2_dat_0"},
+};
+
+static struct msm_gpio sdc2_sleep_cfg_data[] = {
+ {GPIO_CFG(62, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
+ "sdc2_clk"},
+ {GPIO_CFG(63, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
+ "sdc2_cmd"},
+ {GPIO_CFG(64, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
+ "sdc2_dat_3"},
+ {GPIO_CFG(65, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
+ "sdc2_dat_2"},
+ {GPIO_CFG(66, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
+ "sdc2_dat_1"},
+ {GPIO_CFG(67, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
+ "sdc2_dat_0"},
+};
+static struct msm_gpio sdc3_cfg_data[] = {
+ {GPIO_CFG(88, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ "sdc3_clk"},
+ {GPIO_CFG(89, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc3_cmd"},
+ {GPIO_CFG(90, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc3_dat_3"},
+ {GPIO_CFG(91, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc3_dat_2"},
+ {GPIO_CFG(92, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc3_dat_1"},
+ {GPIO_CFG(93, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc3_dat_0"},
+#ifdef CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT
+ {GPIO_CFG(19, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc3_dat_7"},
+ {GPIO_CFG(20, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc3_dat_6"},
+ {GPIO_CFG(21, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc3_dat_5"},
+ {GPIO_CFG(108, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc3_dat_4"},
+#endif
+};
+
+static struct msm_gpio sdc4_cfg_data[] = {
+ {GPIO_CFG(19, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc4_dat_3"},
+ {GPIO_CFG(20, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc4_dat_2"},
+ {GPIO_CFG(21, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc4_dat_1"},
+ {GPIO_CFG(107, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc4_cmd"},
+ {GPIO_CFG(108, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
+ "sdc4_dat_0"},
+ {GPIO_CFG(109, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
+ "sdc4_clk"},
+};
+
+static struct sdcc_gpio sdcc_cfg_data[] = {
+ {
+ .cfg_data = sdc1_cfg_data,
+ .size = ARRAY_SIZE(sdc1_cfg_data),
+ },
+ {
+ .cfg_data = sdc2_cfg_data,
+ .size = ARRAY_SIZE(sdc2_cfg_data),
+ .sleep_cfg_data = sdc2_sleep_cfg_data,
+ },
+ {
+ .cfg_data = sdc3_cfg_data,
+ .size = ARRAY_SIZE(sdc3_cfg_data),
+ },
+ {
+ .cfg_data = sdc4_cfg_data,
+ .size = ARRAY_SIZE(sdc4_cfg_data),
+ },
+};
+
+static int gpio_sdc1_hw_det = 85;
+static void gpio_sdc1_config(void)
+{
+ if (machine_is_msm7627a_qrd1())
+ gpio_sdc1_hw_det = 42;
+}
+
+static struct regulator *sdcc_vreg_data[ARRAY_SIZE(sdcc_cfg_data)];
+static int msm_sdcc_setup_gpio(int dev_id, unsigned int enable)
+{
+ int rc = 0;
+ struct sdcc_gpio *curr;
+
+ curr = &sdcc_cfg_data[dev_id - 1];
+ if (!(test_bit(dev_id, &gpio_sts)^enable))
+ return rc;
+
+ if (enable) {
+ set_bit(dev_id, &gpio_sts);
+ rc = msm_gpios_request_enable(curr->cfg_data, curr->size);
+ if (rc)
+ pr_err("%s: Failed to turn on GPIOs for slot %d\n",
+ __func__, dev_id);
+ } else {
+ clear_bit(dev_id, &gpio_sts);
+ if (curr->sleep_cfg_data) {
+ rc = msm_gpios_enable(curr->sleep_cfg_data, curr->size);
+ msm_gpios_free(curr->sleep_cfg_data, curr->size);
+ return rc;
+ }
+ msm_gpios_disable_free(curr->cfg_data, curr->size);
+ }
+ return rc;
+}
+
+static int msm_sdcc_setup_vreg(int dev_id, unsigned int enable)
+{
+ int rc = 0;
+ struct regulator *curr = sdcc_vreg_data[dev_id - 1];
+
+ if (test_bit(dev_id, &vreg_sts) == enable)
+ return 0;
+
+ if (!curr)
+ return -ENODEV;
+
+ if (IS_ERR(curr))
+ return PTR_ERR(curr);
+
+ if (enable) {
+ set_bit(dev_id, &vreg_sts);
+
+ rc = regulator_enable(curr);
+ if (rc)
+ pr_err("%s: could not enable regulator: %d\n",
+ __func__, rc);
+ } else {
+ clear_bit(dev_id, &vreg_sts);
+
+ rc = regulator_disable(curr);
+ if (rc)
+ pr_err("%s: could not disable regulator: %d\n",
+ __func__, rc);
+ }
+ return rc;
+}
+
+static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd)
+{
+ int rc = 0;
+ struct platform_device *pdev;
+
+ pdev = container_of(dv, struct platform_device, dev);
+
+ rc = msm_sdcc_setup_gpio(pdev->id, !!vdd);
+ if (rc)
+ goto out;
+
+ rc = msm_sdcc_setup_vreg(pdev->id, !!vdd);
+out:
+ return rc;
+}
+
+#if defined(CONFIG_MMC_MSM_SDC1_SUPPORT) \
+ && defined(CONFIG_MMC_MSM_CARD_HW_DETECTION)
+static unsigned int msm7627a_sdcc_slot_status(struct device *dev)
+{
+ int status;
+
+ status = gpio_tlmm_config(GPIO_CFG(gpio_sdc1_hw_det, 2, GPIO_CFG_INPUT,
+ GPIO_CFG_PULL_UP, GPIO_CFG_8MA),
+ GPIO_CFG_ENABLE);
+ if (status)
+ pr_err("%s:Failed to configure tlmm for GPIO %d\n", __func__,
+ gpio_sdc1_hw_det);
+
+ status = gpio_request(gpio_sdc1_hw_det, "SD_HW_Detect");
+ if (status) {
+ pr_err("%s:Failed to request GPIO %d\n", __func__,
+ gpio_sdc1_hw_det);
+ } else {
+ status = gpio_direction_input(gpio_sdc1_hw_det);
+ if (!status) {
+ if (machine_is_msm7627a_qrd1())
+ status = !gpio_get_value(gpio_sdc1_hw_det);
+ else
+ status = gpio_get_value(gpio_sdc1_hw_det);
+ }
+ gpio_free(gpio_sdc1_hw_det);
+ }
+ return status;
+}
+#endif
+
+#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
+static struct mmc_platform_data sdc1_plat_data = {
+ .ocr_mask = MMC_VDD_28_29,
+ .translate_vdd = msm_sdcc_setup_power,
+ .mmc_bus_width = MMC_CAP_4_BIT_DATA,
+ .msmsdcc_fmin = 144000,
+ .msmsdcc_fmid = 24576000,
+ .msmsdcc_fmax = 49152000,
+#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION
+ .status = msm7627a_sdcc_slot_status,
+ .irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+#endif
+};
+#endif
+
+#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
+static struct mmc_platform_data sdc2_plat_data = {
+ /*
+ * SDC2 supports only 1.8V, claim for 2.85V range is just
+ * for allowing buggy cards who advertise 2.8V even though
+ * they can operate at 1.8V supply.
+ */
+ .ocr_mask = MMC_VDD_28_29 | MMC_VDD_165_195,
+ .translate_vdd = msm_sdcc_setup_power,
+ .mmc_bus_width = MMC_CAP_4_BIT_DATA,
+#ifdef CONFIG_MMC_MSM_SDIO_SUPPORT
+ .sdiowakeup_irq = MSM_GPIO_TO_INT(66),
+#endif
+ .msmsdcc_fmin = 144000,
+ .msmsdcc_fmid = 24576000,
+ .msmsdcc_fmax = 49152000,
+#ifdef CONFIG_MMC_MSM_SDC2_DUMMY52_REQUIRED
+ .dummy52_required = 1,
+#endif
+};
+#endif
+
+#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
+static struct mmc_platform_data sdc3_plat_data = {
+ .ocr_mask = MMC_VDD_28_29,
+ .translate_vdd = msm_sdcc_setup_power,
+#ifdef CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT
+ .mmc_bus_width = MMC_CAP_8_BIT_DATA,
+#else
+ .mmc_bus_width = MMC_CAP_4_BIT_DATA,
+#endif
+ .msmsdcc_fmin = 144000,
+ .msmsdcc_fmid = 24576000,
+ .msmsdcc_fmax = 49152000,
+ .nonremovable = 1,
+};
+#endif
+
+#if (defined(CONFIG_MMC_MSM_SDC4_SUPPORT)\
+ && !defined(CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT))
+static struct mmc_platform_data sdc4_plat_data = {
+ .ocr_mask = MMC_VDD_28_29,
+ .translate_vdd = msm_sdcc_setup_power,
+ .mmc_bus_width = MMC_CAP_4_BIT_DATA,
+ .msmsdcc_fmin = 144000,
+ .msmsdcc_fmid = 24576000,
+ .msmsdcc_fmax = 49152000,
+};
+#endif
+
+static int __init mmc_regulator_init(int sdcc_no, const char *supply, int uV)
+{
+ int rc;
+
+ BUG_ON(sdcc_no < 1 || sdcc_no > 4);
+
+ sdcc_no--;
+
+ sdcc_vreg_data[sdcc_no] = regulator_get(NULL, supply);
+
+ if (IS_ERR(sdcc_vreg_data[sdcc_no])) {
+ rc = PTR_ERR(sdcc_vreg_data[sdcc_no]);
+ pr_err("%s: could not get regulator \"%s\": %d\n",
+ __func__, supply, rc);
+ goto out;
+ }
+
+ rc = regulator_set_voltage(sdcc_vreg_data[sdcc_no], uV, uV);
+
+ if (rc) {
+ pr_err("%s: could not set voltage for \"%s\" to %d uV: %d\n",
+ __func__, supply, uV, rc);
+ goto reg_free;
+ }
+
+ return rc;
+
+reg_free:
+ regulator_put(sdcc_vreg_data[sdcc_no]);
+out:
+ sdcc_vreg_data[sdcc_no] = NULL;
+ return rc;
+}
+
+void __init msm7627a_init_mmc(void)
+{
+ /* eMMC slot */
+#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
+ if (mmc_regulator_init(3, "emmc", 3000000))
+ return;
+ msm_add_sdcc(3, &sdc3_plat_data);
+#endif
+ /* Micro-SD slot */
+#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
+ gpio_sdc1_config();
+ if (mmc_regulator_init(1, "mmc", 2850000))
+ return;
+ sdc1_plat_data.status_irq = MSM_GPIO_TO_INT(gpio_sdc1_hw_det);
+ msm_add_sdcc(1, &sdc1_plat_data);
+#endif
+ /* SDIO WLAN slot */
+#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
+ if (mmc_regulator_init(2, "mmc", 2850000))
+ return;
+ msm_add_sdcc(2, &sdc2_plat_data);
+#endif
+ /* Not Used */
+#if (defined(CONFIG_MMC_MSM_SDC4_SUPPORT)\
+ && !defined(CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT))
+ if (mmc_regulator_init(4, "mmc", 2850000))
+ return;
+ msm_add_sdcc(4, &sdc4_plat_data);
+#endif
+}
+#endif
diff --git a/arch/arm/mach-msm/board-msm7627a.h b/arch/arm/mach-msm/board-msm7627a.h
new file mode 100644
index 0000000..6b22c71
--- /dev/null
+++ b/arch/arm/mach-msm/board-msm7627a.h
@@ -0,0 +1,18 @@
+/* Copyright (c) 2011, 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 __ARCH_ARM_MACH_MSM_BOARD_7627A__
+#define __ARCH_ARM_MACH_MSM_BOARD_7627A__
+
+void __init msm7627a_init_mmc(void);
+
+#endif
diff --git a/arch/arm/mach-msm/board-msm7x27.c b/arch/arm/mach-msm/board-msm7x27.c
index 15705ec..a09485a 100644
--- a/arch/arm/mach-msm/board-msm7x27.c
+++ b/arch/arm/mach-msm/board-msm7x27.c
@@ -1625,6 +1625,11 @@
},
};
+static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = {
+ .mode = MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT,
+ .v_addr = (uint32_t *)PAGE_OFFSET,
+};
+
static void
msm_i2c_gpio_config(int iface, int config_type)
{
@@ -1752,10 +1757,7 @@
}
}
#endif
- if (cpu_is_msm7x27())
- acpuclk_init(&acpuclk_7x27_soc_data);
- else
- acpuclk_init(&acpuclk_7201_soc_data);
+ acpuclk_init(&acpuclk_7x27_soc_data);
usb_mpp_init();
@@ -1818,8 +1820,8 @@
msm_pm_set_platform_data(msm7x25_pm_data,
ARRAY_SIZE(msm7x25_pm_data));
- BUG_ON(msm_pm_boot_init(MSM_PM_BOOT_CONFIG_RESET_VECTOR,
- ioremap(0, PAGE_SIZE)));
+ BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
+
msm7x27_wlan_init();
}
diff --git a/arch/arm/mach-msm/board-msm7x27a.c b/arch/arm/mach-msm/board-msm7x27a.c
index f369c6c..dcb12bb3 100644
--- a/arch/arm/mach-msm/board-msm7x27a.c
+++ b/arch/arm/mach-msm/board-msm7x27a.c
@@ -54,6 +54,7 @@
#include <mach/rpc_server_handset.h>
#include <mach/socinfo.h>
#include "pm-boot.h"
+#include "board-msm7627a.h"
#define PMEM_KERNEL_EBI1_SIZE 0x3A000
#define MSM_PMEM_AUDIO_SIZE 0x5B000
@@ -554,7 +555,7 @@
};
static struct bt_vreg_info bt_vregs[] = {
{"msme1", 2, 1800000, 1800000, 0, NULL},
- {"bt", 21, 2900000, 3050000, 1, NULL}
+ {"bt", 21, 2900000, 3300000, 1, NULL}
};
static int bahama_bt(int on)
@@ -1323,362 +1324,6 @@
.resource = smc91x_resources,
};
-#if (defined(CONFIG_MMC_MSM_SDC1_SUPPORT)\
- || defined(CONFIG_MMC_MSM_SDC2_SUPPORT)\
- || defined(CONFIG_MMC_MSM_SDC3_SUPPORT)\
- || defined(CONFIG_MMC_MSM_SDC4_SUPPORT))
-
-static unsigned long vreg_sts, gpio_sts;
-
-struct sdcc_gpio {
- struct msm_gpio *cfg_data;
- uint32_t size;
- struct msm_gpio *sleep_cfg_data;
-};
-
-/**
- * Due to insufficient drive strengths for SDC GPIO lines some old versioned
- * SD/MMC cards may cause data CRC errors. Hence, set optimal values
- * for SDC slots based on timing closure and marginality. SDC1 slot
- * require higher value since it should handle bad signal quality due
- * to size of T-flash adapters.
- */
-static struct msm_gpio sdc1_cfg_data[] = {
- {GPIO_CFG(51, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
- "sdc1_dat_3"},
- {GPIO_CFG(52, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
- "sdc1_dat_2"},
- {GPIO_CFG(53, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
- "sdc1_dat_1"},
- {GPIO_CFG(54, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
- "sdc1_dat_0"},
- {GPIO_CFG(55, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
- "sdc1_cmd"},
- {GPIO_CFG(56, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_14MA),
- "sdc1_clk"},
-};
-
-static struct msm_gpio sdc2_cfg_data[] = {
- {GPIO_CFG(62, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
- "sdc2_clk"},
- {GPIO_CFG(63, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc2_cmd"},
- {GPIO_CFG(64, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc2_dat_3"},
- {GPIO_CFG(65, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc2_dat_2"},
- {GPIO_CFG(66, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc2_dat_1"},
- {GPIO_CFG(67, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc2_dat_0"},
-};
-
-static struct msm_gpio sdc2_sleep_cfg_data[] = {
- {GPIO_CFG(62, 0, GPIO_CFG_INPUT, GPIO_CFG_NO_PULL, GPIO_CFG_2MA),
- "sdc2_clk"},
- {GPIO_CFG(63, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
- "sdc2_cmd"},
- {GPIO_CFG(64, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
- "sdc2_dat_3"},
- {GPIO_CFG(65, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
- "sdc2_dat_2"},
- {GPIO_CFG(66, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
- "sdc2_dat_1"},
- {GPIO_CFG(67, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_UP, GPIO_CFG_2MA),
- "sdc2_dat_0"},
-};
-static struct msm_gpio sdc3_cfg_data[] = {
- {GPIO_CFG(88, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
- "sdc3_clk"},
- {GPIO_CFG(89, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_cmd"},
- {GPIO_CFG(90, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_3"},
- {GPIO_CFG(91, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_2"},
- {GPIO_CFG(92, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_1"},
- {GPIO_CFG(93, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_0"},
-#ifdef CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT
- {GPIO_CFG(19, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_7"},
- {GPIO_CFG(20, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_6"},
- {GPIO_CFG(21, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_5"},
- {GPIO_CFG(108, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_4"},
-#endif
-};
-
-static struct msm_gpio sdc4_cfg_data[] = {
- {GPIO_CFG(19, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc4_dat_3"},
- {GPIO_CFG(20, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc4_dat_2"},
- {GPIO_CFG(21, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc4_dat_1"},
- {GPIO_CFG(107, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc4_cmd"},
- {GPIO_CFG(108, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc4_dat_0"},
- {GPIO_CFG(109, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
- "sdc4_clk"},
-};
-
-static struct sdcc_gpio sdcc_cfg_data[] = {
- {
- .cfg_data = sdc1_cfg_data,
- .size = ARRAY_SIZE(sdc1_cfg_data),
- },
- {
- .cfg_data = sdc2_cfg_data,
- .size = ARRAY_SIZE(sdc2_cfg_data),
- .sleep_cfg_data = sdc2_sleep_cfg_data,
- },
- {
- .cfg_data = sdc3_cfg_data,
- .size = ARRAY_SIZE(sdc3_cfg_data),
- },
- {
- .cfg_data = sdc4_cfg_data,
- .size = ARRAY_SIZE(sdc4_cfg_data),
- },
-};
-
-static struct regulator *sdcc_vreg_data[ARRAY_SIZE(sdcc_cfg_data)];
-
-static int msm_sdcc_setup_gpio(int dev_id, unsigned int enable)
-{
- int rc = 0;
- struct sdcc_gpio *curr;
-
- curr = &sdcc_cfg_data[dev_id - 1];
- if (!(test_bit(dev_id, &gpio_sts)^enable))
- return rc;
-
- if (enable) {
- set_bit(dev_id, &gpio_sts);
- rc = msm_gpios_request_enable(curr->cfg_data, curr->size);
- if (rc)
- pr_err("%s: Failed to turn on GPIOs for slot %d\n",
- __func__, dev_id);
- } else {
- clear_bit(dev_id, &gpio_sts);
- if (curr->sleep_cfg_data) {
- rc = msm_gpios_enable(curr->sleep_cfg_data, curr->size);
- msm_gpios_free(curr->sleep_cfg_data, curr->size);
- return rc;
- }
- msm_gpios_disable_free(curr->cfg_data, curr->size);
- }
- return rc;
-}
-
-static int msm_sdcc_setup_vreg(int dev_id, unsigned int enable)
-{
- int rc = 0;
- struct regulator *curr = sdcc_vreg_data[dev_id - 1];
-
- if (test_bit(dev_id, &vreg_sts) == enable)
- return 0;
-
- if (!curr)
- return -ENODEV;
-
- if (IS_ERR(curr))
- return PTR_ERR(curr);
-
- if (enable) {
- set_bit(dev_id, &vreg_sts);
-
- rc = regulator_enable(curr);
- if (rc)
- pr_err("%s: could not enable regulator: %d\n",
- __func__, rc);
- } else {
- clear_bit(dev_id, &vreg_sts);
-
- rc = regulator_disable(curr);
- if (rc)
- pr_err("%s: could not disable regulator: %d\n",
- __func__, rc);
- }
- return rc;
-}
-
-static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd)
-{
- int rc = 0;
- struct platform_device *pdev;
-
- pdev = container_of(dv, struct platform_device, dev);
-
- rc = msm_sdcc_setup_gpio(pdev->id, !!vdd);
- if (rc)
- goto out;
-
- rc = msm_sdcc_setup_vreg(pdev->id, !!vdd);
-out:
- return rc;
-}
-
-#define GPIO_SDC1_HW_DET 85
-
-#if defined(CONFIG_MMC_MSM_SDC1_SUPPORT) \
- && defined(CONFIG_MMC_MSM_CARD_HW_DETECTION)
-static unsigned int msm7x2xa_sdcc_slot_status(struct device *dev)
-{
- int status;
-
- status = gpio_tlmm_config(GPIO_CFG(GPIO_SDC1_HW_DET, 2, GPIO_CFG_INPUT,
- GPIO_CFG_PULL_UP, GPIO_CFG_8MA), GPIO_CFG_ENABLE);
- if (status)
- pr_err("%s:Failed to configure tlmm for GPIO %d\n", __func__,
- GPIO_SDC1_HW_DET);
-
- status = gpio_request(GPIO_SDC1_HW_DET, "SD_HW_Detect");
- if (status) {
- pr_err("%s:Failed to request GPIO %d\n", __func__,
- GPIO_SDC1_HW_DET);
- } else {
- status = gpio_direction_input(GPIO_SDC1_HW_DET);
- if (!status)
- status = gpio_get_value(GPIO_SDC1_HW_DET);
- gpio_free(GPIO_SDC1_HW_DET);
- }
- return status;
-}
-#endif
-
-#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
-static struct mmc_platform_data sdc1_plat_data = {
- .ocr_mask = MMC_VDD_28_29,
- .translate_vdd = msm_sdcc_setup_power,
- .mmc_bus_width = MMC_CAP_4_BIT_DATA,
- .msmsdcc_fmin = 144000,
- .msmsdcc_fmid = 24576000,
- .msmsdcc_fmax = 49152000,
-#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION
- .status = msm7x2xa_sdcc_slot_status,
- .status_irq = MSM_GPIO_TO_INT(GPIO_SDC1_HW_DET),
- .irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-#endif
-};
-#endif
-
-#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
-static struct mmc_platform_data sdc2_plat_data = {
- /*
- * SDC2 supports only 1.8V, claim for 2.85V range is just
- * for allowing buggy cards who advertise 2.8V even though
- * they can operate at 1.8V supply.
- */
- .ocr_mask = MMC_VDD_28_29 | MMC_VDD_165_195,
- .translate_vdd = msm_sdcc_setup_power,
- .mmc_bus_width = MMC_CAP_4_BIT_DATA,
-#ifdef CONFIG_MMC_MSM_SDIO_SUPPORT
- .sdiowakeup_irq = MSM_GPIO_TO_INT(66),
-#endif
- .msmsdcc_fmin = 144000,
- .msmsdcc_fmid = 24576000,
- .msmsdcc_fmax = 49152000,
-};
-#endif
-
-#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
-static struct mmc_platform_data sdc3_plat_data = {
- .ocr_mask = MMC_VDD_28_29,
- .translate_vdd = msm_sdcc_setup_power,
-#ifdef CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT
- .mmc_bus_width = MMC_CAP_8_BIT_DATA,
-#else
- .mmc_bus_width = MMC_CAP_4_BIT_DATA,
-#endif
- .msmsdcc_fmin = 144000,
- .msmsdcc_fmid = 24576000,
- .msmsdcc_fmax = 49152000,
- .nonremovable = 1,
-};
-#endif
-
-#if (defined(CONFIG_MMC_MSM_SDC4_SUPPORT)\
- && !defined(CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT))
-static struct mmc_platform_data sdc4_plat_data = {
- .ocr_mask = MMC_VDD_28_29,
- .translate_vdd = msm_sdcc_setup_power,
- .mmc_bus_width = MMC_CAP_4_BIT_DATA,
- .msmsdcc_fmin = 144000,
- .msmsdcc_fmid = 24576000,
- .msmsdcc_fmax = 49152000,
-};
-#endif
-
-static int __init mmc_regulator_init(int sdcc_no, const char *supply, int uV)
-{
- int rc;
-
- BUG_ON(sdcc_no < 1 || sdcc_no > 4);
-
- sdcc_no--;
-
- sdcc_vreg_data[sdcc_no] = regulator_get(NULL, supply);
-
- if (IS_ERR(sdcc_vreg_data[sdcc_no])) {
- rc = PTR_ERR(sdcc_vreg_data[sdcc_no]);
- pr_err("%s: could not get regulator \"%s\": %d\n",
- __func__, supply, rc);
- goto out;
- }
-
- rc = regulator_set_voltage(sdcc_vreg_data[sdcc_no], uV, uV);
-
- if (rc) {
- pr_err("%s: could not set voltage for \"%s\" to %d uV: %d\n",
- __func__, supply, uV, rc);
- goto reg_free;
- }
-
- return rc;
-
-reg_free:
- regulator_put(sdcc_vreg_data[sdcc_no]);
-out:
- sdcc_vreg_data[sdcc_no] = NULL;
- return rc;
-}
-
-static void __init msm7x27a_init_mmc(void)
-{
- /* eMMC slot */
-#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
- if (mmc_regulator_init(3, "emmc", 3000000))
- return;
- msm_add_sdcc(3, &sdc3_plat_data);
-#endif
- /* Micro-SD slot */
-#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
- if (mmc_regulator_init(1, "mmc", 2850000))
- return;
- msm_add_sdcc(1, &sdc1_plat_data);
-#endif
- /* SDIO WLAN slot */
-#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
- if (mmc_regulator_init(2, "mmc", 2850000))
- return;
- msm_add_sdcc(2, &sdc2_plat_data);
-#endif
- /* Not Used */
-#if (defined(CONFIG_MMC_MSM_SDC4_SUPPORT)\
- && !defined(CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT))
- if (mmc_regulator_init(4, "mmc", 2850000))
- return;
- msm_add_sdcc(4, &sdc4_plat_data);
-#endif
-}
-#endif
-
#ifdef CONFIG_SERIAL_MSM_HS
static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = {
.inject_rx_on_wakeup = 1,
@@ -1720,6 +1365,11 @@
},
};
+static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = {
+ .mode = MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT,
+ .v_addr = (uint32_t *)PAGE_OFFSET,
+};
+
static struct android_pmem_platform_data android_pmem_adsp_pdata = {
.name = "pmem_adsp",
.allocator_type = PMEM_ALLOCATORTYPE_BITMAP,
@@ -3294,7 +2944,7 @@
/* Ensure ar6000pm device is registered before MMC/SDC */
msm7x27a_init_ar6000pm();
#ifdef CONFIG_MMC_MSM
- msm7x27a_init_mmc();
+ msm7627a_init_mmc();
#endif
msm_fb_add_devices();
#ifdef CONFIG_USB_EHCI_MSM_72K
@@ -3303,8 +2953,7 @@
msm_pm_set_platform_data(msm7x27a_pm_data,
ARRAY_SIZE(msm7x27a_pm_data));
- BUG_ON(msm_pm_boot_init(MSM_PM_BOOT_CONFIG_RESET_VECTOR,
- ioremap(0, PAGE_SIZE)));
+ BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
#if defined(CONFIG_I2C) && defined(CONFIG_GPIO_SX150X)
register_i2c_devices();
diff --git a/arch/arm/mach-msm/board-msm7x30.c b/arch/arm/mach-msm/board-msm7x30.c
index 9ba92ed..883404f 100644
--- a/arch/arm/mach-msm/board-msm7x30.c
+++ b/arch/arm/mach-msm/board-msm7x30.c
@@ -2948,6 +2948,11 @@
},
};
+static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = {
+ .mode = MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT,
+ .v_addr = (uint32_t *)PAGE_OFFSET,
+};
+
static struct resource qsd_spi_resources[] = {
{
.name = "spi_irq_in",
@@ -3857,6 +3862,8 @@
.shared_ce_resource = QCE_SHARE_CE_RESOURCE,
.hw_key_support = QCE_HW_KEY_SUPPORT,
.sha_hmac = QCE_SHA_HMAC_SUPPORT,
+ /* Bus Scaling declaration*/
+ .bus_scale_table = NULL,
};
static struct platform_device qcrypto_device = {
@@ -3879,6 +3886,8 @@
.shared_ce_resource = QCE_SHARE_CE_RESOURCE,
.hw_key_support = QCE_HW_KEY_SUPPORT,
.sha_hmac = QCE_SHA_HMAC_SUPPORT,
+ /* Bus Scaling declaration*/
+ .bus_scale_table = NULL,
};
static struct platform_device qcedev_device = {
.name = "qce",
@@ -6710,8 +6719,7 @@
hdmi_init_regs();
msm_fb_add_devices();
msm_pm_set_platform_data(msm_pm_data, ARRAY_SIZE(msm_pm_data));
- BUG_ON(msm_pm_boot_init(MSM_PM_BOOT_CONFIG_RESET_VECTOR,
- (uint32_t *)PAGE_OFFSET));
+ BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
msm_device_i2c_init();
msm_device_i2c_2_init();
qup_device_i2c_init();
diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c
index 765f49e..53b0fc0 100644
--- a/arch/arm/mach-msm/board-msm8x60.c
+++ b/arch/arm/mach-msm/board-msm8x60.c
@@ -602,6 +602,7 @@
.shared_ce_resource = QCE_SHARE_CE_RESOURCE,
.hw_key_support = QCE_HW_KEY_SUPPORT,
.sha_hmac = QCE_SHA_HMAC_SUPPORT,
+ .bus_scale_table = NULL,
};
static struct platform_device qcrypto_device = {
@@ -624,6 +625,7 @@
.shared_ce_resource = QCE_SHARE_CE_RESOURCE,
.hw_key_support = QCE_HW_KEY_SUPPORT,
.sha_hmac = QCE_SHA_HMAC_SUPPORT,
+ .bus_scale_table = NULL,
};
static struct platform_device qcedev_device = {
@@ -915,6 +917,10 @@
},
};
+static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = {
+ .mode = MSM_PM_BOOT_CONFIG_TZ,
+};
+
#if defined(CONFIG_USB_PEHCI_HCD) || defined(CONFIG_USB_PEHCI_HCD_MODULE)
#define ISP1763_INT_GPIO 117
@@ -2623,23 +2629,12 @@
#define MSM_FB_EXT_BUFT_SIZE 0
#endif
-#ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK
-/* width x height x 3 bpp x 2 frame buffer */
-#define MSM_FB_WRITEBACK_SIZE (1024 * 600 * 3 * 2)
-#define MSM_FB_WRITEBACK_OFFSET \
- (MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE)
-#else
-#define MSM_FB_WRITEBACK_SIZE 0
-#define MSM_FB_WRITEBACK_OFFSET 0
-#endif
-
#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
/* 4 bpp x 2 page HDMI case */
#define MSM_FB_SIZE roundup((1920 * 1088 * 4 * 2), 4096)
#else
/* Note: must be multiple of 4096 */
#define MSM_FB_SIZE roundup(MSM_FB_PRIM_BUF_SIZE + MSM_FB_EXT_BUF_SIZE + \
- MSM_FB_WRITEBACK_SIZE + \
MSM_FB_DSUB_PMEM_ADDER, 4096)
#endif
@@ -2649,10 +2644,17 @@
#define MSM_PMEM_SF_SIZE 0x4000000 /* 64 Mbytes */
#endif
-static int writeback_offset(void)
-{
- return MSM_FB_WRITEBACK_OFFSET;
-}
+#ifdef CONFIG_FB_MSM_OVERLAY0_WRITEBACK
+#define MSM_FB_OVERLAY0_WRITEBACK_SIZE roundup((1376 * 768 * 3 * 2), 4096)
+#else
+#define MSM_FB_OVERLAY0_WRITEBACK_SIZE (0)
+#endif /* CONFIG_FB_MSM_OVERLAY0_WRITEBACK */
+
+#ifdef CONFIG_FB_MSM_OVERLAY1_WRITEBACK
+#define MSM_FB_OVERLAY1_WRITEBACK_SIZE roundup((1920 * 1088 * 3 * 2), 4096)
+#else
+#define MSM_FB_OVERLAY1_WRITEBACK_SIZE (0)
+#endif /* CONFIG_FB_MSM_OVERLAY1_WRITEBACK */
#define MSM_PMEM_KERNEL_EBI1_SIZE 0x600000
#define MSM_PMEM_ADSP_SIZE 0x2000000
@@ -2857,36 +2859,39 @@
}, \
.num_paths = 1, \
}
-#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
-static struct msm_bus_paths pmem_smi_table[] = {
+
+static struct msm_bus_paths mem_smi_table[] = {
[0] = PMEM_BUS_WIDTH(0), /* Off */
[1] = PMEM_BUS_WIDTH(1), /* On */
};
static struct msm_bus_scale_pdata smi_client_pdata = {
- .usecase = pmem_smi_table,
- .num_usecases = ARRAY_SIZE(pmem_smi_table),
- .name = "pmem_smi",
+ .usecase = mem_smi_table,
+ .num_usecases = ARRAY_SIZE(mem_smi_table),
+ .name = "mem_smi",
};
-void request_smi_region(void *data)
+int request_smi_region(void *data)
{
int bus_id = (int) data;
msm_bus_scale_client_update_request(bus_id, 1);
+ return 0;
}
-void release_smi_region(void *data)
+int release_smi_region(void *data)
{
int bus_id = (int) data;
msm_bus_scale_client_update_request(bus_id, 0);
+ return 0;
}
void *setup_smi_region(void)
{
return (void *)msm_bus_scale_register_client(&smi_client_pdata);
}
+#ifndef CONFIG_MSM_MULTIMEDIA_USE_ION
static struct android_pmem_platform_data android_pmem_smipool_pdata = {
.name = "pmem_smipool",
.allocator_type = PMEM_ALLOCATORTYPE_BITMAP,
@@ -5082,6 +5087,7 @@
static struct platform_device *surf_devices[] __initdata = {
&msm_device_smd,
&msm_device_uart_dm12,
+ &msm_pil_q6v3,
#ifdef CONFIG_I2C_QUP
&msm_gsbi3_qup_i2c_device,
&msm_gsbi4_qup_i2c_device,
@@ -5263,6 +5269,9 @@
.name = ION_SMI_HEAP_NAME,
.size = MSM_ION_SMI_SIZE,
.memory_type = ION_SMI_TYPE,
+ .request_region = request_smi_region,
+ .release_region = release_smi_region,
+ .setup_region = setup_smi_region,
},
#endif
}
@@ -5342,13 +5351,14 @@
#endif
}
-
+static void __init reserve_mdp_memory(void);
static void __init msm8x60_calculate_reserve_sizes(void)
{
size_pmem_devices();
reserve_pmem_memory();
reserve_ion_memory();
+ reserve_mdp_memory();
}
static int msm8x60_paddr_to_memtype(unsigned int paddr)
@@ -6633,6 +6643,10 @@
PM8901_VREG_INIT_VS(HDMI_MVS),
};
+static struct pm8xxx_misc_platform_data pm8901_misc_pdata = {
+ .priority = 1,
+};
+
static struct pm8xxx_irq_platform_data pm8901_irq_pdata = {
.irq_base = PM8901_IRQ_BASE,
.devirq = MSM_GPIO_TO_INT(PM8901_GPIO_INT),
@@ -6648,6 +6662,7 @@
.mpp_pdata = &pm8901_mpp_pdata,
.regulator_pdatas = pm8901_vreg_init,
.num_regulators = ARRAY_SIZE(pm8901_vreg_init),
+ .misc_pdata = &pm8901_misc_pdata,
};
static struct msm_ssbi_platform_data msm8x60_ssbi_pm8901_pdata __devinitdata = {
@@ -9411,9 +9426,21 @@
.mdp_bus_scale_table = &mdp_bus_scale_pdata,
#endif
.mdp_rev = MDP_REV_41,
- .writeback_offset = writeback_offset,
+ .mdp_writeback_memtype = MEMTYPE_EBI1,
+ .mdp_writeback_phys = NULL,
};
+static void __init reserve_mdp_memory(void)
+{
+ mdp_pdata.mdp_writeback_size_ov0 = MSM_FB_OVERLAY0_WRITEBACK_SIZE;
+ mdp_pdata.mdp_writeback_size_ov1 = MSM_FB_OVERLAY1_WRITEBACK_SIZE;
+
+ msm8x60_reserve_table[mdp_pdata.mdp_writeback_memtype].size +=
+ mdp_pdata.mdp_writeback_size_ov0;
+ msm8x60_reserve_table[mdp_pdata.mdp_writeback_memtype].size +=
+ mdp_pdata.mdp_writeback_size_ov1;
+}
+
#ifdef CONFIG_FB_MSM_TVOUT
#ifdef CONFIG_MSM_BUS_SCALING
@@ -10169,7 +10196,7 @@
msm_pm_set_rpm_wakeup_irq(RPM_SCSS_CPU0_WAKE_UP_IRQ);
msm_cpuidle_set_states(msm_cstates, ARRAY_SIZE(msm_cstates),
msm_pm_data);
- BUG_ON(msm_pm_boot_init(MSM_PM_BOOT_CONFIG_TZ, NULL));
+ BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
pm8058_gpios_init();
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index 91bb4552..e8f6443 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -52,6 +52,7 @@
#include "timer.h"
#include "pm-boot.h"
#include "board-msm7x27a-regulator.h"
+#include "board-msm7627a.h"
#define PMEM_KERNEL_EBI1_SIZE 0x3A000
#define MSM_PMEM_AUDIO_SIZE 0x5B000
@@ -59,6 +60,15 @@
#define BAHAMA_SLAVE_ID_QMEMBIST_ADDR 0x7B
#define BAHAMA_SLAVE_ID_FM_REG 0x02
#define FM_GPIO 83
+#define BT_PCM_BCLK_MODE 0x88
+#define BT_PCM_DIN_MODE 0x89
+#define BT_PCM_DOUT_MODE 0x8A
+#define BT_PCM_SYNC_MODE 0x8B
+#define FM_I2S_SD_MODE 0x8E
+#define FM_I2S_WS_MODE 0x8F
+#define FM_I2S_SCK_MODE 0x90
+#define I2C_PIN_CTL 0x15
+#define I2C_NORMAL 0x40
enum {
GPIO_HOST_VBUS_EN = 107,
@@ -78,6 +88,57 @@
#define FPGA_MSM_CNTRL_REG2 0x90008010
#if defined(CONFIG_BT) && defined(CONFIG_MARIMBA_CORE)
+static int switch_pcm_i2s_reg_mode(int mode)
+{
+ unsigned char reg = 0;
+ int rc = -1;
+ unsigned char set = I2C_PIN_CTL; /*SET PIN CTL mode*/
+ unsigned char unset = I2C_NORMAL; /* UNSET PIN CTL MODE*/
+ struct marimba config = { .mod_id = SLAVE_ID_BAHAMA};
+
+ if (mode == 0) {
+ /* as we need to switch path to FM we need to move
+ BT AUX PCM lines to PIN CONTROL mode then move
+ FM to normal mode.*/
+ for (reg = BT_PCM_BCLK_MODE; reg <= BT_PCM_SYNC_MODE; reg++) {
+ rc = marimba_write(&config, reg, &set, 1);
+ if (rc < 0) {
+ pr_err("pcm pinctl failed = %d", rc);
+ goto err_all;
+ }
+ }
+ for (reg = FM_I2S_SD_MODE; reg <= FM_I2S_SCK_MODE; reg++) {
+ rc = marimba_write(&config, reg, &unset, 1);
+ if (rc < 0) {
+ pr_err("i2s normal failed = %d", rc);
+ goto err_all;
+ }
+ }
+ } else {
+ /* as we need to switch path to AUXPCM we need to move
+ FM I2S lines to PIN CONTROL mode then move
+ BT AUX_PCM to normal mode.*/
+ for (reg = FM_I2S_SD_MODE; reg <= FM_I2S_SCK_MODE; reg++) {
+ rc = marimba_write(&config, reg, &set, 1);
+ if (rc < 0) {
+ pr_err("i2s pinctl failed = %d", rc);
+ goto err_all;
+ }
+ }
+ for (reg = BT_PCM_BCLK_MODE; reg <= BT_PCM_SYNC_MODE; reg++) {
+ rc = marimba_write(&config, reg, &unset, 1);
+ if (rc < 0) {
+ pr_err("pcm normal failed = %d", rc);
+ goto err_all;
+ }
+ }
+ }
+
+ return 0;
+
+err_all:
+ return rc;
+}
static void config_pcm_i2s_mode(int mode)
{
void __iomem *cfg_ptr;
@@ -173,6 +234,12 @@
if (machine_is_msm7627a_qrd1())
config_pcm_i2s_mode(0);
pr_err("%s mode = FM_I2S_ON", __func__);
+
+ rc = switch_pcm_i2s_reg_mode(0);
+ if (rc) {
+ pr_err("switch mode failed");
+ return rc;
+ }
for (pin = 0; pin < ARRAY_SIZE(fm_i2s_config_power_on);
pin++) {
rc = gpio_tlmm_config(
@@ -184,6 +251,11 @@
}
} else if (mode == FM_I2S_OFF) {
pr_err("%s mode = FM_I2S_OFF", __func__);
+ rc = switch_pcm_i2s_reg_mode(1);
+ if (rc) {
+ pr_err("switch mode failed");
+ return rc;
+ }
for (pin = 0; pin < ARRAY_SIZE(fm_i2s_config_power_off);
pin++) {
rc = gpio_tlmm_config(
@@ -204,6 +276,11 @@
if (machine_is_msm7627a_qrd1())
config_pcm_i2s_mode(1);
pr_err("%s mode =BT_PCM_ON", __func__);
+ rc = switch_pcm_i2s_reg_mode(1);
+ if (rc) {
+ pr_err("switch mode failed");
+ return rc;
+ }
for (pin = 0; pin < ARRAY_SIZE(bt_config_pcm_on);
pin++) {
rc = gpio_tlmm_config(bt_config_pcm_on[pin],
@@ -213,6 +290,11 @@
}
} else if (mode == BT_PCM_OFF) {
pr_err("%s mode =BT_PCM_OFF", __func__);
+ rc = switch_pcm_i2s_reg_mode(0);
+ if (rc) {
+ pr_err("switch mode failed");
+ return rc;
+ }
for (pin = 0; pin < ARRAY_SIZE(bt_config_pcm_off);
pin++) {
rc = gpio_tlmm_config(bt_config_pcm_off[pin],
@@ -676,6 +758,10 @@
goto reg_fail;
}
+ gpio_tlmm_config(GPIO_CFG(GPIO_BT_SYS_REST_EN, 0,
+ GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL,
+ GPIO_CFG_2MA), GPIO_CFG_ENABLE);
+
/*setup Bahama_sys_reset_n*/
rc = gpio_request(GPIO_BT_SYS_REST_EN, "bahama sys_rst_n");
if (rc < 0) {
@@ -1266,301 +1352,6 @@
.is_phy_status_timer_on = 1,
};
-#if (defined(CONFIG_MMC_MSM_SDC1_SUPPORT)\
- || defined(CONFIG_MMC_MSM_SDC2_SUPPORT)\
- || defined(CONFIG_MMC_MSM_SDC3_SUPPORT)\
- || defined(CONFIG_MMC_MSM_SDC4_SUPPORT))
-
-static unsigned long vreg_sts, gpio_sts;
-
-struct sdcc_gpio {
- struct msm_gpio *cfg_data;
- uint32_t size;
- struct msm_gpio *sleep_cfg_data;
-};
-
-/**
- * Due to insufficient drive strengths for SDC GPIO lines some old versioned
- * SD/MMC cards may cause data CRC errors. Hence, set optimal values
- * for SDC slots based on timing closure and marginality. SDC1 slot
- * require higher value since it should handle bad signal quality due
- * to size of T-flash adapters.
- */
-static struct msm_gpio sdc1_cfg_data[] = {
- {GPIO_CFG(51, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
- "sdc1_dat_3"},
- {GPIO_CFG(52, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
- "sdc1_dat_2"},
- {GPIO_CFG(53, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
- "sdc1_dat_1"},
- {GPIO_CFG(54, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
- "sdc1_dat_0"},
- {GPIO_CFG(55, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_14MA),
- "sdc1_cmd"},
- {GPIO_CFG(56, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_14MA),
- "sdc1_clk"},
-};
-
-static struct msm_gpio sdc2_cfg_data[] = {
- {GPIO_CFG(62, 2, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
- "sdc2_clk"},
- {GPIO_CFG(63, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc2_cmd"},
- {GPIO_CFG(64, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc2_dat_3"},
- {GPIO_CFG(65, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc2_dat_2"},
- {GPIO_CFG(66, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc2_dat_1"},
- {GPIO_CFG(67, 2, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc2_dat_0"},
-};
-
-static struct msm_gpio sdc2_sleep_cfg_data[] = {
- {GPIO_CFG(62, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
- "sdc2_clk"},
- {GPIO_CFG(63, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
- "sdc2_cmd"},
- {GPIO_CFG(64, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
- "sdc2_dat_3"},
- {GPIO_CFG(65, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
- "sdc2_dat_2"},
- {GPIO_CFG(66, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
- "sdc2_dat_1"},
- {GPIO_CFG(67, 0, GPIO_CFG_INPUT, GPIO_CFG_PULL_DOWN, GPIO_CFG_2MA),
- "sdc2_dat_0"},
-};
-static struct msm_gpio sdc3_cfg_data[] = {
- {GPIO_CFG(88, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
- "sdc3_clk"},
- {GPIO_CFG(89, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_cmd"},
- {GPIO_CFG(90, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_3"},
- {GPIO_CFG(91, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_2"},
- {GPIO_CFG(92, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_1"},
- {GPIO_CFG(93, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_0"},
-#ifdef CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT
- {GPIO_CFG(19, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_7"},
- {GPIO_CFG(20, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_6"},
- {GPIO_CFG(21, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_5"},
- {GPIO_CFG(108, 3, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc3_dat_4"},
-#endif
-};
-
-static struct msm_gpio sdc4_cfg_data[] = {
- {GPIO_CFG(19, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc4_dat_3"},
- {GPIO_CFG(20, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc4_dat_2"},
- {GPIO_CFG(21, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc4_dat_1"},
- {GPIO_CFG(107, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc4_cmd"},
- {GPIO_CFG(108, 1, GPIO_CFG_OUTPUT, GPIO_CFG_PULL_UP, GPIO_CFG_10MA),
- "sdc4_dat_0"},
- {GPIO_CFG(109, 1, GPIO_CFG_OUTPUT, GPIO_CFG_NO_PULL, GPIO_CFG_8MA),
- "sdc4_clk"},
-};
-
-static struct sdcc_gpio sdcc_cfg_data[] = {
- {
- .cfg_data = sdc1_cfg_data,
- .size = ARRAY_SIZE(sdc1_cfg_data),
- },
- {
- .cfg_data = sdc2_cfg_data,
- .size = ARRAY_SIZE(sdc2_cfg_data),
- .sleep_cfg_data = sdc2_sleep_cfg_data,
- },
- {
- .cfg_data = sdc3_cfg_data,
- .size = ARRAY_SIZE(sdc3_cfg_data),
- },
- {
- .cfg_data = sdc4_cfg_data,
- .size = ARRAY_SIZE(sdc4_cfg_data),
- },
-};
-
-static struct regulator *sdcc_vreg_data[ARRAY_SIZE(sdcc_cfg_data)];
-static int msm_sdcc_setup_gpio(int dev_id, unsigned int enable)
-{
- int rc = 0;
- struct sdcc_gpio *curr;
-
- curr = &sdcc_cfg_data[dev_id - 1];
- if (!(test_bit(dev_id, &gpio_sts)^enable))
- return rc;
-
- if (enable) {
- set_bit(dev_id, &gpio_sts);
- rc = msm_gpios_request_enable(curr->cfg_data, curr->size);
- if (rc)
- pr_err("%s: Failed to turn on GPIOs for slot %d\n",
- __func__, dev_id);
- } else {
- clear_bit(dev_id, &gpio_sts);
- if (curr->sleep_cfg_data) {
- rc = msm_gpios_enable(curr->sleep_cfg_data, curr->size);
- msm_gpios_free(curr->sleep_cfg_data, curr->size);
- return rc;
- }
- msm_gpios_disable_free(curr->cfg_data, curr->size);
- }
- return rc;
-}
-
-static int msm_sdcc_setup_vreg(int dev_id, unsigned int enable)
-{
- int rc = 0;
- struct regulator *curr = sdcc_vreg_data[dev_id - 1];
-
- if (test_bit(dev_id, &vreg_sts) == enable)
- return 0;
-
- if (!curr)
- return -ENODEV;
-
- if (IS_ERR(curr))
- return PTR_ERR(curr);
-
- if (enable) {
- set_bit(dev_id, &vreg_sts);
-
- rc = regulator_enable(curr);
- if (rc)
- pr_err("%s: could not enable regulator: %d\n",
- __func__, rc);
- } else {
- clear_bit(dev_id, &vreg_sts);
-
- rc = regulator_disable(curr);
- if (rc)
- pr_err("%s: could not disable regulator: %d\n",
- __func__, rc);
- }
- return rc;
-}
-
-static uint32_t msm_sdcc_setup_power(struct device *dv, unsigned int vdd)
-{
- int rc = 0;
- struct platform_device *pdev;
-
- pdev = container_of(dv, struct platform_device, dev);
-
- rc = msm_sdcc_setup_gpio(pdev->id, !!vdd);
- if (rc)
- goto out;
-
- rc = msm_sdcc_setup_vreg(pdev->id, !!vdd);
-out:
- return rc;
-}
-
-#define GPIO_SDC1_HW_DET 42
-
-#if defined(CONFIG_MMC_MSM_SDC1_SUPPORT) \
- && defined(CONFIG_MMC_MSM_CARD_HW_DETECTION)
-static unsigned int msm7627a_sdcc_slot_status(struct device *dev)
-{
- int status;
-
- status = gpio_tlmm_config(GPIO_CFG(GPIO_SDC1_HW_DET, 2, GPIO_CFG_INPUT,
- GPIO_CFG_PULL_UP, GPIO_CFG_8MA), GPIO_CFG_ENABLE);
- if (status)
- pr_err("%s:Failed to configure tlmm for GPIO %d\n", __func__,
- GPIO_SDC1_HW_DET);
-
- status = gpio_request(GPIO_SDC1_HW_DET, "SD_HW_Detect");
- if (status) {
- pr_err("%s:Failed to request GPIO %d\n", __func__,
- GPIO_SDC1_HW_DET);
- } else {
- status = gpio_direction_input(GPIO_SDC1_HW_DET);
- if (!status)
- status = !gpio_get_value(GPIO_SDC1_HW_DET);
- gpio_free(GPIO_SDC1_HW_DET);
- }
- return status;
-}
-#endif
-
-#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
-static struct mmc_platform_data sdc1_plat_data = {
- .ocr_mask = MMC_VDD_28_29,
- .translate_vdd = msm_sdcc_setup_power,
- .mmc_bus_width = MMC_CAP_4_BIT_DATA,
- .msmsdcc_fmin = 144000,
- .msmsdcc_fmid = 24576000,
- .msmsdcc_fmax = 49152000,
-#ifdef CONFIG_MMC_MSM_CARD_HW_DETECTION
- .status = msm7627a_sdcc_slot_status,
- .status_irq = MSM_GPIO_TO_INT(GPIO_SDC1_HW_DET),
- .irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-#endif
-};
-#endif
-
-#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
-static struct mmc_platform_data sdc2_plat_data = {
- /*
- * SDC2 supports only 1.8V, claim for 2.85V range is just
- * for allowing buggy cards who advertise 2.8V even though
- * they can operate at 1.8V supply.
- */
- .ocr_mask = MMC_VDD_28_29 | MMC_VDD_165_195,
- .translate_vdd = msm_sdcc_setup_power,
- .mmc_bus_width = MMC_CAP_4_BIT_DATA,
-#ifdef CONFIG_MMC_MSM_SDIO_SUPPORT
- .sdiowakeup_irq = MSM_GPIO_TO_INT(66),
-#endif
- .msmsdcc_fmin = 144000,
- .msmsdcc_fmid = 24576000,
- .msmsdcc_fmax = 49152000,
-#ifdef CONFIG_MMC_MSM_SDC2_DUMMY52_REQUIRED
- .dummy52_required = 1,
-#endif
-};
-#endif
-
-#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
-static struct mmc_platform_data sdc3_plat_data = {
- .ocr_mask = MMC_VDD_28_29,
- .translate_vdd = msm_sdcc_setup_power,
-#ifdef CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT
- .mmc_bus_width = MMC_CAP_8_BIT_DATA,
-#else
- .mmc_bus_width = MMC_CAP_4_BIT_DATA,
-#endif
- .msmsdcc_fmin = 144000,
- .msmsdcc_fmid = 24576000,
- .msmsdcc_fmax = 49152000,
- .nonremovable = 1,
-};
-#endif
-
-#if (defined(CONFIG_MMC_MSM_SDC4_SUPPORT)\
- && !defined(CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT))
-static struct mmc_platform_data sdc4_plat_data = {
- .ocr_mask = MMC_VDD_28_29,
- .translate_vdd = msm_sdcc_setup_power,
- .mmc_bus_width = MMC_CAP_4_BIT_DATA,
- .msmsdcc_fmin = 144000,
- .msmsdcc_fmid = 24576000,
- .msmsdcc_fmax = 49152000,
-};
-#endif
-#endif
-
#ifdef CONFIG_SERIAL_MSM_HS
static struct msm_serial_hs_platform_data msm_uart_dm1_pdata = {
.inject_rx_on_wakeup = 1,
@@ -1602,6 +1393,11 @@
},
};
+static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = {
+ .mode = MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT,
+ .v_addr = (uint32_t *)PAGE_OFFSET,
+};
+
static struct android_pmem_platform_data android_pmem_adsp_pdata = {
.name = "pmem_adsp",
.allocator_type = PMEM_ALLOCATORTYPE_BITMAP,
@@ -1693,69 +1489,6 @@
}
};
-static int __init mmc_regulator_init(int sdcc_no, const char *supply, int uV)
-{
- int rc;
-
- BUG_ON(sdcc_no < 1 || sdcc_no > 4);
-
- sdcc_no--;
-
- sdcc_vreg_data[sdcc_no] = regulator_get(NULL, supply);
-
- if (IS_ERR(sdcc_vreg_data[sdcc_no])) {
- rc = PTR_ERR(sdcc_vreg_data[sdcc_no]);
- pr_err("%s: could not get regulator \"%s\": %d\n",
- __func__, supply, rc);
- goto out;
- }
-
- rc = regulator_set_voltage(sdcc_vreg_data[sdcc_no], uV, uV);
-
- if (rc) {
- pr_err("%s: could not set voltage for \"%s\" to %d uV: %d\n",
- __func__, supply, uV, rc);
- goto reg_free;
- }
-
- return rc;
-
-reg_free:
- regulator_put(sdcc_vreg_data[sdcc_no]);
-out:
- sdcc_vreg_data[sdcc_no] = NULL;
- return rc;
-}
-
-static void __init msm7627a_init_mmc(void)
-{
- /* eMMC slot */
-#ifdef CONFIG_MMC_MSM_SDC3_SUPPORT
- if (mmc_regulator_init(3, "emmc", 3000000))
- return;
- msm_add_sdcc(3, &sdc3_plat_data);
-#endif
- /* Micro-SD slot */
-#ifdef CONFIG_MMC_MSM_SDC1_SUPPORT
- if (mmc_regulator_init(1, "mmc", 2850000))
- return;
- msm_add_sdcc(1, &sdc1_plat_data);
-#endif
- /* SDIO WLAN slot */
-#ifdef CONFIG_MMC_MSM_SDC2_SUPPORT
- if (mmc_regulator_init(2, "mmc", 2850000))
- return;
- msm_add_sdcc(2, &sdc2_plat_data);
-#endif
- /* Not Used */
-#if (defined(CONFIG_MMC_MSM_SDC4_SUPPORT)\
- && !defined(CONFIG_MMC_MSM_SDC3_8_BIT_SUPPORT))
- if (mmc_regulator_init(4, "mmc", 2850000))
- return;
- msm_add_sdcc(4, &sdc4_plat_data);
-#endif
-}
-
#define SND(desc, num) { .name = #desc, .id = num }
static struct snd_endpoint snd_endpoints_list[] = {
SND(HANDSET, 0),
@@ -2539,8 +2272,6 @@
msm7x2x_misc_init();
msm7627a_init_regulators();
msm_device_i2c_init();
- msm7627a_init_mmc();
-
qrd1_camera_gpio_cfg();
#ifdef CONFIG_SERIAL_MSM_HS
msm_uart_dm1_pdata.wakeup_irq = gpio_to_irq(UART1DM_RX_GPIO);
@@ -2556,13 +2287,14 @@
&msm_gadget_pdata;
platform_add_devices(qrd1_devices,
ARRAY_SIZE(qrd1_devices));
+ msm7627a_init_mmc();
+
#ifdef CONFIG_USB_EHCI_MSM_72K
msm7627a_init_host();
#endif
msm_pm_set_platform_data(msm7627a_pm_data,
ARRAY_SIZE(msm7627a_pm_data));
- BUG_ON(msm_pm_boot_init(MSM_PM_BOOT_CONFIG_RESET_VECTOR,
- ioremap(0, PAGE_SIZE)));
+ BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
msm_fb_add_devices();
diff --git a/arch/arm/mach-msm/board-qsd8x50.c b/arch/arm/mach-msm/board-qsd8x50.c
index 0dcd0f4..10432a0 100644
--- a/arch/arm/mach-msm/board-qsd8x50.c
+++ b/arch/arm/mach-msm/board-qsd8x50.c
@@ -2297,6 +2297,11 @@
},
};
+static struct msm_pm_boot_platform_data msm_pm_boot_pdata __initdata = {
+ .mode = MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT,
+ .v_addr = (unsigned int *)PAGE_OFFSET,
+};
+
static void
msm_i2c_gpio_config(int iface, int config_type)
{
@@ -2437,8 +2442,7 @@
spi_register_board_info(msm_spi_board_info,
ARRAY_SIZE(msm_spi_board_info));
msm_pm_set_platform_data(msm_pm_data, ARRAY_SIZE(msm_pm_data));
- BUG_ON(msm_pm_boot_init(MSM_PM_BOOT_CONFIG_RESET_VECTOR,
- (uint32_t *)PAGE_OFFSET));
+ BUG_ON(msm_pm_boot_init(&msm_pm_boot_pdata));
#ifdef CONFIG_SURF_FFA_GPIO_KEYPAD
if (machine_is_qsd8x50_ffa())
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index d7322b6..5521c64 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -41,6 +41,7 @@
#define REG_GCC(off) (MSM_APCS_GCC_BASE + (off))
/* Peripheral clock registers. */
+#define ADM0_PBUS_CLK_CTL_REG REG(0x2208)
#define CE1_HCLK_CTL_REG REG(0x2720)
#define CE1_CORE_CLK_CTL_REG REG(0x2724)
#define CE3_HCLK_CTL_REG REG(0x36C4)
@@ -109,6 +110,7 @@
#define RINGOSC_NS_REG REG(0x2DC0)
#define RINGOSC_STATUS_REG REG(0x2DCC)
#define RINGOSC_TCXO_CTL_REG REG(0x2DC4)
+#define RPM_MSG_RAM_HCLK_CTL_REG REG(0x27E0)
#define SC0_U_CLK_BRANCH_ENA_VOTE_REG REG(0x3080)
#define SDCn_APPS_CLK_MD_REG(n) REG(0x2828+(0x20*((n)-1)))
#define SDCn_APPS_CLK_NS_REG(n) REG(0x282C+(0x20*((n)-1)))
@@ -631,14 +633,12 @@
},
};
-static int soc_clk_reset(struct clk *clk, enum clk_reset_action action)
-{
- return branch_reset(&to_rcg_clk(clk)->b, action);
-}
-
static struct clk_ops clk_ops_rcg_8960 = {
.enable = rcg_clk_enable,
.disable = rcg_clk_disable,
+ .enable_hwcg = rcg_clk_enable_hwcg,
+ .disable_hwcg = rcg_clk_disable_hwcg,
+ .in_hwcg_mode = rcg_clk_in_hwcg_mode,
.auto_off = rcg_clk_disable,
.handoff = rcg_clk_handoff,
.set_rate = rcg_clk_set_rate,
@@ -646,7 +646,7 @@
.list_rate = rcg_clk_list_rate,
.is_enabled = rcg_clk_is_enabled,
.round_rate = rcg_clk_round_rate,
- .reset = soc_clk_reset,
+ .reset = rcg_clk_reset,
.is_local = local_clk_is_local,
.get_parent = rcg_clk_get_parent,
};
@@ -654,12 +654,16 @@
static struct clk_ops clk_ops_branch = {
.enable = branch_clk_enable,
.disable = branch_clk_disable,
+ .enable_hwcg = branch_clk_enable_hwcg,
+ .disable_hwcg = branch_clk_disable_hwcg,
+ .in_hwcg_mode = branch_clk_in_hwcg_mode,
.auto_off = branch_clk_disable,
.is_enabled = branch_clk_is_enabled,
.reset = branch_clk_reset,
.is_local = local_clk_is_local,
.get_parent = branch_clk_get_parent,
.set_parent = branch_clk_set_parent,
+ .handoff = branch_clk_handoff,
};
static struct clk_ops clk_ops_reset = {
@@ -686,6 +690,8 @@
.b = {
.ctl_reg = MAXI_EN_REG,
.en_mask = BIT(21),
+ .hwcg_reg = MAXI_EN_REG,
+ .hwcg_mask = BIT(11),
.reset_reg = SW_RESET_AXI_REG,
.reset_mask = BIT(14),
.halt_reg = DBG_BUS_VEC_E_REG,
@@ -702,6 +708,8 @@
.b = {
.ctl_reg = MAXI_EN_REG,
.en_mask = BIT(22),
+ .hwcg_reg = MAXI_EN_REG,
+ .hwcg_mask = BIT(15),
.reset_reg = SW_RESET_CORE_REG,
.reset_mask = BIT(10),
.halt_reg = DBG_BUS_VEC_E_REG,
@@ -732,6 +740,8 @@
.b = {
.ctl_reg = MAXI_EN4_REG,
.en_mask = BIT(23),
+ .hwcg_reg = MAXI_EN4_REG,
+ .hwcg_mask = BIT(22),
.halt_reg = DBG_BUS_VEC_I_REG,
.halt_bit = 25,
},
@@ -746,6 +756,8 @@
.b = {
.ctl_reg = MAXI_EN4_REG,
.en_mask = BIT(25),
+ .hwcg_reg = MAXI_EN4_REG,
+ .hwcg_mask = BIT(24),
.halt_reg = DBG_BUS_VEC_I_REG,
.halt_bit = 26,
},
@@ -761,6 +773,8 @@
.b = {
.ctl_reg = MAXI_EN_REG,
.en_mask = BIT(19),
+ .hwcg_reg = MAXI_EN_REG,
+ .hwcg_mask = BIT(13),
.reset_reg = SW_RESET_AXI_REG,
.reset_mask = BIT(4)|BIT(5)|BIT(7),
.halt_reg = DBG_BUS_VEC_E_REG,
@@ -794,6 +808,8 @@
.b = {
.ctl_reg = MAXI_EN_REG,
.en_mask = BIT(23),
+ .hwcg_reg = MAXI_EN_REG,
+ .hwcg_mask = BIT(16),
.reset_reg = SW_RESET_AXI_REG,
.reset_mask = BIT(13),
.halt_reg = DBG_BUS_VEC_E_REG,
@@ -810,6 +826,8 @@
.b = {
.ctl_reg = MAXI_EN2_REG,
.en_mask = BIT(24),
+ .hwcg_reg = MAXI_EN2_REG,
+ .hwcg_mask = BIT(25),
.reset_reg = SW_RESET_AXI_REG,
.reset_mask = BIT(6),
.halt_reg = DBG_BUS_VEC_E_REG,
@@ -826,6 +844,8 @@
.b = {
.ctl_reg = MAXI_EN2_REG,
.en_mask = BIT(26),
+ .hwcg_reg = MAXI_EN2_REG,
+ .hwcg_mask = BIT(27),
.reset_reg = SW_RESET_AXI_REG,
.reset_mask = BIT(15),
.halt_reg = DBG_BUS_VEC_E_REG,
@@ -922,6 +942,8 @@
.b = {
.ctl_reg = AHB_EN_REG,
.en_mask = BIT(18),
+ .hwcg_reg = AHB_EN2_REG,
+ .hwcg_mask = BIT(20),
.reset_reg = SW_RESET_AHB_REG,
.reset_mask = BIT(5),
.halt_reg = DBG_BUS_VEC_F_REG,
@@ -954,6 +976,8 @@
.b = {
.ctl_reg = AHB_EN_REG,
.en_mask = BIT(22),
+ .hwcg_reg = AHB_EN2_REG,
+ .hwcg_mask = BIT(15),
.reset_reg = SW_RESET_AHB2_REG,
.reset_mask = BIT(0),
.halt_reg = DBG_BUS_VEC_F_REG,
@@ -970,6 +994,8 @@
.b = {
.ctl_reg = AHB_EN_REG,
.en_mask = BIT(19),
+ .hwcg_reg = AHB_EN2_REG,
+ .hwcg_mask = BIT(28),
.reset_reg = SW_RESET_AHB_REG,
.reset_mask = BIT(12),
.halt_reg = DBG_BUS_VEC_F_REG,
@@ -986,6 +1012,8 @@
.b = {
.ctl_reg = AHB_EN_REG,
.en_mask = BIT(2),
+ .hwcg_reg = AHB_EN2_REG,
+ .hwcg_mask = BIT(29),
.reset_reg = SW_RESET_AHB_REG,
.reset_mask = BIT(11),
.halt_reg = DBG_BUS_VEC_F_REG,
@@ -1002,6 +1030,8 @@
.b = {
.ctl_reg = AHB_EN_REG,
.en_mask = BIT(3),
+ .hwcg_reg = AHB_EN2_REG,
+ .hwcg_mask = BIT(27),
.reset_reg = SW_RESET_AHB_REG,
.reset_mask = BIT(10),
.halt_reg = DBG_BUS_VEC_F_REG,
@@ -1018,6 +1048,8 @@
.b = {
.ctl_reg = AHB_EN_REG,
.en_mask = BIT(14),
+ .hwcg_reg = AHB_EN2_REG,
+ .hwcg_mask = BIT(21),
.reset_reg = SW_RESET_AHB_REG,
.reset_mask = BIT(9),
.halt_reg = DBG_BUS_VEC_F_REG,
@@ -1034,6 +1066,8 @@
.b = {
.ctl_reg = AHB_EN_REG,
.en_mask = BIT(4),
+ .hwcg_reg = AHB_EN2_REG,
+ .hwcg_mask = BIT(22),
.reset_reg = SW_RESET_AHB_REG,
.reset_mask = BIT(9),
.halt_reg = DBG_BUS_VEC_F_REG,
@@ -1066,6 +1100,8 @@
.b = {
.ctl_reg = AHB_EN_REG,
.en_mask = BIT(6),
+ .hwcg_reg = AHB_EN2_REG,
+ .hwcg_mask = BIT(12),
.reset_reg = SW_RESET_AHB_REG,
.reset_mask = BIT(8),
.halt_reg = DBG_BUS_VEC_F_REG,
@@ -1130,6 +1166,8 @@
.b = {
.ctl_reg = AHB_EN_REG,
.en_mask = BIT(15),
+ .hwcg_reg = AHB_EN_REG,
+ .hwcg_mask = BIT(26),
.halt_reg = DBG_BUS_VEC_F_REG,
.halt_bit = 22,
},
@@ -1160,6 +1198,8 @@
.b = {
.ctl_reg = AHB_EN_REG,
.en_mask = BIT(11),
+ .hwcg_reg = AHB_EN2_REG,
+ .hwcg_mask = BIT(26),
.reset_reg = SW_RESET_AHB_REG,
.reset_mask = BIT(1),
.halt_reg = DBG_BUS_VEC_F_REG,
@@ -1537,7 +1577,7 @@
.list_rate = rcg_clk_list_rate,
.is_enabled = rcg_clk_is_enabled,
.round_rate = rcg_clk_round_rate,
- .reset = soc_clk_reset,
+ .reset = rcg_clk_reset,
.is_local = local_clk_is_local,
.get_parent = rcg_clk_get_parent,
};
@@ -1707,6 +1747,8 @@
.b = {
.ctl_reg = PMEM_ACLK_CTL_REG,
.en_mask = BIT(4),
+ .hwcg_reg = PMEM_ACLK_CTL_REG,
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_DFAB_STATE_REG,
.halt_bit = 20,
},
@@ -2133,6 +2175,8 @@
.b = {
.ctl_reg = CE1_CORE_CLK_CTL_REG,
.en_mask = BIT(4),
+ .hwcg_reg = CE1_CORE_CLK_CTL_REG,
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEC_REG,
.halt_bit = 27,
},
@@ -2257,6 +2301,8 @@
.b = {
.ctl_reg = DMA_BAM_HCLK_CTL,
.en_mask = BIT(4),
+ .hwcg_reg = DMA_BAM_HCLK_CTL,
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_DFAB_STATE_REG,
.halt_bit = 12,
},
@@ -2271,6 +2317,8 @@
.b = {
.ctl_reg = GSBIn_HCLK_CTL_REG(1),
.en_mask = BIT(4),
+ .hwcg_reg = GSBIn_HCLK_CTL_REG(1),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEA_REG,
.halt_bit = 11,
},
@@ -2285,6 +2333,8 @@
.b = {
.ctl_reg = GSBIn_HCLK_CTL_REG(2),
.en_mask = BIT(4),
+ .hwcg_reg = GSBIn_HCLK_CTL_REG(2),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEA_REG,
.halt_bit = 7,
},
@@ -2299,6 +2349,8 @@
.b = {
.ctl_reg = GSBIn_HCLK_CTL_REG(3),
.en_mask = BIT(4),
+ .hwcg_reg = GSBIn_HCLK_CTL_REG(3),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEA_REG,
.halt_bit = 3,
},
@@ -2313,6 +2365,8 @@
.b = {
.ctl_reg = GSBIn_HCLK_CTL_REG(4),
.en_mask = BIT(4),
+ .hwcg_reg = GSBIn_HCLK_CTL_REG(4),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEB_REG,
.halt_bit = 27,
},
@@ -2327,6 +2381,8 @@
.b = {
.ctl_reg = GSBIn_HCLK_CTL_REG(5),
.en_mask = BIT(4),
+ .hwcg_reg = GSBIn_HCLK_CTL_REG(5),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEB_REG,
.halt_bit = 23,
},
@@ -2341,6 +2397,8 @@
.b = {
.ctl_reg = GSBIn_HCLK_CTL_REG(6),
.en_mask = BIT(4),
+ .hwcg_reg = GSBIn_HCLK_CTL_REG(6),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEB_REG,
.halt_bit = 19,
},
@@ -2355,6 +2413,8 @@
.b = {
.ctl_reg = GSBIn_HCLK_CTL_REG(7),
.en_mask = BIT(4),
+ .hwcg_reg = GSBIn_HCLK_CTL_REG(7),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEB_REG,
.halt_bit = 15,
},
@@ -2369,6 +2429,8 @@
.b = {
.ctl_reg = GSBIn_HCLK_CTL_REG(8),
.en_mask = BIT(4),
+ .hwcg_reg = GSBIn_HCLK_CTL_REG(8),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEB_REG,
.halt_bit = 11,
},
@@ -2383,6 +2445,8 @@
.b = {
.ctl_reg = GSBIn_HCLK_CTL_REG(9),
.en_mask = BIT(4),
+ .hwcg_reg = GSBIn_HCLK_CTL_REG(9),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEB_REG,
.halt_bit = 7,
},
@@ -2397,6 +2461,8 @@
.b = {
.ctl_reg = GSBIn_HCLK_CTL_REG(10),
.en_mask = BIT(4),
+ .hwcg_reg = GSBIn_HCLK_CTL_REG(10),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEB_REG,
.halt_bit = 3,
},
@@ -2411,6 +2477,8 @@
.b = {
.ctl_reg = GSBIn_HCLK_CTL_REG(11),
.en_mask = BIT(4),
+ .hwcg_reg = GSBIn_HCLK_CTL_REG(11),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEC_REG,
.halt_bit = 18,
},
@@ -2425,6 +2493,8 @@
.b = {
.ctl_reg = GSBIn_HCLK_CTL_REG(12),
.en_mask = BIT(4),
+ .hwcg_reg = GSBIn_HCLK_CTL_REG(12),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEC_REG,
.halt_bit = 14,
},
@@ -2470,6 +2540,8 @@
.b = {
.ctl_reg = TSIF_HCLK_CTL_REG,
.en_mask = BIT(4),
+ .hwcg_reg = TSIF_HCLK_CTL_REG,
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_CFPB_STATEC_REG,
.halt_bit = 7,
},
@@ -2512,6 +2584,8 @@
.b = {
.ctl_reg = USB_HS1_HCLK_CTL_REG,
.en_mask = BIT(4),
+ .hwcg_reg = USB_HS1_HCLK_CTL_REG,
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_DFAB_STATE_REG,
.halt_bit = 1,
},
@@ -2568,6 +2642,8 @@
.b = {
.ctl_reg = SDCn_HCLK_CTL_REG(1),
.en_mask = BIT(4),
+ .hwcg_reg = SDCn_HCLK_CTL_REG(1),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_DFAB_STATE_REG,
.halt_bit = 11,
},
@@ -2582,6 +2658,8 @@
.b = {
.ctl_reg = SDCn_HCLK_CTL_REG(2),
.en_mask = BIT(4),
+ .hwcg_reg = SDCn_HCLK_CTL_REG(2),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_DFAB_STATE_REG,
.halt_bit = 10,
},
@@ -2596,6 +2674,8 @@
.b = {
.ctl_reg = SDCn_HCLK_CTL_REG(3),
.en_mask = BIT(4),
+ .hwcg_reg = SDCn_HCLK_CTL_REG(3),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_DFAB_STATE_REG,
.halt_bit = 9,
},
@@ -2610,6 +2690,8 @@
.b = {
.ctl_reg = SDCn_HCLK_CTL_REG(4),
.en_mask = BIT(4),
+ .hwcg_reg = SDCn_HCLK_CTL_REG(4),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_DFAB_STATE_REG,
.halt_bit = 8,
},
@@ -2624,6 +2706,8 @@
.b = {
.ctl_reg = SDCn_HCLK_CTL_REG(5),
.en_mask = BIT(4),
+ .hwcg_reg = SDCn_HCLK_CTL_REG(5),
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_DFAB_STATE_REG,
.halt_bit = 7,
},
@@ -2654,6 +2738,8 @@
.b = {
.ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG,
.en_mask = BIT(3),
+ .hwcg_reg = ADM0_PBUS_CLK_CTL_REG,
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_MSS_SMPSS_MISC_STATE_REG,
.halt_check = HALT_VOTED,
.halt_bit = 13,
@@ -2714,6 +2800,8 @@
.b = {
.ctl_reg = SC0_U_CLK_BRANCH_ENA_VOTE_REG,
.en_mask = BIT(6),
+ .hwcg_reg = RPM_MSG_RAM_HCLK_CTL_REG,
+ .hwcg_mask = BIT(6),
.halt_reg = CLK_HALT_SFPB_MISC_STATE_REG,
.halt_check = HALT_VOTED,
.halt_bit = 12,
@@ -4665,6 +4753,7 @@
static DEFINE_CLK_VOTER(dfab_sdc5_clk, &dfab_clk.c);
static DEFINE_CLK_VOTER(dfab_sps_clk, &dfab_clk.c);
static DEFINE_CLK_VOTER(dfab_bam_dmux_clk, &dfab_clk.c);
+static DEFINE_CLK_VOTER(dfab_scm_clk, &dfab_clk.c);
static DEFINE_CLK_VOTER(ebi1_msmbus_clk, &ebi1_clk.c);
/*
@@ -5074,22 +5163,24 @@
CLK_LOOKUP("pll4", pll4_clk.c, NULL),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
- CLK_DUMMY("afab_clk", AFAB_CLK, NULL, 0),
- CLK_DUMMY("afab_a_clk", AFAB_A_CLK, NULL, 0),
- CLK_DUMMY("cfpb_clk", CFPB_CLK, NULL, 0),
- CLK_DUMMY("cfpb_a_clk", CFPB_A_CLK, NULL, 0),
+ CLK_DUMMY("bus_clk", AFAB_CLK, "msm_apps_fab", 0),
+ CLK_DUMMY("bus_a_clk", AFAB_A_CLK, "msm_apps_fab", 0),
+ CLK_DUMMY("bus_clk", SFAB_CLK, "msm_sys_fab", 0),
+ CLK_DUMMY("bus_a_clk", SFAB_A_CLK, "msm_sys_fab", 0),
+ CLK_DUMMY("bus_clk", SFPB_CLK, "msm_sys_fpb", 0),
+ CLK_DUMMY("bus_a_clk", SFPB_A_CLK, "msm_sys_fpb", 0),
+ CLK_DUMMY("bus_clk", MMFAB_CLK, "msm_mm_fab", 0),
+ CLK_DUMMY("bus_a_clk", MMFAB_A_CLK, "msm_mm_fab", 0),
+ CLK_DUMMY("bus_clk", CFPB_CLK, "msm_cpss_fpb", 0),
+ CLK_DUMMY("bus_a_clk", CFPB_A_CLK, "msm_cpss_fpb", 0),
+ CLK_LOOKUP("mem_clk", ebi1_msmbus_clk.c, "msm_bus"),
+ CLK_DUMMY("mem_a_clk", EBI1_A_CLK, "msm_bus", 0),
+
+ CLK_DUMMY("ebi1_clk", EBI1_CLK, NULL, 0),
CLK_DUMMY("dfab_clk", DFAB_CLK, NULL, 0),
CLK_DUMMY("dfab_a_clk", DFAB_A_CLK, NULL, 0),
- CLK_DUMMY("ebi1_clk", EBI1_CLK, NULL, 0),
- CLK_DUMMY("ebi1_a_clk", EBI1_A_CLK, NULL, 0),
- CLK_DUMMY("mmfab_clk", MMFAB_CLK, NULL, 0),
- CLK_DUMMY("mmfab_a_clk", MMFAB_A_CLK, NULL, 0),
- CLK_DUMMY("mmfpb_clk", MMFPB_CLK, NULL, 0),
- CLK_DUMMY("mmfpb_a_clk", MMFPB_A_CLK, NULL, 0),
- CLK_DUMMY("sfab_clk", SFAB_CLK, NULL, 0),
- CLK_DUMMY("sfab_a_clk", SFAB_A_CLK, NULL, 0),
- CLK_DUMMY("sfpb_clk", SFPB_CLK, NULL, 0),
- CLK_DUMMY("sfpb_a_clk", SFPB_A_CLK, NULL, 0),
+ CLK_DUMMY("bus_clk", MMFPB_CLK, NULL, 0),
+ CLK_DUMMY("bus_a_clk", MMFPB_A_CLK, NULL, 0),
CLK_LOOKUP("core_clk", gp0_clk.c, NULL),
CLK_LOOKUP("core_clk", gp1_clk.c, NULL),
@@ -5285,13 +5376,13 @@
CLK_DUMMY("bus_clk", DFAB_SDC3_CLK, NULL, 0),
CLK_DUMMY("bus_clk", DFAB_SDC4_CLK, NULL, 0),
CLK_DUMMY("dfab_clk", DFAB_CLK, NULL, 0),
+ CLK_DUMMY("bus_clk", DFAB_SCM_CLK, "scm", 0),
CLK_LOOKUP("usb_hsic_xcvr_fs_clk", usb_hsic_xcvr_fs_clk.c, NULL),
CLK_LOOKUP("usb_hsic_hsic_clk", usb_hsic_hsic_clk.c, NULL),
CLK_LOOKUP("usb_hsic_hsio_cal_clk", usb_hsic_hsio_cal_clk.c, NULL),
CLK_LOOKUP("usb_hsic_system_clk", usb_hsic_system_clk.c, NULL),
CLK_LOOKUP("usb_hsic_p_clk", usb_hsic_p_clk.c, NULL),
- CLK_LOOKUP("ebi1_msmbus_clk", ebi1_msmbus_clk.c, NULL),
CLK_LOOKUP("mem_clk", ebi1_adm_clk.c, "msm_dmov"),
CLK_LOOKUP("l2_mclk", l2_m_clk, NULL),
@@ -5306,24 +5397,25 @@
CLK_LOOKUP("pll4", pll4_clk.c, NULL),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
- CLK_LOOKUP("afab_clk", afab_clk.c, NULL),
- CLK_LOOKUP("afab_a_clk", afab_a_clk.c, NULL),
- CLK_LOOKUP("cfpb_clk", cfpb_clk.c, NULL),
- CLK_LOOKUP("cfpb_a_clk", cfpb_a_clk.c, NULL),
- CLK_LOOKUP("cfpb_a_clk", cfpb_a_clk.c, "clock-8960"),
- CLK_LOOKUP("dfab_clk", dfab_clk.c, NULL),
- CLK_LOOKUP("dfab_a_clk", dfab_a_clk.c, NULL),
- CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL),
- CLK_LOOKUP("ebi1_a_clk", ebi1_a_clk.c, NULL),
- CLK_LOOKUP("mmfab_clk", mmfab_clk.c, NULL),
- CLK_LOOKUP("mmfab_a_clk", mmfab_a_clk.c, NULL),
- CLK_LOOKUP("mmfpb_clk", mmfpb_clk.c, NULL),
- CLK_LOOKUP("mmfpb_a_clk", mmfpb_a_clk.c, NULL),
- CLK_LOOKUP("mmfpb_a_clk", mmfpb_a_clk.c, "clock-8960"),
- CLK_LOOKUP("sfab_clk", sfab_clk.c, NULL),
- CLK_LOOKUP("sfab_a_clk", sfab_a_clk.c, NULL),
- CLK_LOOKUP("sfpb_clk", sfpb_clk.c, NULL),
- CLK_LOOKUP("sfpb_a_clk", sfpb_a_clk.c, NULL),
+ CLK_LOOKUP("bus_clk", afab_clk.c, "msm_apps_fab"),
+ CLK_LOOKUP("bus_a_clk", afab_a_clk.c, "msm_apps_fab"),
+ CLK_LOOKUP("bus_clk", cfpb_clk.c, "msm_cpss_fpb"),
+ CLK_LOOKUP("bus_a_clk", cfpb_a_clk.c, "msm_cpss_fpb"),
+ CLK_LOOKUP("bus_clk", sfab_clk.c, "msm_sys_fab"),
+ CLK_LOOKUP("bus_a_clk", sfab_a_clk.c, "msm_sys_fab"),
+ CLK_LOOKUP("bus_clk", sfpb_clk.c, "msm_sys_fpb"),
+ CLK_LOOKUP("bus_a_clk", sfpb_a_clk.c, "msm_sys_fpb"),
+ CLK_LOOKUP("bus_clk", mmfab_clk.c, "msm_mm_fab"),
+ CLK_LOOKUP("bus_a_clk", mmfab_a_clk.c, "msm_mm_fab"),
+ CLK_LOOKUP("mem_clk", ebi1_msmbus_clk.c, "msm_bus"),
+ CLK_LOOKUP("mem_a_clk", ebi1_a_clk.c, "msm_bus"),
+
+ CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL),
+ CLK_LOOKUP("dfab_clk", dfab_clk.c, NULL),
+ CLK_LOOKUP("dfab_a_clk", dfab_a_clk.c, NULL),
+ CLK_LOOKUP("mmfpb_clk", mmfpb_clk.c, NULL),
+ CLK_LOOKUP("mmfpb_a_clk", mmfpb_a_clk.c, "clock-8960"),
+ CLK_LOOKUP("cfpb_a_clk", cfpb_a_clk.c, "clock-8960"),
CLK_LOOKUP("core_clk", gp0_clk.c, NULL),
CLK_LOOKUP("core_clk", gp1_clk.c, NULL),
@@ -5407,6 +5499,7 @@
CLK_LOOKUP("cam_clk", cam0_clk.c, NULL),
CLK_LOOKUP("cam_clk", cam1_clk.c, NULL),
CLK_LOOKUP("cam_clk", cam0_clk.c, "msm_camera_imx074.0"),
+ CLK_LOOKUP("cam_clk", cam0_clk.c, "msm_camera_mt9m114.0"),
CLK_LOOKUP("cam_clk", cam0_clk.c, "msm_camera_ov2720.0"),
CLK_LOOKUP("csi_src_clk", csi0_src_clk.c, "msm_csid.0"),
CLK_LOOKUP("csi_src_clk", csi1_src_clk.c, "msm_csid.1"),
@@ -5534,8 +5627,8 @@
CLK_LOOKUP("bus_clk", dfab_sdc5_clk.c, "msm_sdcc.5"),
CLK_LOOKUP("dfab_clk", dfab_sps_clk.c, "msm_sps"),
CLK_LOOKUP("bus_clk", dfab_bam_dmux_clk.c, "BAM_RMNT"),
+ CLK_LOOKUP("bus_clk", dfab_scm_clk.c, "scm"),
- CLK_LOOKUP("ebi1_msmbus_clk", ebi1_msmbus_clk.c, NULL),
CLK_LOOKUP("mem_clk", ebi1_adm_clk.c, "msm_dmov"),
CLK_LOOKUP("l2_mclk", l2_m_clk, NULL),
@@ -5604,6 +5697,7 @@
static void __init reg_init(void)
{
+ void __iomem *imem_reg;
/* Deassert MM SW_RESET_ALL signal. */
writel_relaxed(0, SW_RESET_ALL_REG);
@@ -5612,12 +5706,21 @@
* reserved bits on the other SoC. Writing to these reserved bits
* should have no effect.
*/
- /* Initialize MM AHB registers: Enable the FPB clock and disable HW
- * gating for all clocks. Also set VFE_AHB's FORCE_CORE_ON bit to
- * prevent its memory from being collapsed when the clock is halted.
- * The sleep and wake-up delays are set to safe values. */
- rmwreg(0x00000003, AHB_EN_REG, 0x6C000103);
- writel_relaxed(0x000007F9, AHB_EN2_REG);
+ /*
+ * Initialize MM AHB registers: Enable the FPB clock and disable HW
+ * gating on 8960v1/8064 for all clocks. Also set VFE_AHB's
+ * FORCE_CORE_ON bit to prevent its memory from being collapsed when
+ * the clock is halted. The sleep and wake-up delays are set to safe
+ * values.
+ */
+ if (cpu_is_msm8960() &&
+ SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2) {
+ rmwreg(0x44000000, AHB_EN_REG, 0x6C000103);
+ writel_relaxed(0x3C7097F9, AHB_EN2_REG);
+ } else {
+ rmwreg(0x00000003, AHB_EN_REG, 0x6C000103);
+ writel_relaxed(0x000007F9, AHB_EN2_REG);
+ }
if (cpu_is_apq8064())
rmwreg(0x00000000, AHB_EN3_REG, 0x00000001);
@@ -5628,14 +5731,31 @@
/* Initialize MM AXI registers: Enable HW gating for all clocks that
* support it. Also set FORCE_CORE_ON bits, and any sleep and wake-up
* delays to safe values. */
- /* TODO: Enable HW Gating */
- rmwreg(0x000007F9, MAXI_EN_REG, 0x0803FFFF);
- rmwreg(0x3027FCFF, MAXI_EN2_REG, 0x3A3FFFFF);
+ if (cpu_is_msm8960() &&
+ SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 3) {
+ rmwreg(0x0003AFF9, MAXI_EN_REG, 0x0803FFFF);
+ rmwreg(0x3A27FCFF, MAXI_EN2_REG, 0x3A3FFFFF);
+ rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF);
+ } else {
+ rmwreg(0x000007F9, MAXI_EN_REG, 0x0803FFFF);
+ rmwreg(0x3027FCFF, MAXI_EN2_REG, 0x3A3FFFFF);
+ rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF);
+ }
rmwreg(0x0027FCFF, MAXI_EN3_REG, 0x003FFFFF);
- rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF);
if (cpu_is_apq8064())
rmwreg(0x009FE4FF, MAXI_EN5_REG, 0x01FFEFFF);
- rmwreg(0x000003C7, SAXI_EN_REG, 0x00003FFF);
+ if (cpu_is_msm8960() &&
+ SOCINFO_VERSION_MAJOR(socinfo_get_version()) >= 2)
+ rmwreg(0x00003C38, SAXI_EN_REG, 0x00003FFF);
+ else
+ rmwreg(0x000003C7, SAXI_EN_REG, 0x00003FFF);
+
+ /* Enable IMEM's clk_on signal */
+ imem_reg = ioremap(0x04b00040, 4);
+ if (imem_reg) {
+ writel_relaxed(0x3, imem_reg);
+ iounmap(imem_reg);
+ }
/* Initialize MM CC registers: Set MM FORCE_CORE_ON bits so that core
* memories retain state even when not clocked. Also, set sleep and
@@ -5646,7 +5766,7 @@
rmwreg(0x80FF0000, DSI2_BYTE_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, DSI_PIXEL_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, DSI2_PIXEL_CC_REG, 0xE0FF0010);
- rmwreg(0x80FF0000, GFX3D_CC_REG, 0xE0FF0010);
+ rmwreg(0xC0FF0000, GFX3D_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, IJPEG_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, JPEGD_CC_REG, 0xE0FF0010);
rmwreg(0x80FF0000, MDP_CC_REG, 0xE1FF0010);
@@ -5879,7 +5999,7 @@
pr_err("%s: msm_xo_get(PXO) failed.\n", __func__);
BUG();
}
- xo_cxo = msm_xo_get(MSM_XO_TCXO_D0, "clock-8960");
+ xo_cxo = msm_xo_get(MSM_XO_CXO, "clock-8960");
if (IS_ERR(xo_cxo)) {
pr_err("%s: msm_xo_get(CXO) failed.\n", __func__);
BUG();
diff --git a/arch/arm/mach-msm/clock-8x60.c b/arch/arm/mach-msm/clock-8x60.c
index 454e9cf..9af21d3 100644
--- a/arch/arm/mach-msm/clock-8x60.c
+++ b/arch/arm/mach-msm/clock-8x60.c
@@ -535,11 +535,6 @@
writel_relaxed(pll_mode, MM_PLL2_MODE_REG);
}
-static int soc_clk_reset(struct clk *clk, enum clk_reset_action action)
-{
- return branch_reset(&to_rcg_clk(clk)->b, action);
-}
-
static struct clk_ops clk_ops_rcg_8x60 = {
.enable = rcg_clk_enable,
.disable = rcg_clk_disable,
@@ -550,7 +545,7 @@
.list_rate = rcg_clk_list_rate,
.is_enabled = rcg_clk_is_enabled,
.round_rate = rcg_clk_round_rate,
- .reset = soc_clk_reset,
+ .reset = rcg_clk_reset,
.is_local = local_clk_is_local,
.get_parent = rcg_clk_get_parent,
};
@@ -3214,6 +3209,7 @@
static DEFINE_CLK_VOTER(dfab_sdc3_clk, &dfab_clk.c);
static DEFINE_CLK_VOTER(dfab_sdc4_clk, &dfab_clk.c);
static DEFINE_CLK_VOTER(dfab_sdc5_clk, &dfab_clk.c);
+static DEFINE_CLK_VOTER(dfab_scm_clk, &dfab_clk.c);
static DEFINE_CLK_VOTER(ebi1_msmbus_clk, &ebi1_clk.c);
static DEFINE_CLK_VOTER(ebi1_adm0_clk, &ebi1_clk.c);
@@ -3576,28 +3572,29 @@
static struct clk_lookup msm_clocks_8x60[] = {
CLK_LOOKUP("cxo", cxo_clk.c, NULL),
- CLK_LOOKUP("pll4", pll4_clk.c, NULL),
- CLK_LOOKUP("pll4", pll4_clk.c, "peripheral-reset"),
+ CLK_LOOKUP("pll4", pll4_clk.c, "pil_qdsp6v3"),
CLK_LOOKUP("measure", measure_clk.c, "debug"),
- CLK_LOOKUP("afab_clk", afab_clk.c, NULL),
- CLK_LOOKUP("afab_a_clk", afab_a_clk.c, NULL),
- CLK_LOOKUP("cfpb_clk", cfpb_clk.c, NULL),
- CLK_LOOKUP("cfpb_a_clk", cfpb_a_clk.c, NULL),
+ CLK_LOOKUP("bus_clk", afab_clk.c, "msm_apps_fab"),
+ CLK_LOOKUP("bus_a_clk", afab_a_clk.c, "msm_apps_fab"),
+ CLK_LOOKUP("bus_clk", sfab_clk.c, "msm_sys_fab"),
+ CLK_LOOKUP("bus_a_clk", sfab_a_clk.c, "msm_sys_fab"),
+ CLK_LOOKUP("bus_clk", sfpb_clk.c, "msm_sys_fpb"),
+ CLK_LOOKUP("bus_a_clk", sfpb_a_clk.c, "msm_sys_fpb"),
+ CLK_LOOKUP("bus_clk", mmfab_clk.c, "msm_mm_fab"),
+ CLK_LOOKUP("bus_a_clk", mmfab_a_clk.c, "msm_mm_fab"),
+ CLK_LOOKUP("bus_clk", cfpb_clk.c, "msm_cpss_fpb"),
+ CLK_LOOKUP("bus_a_clk", cfpb_a_clk.c, "msm_cpss_fpb"),
+ CLK_LOOKUP("mem_clk", ebi1_msmbus_clk.c, "msm_bus"),
+ CLK_LOOKUP("mem_a_clk", ebi1_a_clk.c, "msm_bus"),
+ CLK_LOOKUP("smi_clk", smi_clk.c, "msm_bus"),
+ CLK_LOOKUP("smi_a_clk", smi_a_clk.c, "msm_bus"),
+
+ CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL),
CLK_LOOKUP("dfab_clk", dfab_clk.c, NULL),
CLK_LOOKUP("dfab_a_clk", dfab_a_clk.c, NULL),
- CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL),
- CLK_LOOKUP("ebi1_a_clk", ebi1_a_clk.c, NULL),
- CLK_LOOKUP("mmfab_clk", mmfab_clk.c, NULL),
- CLK_LOOKUP("mmfab_a_clk", mmfab_a_clk.c, NULL),
CLK_LOOKUP("mmfpb_clk", mmfpb_clk.c, NULL),
CLK_LOOKUP("mmfpb_a_clk", mmfpb_a_clk.c, NULL),
- CLK_LOOKUP("sfab_clk", sfab_clk.c, NULL),
- CLK_LOOKUP("sfab_a_clk", sfab_a_clk.c, NULL),
- CLK_LOOKUP("sfpb_clk", sfpb_clk.c, NULL),
- CLK_LOOKUP("sfpb_a_clk", sfpb_a_clk.c, NULL),
- CLK_LOOKUP("smi_clk", smi_clk.c, NULL),
- CLK_LOOKUP("smi_a_clk", smi_a_clk.c, NULL),
CLK_LOOKUP("core_clk", gp0_clk.c, NULL),
CLK_LOOKUP("core_clk", gp1_clk.c, NULL),
@@ -3792,8 +3789,8 @@
CLK_LOOKUP("bus_clk", dfab_sdc3_clk.c, "msm_sdcc.3"),
CLK_LOOKUP("bus_clk", dfab_sdc4_clk.c, "msm_sdcc.4"),
CLK_LOOKUP("bus_clk", dfab_sdc5_clk.c, "msm_sdcc.5"),
+ CLK_LOOKUP("bus_clk", dfab_scm_clk.c, "scm"),
- CLK_LOOKUP("ebi1_msmbus_clk", ebi1_msmbus_clk.c, NULL),
CLK_LOOKUP("mem_clk", ebi1_adm0_clk.c, "msm_dmov.0"),
CLK_LOOKUP("mem_clk", ebi1_adm1_clk.c, "msm_dmov.1"),
@@ -3908,7 +3905,7 @@
pr_err("%s: msm_xo_get(PXO) failed.\n", __func__);
BUG();
}
- xo_cxo = msm_xo_get(MSM_XO_TCXO_D1, "clock-8x60");
+ xo_cxo = msm_xo_get(MSM_XO_CXO, "clock-8x60");
if (IS_ERR(xo_cxo)) {
pr_err("%s: msm_xo_get(CXO) failed.\n", __func__);
BUG();
diff --git a/arch/arm/mach-msm/clock-9615.c b/arch/arm/mach-msm/clock-9615.c
index 553fb4d..c712afc 100644
--- a/arch/arm/mach-msm/clock-9615.c
+++ b/arch/arm/mach-msm/clock-9615.c
@@ -414,11 +414,6 @@
},
};
-static int soc_clk_reset(struct clk *clk, enum clk_reset_action action)
-{
- return branch_reset(&to_rcg_clk(clk)->b, action);
-}
-
static struct clk_ops clk_ops_rcg_9615 = {
.enable = rcg_clk_enable,
.disable = rcg_clk_disable,
@@ -428,7 +423,7 @@
.list_rate = rcg_clk_list_rate,
.is_enabled = rcg_clk_is_enabled,
.round_rate = rcg_clk_round_rate,
- .reset = soc_clk_reset,
+ .reset = rcg_clk_reset,
.is_local = local_clk_is_local,
.get_parent = rcg_clk_get_parent,
};
@@ -1631,16 +1626,18 @@
CLK_LOOKUP("measure", measure_clk.c, "debug"),
- CLK_LOOKUP("cfpb_clk", cfpb_clk.c, NULL),
- CLK_LOOKUP("cfpb_a_clk", cfpb_a_clk.c, NULL),
+ CLK_LOOKUP("bus_clk", sfab_clk.c, "msm_sys_fab"),
+ CLK_LOOKUP("bus_a_clk", sfab_a_clk.c, "msm_sys_fab"),
+ CLK_LOOKUP("mem_clk", ebi1_msmbus_clk.c, "msm_bus"),
+ CLK_LOOKUP("mem_a_clk", ebi1_a_clk.c, "msm_bus"),
+
+ CLK_LOOKUP("bus_clk", sfpb_clk.c, NULL),
+ CLK_LOOKUP("bus_a_clk", sfpb_a_clk.c, NULL),
+ CLK_LOOKUP("bus_clk", cfpb_clk.c, NULL),
+ CLK_LOOKUP("bus_a_clk", cfpb_a_clk.c, NULL),
+ CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL),
CLK_LOOKUP("dfab_clk", dfab_clk.c, NULL),
CLK_LOOKUP("dfab_a_clk", dfab_a_clk.c, NULL),
- CLK_LOOKUP("ebi1_clk", ebi1_clk.c, NULL),
- CLK_LOOKUP("ebi1_a_clk", ebi1_a_clk.c, NULL),
- CLK_LOOKUP("sfab_clk", sfab_clk.c, NULL),
- CLK_LOOKUP("sfab_a_clk", sfab_a_clk.c, NULL),
- CLK_LOOKUP("sfpb_clk", sfpb_clk.c, NULL),
- CLK_LOOKUP("sfpb_a_clk", sfpb_a_clk.c, NULL),
CLK_LOOKUP("core_clk", gp0_clk.c, NULL),
CLK_LOOKUP("core_clk", gp1_clk.c, NULL),
@@ -1703,7 +1700,6 @@
CLK_LOOKUP("bus_clk", dfab_sdc2_clk.c, "msm_sdcc.2"),
CLK_LOOKUP("dfab_clk", dfab_sps_clk.c, "msm_sps"),
CLK_LOOKUP("bus_clk", dfab_bam_dmux_clk.c, "BAM_RMNT"),
- CLK_LOOKUP("ebi1_msmbus_clk", ebi1_msmbus_clk.c, NULL),
CLK_LOOKUP("mem_clk", ebi1_adm_clk.c, "msm_dmov"),
CLK_LOOKUP("iface_clk", ce1_p_clk.c, "qce.0"),
@@ -1856,7 +1852,7 @@
/* Local clock driver initialization. */
static void __init msm9615_clock_init(void)
{
- xo_cxo = msm_xo_get(MSM_XO_TCXO_D0, "clock-9615");
+ xo_cxo = msm_xo_get(MSM_XO_CXO, "clock-9615");
if (IS_ERR(xo_cxo)) {
pr_err("%s: msm_xo_get(CXO) failed.\n", __func__);
BUG();
diff --git a/arch/arm/mach-msm/clock-debug.c b/arch/arm/mach-msm/clock-debug.c
index 4990c81..411a272 100644
--- a/arch/arm/mach-msm/clock-debug.c
+++ b/arch/arm/mach-msm/clock-debug.c
@@ -54,12 +54,31 @@
static int clock_debug_measure_get(void *data, u64 *val)
{
- int ret;
struct clk *clock = data;
+ int ret, is_hw_gated;
+
+ /* Check to see if the clock is in hardware gating mode */
+ if (clock->flags & CLKFLAG_HWCG)
+ is_hw_gated = clock->ops->in_hwcg_mode(clock);
+ else
+ is_hw_gated = 0;
ret = clk_set_parent(measure, clock);
- if (!ret)
+ if (!ret) {
+ /*
+ * Disable hw gating to get accurate rate measurements. Only do
+ * this if the clock is explictly enabled by software. This
+ * allows us to detect errors where clocks are on even though
+ * software is not requesting them to be on due to broken
+ * hardware gating signals.
+ */
+ if (is_hw_gated && clock->count)
+ clock->ops->disable_hwcg(clock);
*val = clk_get_rate(measure);
+ /* Reenable hwgating if it was disabled */
+ if (is_hw_gated && clock->count)
+ clock->ops->enable_hwcg(clock);
+ }
return ret;
}
@@ -109,6 +128,16 @@
DEFINE_SIMPLE_ATTRIBUTE(clock_local_fops, clock_debug_local_get,
NULL, "%llu\n");
+static int clock_debug_hwcg_get(void *data, u64 *val)
+{
+ struct clk *clock = data;
+ *val = !!(clock->flags & CLKFLAG_HWCG);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(clock_hwcg_fops, clock_debug_hwcg_get,
+ NULL, "%llu\n");
+
static struct dentry *debugfs_base;
static u32 debug_suspend;
static struct clk_lookup *msm_clocks;
@@ -239,6 +268,10 @@
&clock_local_fops))
goto error;
+ if (!debugfs_create_file("has_hw_gating", S_IRUGO, clk_dir, clock,
+ &clock_hwcg_fops))
+ goto error;
+
if (measure &&
!clk_set_parent(measure, clock) &&
!debugfs_create_file("measure", S_IRUGO, clk_dir, clock,
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index f8d84be..2a1c013 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -273,6 +273,14 @@
return invert ? !status_bit : status_bit;
}
+int branch_in_hwcg_mode(const struct branch *b)
+{
+ if (!b->hwcg_mask)
+ return 0;
+
+ return !!(readl_relaxed(b->hwcg_reg) & b->hwcg_mask);
+}
+
void __branch_clk_enable_reg(const struct branch *clk, const char *name)
{
u32 reg_val;
@@ -291,6 +299,10 @@
*/
mb();
+ /* Skip checking halt bit if the clock is in hardware gated mode */
+ if (branch_in_hwcg_mode(clk))
+ return;
+
/* Wait for clock to enable before returning. */
if (clk->halt_check == DELAY)
udelay(HALT_CHECK_DELAY_US);
@@ -362,6 +374,10 @@
*/
mb();
+ /* Skip checking halt bit if the clock is in hardware gated mode */
+ if (branch_in_hwcg_mode(clk))
+ return reg_val;
+
/* Wait for clock to disable before continuing. */
if (clk->halt_check == DELAY || clk->halt_check == ENABLE_VOTED
|| clk->halt_check == HALT_VOTED)
@@ -584,12 +600,32 @@
return to_rcg_clk(clk)->current_freq->src_clk;
}
+/* Disable hw clock gating if not set at boot */
+static void branch_handoff(struct branch *clk, struct clk *c)
+{
+ if (!branch_in_hwcg_mode(clk)) {
+ clk->hwcg_mask = 0;
+ c->flags &= ~CLKFLAG_HWCG;
+ } else {
+ c->flags |= CLKFLAG_HWCG;
+ }
+}
+
+int branch_clk_handoff(struct clk *c)
+{
+ struct branch_clk *clk = to_branch_clk(c);
+ branch_handoff(&clk->b, &clk->c);
+ return 0;
+}
+
int rcg_clk_handoff(struct clk *c)
{
struct rcg_clk *clk = to_rcg_clk(c);
uint32_t ctl_val, ns_val, md_val, ns_mask;
struct clk_freq_tbl *freq;
+ branch_handoff(&clk->b, &clk->c);
+
ctl_val = readl_relaxed(clk->b.ctl_reg);
if (!(ctl_val & clk->root_en_mask))
return 0;
@@ -859,35 +895,100 @@
return branch->enabled;
}
-int branch_reset(struct branch *clk, enum clk_reset_action action)
+static void branch_enable_hwcg(struct branch *b)
+{
+ unsigned long flags;
+ u32 reg_val;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ reg_val = readl_relaxed(b->hwcg_reg);
+ reg_val |= b->hwcg_mask;
+ writel_relaxed(reg_val, b->hwcg_reg);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+static void branch_disable_hwcg(struct branch *b)
+{
+ unsigned long flags;
+ u32 reg_val;
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ reg_val = readl_relaxed(b->hwcg_reg);
+ reg_val &= ~b->hwcg_mask;
+ writel_relaxed(reg_val, b->hwcg_reg);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+void branch_clk_enable_hwcg(struct clk *clk)
+{
+ struct branch_clk *branch = to_branch_clk(clk);
+ branch_enable_hwcg(&branch->b);
+}
+
+void branch_clk_disable_hwcg(struct clk *clk)
+{
+ struct branch_clk *branch = to_branch_clk(clk);
+ branch_disable_hwcg(&branch->b);
+}
+
+int branch_clk_in_hwcg_mode(struct clk *c)
+{
+ struct branch_clk *clk = to_branch_clk(c);
+ return branch_in_hwcg_mode(&clk->b);
+}
+
+void rcg_clk_enable_hwcg(struct clk *clk)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ branch_enable_hwcg(&rcg->b);
+}
+
+void rcg_clk_disable_hwcg(struct clk *clk)
+{
+ struct rcg_clk *rcg = to_rcg_clk(clk);
+ branch_disable_hwcg(&rcg->b);
+}
+
+int rcg_clk_in_hwcg_mode(struct clk *c)
+{
+ struct rcg_clk *clk = to_rcg_clk(c);
+ return branch_in_hwcg_mode(&clk->b);
+}
+
+int branch_reset(struct branch *b, enum clk_reset_action action)
{
int ret = 0;
u32 reg_val;
unsigned long flags;
- if (!clk->reset_reg)
+ if (!b->reset_reg)
return -EPERM;
- spin_lock_irqsave(&local_clock_reg_lock, flags);
+ /* Disable hw gating when asserting a reset */
+ if (b->hwcg_mask && action == CLK_RESET_ASSERT)
+ branch_disable_hwcg(b);
- reg_val = readl_relaxed(clk->reset_reg);
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ /* Assert/Deassert reset */
+ reg_val = readl_relaxed(b->reset_reg);
switch (action) {
case CLK_RESET_ASSERT:
- reg_val |= clk->reset_mask;
+ reg_val |= b->reset_mask;
break;
case CLK_RESET_DEASSERT:
- reg_val &= ~(clk->reset_mask);
+ reg_val &= ~b->reset_mask;
break;
default:
ret = -EINVAL;
}
- writel_relaxed(reg_val, clk->reset_reg);
-
+ writel_relaxed(reg_val, b->reset_reg);
spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+ /* Enable hw gating when deasserting a reset */
+ if (b->hwcg_mask && action == CLK_RESET_DEASSERT)
+ branch_enable_hwcg(b);
/* Make sure write is issued before returning. */
mb();
-
return ret;
}
@@ -896,6 +997,11 @@
return branch_reset(&to_branch_clk(clk)->b, action);
}
+int rcg_clk_reset(struct clk *clk, enum clk_reset_action action)
+{
+ return branch_reset(&to_rcg_clk(clk)->b, action);
+}
+
static int cdiv_clk_enable(struct clk *c)
{
unsigned long flags;
@@ -967,6 +1073,8 @@
struct cdiv_clk *clk = to_cdiv_clk(c);
u32 reg_val;
+ branch_handoff(&clk->b, &clk->c);
+
reg_val = readl_relaxed(clk->ns_reg);
if (reg_val & clk->ext_mask) {
clk->cur_div = 0;
@@ -978,9 +1086,30 @@
return 0;
}
+static void cdiv_clk_enable_hwcg(struct clk *c)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ branch_enable_hwcg(&clk->b);
+}
+
+static void cdiv_clk_disable_hwcg(struct clk *c)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ branch_disable_hwcg(&clk->b);
+}
+
+static int cdiv_clk_in_hwcg_mode(struct clk *c)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ return branch_in_hwcg_mode(&clk->b);
+}
+
struct clk_ops clk_ops_cdiv = {
.enable = cdiv_clk_enable,
.disable = cdiv_clk_disable,
+ .in_hwcg_mode = cdiv_clk_in_hwcg_mode,
+ .enable_hwcg = cdiv_clk_enable_hwcg,
+ .disable_hwcg = cdiv_clk_disable_hwcg,
.auto_off = cdiv_clk_disable,
.handoff = cdiv_clk_handoff,
.set_rate = cdiv_clk_set_rate,
diff --git a/arch/arm/mach-msm/clock-local.h b/arch/arm/mach-msm/clock-local.h
index 2123513..a561802d 100644
--- a/arch/arm/mach-msm/clock-local.h
+++ b/arch/arm/mach-msm/clock-local.h
@@ -90,6 +90,8 @@
* struct branch - branch on/off
* @ctl_reg: clock control register
* @en_mask: ORed with @ctl_reg to enable the clock
+ * @hwcg_reg: hardware clock gating register
+ * @hwcg_mask: ORed with @hwcg_reg to enable hardware clock gating
* @halt_reg: halt register
* @halt_check: type of halt check to perform
* @halt_bit: ANDed with @halt_reg to test for clock halted
@@ -100,6 +102,9 @@
void __iomem *const ctl_reg;
const u32 en_mask;
+ void __iomem *hwcg_reg;
+ u32 hwcg_mask;
+
void __iomem *const halt_reg;
const u16 halt_check;
const u16 halt_bit;
@@ -108,9 +113,10 @@
const u32 reset_mask;
};
-int branch_reset(struct branch *clk, enum clk_reset_action action);
+int branch_reset(struct branch *b, enum clk_reset_action action);
void __branch_clk_enable_reg(const struct branch *clk, const char *name);
u32 __branch_clk_disable_reg(const struct branch *clk, const char *name);
+int branch_clk_handoff(struct clk *c);
/*
* Generic clock-definition struct and macros
@@ -150,6 +156,10 @@
long rcg_clk_round_rate(struct clk *clk, unsigned long rate);
struct clk *rcg_clk_get_parent(struct clk *c);
int rcg_clk_handoff(struct clk *c);
+int rcg_clk_reset(struct clk *clk, enum clk_reset_action action);
+void rcg_clk_enable_hwcg(struct clk *clk);
+void rcg_clk_disable_hwcg(struct clk *clk);
+int rcg_clk_in_hwcg_mode(struct clk *c);
/**
* struct cdiv_clk - integer divider clock with external source selection
@@ -286,6 +296,9 @@
int branch_clk_set_parent(struct clk *clk, struct clk *parent);
int branch_clk_is_enabled(struct clk *clk);
int branch_clk_reset(struct clk *c, enum clk_reset_action action);
+void branch_clk_enable_hwcg(struct clk *clk);
+void branch_clk_disable_hwcg(struct clk *clk);
+int branch_clk_in_hwcg_mode(struct clk *c);
/**
* struct measure_clk - for rate measurement debug use
diff --git a/arch/arm/mach-msm/clock.h b/arch/arm/mach-msm/clock.h
index ec8ff6c..91121e6 100644
--- a/arch/arm/mach-msm/clock.h
+++ b/arch/arm/mach-msm/clock.h
@@ -29,6 +29,7 @@
#define CLKFLAG_NONEST 0x00000004
#define CLKFLAG_NORESET 0x00000008
#define CLKFLAG_HANDOFF_RATE 0x00000010
+#define CLKFLAG_HWCG 0x00000020
#define CLKFLAG_SKIP_AUTO_OFF 0x00000200
#define CLKFLAG_MIN 0x00000400
#define CLKFLAG_MAX 0x00000800
@@ -63,6 +64,9 @@
int (*enable)(struct clk *clk);
void (*disable)(struct clk *clk);
void (*auto_off)(struct clk *clk);
+ void (*enable_hwcg)(struct clk *clk);
+ void (*disable_hwcg)(struct clk *clk);
+ int (*in_hwcg_mode)(struct clk *clk);
int (*handoff)(struct clk *clk);
int (*reset)(struct clk *clk, enum clk_reset_action action);
int (*set_rate)(struct clk *clk, unsigned long rate);
diff --git a/arch/arm/mach-msm/devices-8064.c b/arch/arm/mach-msm/devices-8064.c
index 7420bc0..b0f4c9f 100644
--- a/arch/arm/mach-msm/devices-8064.c
+++ b/arch/arm/mach-msm/devices-8064.c
@@ -22,6 +22,8 @@
#include <mach/usbdiag.h>
#include <mach/msm_sps.h>
#include <mach/dma.h>
+#include <sound/msm-dai-q6.h>
+#include <sound/apr_audio.h>
#include "clock.h"
#include "devices.h"
#include "msm_watchdog.h"
@@ -206,6 +208,134 @@
.resource = resources_qup_spi_gsbi5,
};
+struct platform_device apq_pcm = {
+ .name = "msm-pcm-dsp",
+ .id = -1,
+};
+
+struct platform_device apq_pcm_routing = {
+ .name = "msm-pcm-routing",
+ .id = -1,
+};
+
+struct platform_device apq_cpudai0 = {
+ .name = "msm-dai-q6",
+ .id = 0x4000,
+};
+
+struct platform_device apq_cpudai1 = {
+ .name = "msm-dai-q6",
+ .id = 0x4001,
+};
+
+struct platform_device apq_cpudai_hdmi_rx = {
+ .name = "msm-dai-q6",
+ .id = 8,
+};
+
+struct platform_device apq_cpudai_bt_rx = {
+ .name = "msm-dai-q6",
+ .id = 0x3000,
+};
+
+struct platform_device apq_cpudai_bt_tx = {
+ .name = "msm-dai-q6",
+ .id = 0x3001,
+};
+
+struct platform_device apq_cpudai_fm_rx = {
+ .name = "msm-dai-q6",
+ .id = 0x3004,
+};
+
+struct platform_device apq_cpudai_fm_tx = {
+ .name = "msm-dai-q6",
+ .id = 0x3005,
+};
+
+/*
+ * Machine specific data for AUX PCM Interface
+ * which the driver will be unware of.
+ */
+struct msm_dai_auxpcm_pdata apq_auxpcm_rx_pdata = {
+ .clk = "pcm_clk",
+ .mode = AFE_PCM_CFG_MODE_PCM,
+ .sync = AFE_PCM_CFG_SYNC_INT,
+ .frame = AFE_PCM_CFG_FRM_256BPF,
+ .quant = AFE_PCM_CFG_QUANT_LINEAR_NOPAD,
+ .slot = 0,
+ .data = AFE_PCM_CFG_CDATAOE_MASTER,
+ .pcm_clk_rate = 2048000,
+};
+
+struct platform_device apq_cpudai_auxpcm_rx = {
+ .name = "msm-dai-q6",
+ .id = 2,
+ .dev = {
+ .platform_data = &apq_auxpcm_rx_pdata,
+ },
+};
+
+struct platform_device apq_cpudai_auxpcm_tx = {
+ .name = "msm-dai-q6",
+ .id = 3,
+};
+
+struct platform_device apq_cpu_fe = {
+ .name = "msm-dai-fe",
+ .id = -1,
+};
+
+struct platform_device apq_stub_codec = {
+ .name = "msm-stub-codec",
+ .id = 1,
+};
+
+struct platform_device apq_voice = {
+ .name = "msm-pcm-voice",
+ .id = -1,
+};
+
+struct platform_device apq_voip = {
+ .name = "msm-voip-dsp",
+ .id = -1,
+};
+
+struct platform_device apq_lpa_pcm = {
+ .name = "msm-pcm-lpa",
+ .id = -1,
+};
+
+struct platform_device apq_pcm_hostless = {
+ .name = "msm-pcm-hostless",
+ .id = -1,
+};
+
+struct platform_device apq_cpudai_afe_01_rx = {
+ .name = "msm-dai-q6",
+ .id = 0xE0,
+};
+
+struct platform_device apq_cpudai_afe_01_tx = {
+ .name = "msm-dai-q6",
+ .id = 0xF0,
+};
+
+struct platform_device apq_cpudai_afe_02_rx = {
+ .name = "msm-dai-q6",
+ .id = 0xF1,
+};
+
+struct platform_device apq_cpudai_afe_02_tx = {
+ .name = "msm-dai-q6",
+ .id = 0xE1,
+};
+
+struct platform_device apq_pcm_afe = {
+ .name = "msm-pcm-afe",
+ .id = -1,
+};
+
static struct resource resources_ssbi_pmic1[] = {
{
.start = MSM_PMIC1_SSBI_CMD_PHYS,
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 208ed4e..2d06d97 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -564,6 +564,7 @@
.memtype = MEMTYPE_EBI1,
.enable_ion = 0,
#endif
+ .disable_dmx = 0,
};
struct platform_device msm_device_vidc = {
@@ -872,7 +873,7 @@
.strap_ahb_lower = 0x00000080,
.aclk_reg = SFAB_MSS_Q6_FW_ACLK_CTL,
.jtag_clk_reg = MSS_Q6FW_JTAG_CLK_CTL,
- .xo_id = MSM_XO_TCXO_D0,
+ .xo_id = MSM_XO_CXO,
.name = "modem_fw",
.depends = "q6",
.pas_id = PAS_MODEM_FW,
@@ -910,7 +911,7 @@
.strap_ahb_lower = 0x00000080,
.aclk_reg = SFAB_MSS_Q6_SW_ACLK_CTL,
.jtag_clk_reg = MSS_Q6SW_JTAG_CLK_CTL,
- .xo_id = MSM_XO_TCXO_D0,
+ .xo_id = MSM_XO_CXO,
.name = "modem",
.depends = "modem_fw",
.pas_id = PAS_MODEM_SW,
@@ -925,6 +926,21 @@
.dev.platform_data = &msm_8960_q6_mss_sw_data,
};
+static struct resource msm_8960_riva_resources[] = {
+ {
+ .start = 0x03204000,
+ .end = 0x03204000 + SZ_256 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device msm_8960_riva = {
+ .name = "pil_riva",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(msm_8960_riva_resources),
+ .resource = msm_8960_riva_resources,
+};
+
struct platform_device msm_device_smd = {
.name = "msm_smd",
.id = -1,
@@ -1404,6 +1420,21 @@
.id = 0x3005,
};
+struct platform_device msm_cpudai_incall_music_rx = {
+ .name = "msm-dai-q6",
+ .id = 0x8005,
+};
+
+struct platform_device msm_cpudai_incall_record_rx = {
+ .name = "msm-dai-q6",
+ .id = 0x8004,
+};
+
+struct platform_device msm_cpudai_incall_record_tx = {
+ .name = "msm-dai-q6",
+ .id = 0x8003,
+};
+
/*
* Machine specific data for AUX PCM Interface
* which the driver will be unware of.
@@ -2089,7 +2120,7 @@
},
};
-static struct msm_bus_vectors grp2d0_max_vectors[] = {
+static struct msm_bus_vectors grp2d0_nominal_vectors[] = {
{
.src = MSM_BUS_MASTER_GRAPHICS_2D_CORE0,
.dst = MSM_BUS_SLAVE_EBI_CH0,
@@ -2098,12 +2129,25 @@
},
};
+static struct msm_bus_vectors grp2d0_max_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_GRAPHICS_2D_CORE0,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = KGSL_CONVERT_TO_MBPS(2048),
+ },
+};
+
static struct msm_bus_paths grp2d0_bus_scale_usecases[] = {
{
ARRAY_SIZE(grp2d0_init_vectors),
grp2d0_init_vectors,
},
{
+ ARRAY_SIZE(grp2d0_nominal_vectors),
+ grp2d0_nominal_vectors,
+ },
+ {
ARRAY_SIZE(grp2d0_max_vectors),
grp2d0_max_vectors,
},
@@ -2124,7 +2168,7 @@
},
};
-static struct msm_bus_vectors grp2d1_max_vectors[] = {
+static struct msm_bus_vectors grp2d1_nominal_vectors[] = {
{
.src = MSM_BUS_MASTER_GRAPHICS_2D_CORE1,
.dst = MSM_BUS_SLAVE_EBI_CH0,
@@ -2133,12 +2177,25 @@
},
};
+static struct msm_bus_vectors grp2d1_max_vectors[] = {
+ {
+ .src = MSM_BUS_MASTER_GRAPHICS_2D_CORE1,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ab = 0,
+ .ib = KGSL_CONVERT_TO_MBPS(2048),
+ },
+};
+
static struct msm_bus_paths grp2d1_bus_scale_usecases[] = {
{
ARRAY_SIZE(grp2d1_init_vectors),
grp2d1_init_vectors,
},
{
+ ARRAY_SIZE(grp2d1_nominal_vectors),
+ grp2d1_nominal_vectors,
+ },
+ {
ARRAY_SIZE(grp2d1_max_vectors),
grp2d1_max_vectors,
},
@@ -2235,17 +2292,21 @@
.pwrlevel = {
{
.gpu_freq = 200000000,
+ .bus_freq = 2,
+ },
+ {
+ .gpu_freq = 96000000,
.bus_freq = 1,
},
{
- .gpu_freq = 200000000,
+ .gpu_freq = 27000000,
.bus_freq = 0,
},
},
.init_level = 0,
- .num_levels = 2,
+ .num_levels = 3,
.set_grp_async = NULL,
- .idle_timeout = HZ/10,
+ .idle_timeout = HZ/5,
.nap_allowed = true,
.clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
@@ -2284,17 +2345,21 @@
.pwrlevel = {
{
.gpu_freq = 200000000,
+ .bus_freq = 2,
+ },
+ {
+ .gpu_freq = 96000000,
.bus_freq = 1,
},
{
- .gpu_freq = 200000000,
+ .gpu_freq = 27000000,
.bus_freq = 0,
},
},
.init_level = 0,
- .num_levels = 2,
+ .num_levels = 3,
.set_grp_async = NULL,
- .idle_timeout = HZ/10,
+ .idle_timeout = HZ/5,
.nap_allowed = true,
.clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
diff --git a/arch/arm/mach-msm/devices-9615.c b/arch/arm/mach-msm/devices-9615.c
index 012eb85..3627e0a 100644
--- a/arch/arm/mach-msm/devices-9615.c
+++ b/arch/arm/mach-msm/devices-9615.c
@@ -99,6 +99,9 @@
},
};
+#define MSM_USB_BAM_BASE 0x12502000
+#define MSM_USB_BAM_SIZE 0x3DFFF
+
static struct resource resources_otg[] = {
{
.start = MSM9615_HSUSB_PHYS,
@@ -135,6 +138,28 @@
},
};
+static struct resource resources_usb_bam[] = {
+ {
+ .name = "usb_bam_addr",
+ .start = MSM_USB_BAM_BASE,
+ .end = MSM_USB_BAM_BASE + MSM_USB_BAM_SIZE,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "usb_bam_irq",
+ .start = USB1_HS_BAM_IRQ,
+ .end = USB1_HS_BAM_IRQ,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+struct platform_device msm_device_usb_bam = {
+ .name = "usb_bam",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(resources_usb_bam),
+ .resource = resources_usb_bam,
+};
+
struct platform_device msm_device_gadget_peripheral = {
.name = "msm_hsusb",
.id = -1,
@@ -455,6 +480,7 @@
.shared_ce_resource = QCE_SHARE_CE_RESOURCE,
.hw_key_support = QCE_HW_KEY_SUPPORT,
.sha_hmac = QCE_SHA_HMAC_SUPPORT,
+ .bus_scale_table = NULL,
};
struct platform_device msm9615_qcrypto_device = {
@@ -477,6 +503,7 @@
.shared_ce_resource = QCE_SHARE_CE_RESOURCE,
.hw_key_support = QCE_HW_KEY_SUPPORT,
.sha_hmac = QCE_SHA_HMAC_SUPPORT,
+ .bus_scale_table = NULL,
};
struct platform_device msm9615_qcedev_device = {
diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c
index 41136d2..2c21f57 100644
--- a/arch/arm/mach-msm/devices-msm7x30.c
+++ b/arch/arm/mach-msm/devices-msm7x30.c
@@ -790,7 +790,8 @@
struct msm_vidc_platform_data vidc_platform_data = {
.memtype = MEMTYPE_EBI0,
- .enable_ion = 0
+ .enable_ion = 0,
+ .disable_dmx = 0
};
struct platform_device msm_device_vidc_720p = {
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index b9ff604a..aa9e380 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -188,6 +188,23 @@
}
}
+#define MSM_LPASS_QDSP6SS_PHYS 0x28800000
+
+static struct resource msm_8660_q6_resources[] = {
+ {
+ .start = MSM_LPASS_QDSP6SS_PHYS,
+ .end = MSM_LPASS_QDSP6SS_PHYS + SZ_256 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+struct platform_device msm_pil_q6v3 = {
+ .name = "pil_qdsp6v3",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(msm_8660_q6_resources),
+ .resource = msm_8660_q6_resources,
+};
+
static struct resource msm_uart1_dm_resources[] = {
{
.start = MSM_UART1DM_PHYS,
@@ -2129,6 +2146,7 @@
.memtype = MEMTYPE_SMI_KERNEL,
.enable_ion = 0,
#endif
+ .disable_dmx = 0
};
struct platform_device msm_device_vidc = {
diff --git a/arch/arm/mach-msm/devices.h b/arch/arm/mach-msm/devices.h
index 2fac2ef..0d6aa09 100644
--- a/arch/arm/mach-msm/devices.h
+++ b/arch/arm/mach-msm/devices.h
@@ -118,6 +118,7 @@
extern struct platform_device msm_slim_ctrl;
extern struct platform_device msm_device_sps;
+extern struct platform_device msm_device_usb_bam;
extern struct platform_device msm_device_sps_apq8064;
extern struct platform_device msm_device_bam_dmux;
extern struct platform_device msm_device_smd;
@@ -172,10 +173,38 @@
extern struct platform_device msm_cpudai_afe_02_tx;
extern struct platform_device msm_pcm_afe;
extern struct platform_device msm_compr_dsp;
+extern struct platform_device msm_cpudai_incall_music_rx;
+extern struct platform_device msm_cpudai_incall_record_rx;
+extern struct platform_device msm_cpudai_incall_record_tx;
+extern struct platform_device msm_pil_q6v3;
extern struct platform_device msm_8960_q6_lpass;
extern struct platform_device msm_8960_q6_mss_fw;
extern struct platform_device msm_8960_q6_mss_sw;
+extern struct platform_device msm_8960_riva;
+
+extern struct platform_device apq_pcm;
+extern struct platform_device apq_pcm_routing;
+extern struct platform_device apq_cpudai0;
+extern struct platform_device apq_cpudai1;
+extern struct platform_device apq_cpudai_hdmi_rx;
+extern struct platform_device apq_cpudai_bt_rx;
+extern struct platform_device apq_cpudai_bt_tx;
+extern struct platform_device apq_cpudai_fm_rx;
+extern struct platform_device apq_cpudai_fm_tx;
+extern struct platform_device apq_cpudai_auxpcm_rx;
+extern struct platform_device apq_cpudai_auxpcm_tx;
+extern struct platform_device apq_cpu_fe;
+extern struct platform_device apq_stub_codec;
+extern struct platform_device apq_voice;
+extern struct platform_device apq_voip;
+extern struct platform_device apq_lpa_pcm;
+extern struct platform_device apq_pcm_hostless;
+extern struct platform_device apq_cpudai_afe_01_rx;
+extern struct platform_device apq_cpudai_afe_01_tx;
+extern struct platform_device apq_cpudai_afe_02_rx;
+extern struct platform_device apq_cpudai_afe_02_tx;
+extern struct platform_device apq_pcm_afe;
extern struct platform_device *msm_footswitch_devices[];
extern unsigned msm_num_footswitch_devices;
diff --git a/arch/arm/mach-msm/idle-v7.S b/arch/arm/mach-msm/idle-v7.S
index 40e13fa..e855357 100644
--- a/arch/arm/mach-msm/idle-v7.S
+++ b/arch/arm/mach-msm/idle-v7.S
@@ -212,8 +212,17 @@
mcr p15, 0, r3, c8, c7, 0 /* UTLBIALL */
mcr p15, 0, r3, c7, c5, 6 /* BPIALL */
dsb
-
isb
+#ifdef CONFIG_ARCH_MSM_KRAIT
+ mrc p15, 0, r1, c0, c0, 0
+ ldr r3, =0xff00fc00
+ and r3, r1, r3
+ ldr r1, =0x51000400
+ cmp r3, r1
+ mrceq p15, 7, r3, c15, c0, 2
+ biceq r3, r3, #0x400
+ mcreq p15, 7, r3, c15, c0, 2
+#endif
stmfd sp!, {lr}
bl v7_flush_kern_cache_all
#ifdef CONFIG_MSM_TRACE_ACROSS_PC
diff --git a/arch/arm/mach-msm/idle.h b/arch/arm/mach-msm/idle.h
index 753a47c..830e36b 100644
--- a/arch/arm/mach-msm/idle.h
+++ b/arch/arm/mach-msm/idle.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2007-2009, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2007-2009,2011 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
@@ -17,7 +17,6 @@
int msm_arch_idle(void);
int msm_pm_collapse(void);
void msm_pm_collapse_exit(void);
-void msm_warmboot_entry(void);
#ifdef CONFIG_CPU_V7
void msm_pm_boot_entry(void);
@@ -31,5 +30,4 @@
}
#endif
-
#endif
diff --git a/arch/arm/mach-msm/include/mach/board.h b/arch/arm/mach-msm/include/mach/board.h
index 2116ee75..0cd1c5c 100644
--- a/arch/arm/mach-msm/include/mach/board.h
+++ b/arch/arm/mach-msm/include/mach/board.h
@@ -311,9 +311,14 @@
struct msm_bus_scale_pdata *mdp_bus_scale_table;
#endif
int mdp_rev;
- int (*writeback_offset)(void);
+ int mdp_writeback_memtype;
+ void *mdp_writeback_phys; /* writeback physical addr */
+ int mdp_writeback_size_ov0; /* overlay0 writeback size */
+ int mdp_writeback_size_ov1; /* overlay1 writeback size */
};
+
+
struct lcdc_platform_data {
int (*lcdc_gpio_config)(int on);
int (*lcdc_power_save)(int);
@@ -405,6 +410,7 @@
struct msm_vidc_platform_data {
int memtype;
u32 enable_ion;
+ int disable_dmx;
#ifdef CONFIG_MSM_BUS_SCALING
struct msm_bus_scale_pdata *vidc_bus_client_pdata;
#endif
diff --git a/arch/arm/mach-msm/include/mach/camera.h b/arch/arm/mach-msm/include/mach/camera.h
index b41398e..ae1f753 100644
--- a/arch/arm/mach-msm/include/mach/camera.h
+++ b/arch/arm/mach-msm/include/mach/camera.h
@@ -195,6 +195,7 @@
#define VFE31_OUTPUT_MODE_T (0x1 << 4)
#define CSI_EMBED_DATA 0x12
+#define CSI_YUV422_8 0x1E
#define CSI_RAW8 0x2A
#define CSI_RAW10 0x2B
#define CSI_RAW12 0x2C
@@ -323,7 +324,8 @@
struct msm_actuator_ctrl {
int (*a_init_table)(void);
- int (*a_power_down)(void);
+ int (*a_power_up)(void *);
+ int (*a_power_down)(void *);
int (*a_create_subdevice)(void *, void *);
int (*a_config)(void __user *);
};
diff --git a/arch/arm/mach-msm/include/mach/msm_memtypes.h b/arch/arm/mach-msm/include/mach/msm_memtypes.h
index 2c278a6..29a9d4a 100644
--- a/arch/arm/mach-msm/include/mach/msm_memtypes.h
+++ b/arch/arm/mach-msm/include/mach/msm_memtypes.h
@@ -62,4 +62,6 @@
};
extern struct reserve_info *reserve_info;
+
+unsigned long __init reserve_memory_for_fmem(unsigned long);
#endif
diff --git a/arch/arm/mach-msm/include/mach/msm_tspp.h b/arch/arm/mach-msm/include/mach/msm_tspp.h
new file mode 100644
index 0000000..6912f0c
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/msm_tspp.h
@@ -0,0 +1,24 @@
+/* Copyright (c) 2011, 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_TSPP_H_
+#define _MSM_TSPP_H_
+
+struct msm_tspp_platform_data {
+ int num_gpios;
+ const struct msm_gpio *gpios;
+ const char *tsif_pclk;
+ const char *tsif_ref_clk;
+};
+
+#endif /* _MSM_TSPP_H_ */
+
diff --git a/arch/arm/mach-msm/include/mach/msm_xo.h b/arch/arm/mach-msm/include/mach/msm_xo.h
index 30c3272..77606474 100644
--- a/arch/arm/mach-msm/include/mach/msm_xo.h
+++ b/arch/arm/mach-msm/include/mach/msm_xo.h
@@ -20,6 +20,7 @@
MSM_XO_TCXO_A2,
MSM_XO_CORE,
MSM_XO_PXO,
+ MSM_XO_CXO,
NUM_MSM_XO_IDS
};
diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator-copper.h b/arch/arm/mach-msm/include/mach/rpm-regulator-copper.h
new file mode 100644
index 0000000..2006ad3
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/rpm-regulator-copper.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2011, 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 __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_COPPER_H
+#define __ARCH_ARM_MACH_MSM_INCLUDE_MACH_RPM_REGULATOR_COPPER_H
+
+/**
+ * enum rpm_vreg_id - RPM regulator ID numbers (both real and pin control)
+ */
+enum rpm_vreg_id_copper {
+ RPM_VREG_ID_PM8941_S1,
+ RPM_VREG_ID_PM8941_S2,
+ RPM_VREG_ID_PM8941_L12,
+ RPM_VREG_ID_PM8941_MAX,
+};
+
+#endif
diff --git a/arch/arm/mach-msm/include/mach/rpm-regulator.h b/arch/arm/mach-msm/include/mach/rpm-regulator.h
index f857ab8..1095078 100644
--- a/arch/arm/mach-msm/include/mach/rpm-regulator.h
+++ b/arch/arm/mach-msm/include/mach/rpm-regulator.h
@@ -20,6 +20,7 @@
#include <mach/rpm-regulator-8660.h>
#include <mach/rpm-regulator-8960.h>
#include <mach/rpm-regulator-9615.h>
+#include <mach/rpm-regulator-copper.h>
/**
* enum rpm_vreg_version - supported RPM regulator versions
@@ -136,6 +137,7 @@
RPM_VREG_VOTER_COUNT,
};
+#ifdef CONFIG_MSM_RPM_REGULATOR
/**
* rpm_vreg_set_voltage - vote for a min_uV value of specified regualtor
* @vreg: ID for regulator
@@ -172,4 +174,25 @@
*/
int rpm_vreg_set_frequency(int vreg_id, enum rpm_vreg_freq freq);
+#else
+
+/*
+ * These stubs exist to allow consumers of these APIs to compile and run
+ * in absence of a real RPM regulator driver. It is assumed that they are
+ * aware of the state of their regulators and have either set them
+ * correctly by some other means or don't care about their state at all.
+ */
+static inline int rpm_vreg_set_voltage(int vreg_id, enum rpm_vreg_voter voter,
+ int min_uV, int max_uV, int sleep_also)
+{
+ return 0;
+}
+
+static inline int rpm_vreg_set_frequency(int vreg_id, enum rpm_vreg_freq freq)
+{
+ return 0;
+}
+
+#endif /* CONFIG_MSM_RPM_REGULATOR */
+
#endif
diff --git a/arch/arm/mach-msm/include/mach/scm.h b/arch/arm/mach-msm/include/mach/scm.h
index e511b2e..a332b67 100644
--- a/arch/arm/mach-msm/include/mach/scm.h
+++ b/arch/arm/mach-msm/include/mach/scm.h
@@ -27,8 +27,8 @@
extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
void *resp_buf, size_t resp_len);
-extern u32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1);
-extern u32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2);
+extern s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1);
+extern s32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2);
#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))
@@ -43,12 +43,12 @@
return 0;
}
-static inline u32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1)
+static inline s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1)
{
return 0;
}
-static inline u32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2)
+static inline s32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2)
{
return 0;
}
diff --git a/arch/arm/mach-msm/include/mach/usb_bam.h b/arch/arm/mach-msm/include/mach/usb_bam.h
new file mode 100644
index 0000000..4caa71bb
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/usb_bam.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2011, 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 _USB_BAM_H_
+#define _USB_BAM_H_
+
+/**
+ * Connect USB-to-Periperal SPS connection.
+ *
+ * This function returns the allocated pipes number.
+ *
+ * @idx - Connection index.
+ *
+ * @src_pipe_idx - allocated pipe index - USB as a
+ * source (output)
+ *
+ * @dst_pipe_idx - allocated pipe index - USB as a
+ * destination (output)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+#ifdef CONFIG_USB_BAM
+int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx);
+#else
+int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx)
+{
+ return -ENODEV;
+}
+#endif
+#endif /* _USB_BAM_H_ */
diff --git a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
index 54566dc..d8a3e60 100644
--- a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
+++ b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
@@ -20,6 +20,7 @@
USB_GADGET_XPORT_SDIO,
USB_GADGET_XPORT_SMD,
USB_GADGET_XPORT_BAM,
+ USB_GADGET_XPORT_BAM2BAM,
USB_GADGET_XPORT_HSIC,
USB_GADGET_XPORT_NONE,
};
@@ -37,6 +38,8 @@
return "SMD";
case USB_GADGET_XPORT_BAM:
return "BAM";
+ case USB_GADGET_XPORT_BAM2BAM:
+ return "BAM2BAM";
case USB_GADGET_XPORT_HSIC:
return "HSIC";
case USB_GADGET_XPORT_NONE:
@@ -56,6 +59,8 @@
return USB_GADGET_XPORT_SMD;
if (!strncasecmp("BAM", name, XPORT_STR_LEN))
return USB_GADGET_XPORT_BAM;
+ if (!strncasecmp("BAM2BAM", name, XPORT_STR_LEN))
+ return USB_GADGET_XPORT_BAM2BAM;
if (!strncasecmp("HSIC", name, XPORT_STR_LEN))
return USB_GADGET_XPORT_HSIC;
if (!strncasecmp("", name, XPORT_STR_LEN))
diff --git a/arch/arm/mach-msm/ipc_router.c b/arch/arm/mach-msm/ipc_router.c
index 8e9cc0f..15ea8ba 100644
--- a/arch/arm/mach-msm/ipc_router.c
+++ b/arch/arm/mach-msm/ipc_router.c
@@ -1183,7 +1183,15 @@
}
temp_ptr = skb_peek(pkt->pkt_fragment_q);
+ if (!temp_ptr) {
+ pr_err("%s: pkt_fragment_q is empty\n", __func__);
+ return -EINVAL;
+ }
hdr = (struct rr_header *)temp_ptr->data;
+ if (!hdr) {
+ pr_err("%s: No data inside the skb\n", __func__);
+ return -EINVAL;
+ }
msg = (union rr_control_msg *)((char *)hdr + IPC_ROUTER_HDR_SIZE);
switch (msg->cmd) {
@@ -1595,6 +1603,10 @@
}
head_skb = skb_peek(pkt->pkt_fragment_q);
+ if (!head_skb) {
+ pr_err("%s: pkt_fragment_q is empty\n", __func__);
+ return -EINVAL;
+ }
hdr = (struct rr_header *)skb_push(head_skb, IPC_ROUTER_HDR_SIZE);
if (!hdr) {
pr_err("%s: Prepend Header failed\n", __func__);
@@ -1642,6 +1654,10 @@
return -EINVAL;
head_skb = skb_peek(pkt->pkt_fragment_q);
+ if (!head_skb) {
+ pr_err("%s: pkt_fragment_q is empty\n", __func__);
+ return -EINVAL;
+ }
hdr = (struct rr_header *)skb_push(head_skb, IPC_ROUTER_HDR_SIZE);
if (!hdr) {
pr_err("%s: Prepend Header failed\n", __func__);
diff --git a/arch/arm/mach-msm/mdm.c b/arch/arm/mach-msm/mdm.c
index 01cc48a..cbdc92a 100644
--- a/arch/arm/mach-msm/mdm.c
+++ b/arch/arm/mach-msm/mdm.c
@@ -29,9 +29,9 @@
#include <linux/completion.h>
#include <linux/workqueue.h>
#include <linux/clk.h>
-#include <linux/mfd/pmic8058.h>
#include <asm/mach-types.h>
#include <asm/uaccess.h>
+#include <linux/mfd/pm8xxx/misc.h>
#include <mach/mdm.h>
#include <mach/restart.h>
#include <mach/subsystem_notif.h>
@@ -120,7 +120,7 @@
CHARM_DBG("%s: setting AP2MDM_ERRFATAL high for a non graceful reset\n",
__func__);
if (get_restart_level() == RESET_SOC)
- pm8058_stay_on();
+ pm8xxx_stay_on();
charm_disable_irqs();
gpio_set_value(AP2MDM_ERRFATAL, 1);
@@ -238,7 +238,7 @@
{
pr_info("Reseting the charm due to an errfatal\n");
if (get_restart_level() == RESET_SOC)
- pm8058_stay_on();
+ pm8xxx_stay_on();
subsystem_restart("external_modem");
}
diff --git a/arch/arm/mach-msm/memory.c b/arch/arm/mach-msm/memory.c
index 31fbd43..46695c4 100644
--- a/arch/arm/mach-msm/memory.c
+++ b/arch/arm/mach-msm/memory.c
@@ -37,6 +37,7 @@
#include <mach/msm_iomap.h>
#include <mach/socinfo.h>
#include <../../mm/mm.h>
+#include <linux/fmem.h>
void *strongly_ordered_page;
char strongly_ordered_mem[PAGE_SIZE*2-4];
@@ -304,6 +305,24 @@
}
}
+unsigned long __init reserve_memory_for_fmem(unsigned long fmem_size)
+{
+ struct membank *mb;
+ int ret;
+ unsigned long fmem_phys;
+
+ if (!fmem_size)
+ return 0;
+
+ mb = &meminfo.bank[meminfo.nr_banks - 1];
+ fmem_phys = mb->start + (mb->size - fmem_size);
+ ret = memblock_remove(fmem_phys, fmem_size);
+ BUG_ON(ret);
+
+ pr_info("fmem start %lx size %lx\n", fmem_phys, fmem_size);
+ return fmem_phys;
+}
+
static void __init initialize_mempools(void)
{
struct mem_pool *mpool;
@@ -421,3 +440,13 @@
asm("mrc p15, 0, %0, c2, c0, 0\n"
: "=r" (msm_ttbr0));
}
+
+int request_fmem_c_region(void *unused)
+{
+ return fmem_set_state(FMEM_C_STATE);
+}
+
+int release_fmem_c_region(void *unused)
+{
+ return fmem_set_state(FMEM_T_STATE);
+}
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8660.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8660.c
index fc91291..296c6dc 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_8660.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8660.c
@@ -140,8 +140,8 @@
.tier = tiered_slave_ebi,
.num_tiers = ARRAY_SIZE(tiered_slave_ebi),
.buswidth = 8,
- .slaveclk[DUAL_CTX] = "ebi1_msmbus_clk",
- .slaveclk[ACTIVE_CTX] = "ebi1_a_clk",
+ .slaveclk[DUAL_CTX] = "mem_clk",
+ .slaveclk[ACTIVE_CTX] = "mem_a_clk",
},
{
.id = MSM_BUS_SLAVE_AMPSS_L2,
@@ -848,8 +848,8 @@
.info = apps_fabric_info,
.len = ARRAY_SIZE(apps_fabric_info),
.ahb = 0,
- .fabclk[DUAL_CTX] = "afab_clk",
- .fabclk[ACTIVE_CTX] = "afab_a_clk",
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
.haltid = MSM_RPM_ID_APPS_FABRIC_HALT_0,
.offset = MSM_RPM_ID_APPS_FABRIC_ARB_0,
.nmasters = 4,
@@ -864,8 +864,8 @@
system_fabric_info,
ARRAY_SIZE(system_fabric_info),
.ahb = 0,
- .fabclk[DUAL_CTX] = "sfab_clk",
- .fabclk[ACTIVE_CTX] = "sfab_a_clk",
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
.haltid = MSM_RPM_ID_SYSTEM_FABRIC_HALT_0,
.offset = MSM_RPM_ID_SYSTEM_FABRIC_ARB_0,
.nmasters = 17,
@@ -880,8 +880,8 @@
mmss_fabric_info,
ARRAY_SIZE(mmss_fabric_info),
.ahb = 0,
- .fabclk[DUAL_CTX] = "mmfab_clk",
- .fabclk[ACTIVE_CTX] = "mmfab_a_clk",
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
.haltid = MSM_RPM_ID_MM_FABRIC_HALT_0,
.offset = MSM_RPM_ID_MM_FABRIC_ARB_0,
.nmasters = 14,
@@ -896,8 +896,8 @@
sys_fpb_fabric_info,
ARRAY_SIZE(sys_fpb_fabric_info),
.ahb = 1,
- .fabclk[DUAL_CTX] = "sfpb_clk",
- .fabclk[ACTIVE_CTX] = "sfpb_a_clk",
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
.nmasters = 0,
.nslaves = 0,
.ntieredslaves = 0,
@@ -910,8 +910,8 @@
cpss_fpb_fabric_info,
ARRAY_SIZE(cpss_fpb_fabric_info),
.ahb = 1,
- .fabclk[DUAL_CTX] = "cfpb_clk",
- .fabclk[ACTIVE_CTX] = "cfpb_a_clk",
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
.nmasters = 0,
.nslaves = 0,
.ntieredslaves = 0,
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c
index 97a3145..0d265c7 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_8960.c
@@ -154,8 +154,8 @@
.tier = tiered_slave_ebi1_ch0,
.num_tiers = ARRAY_SIZE(tiered_slave_ebi1_ch0),
.buswidth = 8,
- .slaveclk[DUAL_CTX] = "ebi1_msmbus_clk",
- .slaveclk[ACTIVE_CTX] = "ebi1_a_clk",
+ .slaveclk[DUAL_CTX] = "mem_clk",
+ .slaveclk[ACTIVE_CTX] = "mem_a_clk",
},
{
.id = MSM_BUS_SLAVE_EBI_CH1,
@@ -164,8 +164,8 @@
.tier = tiered_slave_ebi1_ch1,
.num_tiers = ARRAY_SIZE(tiered_slave_ebi1_ch1),
.buswidth = 8,
- .slaveclk[DUAL_CTX] = "ebi1_msmbus_clk",
- .slaveclk[ACTIVE_CTX] = "ebi1_a_clk",
+ .slaveclk[DUAL_CTX] = "mem_clk",
+ .slaveclk[ACTIVE_CTX] = "mem_a_clk",
},
{
.id = MSM_BUS_SLAVE_AMPSS_L2,
@@ -875,8 +875,8 @@
.info = apps_fabric_info,
.len = ARRAY_SIZE(apps_fabric_info),
.ahb = 0,
- .fabclk[DUAL_CTX] = "afab_clk",
- .fabclk[ACTIVE_CTX] = "afab_a_clk",
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
.haltid = MSM_RPM_ID_APPS_FABRIC_CFG_HALT_0,
.offset = MSM_RPM_ID_APPS_FABRIC_ARB_0,
.nmasters = 6,
@@ -891,8 +891,8 @@
system_fabric_info,
ARRAY_SIZE(system_fabric_info),
.ahb = 0,
- .fabclk[DUAL_CTX] = "sfab_clk",
- .fabclk[ACTIVE_CTX] = "sfab_a_clk",
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
.haltid = MSM_RPM_ID_SYS_FABRIC_CFG_HALT_0,
.offset = MSM_RPM_ID_SYSTEM_FABRIC_ARB_0,
.nmasters = 15,
@@ -907,8 +907,8 @@
mmss_fabric_info,
ARRAY_SIZE(mmss_fabric_info),
.ahb = 0,
- .fabclk[DUAL_CTX] = "mmfab_clk",
- .fabclk[ACTIVE_CTX] = "mmfab_a_clk",
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
.haltid = MSM_RPM_ID_MMSS_FABRIC_CFG_HALT_0,
.offset = MSM_RPM_ID_MM_FABRIC_ARB_0,
.nmasters = 14,
@@ -923,8 +923,8 @@
sys_fpb_fabric_info,
ARRAY_SIZE(sys_fpb_fabric_info),
.ahb = 1,
- .fabclk[DUAL_CTX] = "sfpb_clk",
- .fabclk[ACTIVE_CTX] = "sfpb_a_clk",
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
.nmasters = 0,
.nslaves = 0,
.ntieredslaves = 0,
@@ -937,8 +937,8 @@
cpss_fpb_fabric_info,
ARRAY_SIZE(cpss_fpb_fabric_info),
.ahb = 1,
- .fabclk[DUAL_CTX] = "cfpb_clk",
- .fabclk[ACTIVE_CTX] = "cfpb_a_clk",
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
.nmasters = 0,
.nslaves = 0,
.ntieredslaves = 0,
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_board_9615.c b/arch/arm/mach-msm/msm_bus/msm_bus_board_9615.c
index a941a89..5b52cb9 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_board_9615.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_board_9615.c
@@ -192,8 +192,8 @@
.tier = tiered_slave_ebi1_ch0,
.num_tiers = ARRAY_SIZE(tiered_slave_ebi1_ch0),
.buswidth = 8,
- .slaveclk[DUAL_CTX] = "ebi1_msmbus_clk",
- .slaveclk[ACTIVE_CTX] = "ebi1_a_clk",
+ .slaveclk[DUAL_CTX] = "mem_clk",
+ .slaveclk[ACTIVE_CTX] = "mem_a_clk",
},
{
.id = MSM_BUS_SLAVE_SYSTEM_IMEM,
@@ -285,8 +285,8 @@
system_fabric_info,
ARRAY_SIZE(system_fabric_info),
.ahb = 0,
- .fabclk[DUAL_CTX] = "sfab_clk",
- .fabclk[ACTIVE_CTX] = "sfab_a_clk",
+ .fabclk[DUAL_CTX] = "bus_clk",
+ .fabclk[ACTIVE_CTX] = "bus_a_clk",
.haltid = MSM_RPM_ID_SYS_FABRIC_CFG_HALT_0,
.offset = MSM_RPM_ID_SYSTEM_FABRIC_ARB_0,
.nmasters = 12,
diff --git a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
index fd82111..02dfed6 100644
--- a/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
+++ b/arch/arm/mach-msm/msm_bus/msm_bus_fabric.c
@@ -71,7 +71,7 @@
SLAVE_NODE);
if (info->node_info->slaveclk[DUAL_CTX]) {
- info->nodeclk[DUAL_CTX].clk = clk_get(NULL,
+ info->nodeclk[DUAL_CTX].clk = clk_get_sys("msm_bus",
info->node_info->slaveclk[DUAL_CTX]);
if (IS_ERR(info->nodeclk[DUAL_CTX].clk)) {
MSM_BUS_ERR("Could not get clock for %s\n",
@@ -144,8 +144,8 @@
for (ctx = 0; ctx < NUM_CTX; ctx++) {
if (info->node_info->slaveclk[ctx]) {
- info->nodeclk[ctx].clk = clk_get(NULL,
- info->node_info->slaveclk[ctx]);
+ info->nodeclk[ctx].clk = clk_get_sys("msm_bus",
+ info->node_info->slaveclk[ctx]);
if (IS_ERR(info->nodeclk[ctx].clk)) {
MSM_BUS_ERR("Couldn't get clk %s\n",
info->node_info->slaveclk[ctx]);
@@ -156,7 +156,7 @@
}
}
if (info->node_info->memclk) {
- info->memclk.clk = clk_get(NULL,
+ info->memclk.clk = clk_get_sys("msm_bus",
info->node_info->memclk);
if (IS_ERR(info->memclk.clk)) {
MSM_BUS_ERR("Couldn't get clk %s\n",
@@ -652,10 +652,21 @@
fabric->fabdev.id);
fabric->fabdev.board_algo = fabric->pdata->board_algo;
+ /*
+ * clk and bw for fabric->info will contain the max bw and clk
+ * it will allow. This info will come from the boards file.
+ */
+ ret = msm_bus_fabric_device_register(&fabric->fabdev);
+ if (ret) {
+ MSM_BUS_ERR("Error registering fabric %d ret %d\n",
+ fabric->fabdev.id, ret);
+ goto err;
+ }
+
for (ctx = 0; ctx < NUM_CTX; ctx++) {
if (pdata->fabclk[ctx]) {
- fabric->info.nodeclk[ctx].clk = clk_get(NULL,
- pdata->fabclk[ctx]);
+ fabric->info.nodeclk[ctx].clk = clk_get(
+ &fabric->fabdev.dev, pdata->fabclk[ctx]);
if (IS_ERR(fabric->info.nodeclk[ctx].clk)) {
MSM_BUS_ERR("Couldn't get clock %s\n",
pdata->fabclk[ctx]);
@@ -687,16 +698,6 @@
}
}
}
- /*
- * clk and bw for fabric->info will contain the max bw and clk
- * it will allow. This info will come from the boards file.
- */
- ret = msm_bus_fabric_device_register(&fabric->fabdev);
- if (ret) {
- MSM_BUS_ERR("Error registering fabric %d ret %d\n",
- fabric->fabdev.id, ret);
- goto err;
- }
return ret;
err:
diff --git a/arch/arm/mach-msm/msm_rq_stats.c b/arch/arm/mach-msm/msm_rq_stats.c
index da7fb51..492612f 100644
--- a/arch/arm/mach-msm/msm_rq_stats.c
+++ b/arch/arm/mach-msm/msm_rq_stats.c
@@ -95,10 +95,7 @@
static ssize_t show_def_timer_ms(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
- int64_t diff_ms;
- diff_ms = ktime_to_ns(ktime_get()) - rq_info.def_start_time;
- do_div(diff_ms, 1000 * 1000);
- return snprintf(buf, MAX_LONG_SIZE, "%lld\n", diff_ms);
+ return snprintf(buf, MAX_LONG_SIZE, "%u\n", rq_info.def_interval);
}
static ssize_t store_def_timer_ms(struct kobject *kobj,
diff --git a/arch/arm/mach-msm/msm_xo.c b/arch/arm/mach-msm/msm_xo.c
index 29cb1a8..802ee2a 100644
--- a/arch/arm/mach-msm/msm_xo.c
+++ b/arch/arm/mach-msm/msm_xo.c
@@ -83,6 +83,7 @@
msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_TCXO_A2], "TCXO A2");
msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_CORE], "TCXO Core");
msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_PXO], "PXO during sleep");
+ msm_xo_dump_xo(m, &msm_xo_sources[MSM_XO_CXO], "CXO");
spin_unlock_irqrestore(&msm_xo_lock, flags);
return 0;
@@ -137,6 +138,10 @@
cmd.id = MSM_RPM_ID_PXO_CLK;
cmd.value = msm_xo_sources[MSM_XO_PXO].mode ? 1 : 0;
ret = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_SLEEP, &cmd, 1);
+ } else if (xo == &msm_xo_sources[MSM_XO_CXO]) {
+ cmd.id = MSM_RPM_ID_CXO_CLK;
+ cmd.value = msm_xo_sources[MSM_XO_CXO].mode ? 1 : 0;
+ ret = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_0, &cmd, 1);
} else {
cmd.id = MSM_RPM_ID_CXO_BUFFERS;
cmd.value = (msm_xo_sources[MSM_XO_TCXO_D0].mode << 0) |
@@ -223,10 +228,10 @@
/*
* TODO: Remove early return for 8064 once RPM XO voting support
- * is available. Remove early return for 8960 TCXO_D0 once all
- * voters for it are in place.
+ * is available. Remove early return for 8960 CXO once all voters
+ * for it are in place.
*/
- if (cpu_is_apq8064() || (cpu_is_msm8960() && xo_id == MSM_XO_TCXO_D0))
+ if (cpu_is_apq8064() || (cpu_is_msm8960() && xo_id == MSM_XO_CXO))
return NULL;
if (xo_id >= NUM_MSM_XO_IDS) {
diff --git a/arch/arm/mach-msm/peripheral-reset-8960.c b/arch/arm/mach-msm/peripheral-reset-8960.c
index fa22e4e..d01851e 100644
--- a/arch/arm/mach-msm/peripheral-reset-8960.c
+++ b/arch/arm/mach-msm/peripheral-reset-8960.c
@@ -17,7 +17,6 @@
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <asm/mach-types.h>
@@ -28,195 +27,15 @@
#include "peripheral-loader.h"
#include "scm-pas.h"
-#define MSM_RIVA_PHYS 0x03204000
-#define RIVA_PMU_A2XB_CFG (msm_riva_base + 0xB8)
-#define RIVA_PMU_A2XB_CFG_EN BIT(0)
-
-#define RIVA_PMU_CFG (msm_riva_base + 0x28)
-#define RIVA_PMU_CFG_WARM_BOOT BIT(0)
-#define RIVA_PMU_CFG_IRIS_XO_MODE 0x6
-#define RIVA_PMU_CFG_IRIS_XO_MODE_48 (3 << 1)
-
-#define RIVA_PMU_OVRD_VAL (msm_riva_base + 0x30)
-#define RIVA_PMU_OVRD_VAL_CCPU_RESET BIT(0)
-#define RIVA_PMU_OVRD_VAL_CCPU_CLK BIT(1)
-
-#define RIVA_PMU_CCPU_CTL (msm_riva_base + 0x9C)
-#define RIVA_PMU_CCPU_CTL_HIGH_IVT BIT(0)
-#define RIVA_PMU_CCPU_CTL_REMAP_EN BIT(2)
-
-#define RIVA_PMU_CCPU_BOOT_REMAP_ADDR (msm_riva_base + 0xA0)
-
-#define RIVA_PLL_MODE (MSM_CLK_CTL_BASE + 0x31A0)
-#define PLL_MODE_OUTCTRL BIT(0)
-#define PLL_MODE_BYPASSNL BIT(1)
-#define PLL_MODE_RESET_N BIT(2)
-#define PLL_MODE_REF_XO_SEL 0x30
-#define PLL_MODE_REF_XO_SEL_CXO (2 << 4)
-#define PLL_MODE_REF_XO_SEL_RF (3 << 4)
-#define RIVA_PLL_L_VAL (MSM_CLK_CTL_BASE + 0x31A4)
-#define RIVA_PLL_M_VAL (MSM_CLK_CTL_BASE + 0x31A8)
-#define RIVA_PLL_N_VAL (MSM_CLK_CTL_BASE + 0x31Ac)
-#define RIVA_PLL_CONFIG (MSM_CLK_CTL_BASE + 0x31B4)
-#define RIVA_PLL_STATUS (MSM_CLK_CTL_BASE + 0x31B8)
-
-#define RIVA_PMU_ROOT_CLK_SEL (msm_riva_base + 0xC8)
-#define RIVA_PMU_ROOT_CLK_SEL_3 BIT(2)
-
-#define RIVA_PMU_CLK_ROOT3 (msm_riva_base + 0x78)
-#define RIVA_PMU_CLK_ROOT3_ENA BIT(0)
-#define RIVA_PMU_CLK_ROOT3_SRC0_DIV 0x3C
-#define RIVA_PMU_CLK_ROOT3_SRC0_DIV_2 (1 << 2)
-#define RIVA_PMU_CLK_ROOT3_SRC0_SEL 0x1C0
-#define RIVA_PMU_CLK_ROOT3_SRC0_SEL_RIVA (1 << 6)
-#define RIVA_PMU_CLK_ROOT3_SRC1_DIV 0x1E00
-#define RIVA_PMU_CLK_ROOT3_SRC1_DIV_2 (1 << 9)
-#define RIVA_PMU_CLK_ROOT3_SRC1_SEL 0xE000
-#define RIVA_PMU_CLK_ROOT3_SRC1_SEL_RIVA (1 << 13)
-
#define PPSS_RESET (MSM_CLK_CTL_BASE + 0x2594)
#define PPSS_PROC_CLK_CTL (MSM_CLK_CTL_BASE + 0x2588)
#define PPSS_HCLK_CTL (MSM_CLK_CTL_BASE + 0x2580)
-static void __iomem *msm_riva_base;
-static unsigned long riva_start;
-
static int verify_blob(struct pil_desc *pil, u32 phy_addr, size_t size)
{
return 0;
}
-static int init_image_riva_untrusted(struct pil_desc *pil, const u8 *metadata,
- size_t size)
-{
- const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
- riva_start = ehdr->e_entry;
- return 0;
-}
-
-static int reset_riva_untrusted(struct pil_desc *pil)
-{
- u32 reg;
- bool xo;
-
- /* Enable A2XB bridge */
- reg = readl_relaxed(RIVA_PMU_A2XB_CFG);
- reg |= RIVA_PMU_A2XB_CFG_EN;
- writel_relaxed(reg, RIVA_PMU_A2XB_CFG);
-
- /* Determine which XO to use */
- reg = readl_relaxed(RIVA_PMU_CFG);
- xo = (reg & RIVA_PMU_CFG_IRIS_XO_MODE) == RIVA_PMU_CFG_IRIS_XO_MODE_48;
-
- /* Program PLL 13 to 960 MHz */
- reg = readl_relaxed(RIVA_PLL_MODE);
- reg &= ~(PLL_MODE_BYPASSNL | PLL_MODE_OUTCTRL | PLL_MODE_RESET_N);
- writel_relaxed(reg, RIVA_PLL_MODE);
-
- if (xo)
- writel_relaxed(0x40000C00 | 40, RIVA_PLL_L_VAL);
- else
- writel_relaxed(0x40000C00 | 50, RIVA_PLL_L_VAL);
- writel_relaxed(0, RIVA_PLL_M_VAL);
- writel_relaxed(1, RIVA_PLL_N_VAL);
- writel_relaxed(0x01495227, RIVA_PLL_CONFIG);
-
- reg = readl_relaxed(RIVA_PLL_MODE);
- reg &= ~(PLL_MODE_REF_XO_SEL);
- reg |= xo ? PLL_MODE_REF_XO_SEL_RF : PLL_MODE_REF_XO_SEL_CXO;
- writel_relaxed(reg, RIVA_PLL_MODE);
-
- /* Enable PLL 13 */
- reg |= PLL_MODE_BYPASSNL;
- writel_relaxed(reg, RIVA_PLL_MODE);
-
- /*
- * H/W requires a 5us delay between disabling the bypass and
- * de-asserting the reset. Delay 10us just to be safe.
- */
- mb();
- usleep_range(10, 20);
-
- reg |= PLL_MODE_RESET_N;
- writel_relaxed(reg, RIVA_PLL_MODE);
- reg |= PLL_MODE_OUTCTRL;
- writel_relaxed(reg, RIVA_PLL_MODE);
-
- /* Wait for PLL to settle */
- mb();
- usleep_range(50, 100);
-
- /* Configure cCPU for 240 MHz */
- reg = readl_relaxed(RIVA_PMU_CLK_ROOT3);
- if (readl_relaxed(RIVA_PMU_ROOT_CLK_SEL) & RIVA_PMU_ROOT_CLK_SEL_3) {
- reg &= ~(RIVA_PMU_CLK_ROOT3_SRC0_SEL |
- RIVA_PMU_CLK_ROOT3_SRC0_DIV);
- reg |= RIVA_PMU_CLK_ROOT3_SRC0_SEL_RIVA |
- RIVA_PMU_CLK_ROOT3_SRC0_DIV_2;
- } else {
- reg &= ~(RIVA_PMU_CLK_ROOT3_SRC1_SEL |
- RIVA_PMU_CLK_ROOT3_SRC1_DIV);
- reg |= RIVA_PMU_CLK_ROOT3_SRC1_SEL_RIVA |
- RIVA_PMU_CLK_ROOT3_SRC1_DIV_2;
- }
- writel_relaxed(reg, RIVA_PMU_CLK_ROOT3);
- reg |= RIVA_PMU_CLK_ROOT3_ENA;
- writel_relaxed(reg, RIVA_PMU_CLK_ROOT3);
- reg = readl_relaxed(RIVA_PMU_ROOT_CLK_SEL);
- reg ^= RIVA_PMU_ROOT_CLK_SEL_3;
- writel_relaxed(reg, RIVA_PMU_ROOT_CLK_SEL);
-
- /* Use the high vector table */
- reg = readl_relaxed(RIVA_PMU_CCPU_CTL);
- reg |= RIVA_PMU_CCPU_CTL_HIGH_IVT | RIVA_PMU_CCPU_CTL_REMAP_EN;
- writel_relaxed(reg, RIVA_PMU_CCPU_CTL);
-
- /* Set base memory address */
- writel_relaxed(riva_start >> 16, RIVA_PMU_CCPU_BOOT_REMAP_ADDR);
-
- /* Clear warmboot bit indicating this is a cold boot */
- reg = readl_relaxed(RIVA_PMU_CFG);
- reg &= ~(RIVA_PMU_CFG_WARM_BOOT);
- writel_relaxed(reg, RIVA_PMU_CFG);
-
- /* Enable the cCPU clock */
- reg = readl_relaxed(RIVA_PMU_OVRD_VAL);
- reg |= RIVA_PMU_OVRD_VAL_CCPU_CLK;
- writel_relaxed(reg, RIVA_PMU_OVRD_VAL);
-
- /* Take cCPU out of reset */
- reg |= RIVA_PMU_OVRD_VAL_CCPU_RESET;
- writel_relaxed(reg, RIVA_PMU_OVRD_VAL);
-
- return 0;
-}
-
-static int shutdown_riva_untrusted(struct pil_desc *pil)
-{
- u32 reg;
- /* Put riva into reset */
- reg = readl_relaxed(RIVA_PMU_OVRD_VAL);
- reg &= ~(RIVA_PMU_OVRD_VAL_CCPU_RESET | RIVA_PMU_OVRD_VAL_CCPU_CLK);
- writel_relaxed(reg, RIVA_PMU_OVRD_VAL);
- return 0;
-}
-
-static int init_image_riva_trusted(struct pil_desc *pil, const u8 *metadata,
- size_t size)
-{
- return pas_init_image(PAS_RIVA, metadata, size);
-}
-
-static int reset_riva_trusted(struct pil_desc *pil)
-{
- return pas_auth_and_reset(PAS_RIVA);
-}
-
-static int shutdown_riva_trusted(struct pil_desc *pil)
-{
- return pas_shutdown(PAS_RIVA);
-}
-
static int init_image_dsps_untrusted(struct pil_desc *pil, const u8 *metadata,
size_t size)
{
@@ -273,13 +92,6 @@
return pas_shutdown(PAS_TZAPPS);
}
-static struct pil_reset_ops pil_riva_ops = {
- .init_image = init_image_riva_untrusted,
- .verify_blob = verify_blob,
- .auth_and_reset = reset_riva_untrusted,
- .shutdown = shutdown_riva_untrusted,
-};
-
struct pil_reset_ops pil_dsps_ops = {
.init_image = init_image_dsps_untrusted,
.verify_blob = verify_blob,
@@ -294,16 +106,6 @@
.shutdown = shutdown_tzapps,
};
-static struct platform_device pil_riva = {
- .name = "pil_riva",
-};
-
-static struct pil_desc pil_riva_desc = {
- .name = "wcnss",
- .dev = &pil_riva.dev,
- .ops = &pil_riva_ops,
-};
-
static struct platform_device pil_dsps = {
.name = "pil_dsps",
};
@@ -331,12 +133,6 @@
pil_dsps_ops.auth_and_reset = reset_dsps_trusted;
pil_dsps_ops.shutdown = shutdown_dsps_trusted;
}
-
- if (pas_supported(PAS_RIVA) > 0) {
- pil_riva_ops.init_image = init_image_riva_trusted;
- pil_riva_ops.auth_and_reset = reset_riva_trusted;
- pil_riva_ops.shutdown = shutdown_riva_trusted;
- }
}
static int __init msm_peripheral_reset_init(void)
@@ -355,12 +151,6 @@
BUG_ON(platform_device_register(&pil_tzapps));
BUG_ON(msm_pil_register(&pil_tzapps_desc));
- msm_riva_base = ioremap(MSM_RIVA_PHYS, SZ_256);
- if (!msm_riva_base)
- return -ENOMEM;
- BUG_ON(platform_device_register(&pil_riva));
- BUG_ON(msm_pil_register(&pil_riva_desc));
-
return 0;
}
arch_initcall(msm_peripheral_reset_init);
diff --git a/arch/arm/mach-msm/peripheral-reset.c b/arch/arm/mach-msm/peripheral-reset.c
index f3f5388..2d60a7e 100644
--- a/arch/arm/mach-msm/peripheral-reset.c
+++ b/arch/arm/mach-msm/peripheral-reset.c
@@ -34,7 +34,6 @@
#define PROXY_VOTE_TIMEOUT 10000
#define MSM_MMS_REGS_BASE 0x10200000
-#define MSM_LPASS_QDSP6SS_BASE 0x28800000
#define MARM_RESET (MSM_CLK_CTL_BASE + 0x2BD4)
#define MARM_BOOT_CONTROL (msm_mms_regs_base + 0x0010)
@@ -58,18 +57,12 @@
#define PLL8_STATUS (MSM_CLK_CTL_BASE + 0x3158)
#define CLK_HALT_MSS_SMPSS_MISC_STATE (MSM_CLK_CTL_BASE + 0x2FDC)
-#define LCC_Q6_FUNC (MSM_LPASS_CLK_CTL_BASE + 0x001C)
-#define QDSP6SS_RST_EVB (msm_lpass_qdsp6ss_base + 0x0000)
-#define QDSP6SS_STRAP_TCM (msm_lpass_qdsp6ss_base + 0x001C)
-#define QDSP6SS_STRAP_AHB (msm_lpass_qdsp6ss_base + 0x0020)
-
#define PPSS_RESET (MSM_CLK_CTL_BASE + 0x2594)
#define PPSS_PROC_CLK_CTL (MSM_CLK_CTL_BASE + 0x2588)
#define CLK_HALT_DFAB_STATE (MSM_CLK_CTL_BASE + 0x2FC8)
-static int modem_start, q6_start, dsps_start;
+static int modem_start, dsps_start;
static void __iomem *msm_mms_regs_base;
-static void __iomem *msm_lpass_qdsp6ss_base;
static int init_image_modem_trusted(struct pil_desc *pil, const u8 *metadata,
size_t size)
@@ -85,20 +78,6 @@
return 0;
}
-static int init_image_q6_trusted(struct pil_desc *pil,
- const u8 *metadata, size_t size)
-{
- return pas_init_image(PAS_Q6, metadata, size);
-}
-
-static int init_image_q6_untrusted(struct pil_desc *pil, const u8 *metadata,
- size_t size)
-{
- struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
- q6_start = ehdr->e_entry;
- return 0;
-}
-
static int init_image_dsps_trusted(struct pil_desc *pil, const u8 *metadata,
size_t size)
{
@@ -291,154 +270,6 @@
return 0;
}
-#define LV_EN BIT(27)
-#define STOP_CORE BIT(26)
-#define CLAMP_IO BIT(25)
-#define Q6SS_PRIV_ARES BIT(24)
-#define Q6SS_SS_ARES BIT(23)
-#define Q6SS_ISDB_ARES BIT(22)
-#define Q6SS_ETM_ARES BIT(21)
-#define Q6_JTAG_CRC_EN BIT(20)
-#define Q6_JTAG_INV_EN BIT(19)
-#define Q6_JTAG_CXC_EN BIT(18)
-#define Q6_PXO_CRC_EN BIT(17)
-#define Q6_PXO_INV_EN BIT(16)
-#define Q6_PXO_CXC_EN BIT(15)
-#define Q6_PXO_SLEEP_EN BIT(14)
-#define Q6_SLP_CRC_EN BIT(13)
-#define Q6_SLP_INV_EN BIT(12)
-#define Q6_SLP_CXC_EN BIT(11)
-#define CORE_ARES BIT(10)
-#define CORE_L1_MEM_CORE_EN BIT(9)
-#define CORE_TCM_MEM_CORE_EN BIT(8)
-#define CORE_TCM_MEM_PERPH_EN BIT(7)
-#define CORE_GFM4_CLK_EN BIT(2)
-#define CORE_GFM4_RES BIT(1)
-#define RAMP_PLL_SRC_SEL BIT(0)
-
-#define Q6_STRAP_AHB_UPPER (0x290 << 12)
-#define Q6_STRAP_AHB_LOWER 0x280
-#define Q6_STRAP_TCM_BASE (0x28C << 15)
-#define Q6_STRAP_TCM_CONFIG 0x28B
-
-static struct clk *pll4;
-
-static void remove_q6_proxy_votes(unsigned long data)
-{
- clk_disable(pll4);
-}
-static DEFINE_TIMER(q6_timer, remove_q6_proxy_votes, 0, 0);
-
-static void make_q6_proxy_votes(void)
-{
- /* Make proxy votes for Q6 and set up timer to disable it. */
- clk_enable(pll4);
- mod_timer(&q6_timer, jiffies + msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
-}
-
-static void remove_q6_proxy_votes_now(void)
-{
- /*
- * If the Q6 proxy vote hasn't been removed yet, them remove the
- * votes immediately.
- */
- if (del_timer(&q6_timer))
- remove_q6_proxy_votes(0);
-}
-
-static int reset_q6_untrusted(struct pil_desc *pil)
-{
- u32 reg;
-
- make_q6_proxy_votes();
-
- /* Put Q6 into reset */
- reg = __raw_readl(LCC_Q6_FUNC);
- reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
- CORE_ARES;
- reg &= ~CORE_GFM4_CLK_EN;
- __raw_writel(reg, LCC_Q6_FUNC);
-
- /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
- usleep_range(20, 30);
-
- /* Turn on Q6 memory */
- reg |= CORE_GFM4_CLK_EN | CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
- CORE_TCM_MEM_PERPH_EN;
- __raw_writel(reg, LCC_Q6_FUNC);
-
- /* Turn on Q6 core clocks and take core out of reset */
- reg &= ~(CLAMP_IO | Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES |
- CORE_ARES);
- __raw_writel(reg, LCC_Q6_FUNC);
-
- /* Wait for clocks to be enabled */
- mb();
- /* Program boot address */
- __raw_writel((q6_start >> 12) & 0xFFFFF, QDSP6SS_RST_EVB);
-
- __raw_writel(Q6_STRAP_TCM_CONFIG | Q6_STRAP_TCM_BASE,
- QDSP6SS_STRAP_TCM);
- __raw_writel(Q6_STRAP_AHB_UPPER | Q6_STRAP_AHB_LOWER,
- QDSP6SS_STRAP_AHB);
-
- /* Wait for addresses to be programmed before starting Q6 */
- mb();
-
- /* Start Q6 instruction execution */
- reg &= ~STOP_CORE;
- __raw_writel(reg, LCC_Q6_FUNC);
-
- return 0;
-}
-
-static int reset_q6_trusted(struct pil_desc *pil)
-{
- make_q6_proxy_votes();
-
- return pas_auth_and_reset(PAS_Q6);
-}
-
-static int shutdown_q6_untrusted(struct pil_desc *pil)
-{
- u32 reg;
-
- /* Put Q6 into reset */
- reg = __raw_readl(LCC_Q6_FUNC);
- reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
- CORE_ARES;
- reg &= ~CORE_GFM4_CLK_EN;
- __raw_writel(reg, LCC_Q6_FUNC);
-
- /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
- usleep_range(20, 30);
-
- /* Turn off Q6 memory */
- reg &= ~(CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
- CORE_TCM_MEM_PERPH_EN);
- __raw_writel(reg, LCC_Q6_FUNC);
-
- reg |= CLAMP_IO;
- __raw_writel(reg, LCC_Q6_FUNC);
-
- remove_q6_proxy_votes_now();
-
- return 0;
-}
-
-static int shutdown_q6_trusted(struct pil_desc *pil)
-{
- int ret;
-
- ret = pas_shutdown(PAS_Q6);
- if (ret)
- return ret;
-
- remove_q6_proxy_votes_now();
-
- return 0;
-}
-
static int reset_dsps_untrusted(struct pil_desc *pil)
{
__raw_writel(0x10, PPSS_PROC_CLK_CTL);
@@ -490,13 +321,6 @@
.shutdown = shutdown_modem_untrusted,
};
-struct pil_reset_ops pil_q6_ops = {
- .init_image = init_image_q6_untrusted,
- .verify_blob = verify_blob,
- .auth_and_reset = reset_q6_untrusted,
- .shutdown = shutdown_q6_untrusted,
-};
-
struct pil_reset_ops pil_dsps_ops = {
.init_image = init_image_dsps_untrusted,
.verify_blob = verify_blob,
@@ -522,16 +346,6 @@
.ops = &pil_modem_ops,
};
-static struct platform_device pil_q6 = {
- .name = "pil_q6",
-};
-
-static struct pil_desc pil_q6_desc = {
- .name = "q6",
- .dev = &pil_q6.dev,
- .ops = &pil_q6_ops,
-};
-
static struct platform_device pil_playready = {
.name = "pil_playready",
};
@@ -558,38 +372,22 @@
if (!msm_mms_regs_base)
goto err;
- msm_lpass_qdsp6ss_base = ioremap(MSM_LPASS_QDSP6SS_BASE, SZ_256);
- if (!msm_lpass_qdsp6ss_base)
- goto err_lpass;
-
pxo = msm_xo_get(MSM_XO_PXO, "pil");
if (IS_ERR(pxo))
goto err_pxo;
- pll4 = clk_get_sys("peripheral-reset", "pll4");
- if (IS_ERR(pll4))
- goto err_clk;
-
if (pas_supported(PAS_MODEM) > 0) {
pil_modem_ops.init_image = init_image_modem_trusted;
pil_modem_ops.auth_and_reset = reset_modem_trusted;
pil_modem_ops.shutdown = shutdown_modem_trusted;
}
- if (pas_supported(PAS_Q6) > 0) {
- pil_q6_ops.init_image = init_image_q6_trusted;
- pil_q6_ops.auth_and_reset = reset_q6_trusted;
- pil_q6_ops.shutdown = shutdown_q6_trusted;
- }
-
if (pas_supported(PAS_DSPS) > 0) {
pil_dsps_ops.init_image = init_image_dsps_trusted;
pil_dsps_ops.auth_and_reset = reset_dsps_trusted;
pil_dsps_ops.shutdown = shutdown_dsps_trusted;
}
- BUG_ON(platform_device_register(&pil_q6));
- BUG_ON(msm_pil_register(&pil_q6_desc));
BUG_ON(platform_device_register(&pil_modem));
BUG_ON(msm_pil_register(&pil_modem_desc));
BUG_ON(platform_device_register(&pil_playready));
@@ -602,11 +400,7 @@
return 0;
-err_clk:
- msm_xo_put(pxo);
err_pxo:
- iounmap(msm_lpass_qdsp6ss_base);
-err_lpass:
iounmap(msm_mms_regs_base);
err:
return -ENOMEM;
@@ -615,7 +409,6 @@
static void __exit msm_peripheral_reset_exit(void)
{
iounmap(msm_mms_regs_base);
- iounmap(msm_lpass_qdsp6ss_base);
}
arch_initcall(msm_peripheral_reset_init);
diff --git a/arch/arm/mach-msm/pil-q6v3.c b/arch/arm/mach-msm/pil-q6v3.c
new file mode 100644
index 0000000..7354d93
--- /dev/null
+++ b/arch/arm/mach-msm/pil-q6v3.c
@@ -0,0 +1,299 @@
+/* Copyright (c) 2010-2011, 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/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/elf.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include <mach/msm_iomap.h>
+
+#include "peripheral-loader.h"
+#include "scm-pas.h"
+
+#define QDSP6SS_RST_EVB 0x0000
+#define QDSP6SS_STRAP_TCM 0x001C
+#define QDSP6SS_STRAP_AHB 0x0020
+
+#define LCC_Q6_FUNC (MSM_LPASS_CLK_CTL_BASE + 0x001C)
+#define LV_EN BIT(27)
+#define STOP_CORE BIT(26)
+#define CLAMP_IO BIT(25)
+#define Q6SS_PRIV_ARES BIT(24)
+#define Q6SS_SS_ARES BIT(23)
+#define Q6SS_ISDB_ARES BIT(22)
+#define Q6SS_ETM_ARES BIT(21)
+#define Q6_JTAG_CRC_EN BIT(20)
+#define Q6_JTAG_INV_EN BIT(19)
+#define Q6_JTAG_CXC_EN BIT(18)
+#define Q6_PXO_CRC_EN BIT(17)
+#define Q6_PXO_INV_EN BIT(16)
+#define Q6_PXO_CXC_EN BIT(15)
+#define Q6_PXO_SLEEP_EN BIT(14)
+#define Q6_SLP_CRC_EN BIT(13)
+#define Q6_SLP_INV_EN BIT(12)
+#define Q6_SLP_CXC_EN BIT(11)
+#define CORE_ARES BIT(10)
+#define CORE_L1_MEM_CORE_EN BIT(9)
+#define CORE_TCM_MEM_CORE_EN BIT(8)
+#define CORE_TCM_MEM_PERPH_EN BIT(7)
+#define CORE_GFM4_CLK_EN BIT(2)
+#define CORE_GFM4_RES BIT(1)
+#define RAMP_PLL_SRC_SEL BIT(0)
+
+#define Q6_STRAP_AHB_UPPER (0x290 << 12)
+#define Q6_STRAP_AHB_LOWER 0x280
+#define Q6_STRAP_TCM_BASE (0x28C << 15)
+#define Q6_STRAP_TCM_CONFIG 0x28B
+
+#define PROXY_VOTE_TIMEOUT 10000
+
+struct q6v3_data {
+ void __iomem *base;
+ unsigned long start_addr;
+ struct clk *pll;
+ struct timer_list timer;
+};
+
+static int nop_verify_blob(struct pil_desc *pil, u32 phy_addr, size_t size)
+{
+ return 0;
+}
+
+static int pil_q6v3_init_image(struct pil_desc *pil, const u8 *metadata,
+ size_t size)
+{
+ const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
+ struct q6v3_data *drv = dev_get_drvdata(pil->dev);
+ drv->start_addr = ehdr->e_entry;
+ return 0;
+}
+
+static void q6v3_remove_proxy_votes(unsigned long data)
+{
+ struct q6v3_data *drv = (struct q6v3_data *)data;
+ clk_disable(drv->pll);
+}
+
+static void q6v3_make_proxy_votes(struct device *dev)
+{
+ int ret;
+ struct q6v3_data *drv = dev_get_drvdata(dev);
+
+ ret = clk_enable(drv->pll);
+ if (ret)
+ dev_err(dev, "Failed to enable PLL\n");
+ mod_timer(&drv->timer, jiffies + msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
+}
+
+static void q6v3_remove_proxy_votes_now(struct q6v3_data *drv)
+{
+ /* If the proxy vote hasn't been removed yet, remove it immediately. */
+ if (del_timer(&drv->timer))
+ q6v3_remove_proxy_votes((unsigned long)drv);
+}
+
+static int pil_q6v3_reset(struct pil_desc *pil)
+{
+ u32 reg;
+ struct q6v3_data *drv = dev_get_drvdata(pil->dev);
+
+ q6v3_make_proxy_votes(pil->dev);
+
+ /* Put Q6 into reset */
+ reg = readl_relaxed(LCC_Q6_FUNC);
+ reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
+ CORE_ARES;
+ reg &= ~CORE_GFM4_CLK_EN;
+ writel_relaxed(reg, LCC_Q6_FUNC);
+
+ /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
+ usleep_range(20, 30);
+
+ /* Turn on Q6 memory */
+ reg |= CORE_GFM4_CLK_EN | CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
+ CORE_TCM_MEM_PERPH_EN;
+ writel_relaxed(reg, LCC_Q6_FUNC);
+
+ /* Turn on Q6 core clocks and take core out of reset */
+ reg &= ~(CLAMP_IO | Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES |
+ CORE_ARES);
+ writel_relaxed(reg, LCC_Q6_FUNC);
+
+ /* Wait for clocks to be enabled */
+ mb();
+ /* Program boot address */
+ writel_relaxed((drv->start_addr >> 12) & 0xFFFFF,
+ drv->base + QDSP6SS_RST_EVB);
+
+ writel_relaxed(Q6_STRAP_TCM_CONFIG | Q6_STRAP_TCM_BASE,
+ drv->base + QDSP6SS_STRAP_TCM);
+ writel_relaxed(Q6_STRAP_AHB_UPPER | Q6_STRAP_AHB_LOWER,
+ drv->base + QDSP6SS_STRAP_AHB);
+
+ /* Wait for addresses to be programmed before starting Q6 */
+ mb();
+
+ /* Start Q6 instruction execution */
+ reg &= ~STOP_CORE;
+ writel_relaxed(reg, LCC_Q6_FUNC);
+
+ return 0;
+}
+
+static int pil_q6v3_shutdown(struct pil_desc *pil)
+{
+ u32 reg;
+ struct q6v3_data *drv = dev_get_drvdata(pil->dev);
+
+ /* Put Q6 into reset */
+ reg = readl_relaxed(LCC_Q6_FUNC);
+ reg |= Q6SS_SS_ARES | Q6SS_ISDB_ARES | Q6SS_ETM_ARES | STOP_CORE |
+ CORE_ARES;
+ reg &= ~CORE_GFM4_CLK_EN;
+ writel_relaxed(reg, LCC_Q6_FUNC);
+
+ /* Wait 8 AHB cycles for Q6 to be fully reset (AHB = 1.5Mhz) */
+ usleep_range(20, 30);
+
+ /* Turn off Q6 memory */
+ reg &= ~(CORE_L1_MEM_CORE_EN | CORE_TCM_MEM_CORE_EN |
+ CORE_TCM_MEM_PERPH_EN);
+ writel_relaxed(reg, LCC_Q6_FUNC);
+
+ reg |= CLAMP_IO;
+ writel_relaxed(reg, LCC_Q6_FUNC);
+
+ q6v3_remove_proxy_votes_now(drv);
+
+ return 0;
+}
+
+static struct pil_reset_ops pil_q6v3_ops = {
+ .init_image = pil_q6v3_init_image,
+ .verify_blob = nop_verify_blob,
+ .auth_and_reset = pil_q6v3_reset,
+ .shutdown = pil_q6v3_shutdown,
+};
+
+static int pil_q6v3_init_image_trusted(struct pil_desc *pil,
+ const u8 *metadata, size_t size)
+{
+ return pas_init_image(PAS_Q6, metadata, size);
+}
+
+static int pil_q6v3_reset_trusted(struct pil_desc *pil)
+{
+ q6v3_make_proxy_votes(pil->dev);
+ return pas_auth_and_reset(PAS_Q6);
+}
+
+static int pil_q6v3_shutdown_trusted(struct pil_desc *pil)
+{
+ int ret;
+ struct q6v3_data *drv = dev_get_drvdata(pil->dev);
+
+ ret = pas_shutdown(PAS_Q6);
+ if (ret)
+ return ret;
+
+ q6v3_remove_proxy_votes_now(drv);
+
+ return 0;
+}
+
+static struct pil_reset_ops pil_q6v3_ops_trusted = {
+ .init_image = pil_q6v3_init_image_trusted,
+ .verify_blob = nop_verify_blob,
+ .auth_and_reset = pil_q6v3_reset_trusted,
+ .shutdown = pil_q6v3_shutdown_trusted,
+};
+
+static int __devinit pil_q6v3_driver_probe(struct platform_device *pdev)
+{
+ struct q6v3_data *drv;
+ struct resource *res;
+ struct pil_desc *desc;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
+
+ drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!drv->base)
+ return -ENOMEM;
+
+ desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ drv->pll = clk_get(&pdev->dev, "pll4");
+ if (IS_ERR(drv->pll))
+ return PTR_ERR(drv->pll);
+
+ setup_timer(&drv->timer, q6v3_remove_proxy_votes, (unsigned long)drv);
+ desc->name = "q6";
+ desc->dev = &pdev->dev;
+
+ if (pas_supported(PAS_Q6) > 0) {
+ desc->ops = &pil_q6v3_ops_trusted;
+ dev_info(&pdev->dev, "using secure boot\n");
+ } else {
+ desc->ops = &pil_q6v3_ops;
+ dev_info(&pdev->dev, "using non-secure boot\n");
+ }
+
+ if (msm_pil_register(desc))
+ return -EINVAL;
+ return 0;
+}
+
+static int __devexit pil_q6v3_driver_exit(struct platform_device *pdev)
+{
+ struct q6v3_data *drv = platform_get_drvdata(pdev);
+ del_timer_sync(&drv->timer);
+ return 0;
+}
+
+static struct platform_driver pil_q6v3_driver = {
+ .probe = pil_q6v3_driver_probe,
+ .remove = __devexit_p(pil_q6v3_driver_exit),
+ .driver = {
+ .name = "pil_qdsp6v3",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pil_q6v3_init(void)
+{
+ return platform_driver_register(&pil_q6v3_driver);
+}
+module_init(pil_q6v3_init);
+
+static void __exit pil_q6v3_exit(void)
+{
+ platform_driver_unregister(&pil_q6v3_driver);
+}
+module_exit(pil_q6v3_exit);
+
+MODULE_DESCRIPTION("Support for booting QDSP6v3 (Hexagon) processors");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/pil-q6v4.c b/arch/arm/mach-msm/pil-q6v4.c
index 24c479c..d8ebab5 100644
--- a/arch/arm/mach-msm/pil-q6v4.c
+++ b/arch/arm/mach-msm/pil-q6v4.c
@@ -28,7 +28,7 @@
#include "pil-q6v4.h"
#include "scm-pas.h"
-#define PROXY_VOTE_TIMEOUT 10000
+#define PROXY_VOTE_TIMEOUT 40000
#define QDSP6SS_RST_EVB 0x0
#define QDSP6SS_RESET 0x04
diff --git a/arch/arm/mach-msm/pil-riva.c b/arch/arm/mach-msm/pil-riva.c
new file mode 100644
index 0000000..de61041
--- /dev/null
+++ b/arch/arm/mach-msm/pil-riva.c
@@ -0,0 +1,352 @@
+/* Copyright (c) 2011, 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/kernel.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/elf.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+#include <mach/msm_iomap.h>
+#include <mach/msm_xo.h>
+
+#include "peripheral-loader.h"
+#include "scm-pas.h"
+
+#define PROXY_VOTE_TIMEOUT 10000
+
+#define RIVA_PMU_A2XB_CFG 0xB8
+#define RIVA_PMU_A2XB_CFG_EN BIT(0)
+
+#define RIVA_PMU_CFG 0x28
+#define RIVA_PMU_CFG_WARM_BOOT BIT(0)
+#define RIVA_PMU_CFG_IRIS_XO_MODE 0x6
+#define RIVA_PMU_CFG_IRIS_XO_MODE_48 (3 << 1)
+
+#define RIVA_PMU_OVRD_VAL 0x30
+#define RIVA_PMU_OVRD_VAL_CCPU_RESET BIT(0)
+#define RIVA_PMU_OVRD_VAL_CCPU_CLK BIT(1)
+
+#define RIVA_PMU_CCPU_CTL 0x9C
+#define RIVA_PMU_CCPU_CTL_HIGH_IVT BIT(0)
+#define RIVA_PMU_CCPU_CTL_REMAP_EN BIT(2)
+
+#define RIVA_PMU_CCPU_BOOT_REMAP_ADDR 0xA0
+
+#define RIVA_PLL_MODE (MSM_CLK_CTL_BASE + 0x31A0)
+#define PLL_MODE_OUTCTRL BIT(0)
+#define PLL_MODE_BYPASSNL BIT(1)
+#define PLL_MODE_RESET_N BIT(2)
+#define PLL_MODE_REF_XO_SEL 0x30
+#define PLL_MODE_REF_XO_SEL_CXO (2 << 4)
+#define PLL_MODE_REF_XO_SEL_RF (3 << 4)
+#define RIVA_PLL_L_VAL (MSM_CLK_CTL_BASE + 0x31A4)
+#define RIVA_PLL_M_VAL (MSM_CLK_CTL_BASE + 0x31A8)
+#define RIVA_PLL_N_VAL (MSM_CLK_CTL_BASE + 0x31Ac)
+#define RIVA_PLL_CONFIG (MSM_CLK_CTL_BASE + 0x31B4)
+#define RIVA_PLL_STATUS (MSM_CLK_CTL_BASE + 0x31B8)
+
+#define RIVA_PMU_ROOT_CLK_SEL 0xC8
+#define RIVA_PMU_ROOT_CLK_SEL_3 BIT(2)
+
+#define RIVA_PMU_CLK_ROOT3 0x78
+#define RIVA_PMU_CLK_ROOT3_ENA BIT(0)
+#define RIVA_PMU_CLK_ROOT3_SRC0_DIV 0x3C
+#define RIVA_PMU_CLK_ROOT3_SRC0_DIV_2 (1 << 2)
+#define RIVA_PMU_CLK_ROOT3_SRC0_SEL 0x1C0
+#define RIVA_PMU_CLK_ROOT3_SRC0_SEL_RIVA (1 << 6)
+#define RIVA_PMU_CLK_ROOT3_SRC1_DIV 0x1E00
+#define RIVA_PMU_CLK_ROOT3_SRC1_DIV_2 (1 << 9)
+#define RIVA_PMU_CLK_ROOT3_SRC1_SEL 0xE000
+#define RIVA_PMU_CLK_ROOT3_SRC1_SEL_RIVA (1 << 13)
+
+struct riva_data {
+ void __iomem *base;
+ unsigned long start_addr;
+ struct msm_xo_voter *xo;
+ struct timer_list xo_timer;
+};
+
+static void pil_riva_make_xo_proxy_votes(struct device *dev)
+{
+ struct riva_data *drv = dev_get_drvdata(dev);
+
+ msm_xo_mode_vote(drv->xo, MSM_XO_MODE_ON);
+ mod_timer(&drv->xo_timer, jiffies+msecs_to_jiffies(PROXY_VOTE_TIMEOUT));
+}
+
+static void pil_riva_remove_xo_proxy_votes(unsigned long data)
+{
+ struct riva_data *drv = (struct riva_data *)data;
+
+ msm_xo_mode_vote(drv->xo, MSM_XO_MODE_OFF);
+}
+
+static void pil_riva_remove_xo_proxy_votes_now(struct device *dev)
+{
+ struct riva_data *drv = dev_get_drvdata(dev);
+
+ if (del_timer(&drv->xo_timer))
+ pil_riva_remove_xo_proxy_votes((unsigned long)drv);
+}
+
+static bool cxo_is_needed(struct riva_data *drv)
+{
+ u32 reg = readl_relaxed(drv->base + RIVA_PMU_CFG);
+ return (reg & RIVA_PMU_CFG_IRIS_XO_MODE)
+ != RIVA_PMU_CFG_IRIS_XO_MODE_48;
+}
+
+static int nop_verify_blob(struct pil_desc *pil, u32 phy_addr, size_t size)
+{
+ return 0;
+}
+
+static int pil_riva_init_image(struct pil_desc *pil, const u8 *metadata,
+ size_t size)
+{
+ const struct elf32_hdr *ehdr = (struct elf32_hdr *)metadata;
+ struct riva_data *drv = dev_get_drvdata(pil->dev);
+ drv->start_addr = ehdr->e_entry;
+ return 0;
+}
+
+static int pil_riva_reset(struct pil_desc *pil)
+{
+ u32 reg, sel;
+ bool use_cxo;
+ struct riva_data *drv = dev_get_drvdata(pil->dev);
+ void __iomem *base = drv->base;
+ unsigned long start_addr = drv->start_addr;
+
+ /* Enable A2XB bridge */
+ reg = readl_relaxed(base + RIVA_PMU_A2XB_CFG);
+ reg |= RIVA_PMU_A2XB_CFG_EN;
+ writel_relaxed(reg, base + RIVA_PMU_A2XB_CFG);
+
+ /* Proxy-vote for CXO if it's needed */
+ use_cxo = cxo_is_needed(drv);
+ if (use_cxo)
+ pil_riva_make_xo_proxy_votes(pil->dev);
+
+ /* Program PLL 13 to 960 MHz */
+ reg = readl_relaxed(RIVA_PLL_MODE);
+ reg &= ~(PLL_MODE_BYPASSNL | PLL_MODE_OUTCTRL | PLL_MODE_RESET_N);
+ writel_relaxed(reg, RIVA_PLL_MODE);
+
+ if (use_cxo)
+ writel_relaxed(0x40000C00 | 50, RIVA_PLL_L_VAL);
+ else
+ writel_relaxed(0x40000C00 | 40, RIVA_PLL_L_VAL);
+ writel_relaxed(0, RIVA_PLL_M_VAL);
+ writel_relaxed(1, RIVA_PLL_N_VAL);
+ writel_relaxed(0x01495227, RIVA_PLL_CONFIG);
+
+ reg = readl_relaxed(RIVA_PLL_MODE);
+ reg &= ~(PLL_MODE_REF_XO_SEL);
+ reg |= use_cxo ? PLL_MODE_REF_XO_SEL_CXO : PLL_MODE_REF_XO_SEL_RF;
+ writel_relaxed(reg, RIVA_PLL_MODE);
+
+ /* Enable PLL 13 */
+ reg |= PLL_MODE_BYPASSNL;
+ writel_relaxed(reg, RIVA_PLL_MODE);
+
+ /*
+ * H/W requires a 5us delay between disabling the bypass and
+ * de-asserting the reset. Delay 10us just to be safe.
+ */
+ mb();
+ usleep_range(10, 20);
+
+ reg |= PLL_MODE_RESET_N;
+ writel_relaxed(reg, RIVA_PLL_MODE);
+ reg |= PLL_MODE_OUTCTRL;
+ writel_relaxed(reg, RIVA_PLL_MODE);
+
+ /* Wait for PLL to settle */
+ mb();
+ usleep_range(50, 100);
+
+ /* Configure cCPU for 240 MHz */
+ sel = readl_relaxed(base + RIVA_PMU_ROOT_CLK_SEL);
+ reg = readl_relaxed(base + RIVA_PMU_CLK_ROOT3);
+ if (sel & RIVA_PMU_ROOT_CLK_SEL_3) {
+ reg &= ~(RIVA_PMU_CLK_ROOT3_SRC0_SEL |
+ RIVA_PMU_CLK_ROOT3_SRC0_DIV);
+ reg |= RIVA_PMU_CLK_ROOT3_SRC0_SEL_RIVA |
+ RIVA_PMU_CLK_ROOT3_SRC0_DIV_2;
+ } else {
+ reg &= ~(RIVA_PMU_CLK_ROOT3_SRC1_SEL |
+ RIVA_PMU_CLK_ROOT3_SRC1_DIV);
+ reg |= RIVA_PMU_CLK_ROOT3_SRC1_SEL_RIVA |
+ RIVA_PMU_CLK_ROOT3_SRC1_DIV_2;
+ }
+ writel_relaxed(reg, base + RIVA_PMU_CLK_ROOT3);
+ reg |= RIVA_PMU_CLK_ROOT3_ENA;
+ writel_relaxed(reg, base + RIVA_PMU_CLK_ROOT3);
+ reg = readl_relaxed(base + RIVA_PMU_ROOT_CLK_SEL);
+ reg ^= RIVA_PMU_ROOT_CLK_SEL_3;
+ writel_relaxed(reg, base + RIVA_PMU_ROOT_CLK_SEL);
+
+ /* Use the high vector table */
+ reg = readl_relaxed(base + RIVA_PMU_CCPU_CTL);
+ reg |= RIVA_PMU_CCPU_CTL_HIGH_IVT | RIVA_PMU_CCPU_CTL_REMAP_EN;
+ writel_relaxed(reg, base + RIVA_PMU_CCPU_CTL);
+
+ /* Set base memory address */
+ writel_relaxed(start_addr >> 16, base + RIVA_PMU_CCPU_BOOT_REMAP_ADDR);
+
+ /* Clear warmboot bit indicating this is a cold boot */
+ reg = readl_relaxed(base + RIVA_PMU_CFG);
+ reg &= ~(RIVA_PMU_CFG_WARM_BOOT);
+ writel_relaxed(reg, base + RIVA_PMU_CFG);
+
+ /* Enable the cCPU clock */
+ reg = readl_relaxed(base + RIVA_PMU_OVRD_VAL);
+ reg |= RIVA_PMU_OVRD_VAL_CCPU_CLK;
+ writel_relaxed(reg, base + RIVA_PMU_OVRD_VAL);
+
+ /* Take cCPU out of reset */
+ reg |= RIVA_PMU_OVRD_VAL_CCPU_RESET;
+ writel_relaxed(reg, base + RIVA_PMU_OVRD_VAL);
+
+ return 0;
+}
+
+static int pil_riva_shutdown(struct pil_desc *pil)
+{
+ struct riva_data *drv = dev_get_drvdata(pil->dev);
+ u32 reg;
+
+ reg = readl_relaxed(drv->base + RIVA_PMU_OVRD_VAL);
+ reg &= ~(RIVA_PMU_OVRD_VAL_CCPU_RESET | RIVA_PMU_OVRD_VAL_CCPU_CLK);
+ writel_relaxed(reg, drv->base + RIVA_PMU_OVRD_VAL);
+
+ pil_riva_remove_xo_proxy_votes_now(pil->dev);
+
+ return 0;
+}
+
+static struct pil_reset_ops pil_riva_ops = {
+ .init_image = pil_riva_init_image,
+ .verify_blob = nop_verify_blob,
+ .auth_and_reset = pil_riva_reset,
+ .shutdown = pil_riva_shutdown,
+};
+
+static int pil_riva_init_image_trusted(struct pil_desc *pil,
+ const u8 *metadata, size_t size)
+{
+ return pas_init_image(PAS_RIVA, metadata, size);
+}
+
+static int pil_riva_reset_trusted(struct pil_desc *pil)
+{
+ struct riva_data *drv = dev_get_drvdata(pil->dev);
+
+ /* Proxy-vote for CXO if it's needed */
+ if (cxo_is_needed(drv))
+ pil_riva_make_xo_proxy_votes(pil->dev);
+
+ return pas_auth_and_reset(PAS_RIVA);
+}
+
+static int pil_riva_shutdown_trusted(struct pil_desc *pil)
+{
+ int ret = pas_shutdown(PAS_RIVA);
+
+ pil_riva_remove_xo_proxy_votes_now(pil->dev);
+
+ return ret;
+}
+
+static struct pil_reset_ops pil_riva_ops_trusted = {
+ .init_image = pil_riva_init_image_trusted,
+ .verify_blob = nop_verify_blob,
+ .auth_and_reset = pil_riva_reset_trusted,
+ .shutdown = pil_riva_shutdown_trusted,
+};
+
+static int __devinit pil_riva_probe(struct platform_device *pdev)
+{
+ struct riva_data *drv;
+ struct resource *res;
+ struct pil_desc *desc;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, drv);
+
+ drv->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!drv->base)
+ return -ENOMEM;
+
+ desc = devm_kzalloc(&pdev->dev, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ desc->name = "wcnss";
+ desc->dev = &pdev->dev;
+
+ if (pas_supported(PAS_RIVA) > 0) {
+ desc->ops = &pil_riva_ops_trusted;
+ dev_info(&pdev->dev, "using secure boot\n");
+ } else {
+ desc->ops = &pil_riva_ops;
+ dev_info(&pdev->dev, "using non-secure boot\n");
+ }
+
+ setup_timer(&drv->xo_timer, pil_riva_remove_xo_proxy_votes,
+ (unsigned long)drv);
+ drv->xo = msm_xo_get(MSM_XO_CXO, desc->name);
+ if (IS_ERR(drv->xo))
+ return PTR_ERR(drv->xo);
+
+ return msm_pil_register(desc);
+}
+
+static int __devexit pil_riva_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver pil_riva_driver = {
+ .probe = pil_riva_probe,
+ .remove = __devexit_p(pil_riva_remove),
+ .driver = {
+ .name = "pil_riva",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pil_riva_init(void)
+{
+ return platform_driver_register(&pil_riva_driver);
+}
+module_init(pil_riva_init);
+
+static void __exit pil_riva_exit(void)
+{
+ platform_driver_unregister(&pil_riva_driver);
+}
+module_exit(pil_riva_exit);
+
+MODULE_DESCRIPTION("Support for booting RIVA (WCNSS) processors");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
index 3f40a12..93624a3c 100644
--- a/arch/arm/mach-msm/platsmp.c
+++ b/arch/arm/mach-msm/platsmp.c
@@ -8,6 +8,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cpumask.h>
#include <linux/delay.h>
@@ -56,12 +57,14 @@
/* KraitMP or ScorpionMP ? */
if ((read_cpuid_id() & 0xFF0) >> 4 != 0x2D) {
- base_ptr = ioremap_nocache(0x02098000, SZ_4K);
+ base_ptr = ioremap_nocache(0x02088000 + (cpu * 0x10000), SZ_4K);
if (base_ptr) {
if (machine_is_msm8960_sim() ||
machine_is_msm8960_rumi3()) {
writel_relaxed(0x10, base_ptr+0x04);
writel_relaxed(0x80, base_ptr+0x04);
+ } else if (machine_is_apq8064_sim()) {
+ writel_relaxed(0xf0000, base_ptr+0x04);
} else if (get_core_count() == 2) {
writel_relaxed(0x109, base_ptr+0x04);
writel_relaxed(0x101, base_ptr+0x04);
@@ -95,6 +98,12 @@
}
DEFINE_PER_CPU(int, cold_boot_done);
+static int cold_boot_flags[] = {
+ 0,
+ SCM_FLAG_COLDBOOT_CPU1,
+ SCM_FLAG_COLDBOOT_CPU2,
+ SCM_FLAG_COLDBOOT_CPU3,
+};
/* Executed by primary CPU, brings other CPUs out of reset. Called at boot
as well as when a CPU is coming out of shutdown induced by echo 0 >
@@ -104,16 +113,22 @@
{
int cnt = 0;
int ret;
+ int flag = 0;
pr_debug("Starting secondary CPU %d\n", cpu);
/* Set preset_lpj to avoid subsequent lpj recalculations */
preset_lpj = loops_per_jiffy;
+ if (cpu > 0 && cpu < ARRAY_SIZE(cold_boot_flags))
+ flag = cold_boot_flags[cpu];
+ else
+ __WARN();
+
if (per_cpu(cold_boot_done, cpu) == false) {
ret = scm_set_boot_addr((void *)
virt_to_phys(msm_secondary_startup),
- SCM_FLAG_COLDBOOT_CPU1);
+ flag);
if (ret == 0)
release_secondary(cpu);
else
diff --git a/arch/arm/mach-msm/pm-8x60.c b/arch/arm/mach-msm/pm-8x60.c
index 8db21f9..f479dafb 100644
--- a/arch/arm/mach-msm/pm-8x60.c
+++ b/arch/arm/mach-msm/pm-8x60.c
@@ -33,6 +33,7 @@
#include <asm/hardware/gic.h>
#include <asm/pgtable.h>
#include <asm/pgalloc.h>
+#include <asm/hardware/cache-l2x0.h>
#ifdef CONFIG_VFP
#include <asm/vfp.h>
#endif
@@ -636,6 +637,24 @@
msm_arch_idle();
}
+#ifdef CONFIG_CACHE_L2X0
+static inline bool msm_pm_l2x0_power_collapse(void)
+{
+ bool collapsed = 0;
+
+ l2x0_suspend();
+ collapsed = msm_pm_collapse();
+ l2x0_resume(collapsed);
+
+ return collapsed;
+}
+#else
+static inline bool msm_pm_l2x0_power_collapse(void)
+{
+ return msm_pm_collapse();
+}
+#endif
+
static bool msm_pm_spm_power_collapse(
struct msm_pm_device *dev, bool from_idle, bool notify_rpm)
{
@@ -663,7 +682,7 @@
vfp_flush_context();
#endif
- collapsed = msm_pm_collapse();
+ collapsed = msm_pm_l2x0_power_collapse();
msm_pm_boot_config_after_pc(dev->cpu);
diff --git a/arch/arm/mach-msm/pm-boot.c b/arch/arm/mach-msm/pm-boot.c
index bcc4c64..24cd26a 100644
--- a/arch/arm/mach-msm/pm-boot.c
+++ b/arch/arm/mach-msm/pm-boot.c
@@ -15,6 +15,10 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <mach/msm_iomap.h>
+#include <mach/socinfo.h>
+#include <asm/mach-types.h>
+#include <asm/sizes.h>
#include "scm-boot.h"
#include "idle.h"
#include "pm-boot.h"
@@ -32,6 +36,9 @@
flag = SCM_FLAG_WARMBOOT_CPU0;
else if (num_possible_cpus() == 2)
flag = SCM_FLAG_WARMBOOT_CPU0 | SCM_FLAG_WARMBOOT_CPU1;
+ else if (num_possible_cpus() == 4)
+ flag = SCM_FLAG_WARMBOOT_CPU0 | SCM_FLAG_WARMBOOT_CPU1 |
+ SCM_FLAG_WARMBOOT_CPU2 | SCM_FLAG_WARMBOOT_CPU3;
else
__WARN();
@@ -55,7 +62,8 @@
static int __init msm_pm_boot_reset_vector_init(uint32_t *reset_vector)
{
- WARN_ON(!reset_vector);
+ if (!reset_vector)
+ return -ENODEV;
msm_pm_reset_vector = reset_vector;
mb();
@@ -88,19 +96,47 @@
if (msm_pm_boot_after_pc)
msm_pm_boot_after_pc(cpu);
}
+#define BOOT_REMAP_ENABLE BIT(0)
-int __init msm_pm_boot_init(int tz_available, uint32_t* address)
+int __init msm_pm_boot_init(struct msm_pm_boot_platform_data *pdata)
{
int ret = 0;
- switch (tz_available) {
+ switch (pdata->mode) {
case MSM_PM_BOOT_CONFIG_TZ:
ret = msm_pm_tz_boot_init();
msm_pm_boot_before_pc = msm_pm_config_tz_before_pc;
msm_pm_boot_after_pc = NULL;
break;
- case MSM_PM_BOOT_CONFIG_RESET_VECTOR:
- ret = msm_pm_boot_reset_vector_init(address);
+ case MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS:
+ if (!pdata->p_addr)
+ return -ENODEV;
+ pdata->v_addr = ioremap(pdata->p_addr, PAGE_SIZE);
+ /* Fall through */
+ case MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT:
+
+ if (!pdata->v_addr)
+ return -ENODEV;
+
+ ret = msm_pm_boot_reset_vector_init(pdata->v_addr);
+ msm_pm_boot_before_pc
+ = msm_pm_config_rst_vector_before_pc;
+ msm_pm_boot_after_pc
+ = msm_pm_config_rst_vector_after_pc;
+ break;
+ case MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR:
+ /*
+ * Set the boot remap address and enable remapping of
+ * reset vector
+ */
+ if (!pdata->p_addr || !pdata->v_addr)
+ return -ENODEV;
+
+ __raw_writel((pdata->p_addr | BOOT_REMAP_ENABLE),
+ pdata->v_addr);
+
+ ret = msm_pm_boot_reset_vector_init(__va(pdata->p_addr));
+
msm_pm_boot_before_pc
= msm_pm_config_rst_vector_before_pc;
msm_pm_boot_after_pc
diff --git a/arch/arm/mach-msm/pm-boot.h b/arch/arm/mach-msm/pm-boot.h
index 2234192..8a7238a 100644
--- a/arch/arm/mach-msm/pm-boot.h
+++ b/arch/arm/mach-msm/pm-boot.h
@@ -14,18 +14,28 @@
#define _ARCH_ARM_MACH_MSM_PM_BOOT_H
enum {
- MSM_PM_BOOT_CONFIG_TZ = 0,
- MSM_PM_BOOT_CONFIG_RESET_VECTOR = 1,
+ MSM_PM_BOOT_CONFIG_TZ = 0,
+ MSM_PM_BOOT_CONFIG_RESET_VECTOR_PHYS = 1,
+ MSM_PM_BOOT_CONFIG_RESET_VECTOR_VIRT = 2,
+ MSM_PM_BOOT_CONFIG_REMAP_BOOT_ADDR = 3,
+};
+
+struct msm_pm_boot_platform_data {
+ int mode;
+ phys_addr_t p_addr;
+ void __iomem *v_addr;
};
#ifdef CONFIG_PM
-int __init msm_pm_boot_init(int boot_config, uint32_t* address);
+int __init msm_pm_boot_init(struct msm_pm_boot_platform_data *pdata);
#else
-static inline int __init msm_pm_boot_init(int boot_config, uint32_t* address)
+static inline int __init msm_pm_boot_init(
+ struct msm_pm_boot_platform_data *pdata);
{
return 0;
}
#endif
+
void msm_pm_boot_config_before_pc(unsigned int cpu, unsigned long entry);
void msm_pm_boot_config_after_pc(unsigned int cpu);
diff --git a/arch/arm/mach-msm/qdsp6v2/aac_in.c b/arch/arm/mach-msm/qdsp6v2/aac_in.c
index 7140601..41d3ff3 100644
--- a/arch/arm/mach-msm/qdsp6v2/aac_in.c
+++ b/arch/arm/mach-msm/qdsp6v2/aac_in.c
@@ -214,6 +214,15 @@
rc = -EINVAL;
break;
}
+ /* For aac-lc, min_bit_rate = min(24Kbps, 0.5*SR*num_chan);
+ max_bi_rate = min(192Kbps, 6*SR*num_chan);
+ min_sample_rate = 8000Hz, max_rate=48000 */
+ if ((cfg.bit_rate < 4000) || (cfg.bit_rate > 192000)) {
+ pr_err("%s: ERROR in setting bitrate = %d\n",
+ __func__, cfg.bit_rate);
+ rc = -EINVAL;
+ break;
+ }
enc_cfg->sample_rate = cfg.sample_rate;
enc_cfg->channels = cfg.channels;
enc_cfg->bit_rate = cfg.bit_rate;
diff --git a/arch/arm/mach-msm/qdsp6v2/board-msm8x60-audio.c b/arch/arm/mach-msm/qdsp6v2/board-msm8x60-audio.c
index 11687ac..51a209d 100644
--- a/arch/arm/mach-msm/qdsp6v2/board-msm8x60-audio.c
+++ b/arch/arm/mach-msm/qdsp6v2/board-msm8x60-audio.c
@@ -315,10 +315,10 @@
}
gpio_direction_output(SNDDEV_GPIO_CLASS_D1_EN, 1);
- gpio_set_value(SNDDEV_GPIO_CLASS_D1_EN, 1);
+ gpio_set_value_cansleep(SNDDEV_GPIO_CLASS_D1_EN, 1);
} else {
- gpio_set_value(SNDDEV_GPIO_CLASS_D1_EN, 0);
+ gpio_set_value_cansleep(SNDDEV_GPIO_CLASS_D1_EN, 0);
gpio_free(SNDDEV_GPIO_CLASS_D1_EN);
}
return 0;
diff --git a/arch/arm/mach-msm/restart.c b/arch/arm/mach-msm/restart.c
index 28bf064..06e3d37 100644
--- a/arch/arm/mach-msm/restart.c
+++ b/arch/arm/mach-msm/restart.c
@@ -122,9 +122,6 @@
#endif
pm8xxx_reset_pwr_off(0);
- if (cpu_is_msm8x60())
- pm8901_reset_pwr_off(0);
-
if (lower_pshold) {
__raw_writel(0, PSHOLD_CTL_SU);
mdelay(10000);
diff --git a/arch/arm/mach-msm/scm-boot.h b/arch/arm/mach-msm/scm-boot.h
index c2c473b..b14c968 100644
--- a/arch/arm/mach-msm/scm-boot.h
+++ b/arch/arm/mach-msm/scm-boot.h
@@ -12,10 +12,14 @@
#ifndef __MACH_SCM_BOOT_H
#define __MACH_SCM_BOOT_H
-#define SCM_BOOT_ADDR 0x1
-#define SCM_FLAG_COLDBOOT_CPU1 0x1
-#define SCM_FLAG_WARMBOOT_CPU1 0x2
-#define SCM_FLAG_WARMBOOT_CPU0 0x4
+#define SCM_BOOT_ADDR 0x1
+#define SCM_FLAG_COLDBOOT_CPU1 0x01
+#define SCM_FLAG_COLDBOOT_CPU2 0x08
+#define SCM_FLAG_COLDBOOT_CPU3 0x20
+#define SCM_FLAG_WARMBOOT_CPU1 0x02
+#define SCM_FLAG_WARMBOOT_CPU0 0x04
+#define SCM_FLAG_WARMBOOT_CPU2 0x10
+#define SCM_FLAG_WARMBOOT_CPU3 0x40
int scm_set_boot_addr(void *addr, int flags);
diff --git a/arch/arm/mach-msm/scm-pas.c b/arch/arm/mach-msm/scm-pas.c
index 5bbffd3..eaaa66d 100644
--- a/arch/arm/mach-msm/scm-pas.c
+++ b/arch/arm/mach-msm/scm-pas.c
@@ -10,12 +10,17 @@
* GNU General Public License for more details.
*/
+#define pr_fmt(fmt) "scm-pas: " fmt
+
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/string.h>
+#include <linux/clk.h>
#include <mach/scm.h>
#include <mach/socinfo.h>
+#include <mach/msm_bus.h>
+#include <mach/msm_bus_board.h>
#include "scm-pas.h"
#define PAS_INIT_IMAGE_CMD 1
@@ -50,15 +55,89 @@
}
EXPORT_SYMBOL(pas_init_image);
+static struct msm_bus_paths scm_pas_bw_tbl[] = {
+ {
+ .vectors = (struct msm_bus_vectors[]){
+ {
+ .src = MSM_BUS_MASTER_SPS,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ },
+ },
+ .num_paths = 1,
+ },
+ {
+ .vectors = (struct msm_bus_vectors[]){
+ {
+ .src = MSM_BUS_MASTER_SPS,
+ .dst = MSM_BUS_SLAVE_EBI_CH0,
+ .ib = 492 * 8 * 1000000UL,
+ .ab = 492 * 8 * 100000UL,
+ },
+ },
+ .num_paths = 1,
+ },
+};
+
+static struct msm_bus_scale_pdata scm_pas_bus_pdata = {
+ .usecase = scm_pas_bw_tbl,
+ .num_usecases = ARRAY_SIZE(scm_pas_bw_tbl),
+ .name = "scm_pas",
+};
+
+static uint32_t scm_perf_client;
+static struct clk *scm_bus_clk;
+
+static DEFINE_MUTEX(scm_pas_bw_mutex);
+static int scm_pas_bw_count;
+
+static int scm_pas_enable_bw(void)
+{
+ int ret = 0;
+
+ if (!scm_perf_client || scm_bus_clk)
+ return -EINVAL;
+
+ mutex_lock(&scm_pas_bw_mutex);
+ if (!scm_pas_bw_count) {
+ ret = msm_bus_scale_client_update_request(scm_perf_client, 1);
+ if (ret) {
+ pr_err("bandwidth request failed (%d)\n", ret);
+ } else {
+ ret = clk_enable(scm_bus_clk);
+ if (ret)
+ pr_err("clock enable failed\n");
+ }
+ }
+ if (ret)
+ msm_bus_scale_client_update_request(scm_perf_client, 0);
+ else
+ scm_pas_bw_count++;
+ mutex_unlock(&scm_pas_bw_mutex);
+ return ret;
+}
+
+static void scm_pas_disable_bw(void)
+{
+ mutex_lock(&scm_pas_bw_mutex);
+ if (scm_pas_bw_count-- == 1) {
+ msm_bus_scale_client_update_request(scm_perf_client, 0);
+ clk_disable(scm_bus_clk);
+ }
+ mutex_unlock(&scm_pas_bw_mutex);
+}
+
int pas_auth_and_reset(enum pas_id id)
{
- int ret;
+ int ret, bus_ret;
u32 proc = id, scm_ret = 0;
+ bus_ret = scm_pas_enable_bw();
ret = scm_call(SCM_SVC_PIL, PAS_AUTH_AND_RESET_CMD, &proc,
sizeof(proc), &scm_ret, sizeof(scm_ret));
if (ret)
- return ret;
+ scm_ret = ret;
+ if (!bus_ret)
+ scm_pas_disable_bw();
return scm_ret;
}
@@ -108,3 +187,19 @@
return ret_val;
}
EXPORT_SYMBOL(pas_supported);
+
+static int __init scm_pas_init(void)
+{
+ scm_perf_client = msm_bus_scale_register_client(&scm_pas_bus_pdata);
+ if (!scm_perf_client)
+ pr_warn("unable to register bus client\n");
+ scm_bus_clk = clk_get_sys("scm", "bus_clk");
+ if (!IS_ERR(scm_bus_clk)) {
+ clk_set_rate(scm_bus_clk, 64000000);
+ } else {
+ scm_bus_clk = NULL;
+ pr_warn("unable to get bus clock\n");
+ }
+ return 0;
+}
+module_init(scm_pas_init);
diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c
index 19f7290..6794a88 100644
--- a/arch/arm/mach-msm/scm.c
+++ b/arch/arm/mach-msm/scm.c
@@ -282,7 +282,7 @@
* This shall only be used with commands that are guaranteed to be
* uninterruptable, atomic and SMP safe.
*/
-u32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1)
+s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1)
{
int context_id;
register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1);
@@ -312,7 +312,7 @@
* This shall only be used with commands that are guaranteed to be
* uninterruptable, atomic and SMP safe.
*/
-u32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2)
+s32 scm_call_atomic2(u32 svc, u32 cmd, u32 arg1, u32 arg2)
{
int context_id;
register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 2);
diff --git a/arch/arm/mach-msm/sdio_tty.c b/arch/arm/mach-msm/sdio_tty.c
index e249d06..2aaa41ea 100644
--- a/arch/arm/mach-msm/sdio_tty.c
+++ b/arch/arm/mach-msm/sdio_tty.c
@@ -18,6 +18,7 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
+#include <linux/debugfs.h>
#include <mach/sdio_al.h>
#define INPUT_SPEED 4800
@@ -77,10 +78,17 @@
enum sdio_tty_state sdio_tty_state;
int is_sdio_open;
int tty_open_count;
+ int total_rx;
+ int total_tx;
};
static struct sdio_tty *sdio_tty[MAX_SDIO_TTY_DEVS];
+#ifdef CONFIG_DEBUG_FS
+struct dentry *sdio_tty_debug_root;
+struct dentry *sdio_tty_debug_info;
+#endif
+
#define DEBUG_MSG(sdio_tty_drv, x...) if (sdio_tty_drv->debug_msg_on) pr_info(x)
/*
@@ -183,10 +191,11 @@
}
tty_flip_buffer_push(sdio_tty_drv->tty_str);
+ sdio_tty_drv->total_rx += read_avail;
- DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME
- ": %s: End of read %d bytes for dev %s",
- __func__, read_avail,
+ DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Rx: %d, "
+ "Total Rx = %d bytes for dev %s", __func__,
+ read_avail, sdio_tty_drv->total_rx,
sdio_tty_drv->tty_dev_name);
}
}
@@ -302,10 +311,11 @@
return 0;
}
- DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: End of function, "
- "dev=%s, len=%d bytes\n", __func__,
- sdio_tty_drv->tty_dev_name, len);
+ sdio_tty_drv->total_tx += len;
+ DEBUG_MSG(sdio_tty_drv, SDIO_TTY_MODULE_NAME ": %s: Tx: %d, "
+ "Total Tx = %d for dev %s", __func__, len,
+ sdio_tty_drv->total_tx, sdio_tty_drv->tty_dev_name);
return len;
}
@@ -407,8 +417,8 @@
return ret;
}
- pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY channel(%s) opened "
- "\n", __func__, sdio_tty_drv->sdio_ch_name);
+ pr_info(SDIO_TTY_MODULE_NAME ": %s: SDIO_TTY channel(%s) "
+ "opened\n", __func__, sdio_tty_drv->sdio_ch_name);
sdio_tty_drv->is_sdio_open = 1;
} else {
@@ -748,6 +758,39 @@
},
};
+#ifdef CONFIG_DEBUG_FS
+void sdio_tty_print_info(void)
+{
+ int i = 0;
+
+ for (i = 0; i < MAX_SDIO_TTY_DEVS; i++) {
+ if (sdio_tty[i] == NULL)
+ continue;
+ pr_info(SDIO_TTY_MODULE_NAME ": %s: Total Rx=%d, Tx = %d "
+ "for dev %s", __func__, sdio_tty[i]->total_rx,
+ sdio_tty[i]->total_tx, sdio_tty[i]->tty_dev_name);
+ }
+}
+
+static int tty_debug_info_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static ssize_t tty_debug_info_write(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ sdio_tty_print_info();
+ return count;
+}
+
+const struct file_operations tty_debug_info_ops = {
+ .open = tty_debug_info_open,
+ .write = tty_debug_info_write,
+};
+#endif
+
/*
* Module Init.
*
@@ -763,6 +806,19 @@
pr_err(SDIO_TTY_MODULE_NAME ": %s: platform_driver_register "
"failed", __func__);
}
+#ifdef CONFIG_DEBUG_FS
+ else {
+ sdio_tty_debug_root = debugfs_create_dir("sdio_tty", NULL);
+ if (sdio_tty_debug_root) {
+ sdio_tty_debug_info = debugfs_create_file(
+ "sdio_tty_debug",
+ S_IRUGO | S_IWUGO,
+ sdio_tty_debug_root,
+ NULL,
+ &tty_debug_info_ops);
+ }
+ }
+#endif
return ret;
};
@@ -774,6 +830,10 @@
*/
static void __exit sdio_tty_exit(void)
{
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(sdio_tty_debug_info);
+ debugfs_remove(sdio_tty_debug_root);
+#endif
platform_driver_unregister(&sdio_tty_pdrv);
}
diff --git a/arch/arm/mach-msm/smd_debug.c b/arch/arm/mach-msm/smd_debug.c
index 855482b..d7602f2 100644
--- a/arch/arm/mach-msm/smd_debug.c
+++ b/arch/arm/mach-msm/smd_debug.c
@@ -431,7 +431,8 @@
return i;
}
-static int debug_read_ch_v1(char *buf, int max)
+#if (!defined(CONFIG_MSM_SMD_PKG4) && !defined(CONFIG_MSM_SMD_PKG3))
+static int debug_read_ch(char *buf, int max)
{
void *shared;
int n, i = 0;
@@ -450,8 +451,8 @@
return i;
}
-
-static int debug_read_ch_v2(char *buf, int max)
+#else
+static int debug_read_ch(char *buf, int max)
{
void *shared, *buffer;
unsigned buffer_sz;
@@ -476,20 +477,7 @@
return i;
}
-
-static int debug_read_ch(char *buf, int max)
-{
- uint32_t *smd_ver;
-
- smd_ver = smem_alloc(SMEM_VERSION_SMD, 32 * sizeof(uint32_t));
-
- if (smd_ver && (((smd_ver[VERSION_MODEM] >> 16) >= 1) ||
- ((smd_ver[VERSION_QDSP6] >> 16) >= 1) ||
- ((smd_ver[VERSION_DSPS] >> 16) >= 1)))
- return debug_read_ch_v2(buf, max);
- else
- return debug_read_ch_v1(buf, max);
-}
+#endif
static int debug_read_smem_version(char *buf, int max)
{
diff --git a/arch/arm/mach-msm/smd_tty.c b/arch/arm/mach-msm/smd_tty.c
index dc6ba5c..c8248a7 100644
--- a/arch/arm/mach-msm/smd_tty.c
+++ b/arch/arm/mach-msm/smd_tty.c
@@ -59,33 +59,38 @@
int is_open;
wait_queue_head_t ch_opened_wait_queue;
spinlock_t reset_lock;
+ struct smd_config *smd;
};
+/**
+ * SMD port configuration.
+ *
+ * @tty_dev_index Index into smd_tty[]
+ * @port_name Name of the SMD port
+ * @dev_name Name of the TTY Device (if NULL, @port_name is used)
+ * @edge SMD edge
+ */
+struct smd_config {
+ uint32_t tty_dev_index;
+ const char *port_name;
+ const char *dev_name;
+ uint32_t edge;
+};
+
+static struct smd_config smd_configs[] = {
+ {0, "DS", NULL, SMD_APPS_MODEM},
+ {1, "APPS_FM", NULL, SMD_APPS_WCNSS},
+ {2, "APPS_RIVA_BT_ACL", NULL, SMD_APPS_WCNSS},
+ {3, "APPS_RIVA_BT_CMD", NULL, SMD_APPS_WCNSS},
+ {4, "MBALBRIDGE", NULL, SMD_APPS_MODEM},
+ {7, "DATA1", NULL, SMD_APPS_MODEM},
+ {11, "DATA11", NULL, SMD_APPS_MODEM},
+ {21, "DATA21", NULL, SMD_APPS_MODEM},
+ {27, "GPSNMEA", NULL, SMD_APPS_MODEM},
+ {36, "LOOPBACK", "LOOPBACK_TTY", SMD_APPS_MODEM},
+};
+#define DS_IDX 0
#define LOOPBACK_IDX 36
-static char *smd_ch_name[] = {
- [0] = "DS",
- [1] = "APPS_FM",
- [2] = "APPS_RIVA_BT_ACL",
- [3] = "APPS_RIVA_BT_CMD",
- [4] = "MBALBRIDGE",
- [7] = "DATA1",
- [21] = "DATA21",
- [27] = "GPSNMEA",
- [36] = "LOOPBACK",
-};
-
-static uint32_t smd_ch_edge[] = {
- [0] = SMD_APPS_MODEM,
- [1] = SMD_APPS_WCNSS,
- [2] = SMD_APPS_WCNSS,
- [3] = SMD_APPS_WCNSS,
- [4] = SMD_APPS_MODEM,
- [7] = SMD_APPS_MODEM,
- [21] = SMD_APPS_MODEM,
- [27] = SMD_APPS_MODEM,
- [36] = SMD_APPS_MODEM,
-};
-
static struct delayed_work loopback_work;
static struct smd_tty_info smd_tty[MAX_SMD_TTYS];
@@ -228,12 +233,12 @@
static int smd_tty_open(struct tty_struct *tty, struct file *f)
{
int res = 0;
- int n = tty->index;
+ unsigned int n = tty->index;
struct smd_tty_info *info;
char *peripheral = NULL;
- if (!smd_ch_name[n])
+ if (n >= MAX_SMD_TTYS || !smd_tty[n].smd)
return -ENODEV;
info = smd_tty + n;
@@ -242,11 +247,11 @@
tty->driver_data = info;
if (info->open_count++ == 0) {
- if (smd_ch_edge[n] == SMD_APPS_MODEM)
+ if (smd_tty[n].smd->edge == SMD_APPS_MODEM)
peripheral = "modem";
if (peripheral) {
- info->pil = pil_get("modem");
+ info->pil = pil_get(peripheral);
if (IS_ERR(info->pil)) {
res = PTR_ERR(info->pil);
goto out;
@@ -257,7 +262,7 @@
* the wait need to be done atmost once, using msleep
* doesn't degrade the performance.
*/
- if (n == 36) {
+ if (n == LOOPBACK_IDX) {
if (!is_modem_smsm_inited())
msleep(5000);
smsm_change_state(SMSM_APPS_STATE,
@@ -297,15 +302,15 @@
tasklet_init(&info->tty_tsklt, smd_tty_read,
(unsigned long)info);
wake_lock_init(&info->wake_lock, WAKE_LOCK_SUSPEND,
- smd_ch_name[n]);
+ smd_tty[n].smd->port_name);
if (!info->ch) {
- res = smd_named_open_on_edge(smd_ch_name[n],
- smd_ch_edge[n],
+ res = smd_named_open_on_edge(smd_tty[n].smd->port_name,
+ smd_tty[n].smd->edge,
&info->ch, info,
smd_tty_notify);
if (res < 0) {
pr_err("%s: %s open failed %d\n", __func__,
- smd_ch_name[n], res);
+ smd_tty[n].smd->port_name, res);
goto release_pil;
}
@@ -316,7 +321,8 @@
res = -ETIMEDOUT;
if (res < 0) {
pr_err("%s: wait for %s smd_open failed %d\n",
- __func__, smd_ch_name[n], res);
+ __func__, smd_tty[n].smd->port_name,
+ res);
goto release_pil;
}
res = 0;
@@ -474,35 +480,24 @@
static int smd_tty_dummy_probe(struct platform_device *pdev)
{
- if (!strncmp(pdev->name, smd_ch_name[0],
- strnlen(smd_ch_name[0], SMD_MAX_CH_NAME_LEN)))
- complete_all(&smd_tty[0].ch_allocated);
- else if (!strncmp(pdev->name, smd_ch_name[1],
- strnlen(smd_ch_name[1], SMD_MAX_CH_NAME_LEN)))
- complete_all(&smd_tty[1].ch_allocated);
- else if (!strncmp(pdev->name, smd_ch_name[2],
- strnlen(smd_ch_name[2], SMD_MAX_CH_NAME_LEN)))
- complete_all(&smd_tty[2].ch_allocated);
- else if (!strncmp(pdev->name, smd_ch_name[3],
- strnlen(smd_ch_name[3], SMD_MAX_CH_NAME_LEN)))
- complete_all(&smd_tty[3].ch_allocated);
- else if (!strncmp(pdev->name, smd_ch_name[4],
- strnlen(smd_ch_name[4], SMD_MAX_CH_NAME_LEN)))
- complete_all(&smd_tty[4].ch_allocated);
- else if (!strncmp(pdev->name, smd_ch_name[7],
- strnlen(smd_ch_name[7], SMD_MAX_CH_NAME_LEN)))
- complete_all(&smd_tty[7].ch_allocated);
- else if (!strncmp(pdev->name, smd_ch_name[21],
- strnlen(smd_ch_name[21], SMD_MAX_CH_NAME_LEN)))
- complete_all(&smd_tty[21].ch_allocated);
- else if (!strncmp(pdev->name, smd_ch_name[27],
- strnlen(smd_ch_name[27], SMD_MAX_CH_NAME_LEN)))
- complete_all(&smd_tty[27].ch_allocated);
- else if (!strncmp(pdev->name, "LOOPBACK_TTY",
- strnlen("LOOPBACK_TTY", SMD_MAX_CH_NAME_LEN)))
- complete_all(&smd_tty[36].ch_allocated);
+ int n;
+ int idx;
- return 0;
+ for (n = 0; n < ARRAY_SIZE(smd_configs); ++n) {
+ idx = smd_configs[n].tty_dev_index;
+
+ if (!smd_configs[n].dev_name)
+ continue;
+
+ if (!strncmp(pdev->name, smd_configs[n].dev_name,
+ SMD_MAX_CH_NAME_LEN)) {
+ complete_all(&smd_tty[idx].ch_allocated);
+ return 0;
+ }
+ }
+ pr_err("%s: unknown device '%s'\n", __func__, pdev->name);
+
+ return -ENODEV;
}
static struct tty_driver *smd_tty_driver;
@@ -510,7 +505,8 @@
static int __init smd_tty_init(void)
{
int ret;
- int ds_registered = 0;
+ int n;
+ int idx;
smd_tty_driver = alloc_tty_driver(MAX_SMD_TTYS);
if (smd_tty_driver == 0)
@@ -533,150 +529,69 @@
tty_set_operations(smd_tty_driver, &smd_tty_ops);
ret = tty_register_driver(smd_tty_driver);
- if (ret) return ret;
-
- /* this should be dynamic */
- tty_register_device(smd_tty_driver, 0, 0);
- tty_register_device(smd_tty_driver, 1, 0);
- tty_register_device(smd_tty_driver, 2, 0);
- tty_register_device(smd_tty_driver, 3, 0);
- tty_register_device(smd_tty_driver, 4, 0);
- tty_register_device(smd_tty_driver, 7, 0);
- tty_register_device(smd_tty_driver, 21, 0);
- tty_register_device(smd_tty_driver, 27, 0);
- tty_register_device(smd_tty_driver, 36, 0);
-
- init_completion(&smd_tty[0].ch_allocated);
- init_completion(&smd_tty[1].ch_allocated);
- init_completion(&smd_tty[2].ch_allocated);
- init_completion(&smd_tty[3].ch_allocated);
- init_completion(&smd_tty[4].ch_allocated);
- init_completion(&smd_tty[7].ch_allocated);
- init_completion(&smd_tty[21].ch_allocated);
- init_completion(&smd_tty[27].ch_allocated);
- init_completion(&smd_tty[36].ch_allocated);
-
- smd_tty[0].driver.probe = smd_tty_dummy_probe;
- smd_tty[0].driver.driver.name = smd_ch_name[0];
- smd_tty[0].driver.driver.owner = THIS_MODULE;
- spin_lock_init(&smd_tty[0].reset_lock);
- smd_tty[0].is_open = 0;
- init_waitqueue_head(&smd_tty[0].ch_opened_wait_queue);
- /*
- * DS port is opened in the kernel starting with 8660 fusion.
- * Only register the platform driver for targets older than that.
- */
- if (cpu_is_msm7x01() || cpu_is_msm7x25() || cpu_is_msm7x27() ||
- cpu_is_msm7x30() || cpu_is_qsd8x50() ||
- cpu_is_msm8x55() || (cpu_is_msm8x60() &&
- socinfo_get_platform_subtype() == 0x1)) {
- ret = platform_driver_register(&smd_tty[0].driver);
- if (ret)
- goto out;
- ds_registered = 1;
+ if (ret) {
+ put_tty_driver(smd_tty_driver);
+ pr_err("%s: driver registration failed %d\n", __func__, ret);
+ return ret;
}
- smd_tty[1].driver.probe = smd_tty_dummy_probe;
- smd_tty[1].driver.driver.name = smd_ch_name[1];
- smd_tty[1].driver.driver.owner = THIS_MODULE;
- spin_lock_init(&smd_tty[1].reset_lock);
- smd_tty[1].is_open = 0;
- init_waitqueue_head(&smd_tty[1].ch_opened_wait_queue);
- ret = platform_driver_register(&smd_tty[1].driver);
- if (ret)
- goto unreg0;
- smd_tty[2].driver.probe = smd_tty_dummy_probe;
- smd_tty[2].driver.driver.name = smd_ch_name[2];
- smd_tty[2].driver.driver.owner = THIS_MODULE;
- spin_lock_init(&smd_tty[2].reset_lock);
- smd_tty[2].is_open = 0;
- init_waitqueue_head(&smd_tty[2].ch_opened_wait_queue);
- ret = platform_driver_register(&smd_tty[2].driver);
- if (ret)
- goto unreg1;
- smd_tty[3].driver.probe = smd_tty_dummy_probe;
- smd_tty[3].driver.driver.name = smd_ch_name[3];
- smd_tty[3].driver.driver.owner = THIS_MODULE;
- spin_lock_init(&smd_tty[3].reset_lock);
- smd_tty[3].is_open = 0;
- init_waitqueue_head(&smd_tty[3].ch_opened_wait_queue);
- ret = platform_driver_register(&smd_tty[3].driver);
- if (ret)
- goto unreg2;
- smd_tty[4].driver.probe = smd_tty_dummy_probe;
- smd_tty[4].driver.driver.name = smd_ch_name[4];
- smd_tty[4].driver.driver.owner = THIS_MODULE;
- spin_lock_init(&smd_tty[4].reset_lock);
- smd_tty[4].is_open = 0;
- init_waitqueue_head(&smd_tty[4].ch_opened_wait_queue);
- ret = platform_driver_register(&smd_tty[4].driver);
- if (ret)
- goto unreg3;
- smd_tty[7].driver.probe = smd_tty_dummy_probe;
- smd_tty[7].driver.driver.name = smd_ch_name[7];
- smd_tty[7].driver.driver.owner = THIS_MODULE;
- spin_lock_init(&smd_tty[7].reset_lock);
- smd_tty[7].is_open = 0;
- init_waitqueue_head(&smd_tty[7].ch_opened_wait_queue);
- ret = platform_driver_register(&smd_tty[7].driver);
- if (ret)
- goto unreg4;
- smd_tty[21].driver.probe = smd_tty_dummy_probe;
- smd_tty[21].driver.driver.name = smd_ch_name[21];
- smd_tty[21].driver.driver.owner = THIS_MODULE;
- spin_lock_init(&smd_tty[21].reset_lock);
- smd_tty[21].is_open = 0;
- init_waitqueue_head(&smd_tty[21].ch_opened_wait_queue);
- ret = platform_driver_register(&smd_tty[21].driver);
- if (ret)
- goto unreg7;
- smd_tty[27].driver.probe = smd_tty_dummy_probe;
- smd_tty[27].driver.driver.name = smd_ch_name[27];
- smd_tty[27].driver.driver.owner = THIS_MODULE;
- spin_lock_init(&smd_tty[27].reset_lock);
- smd_tty[27].is_open = 0;
- init_waitqueue_head(&smd_tty[27].ch_opened_wait_queue);
- ret = platform_driver_register(&smd_tty[27].driver);
- if (ret)
- goto unreg21;
- smd_tty[36].driver.probe = smd_tty_dummy_probe;
- smd_tty[36].driver.driver.name = "LOOPBACK_TTY";
- smd_tty[36].driver.driver.owner = THIS_MODULE;
- spin_lock_init(&smd_tty[36].reset_lock);
- smd_tty[36].is_open = 0;
- init_waitqueue_head(&smd_tty[36].ch_opened_wait_queue);
- INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
- ret = platform_driver_register(&smd_tty[36].driver);
- if (ret)
- goto unreg27;
+ for (n = 0; n < ARRAY_SIZE(smd_configs); ++n) {
+ idx = smd_configs[n].tty_dev_index;
+
+ if (smd_configs[n].dev_name == NULL)
+ smd_configs[n].dev_name = smd_configs[n].port_name;
+
+ if (idx == DS_IDX) {
+ /*
+ * DS port uses the kernel API starting with
+ * 8660 Fusion. Only register the userspace
+ * platform device for older targets.
+ */
+ int legacy_ds = 0;
+
+ legacy_ds |= cpu_is_msm7x01() || cpu_is_msm7x25();
+ legacy_ds |= cpu_is_msm7x27() || cpu_is_msm7x30();
+ legacy_ds |= cpu_is_qsd8x50() || cpu_is_msm8x55();
+ legacy_ds |= cpu_is_msm8x60() &&
+ (socinfo_get_platform_subtype() == 0x1);
+
+ if (!legacy_ds)
+ continue;
+ }
+
+ tty_register_device(smd_tty_driver, idx, 0);
+ init_completion(&smd_tty[idx].ch_allocated);
+
+ /* register platform device */
+ smd_tty[idx].driver.probe = smd_tty_dummy_probe;
+ smd_tty[idx].driver.driver.name = smd_configs[n].dev_name;
+ smd_tty[idx].driver.driver.owner = THIS_MODULE;
+ spin_lock_init(&smd_tty[idx].reset_lock);
+ smd_tty[idx].is_open = 0;
+ init_waitqueue_head(&smd_tty[idx].ch_opened_wait_queue);
+ ret = platform_driver_register(&smd_tty[idx].driver);
+
+ if (ret) {
+ pr_err("%s: init failed %d (%d)\n", __func__, idx, ret);
+ smd_tty[idx].driver.probe = NULL;
+ goto out;
+ }
+ smd_tty[idx].smd = &smd_configs[n];
+ }
+ INIT_DELAYED_WORK(&loopback_work, loopback_probe_worker);
return 0;
-unreg27:
- platform_driver_unregister(&smd_tty[27].driver);
-unreg21:
- platform_driver_unregister(&smd_tty[21].driver);
-unreg7:
- platform_driver_unregister(&smd_tty[7].driver);
-unreg4:
- platform_driver_unregister(&smd_tty[4].driver);
-unreg3:
- platform_driver_unregister(&smd_tty[3].driver);
-unreg2:
- platform_driver_unregister(&smd_tty[2].driver);
-unreg1:
- platform_driver_unregister(&smd_tty[1].driver);
-unreg0:
- if (ds_registered)
- platform_driver_unregister(&smd_tty[0].driver);
out:
- tty_unregister_device(smd_tty_driver, 0);
- tty_unregister_device(smd_tty_driver, 1);
- tty_unregister_device(smd_tty_driver, 2);
- tty_unregister_device(smd_tty_driver, 3);
- tty_unregister_device(smd_tty_driver, 7);
- tty_unregister_device(smd_tty_driver, 21);
- tty_unregister_device(smd_tty_driver, 27);
- tty_unregister_device(smd_tty_driver, 36);
+ /* unregister platform devices */
+ for (n = 0; n < ARRAY_SIZE(smd_configs); ++n) {
+ idx = smd_configs[n].tty_dev_index;
+
+ if (smd_tty[idx].driver.probe) {
+ platform_driver_unregister(&smd_tty[idx].driver);
+ tty_unregister_device(smd_tty_driver, idx);
+ }
+ }
+
tty_unregister_driver(smd_tty_driver);
put_tty_driver(smd_tty_driver);
return ret;
diff --git a/arch/arm/mach-msm/socinfo.c b/arch/arm/mach-msm/socinfo.c
index 87b6d3f..0c2d4f7 100644
--- a/arch/arm/mach-msm/socinfo.c
+++ b/arch/arm/mach-msm/socinfo.c
@@ -31,6 +31,7 @@
HW_PLATFORM_FLUID = 3,
HW_PLATFORM_SVLTE_FFA = 4,
HW_PLATFORM_SVLTE_SURF = 5,
+ HW_PLATFORM_MTP = 8,
HW_PLATFORM_LIQUID = 9,
/* Dragonboard platform id is assigned as 10 in CDT */
HW_PLATFORM_DRAGON = 10,
@@ -42,9 +43,10 @@
[HW_PLATFORM_SURF] = "Surf",
[HW_PLATFORM_FFA] = "FFA",
[HW_PLATFORM_FLUID] = "Fluid",
- [HW_PLATFORM_LIQUID] = "Liquid",
[HW_PLATFORM_SVLTE_FFA] = "SVLTE_FFA",
[HW_PLATFORM_SVLTE_SURF] = "SLVTE_SURF",
+ [HW_PLATFORM_MTP] = "MTP",
+ [HW_PLATFORM_LIQUID] = "Liquid",
[HW_PLATFORM_DRAGON] = "Dragon"
};
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index 90a1c7e..7e61e8b 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -939,15 +939,17 @@
* Store the most recent timestamp read from hardware
* in last_ns. This is useful for debugging crashes.
*/
-static u64 last_ns;
+static atomic64_t last_ns;
unsigned long long notrace sched_clock(void)
{
struct msm_clock *clock = &msm_clocks[msm_global_timer];
struct clocksource *cs = &clock->clocksource;
- u32 cyc = cs->read(cs);
- last_ns = cyc_to_sched_clock(&cd, cyc, ((u32)~0 >> clock->shift));
- return last_ns;
+ u64 cyc = cs->read(cs);
+ u64 last_ns_local;
+ last_ns_local = cyc_to_sched_clock(&cd, cyc, ((u32)~0 >> clock->shift));
+ atomic64_set(&last_ns, last_ns_local);
+ return last_ns_local;
}
static void notrace msm_update_sched_clock(void)
diff --git a/arch/arm/mach-msm/wcnss-ssr-8960.c b/arch/arm/mach-msm/wcnss-ssr-8960.c
index af92d22..07a9241 100644
--- a/arch/arm/mach-msm/wcnss-ssr-8960.c
+++ b/arch/arm/mach-msm/wcnss-ssr-8960.c
@@ -59,14 +59,33 @@
MODULE_NAME);
return;
}
- if (new_state & SMSM_RESET)
+ if (new_state & SMSM_RESET) {
+ ss_restart_inprogress = true;
schedule_work(&riva_smsm_cb_work);
+ }
}
static void riva_fatal_fn(struct work_struct *work)
{
- if (!ss_restart_inprogress)
+ if (!enable_riva_ssr)
panic(MODULE_NAME ": Watchdog bite received from Riva");
+ else
+ subsystem_restart("riva");
+}
+
+static irqreturn_t riva_wdog_bite_irq_hdlr(int irq, void *dev_id)
+{
+ int ret;
+
+ if (ss_restart_inprogress) {
+ pr_err("%s: Ignoring riva bite irq, restart in progress\n",
+ MODULE_NAME);
+ return IRQ_HANDLED;
+ }
+ disable_irq_nosync(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ);
+ ss_restart_inprogress = true;
+ ret = schedule_work(&riva_fatal_work);
+ return IRQ_HANDLED;
}
/* SMSM reset Riva */
@@ -106,6 +125,9 @@
if (!ret)
pil_force_boot("wcnss");
+ ss_restart_inprogress = false;
+ enable_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ);
+
return ret;
}
@@ -128,8 +150,6 @@
/* Riva crash handler */
static void riva_crash_shutdown(const struct subsys_data *subsys)
{
- ss_restart_inprogress = true;
-
pr_err("%s: crash shutdown : %d\n", MODULE_NAME, riva_crash);
if (riva_crash != true)
smsm_riva_reset();
@@ -176,6 +196,15 @@
" (%d)\n", MODULE_NAME, ret);
goto out;
}
+ ret = request_irq(RIVA_APSS_WDOG_BITE_RESET_RDY_IRQ,
+ riva_wdog_bite_irq_hdlr, IRQF_TRIGGER_HIGH,
+ "riva_wdog", NULL);
+
+ if (ret < 0) {
+ pr_err("%s: Unable to register for Riva bite interrupt"
+ " (%d)\n", MODULE_NAME, ret);
+ goto out;
+ }
ret = riva_restart_init();
if (ret < 0) {
pr_err("%s: Unable to register with ssr. (%d)\n",
diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
index e5e3486..da31deb3 100644
--- a/arch/arm/mm/init.c
+++ b/arch/arm/mm/init.c
@@ -329,11 +329,12 @@
#else
static void arm_memory_present(void)
{
- struct memblock_region *reg;
-
- for_each_memblock(memory, reg)
- memory_present(0, memblock_region_memory_base_pfn(reg),
- memblock_region_memory_end_pfn(reg));
+ struct meminfo *mi = &meminfo;
+ int i;
+ for_each_bank(i, mi) {
+ memory_present(0, bank_pfn_start(&mi->bank[i]),
+ bank_pfn_end(&mi->bank[i]));
+ }
}
#endif
@@ -491,6 +492,17 @@
return pages;
}
+/*
+ * Poison init memory with an undefined instruction (ARM) or a branch to an
+ * undefined instruction (Thumb).
+ */
+static inline void poison_init_mem(void *s, size_t count)
+{
+ u32 *p = (u32 *)s;
+ for (; count != 0; count -= 4)
+ *p++ = 0xe7fddef0;
+}
+
static inline void
free_memmap(unsigned long start_pfn, unsigned long end_pfn)
{
@@ -799,12 +811,14 @@
#ifdef CONFIG_HAVE_TCM
extern char __tcm_start, __tcm_end;
+ poison_init_mem(&__tcm_start, &__tcm_end - &__tcm_start);
totalram_pages += free_area(__phys_to_pfn(__pa(&__tcm_start)),
__phys_to_pfn(__pa(&__tcm_end)),
"TCM link");
#endif
if (!machine_is_integrator() && !machine_is_cintegrator()) {
+ poison_init_mem(__init_begin, __init_end - __init_begin);
reclaimed_initmem = free_area(__phys_to_pfn(__pa(__init_begin)),
__phys_to_pfn(__pa(__init_end)),
"init");
@@ -850,6 +864,7 @@
{
unsigned long reclaimed_initrd_mem;
if (!keep_initrd) {
+ poison_init_mem((void *)start, PAGE_ALIGN(end) - start);
reclaimed_initrd_mem = free_area(__phys_to_pfn(__pa(start)),
__phys_to_pfn(__pa(end)),
"initrd");
diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
index 0f79b76..6d589e2 100644
--- a/arch/arm/mm/mmu.c
+++ b/arch/arm/mm/mmu.c
@@ -797,11 +797,12 @@
#if (defined CONFIG_HIGHMEM) && (defined CONFIG_FIX_MOVABLE_ZONE)
void *v_movable_start;
+ if (movable_reserved_size) {
+ v_movable_start = __va(movable_reserved_start);
- v_movable_start = __va(movable_reserved_start);
-
- if (vmalloc_min > v_movable_start)
- vmalloc_min = v_movable_start;
+ if (vmalloc_min > v_movable_start)
+ vmalloc_min = v_movable_start;
+ }
#endif
for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
struct membank *bank = &meminfo.bank[j];
diff --git a/drivers/base/genlock.c b/drivers/base/genlock.c
index 41bbd3a..7e25684 100644
--- a/drivers/base/genlock.c
+++ b/drivers/base/genlock.c
@@ -31,6 +31,9 @@
#define _RDLOCK GENLOCK_RDLOCK
#define _WRLOCK GENLOCK_WRLOCK
+#define GENLOCK_LOG_ERR(fmt, args...) \
+pr_err("genlock: %s: " fmt, __func__, ##args)
+
struct genlock {
struct list_head active; /* List of handles holding lock */
spinlock_t lock; /* Spinlock to protect the lock internals */
@@ -48,11 +51,28 @@
taken */
};
+/*
+ * Create a spinlock to protect against a race condition when a lock gets
+ * released while another process tries to attach it
+ */
+
+static DEFINE_SPINLOCK(genlock_file_lock);
+
static void genlock_destroy(struct kref *kref)
{
struct genlock *lock = container_of(kref, struct genlock,
refcount);
+ /*
+ * Clear the private data for the file descriptor in case the fd is
+ * still active after the lock gets released
+ */
+
+ spin_lock(&genlock_file_lock);
+ if (lock->file)
+ lock->file->private_data = NULL;
+ spin_unlock(&genlock_file_lock);
+
kfree(lock);
}
@@ -63,6 +83,15 @@
static int genlock_release(struct inode *inodep, struct file *file)
{
+ struct genlock *lock = file->private_data;
+ /*
+ * Clear the refrence back to this file structure to avoid
+ * somehow reusing the lock after the file has been destroyed
+ */
+
+ if (lock)
+ lock->file = NULL;
+
return 0;
}
@@ -81,12 +110,16 @@
{
struct genlock *lock;
- if (handle->lock != NULL)
+ if (handle->lock != NULL) {
+ GENLOCK_LOG_ERR("Handle already has a lock attached\n");
return ERR_PTR(-EINVAL);
+ }
lock = kzalloc(sizeof(*lock), GFP_KERNEL);
- if (lock == NULL)
+ if (lock == NULL) {
+ GENLOCK_LOG_ERR("Unable to allocate memory for a lock\n");
return ERR_PTR(-ENOMEM);
+ }
INIT_LIST_HEAD(&lock->active);
init_waitqueue_head(&lock->queue);
@@ -119,8 +152,10 @@
{
int ret;
- if (!lock->file)
+ if (!lock->file) {
+ GENLOCK_LOG_ERR("No file attached to the lock\n");
return -EINVAL;
+ }
ret = get_unused_fd_flags(0);
if (ret < 0)
@@ -142,19 +177,32 @@
struct file *file;
struct genlock *lock;
- if (handle->lock != NULL)
+ if (handle->lock != NULL) {
+ GENLOCK_LOG_ERR("Handle already has a lock attached\n");
return ERR_PTR(-EINVAL);
+ }
file = fget(fd);
- if (file == NULL)
+ if (file == NULL) {
+ GENLOCK_LOG_ERR("Bad file descriptor\n");
return ERR_PTR(-EBADF);
+ }
+ /*
+ * take a spinlock to avoid a race condition if the lock is
+ * released and then attached
+ */
+
+ spin_lock(&genlock_file_lock);
lock = file->private_data;
+ spin_unlock(&genlock_file_lock);
fput(file);
- if (lock == NULL)
+ if (lock == NULL) {
+ GENLOCK_LOG_ERR("File descriptor is invalid\n");
return ERR_PTR(-EINVAL);
+ }
handle->lock = lock;
kref_get(&lock->refcount);
@@ -198,13 +246,16 @@
spin_lock_irqsave(&lock->lock, irqflags);
- if (lock->state == _UNLOCKED)
+ if (lock->state == _UNLOCKED) {
+ GENLOCK_LOG_ERR("Trying to unlock an unlocked handle\n");
goto done;
+ }
/* Make sure this handle is an owner of the lock */
- if (!handle_has_lock(lock, handle))
+ if (!handle_has_lock(lock, handle)) {
+ GENLOCK_LOG_ERR("handle does not have lock attached to it\n");
goto done;
-
+ }
/* If the handle holds no more references to the lock then
release it (maybe) */
@@ -273,7 +324,8 @@
* Otherwise the user tried to turn a read into a write, and we
* don't allow that.
*/
-
+ GENLOCK_LOG_ERR("Trying to upgrade a read lock to a write"
+ "lock\n");
ret = -EINVAL;
goto done;
}
@@ -343,8 +395,10 @@
struct genlock *lock = handle->lock;
int ret = 0;
- if (lock == NULL)
+ if (lock == NULL) {
+ GENLOCK_LOG_ERR("Handle does not have a lock attached\n");
return -EINVAL;
+ }
switch (op) {
case GENLOCK_UNLOCK:
@@ -355,6 +409,7 @@
ret = _genlock_lock(lock, handle, op, flags, timeout);
break;
default:
+ GENLOCK_LOG_ERR("Invalid lock operation\n");
ret = -EINVAL;
break;
}
@@ -376,8 +431,10 @@
int ret = 0;
unsigned int ticks = msecs_to_jiffies(timeout);
- if (lock == NULL)
+ if (lock == NULL) {
+ GENLOCK_LOG_ERR("Handle does not have a lock attached\n");
return -EINVAL;
+ }
spin_lock_irqsave(&lock->lock, irqflags);
@@ -467,8 +524,10 @@
static struct genlock_handle *_genlock_get_handle(void)
{
struct genlock_handle *handle = kzalloc(sizeof(*handle), GFP_KERNEL);
- if (handle == NULL)
+ if (handle == NULL) {
+ GENLOCK_LOG_ERR("Unable to allocate memory for the handle\n");
return ERR_PTR(-ENOMEM);
+ }
return handle;
}
@@ -539,8 +598,11 @@
return 0;
}
case GENLOCK_IOC_EXPORT: {
- if (handle->lock == NULL)
+ if (handle->lock == NULL) {
+ GENLOCK_LOG_ERR("Handle does not have a lock"
+ "attached\n");
return -EINVAL;
+ }
ret = genlock_get_fd(handle->lock);
if (ret < 0)
@@ -585,6 +647,7 @@
return 0;
}
default:
+ GENLOCK_LOG_ERR("Invalid ioctl\n");
return -EINVAL;
}
}
diff --git a/drivers/bluetooth/hci_smd.c b/drivers/bluetooth/hci_smd.c
index a321849..3c4aa8f 100644
--- a/drivers/bluetooth/hci_smd.c
+++ b/drivers/bluetooth/hci_smd.c
@@ -36,8 +36,6 @@
#define RX_Q_MONITOR (1) /* 1 milli second */
-static unsigned int driver_state;
-
static int hcismd_set;
static DEFINE_MUTEX(hci_smd_enable);
@@ -127,7 +125,8 @@
static void hci_smd_destruct(struct hci_dev *hdev)
{
- kfree(hdev->driver_data);
+ if (NULL != hdev->driver_data)
+ kfree(hdev->driver_data);
}
static void hci_smd_recv_data(unsigned long arg)
@@ -388,33 +387,25 @@
int rc;
/* Initialize and register HCI device */
- if (!driver_state) {
- hdev = hci_alloc_dev();
- if (!hdev) {
- BT_ERR("Can't allocate HCI device");
- return -ENOMEM;
- }
-
- hsmd->hdev = hdev;
- hdev->bus = HCI_SMD;
- hdev->driver_data = hsmd;
- hdev->open = hci_smd_open;
- hdev->close = hci_smd_close;
- hdev->send = hci_smd_send_frame;
- hdev->destruct = hci_smd_destruct;
- hdev->owner = THIS_MODULE;
+ hdev = hci_alloc_dev();
+ if (!hdev) {
+ BT_ERR("Can't allocate HCI device");
+ return -ENOMEM;
}
+ hsmd->hdev = hdev;
+ hdev->bus = HCI_SMD;
+ hdev->driver_data = NULL;
+ hdev->open = hci_smd_open;
+ hdev->close = hci_smd_close;
+ hdev->send = hci_smd_send_frame;
+ hdev->destruct = hci_smd_destruct;
+ hdev->owner = THIS_MODULE;
+
tasklet_init(&hsmd->hci_event_task,
hci_smd_recv_event, (unsigned long) hsmd);
tasklet_init(&hsmd->hci_data_task,
hci_smd_recv_data, (unsigned long) hsmd);
- if (!driver_state) {
- wake_lock_init(&hs.wake_lock_rx, WAKE_LOCK_SUSPEND,
- "msm_smd_Rx");
- wake_lock_init(&hs.wake_lock_tx, WAKE_LOCK_SUSPEND,
- "msm_smd_Tx");
- }
/*
* Setup the timer to monitor whether the Rx queue is empty,
* to control the wake lock release
@@ -442,29 +433,32 @@
/* Disable the read interrupts on the channel */
smd_disable_read_intr(hsmd->event_channel);
smd_disable_read_intr(hsmd->data_channel);
- if (!driver_state) {
- if (hci_register_dev(hdev) < 0) {
- BT_ERR("Can't register HCI device");
- hci_free_dev(hdev);
- return -ENODEV;
- }
- driver_state = 1;
+ if (hci_register_dev(hdev) < 0) {
+ BT_ERR("Can't register HCI device");
+ hci_free_dev(hdev);
+ return -ENODEV;
}
return 0;
}
-static void hci_smd_deregister_dev(void)
+static void hci_smd_deregister_dev(struct hci_smd_data *hsmd)
{
smd_close(hs.event_channel);
smd_close(hs.data_channel);
if (wake_lock_active(&hs.wake_lock_rx))
wake_unlock(&hs.wake_lock_rx);
+ if (wake_lock_active(&hs.wake_lock_tx))
+ wake_unlock(&hs.wake_lock_tx);
/*Destroy the timer used to monitor the Rx queue for emptiness */
del_timer_sync(&hs.rx_q_timer);
tasklet_kill(&hs.hci_event_task);
tasklet_kill(&hs.hci_data_task);
+ if (hci_unregister_dev(hsmd->hdev) < 0)
+ BT_ERR("Can't unregister HCI device %s", hsmd->hdev->name);
+
+ hci_free_dev(hsmd->hdev);
}
static int hcismd_set_enable(const char *val, struct kernel_param *kp)
@@ -484,7 +478,7 @@
hci_smd_register_dev(&hs);
break;
case 0:
- hci_smd_deregister_dev();
+ hci_smd_deregister_dev(&hs);
break;
default:
ret = -EFAULT;
@@ -494,7 +488,22 @@
mutex_unlock(&hci_smd_enable);
return ret;
}
+static int __init hci_smd_init(void)
+{
+ wake_lock_init(&hs.wake_lock_rx, WAKE_LOCK_SUSPEND,
+ "msm_smd_Rx");
+ wake_lock_init(&hs.wake_lock_tx, WAKE_LOCK_SUSPEND,
+ "msm_smd_Tx");
+ return 0;
+}
+module_init(hci_smd_init);
+static void __exit hci_smd_exit(void)
+{
+ wake_lock_destroy(&hs.wake_lock_rx);
+ wake_lock_destroy(&hs.wake_lock_tx);
+}
+module_exit(hci_smd_exit);
MODULE_AUTHOR("Ankur Nandwani <ankurn@codeaurora.org>");
MODULE_DESCRIPTION("Bluetooth SMD driver");
diff --git a/drivers/crypto/msm/qcedev.c b/drivers/crypto/msm/qcedev.c
index 3ae1647..c1a65fc 100644
--- a/drivers/crypto/msm/qcedev.c
+++ b/drivers/crypto/msm/qcedev.c
@@ -31,6 +31,7 @@
#include <crypto/hash.h>
#include <linux/platform_data/qcom_crypto_device.h>
#include <mach/scm.h>
+#include <mach/msm_bus.h>
#include <linux/qcedev.h>
#include "qce.h"
@@ -97,7 +98,7 @@
};
static DEFINE_MUTEX(send_cmd_lock);
-
+static DEFINE_MUTEX(sent_bw_req);
/**********************************************************************
* Register ourselves as a misc device to be able to access the dev driver
* from userspace. */
@@ -111,9 +112,13 @@
struct msm_ce_hw_support platform_support;
uint32_t ce_lock_count;
+ uint32_t high_bw_req_count;
+
/* CE features/algorithms supported by HW engine*/
struct ce_hw_support ce_support;
+ uint32_t bus_scale_handle;
+
/* misc device */
struct miscdevice miscdevice;
@@ -167,6 +172,29 @@
#endif
}
+static int qcedev_ce_high_bw_req(struct qcedev_control *podev,
+ bool high_bw_req)
+{
+ int ret = 0;
+
+ mutex_lock(&sent_bw_req);
+ if (high_bw_req) {
+ if (podev->high_bw_req_count == 0)
+ msm_bus_scale_client_update_request(
+ podev->bus_scale_handle, 1);
+ podev->high_bw_req_count++;
+ } else {
+ if (podev->high_bw_req_count == 1)
+ msm_bus_scale_client_update_request(
+ podev->bus_scale_handle, 0);
+ podev->high_bw_req_count--;
+ }
+ mutex_unlock(&sent_bw_req);
+
+ return ret;
+}
+
+
static int qcedev_unlock_ce(struct qcedev_control *podev)
{
int ret = 0;
@@ -302,7 +330,8 @@
handle->cntl = podev;
file->private_data = handle;
-
+ if (podev->platform_support.bus_scale_table != NULL)
+ return qcedev_ce_high_bw_req(podev, true);
return 0;
}
@@ -319,7 +348,8 @@
}
kzfree(handle);
file->private_data = NULL;
-
+ if (podev->platform_support.bus_scale_table != NULL)
+ return qcedev_ce_high_bw_req(podev, false);
return 0;
}
@@ -1992,7 +2022,10 @@
platform_support->shared_ce_resource;
podev->platform_support.hw_key_support =
platform_support->hw_key_support;
+ podev->platform_support.bus_scale_table =
+ platform_support->bus_scale_table;
podev->ce_lock_count = 0;
+ podev->high_bw_req_count = 0;
INIT_LIST_HEAD(&podev->ready_commands);
podev->active_command = NULL;
@@ -2011,10 +2044,28 @@
podev->pdev = pdev;
platform_set_drvdata(pdev, podev);
qce_hw_support(podev->qce, &podev->ce_support);
+
+ if (podev->platform_support.bus_scale_table != NULL) {
+ podev->bus_scale_handle =
+ msm_bus_scale_register_client(
+ (struct msm_bus_scale_pdata *)
+ podev->platform_support.bus_scale_table);
+ if (!podev->bus_scale_handle) {
+ printk(KERN_ERR "%s not able to get bus scale\n",
+ __func__);
+ rc = -ENOMEM;
+ goto err;
+ }
+ }
rc = misc_register(&podev->miscdevice);
if (rc >= 0)
return 0;
+ else
+ if (podev->platform_support.bus_scale_table != NULL)
+ msm_bus_scale_unregister_client(
+ podev->bus_scale_handle);
+err:
if (handle)
qce_close(handle);
@@ -2034,6 +2085,9 @@
if (podev->qce)
qce_close(podev->qce);
+ if (podev->platform_support.bus_scale_table != NULL)
+ msm_bus_scale_unregister_client(podev->bus_scale_handle);
+
if (podev->miscdevice.minor != MISC_DYNAMIC_MINOR)
misc_deregister(&podev->miscdevice);
tasklet_kill(&podev->done_tasklet);
@@ -2164,7 +2218,7 @@
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Mona Hossain <mhossain@codeaurora.org>");
MODULE_DESCRIPTION("Qualcomm DEV Crypto driver");
-MODULE_VERSION("1.24");
+MODULE_VERSION("1.25");
module_init(qcedev_init);
module_exit(qcedev_exit);
diff --git a/drivers/crypto/msm/qcrypto.c b/drivers/crypto/msm/qcrypto.c
index 67301877..3fff05c 100644
--- a/drivers/crypto/msm/qcrypto.c
+++ b/drivers/crypto/msm/qcrypto.c
@@ -37,6 +37,7 @@
#include <mach/scm.h>
#include <linux/platform_data/qcom_crypto_device.h>
+#include <mach/msm_bus.h>
#include "qce.h"
@@ -80,6 +81,8 @@
/* CE features/algorithms supported by HW engine*/
struct ce_hw_support ce_support;
+
+ uint32_t bus_scale_handle;
/* the lock protects queue and req*/
spinlock_t lock;
@@ -100,6 +103,7 @@
struct crypto_queue queue;
uint32_t ce_lock_count;
+ uint32_t high_bw_req_count;
struct work_struct unlock_ce_ws;
@@ -116,6 +120,8 @@
#define NUM_RETRY 1000
#define CE_BUSY 55
+static DEFINE_MUTEX(sent_bw_req);
+
static int qcrypto_scm_cmd(int resource, int cmd, int *response)
{
#ifdef CONFIG_MSM_SCM
@@ -323,6 +329,27 @@
}
}
+static int qcrypto_ce_high_bw_req(struct crypto_priv *cp, bool high_bw_req)
+{
+ int ret = 0;
+
+ mutex_lock(&sent_bw_req);
+ if (high_bw_req) {
+ if (cp->high_bw_req_count == 0)
+ ret = msm_bus_scale_client_update_request(
+ cp->bus_scale_handle, 1);
+ cp->high_bw_req_count++;
+ } else {
+ if (cp->high_bw_req_count == 1)
+ ret = msm_bus_scale_client_update_request(
+ cp->bus_scale_handle, 0);
+ cp->high_bw_req_count--;
+ }
+ mutex_unlock(&sent_bw_req);
+
+ return ret;
+}
+
static void _start_qcrypto_process(struct crypto_priv *cp);
static struct qcrypto_alg *_qcrypto_sha_alg_alloc(struct crypto_priv *cp,
@@ -375,6 +402,8 @@
/* random first IV */
get_random_bytes(ctx->iv, QCRYPTO_MAX_IV_LENGTH);
+ if (ctx->cp->platform_support.bus_scale_table != NULL)
+ return qcrypto_ce_high_bw_req(ctx->cp, true);
return 0;
};
@@ -410,6 +439,9 @@
}
sha_ctx->ahash_req = NULL;
+ if (sha_ctx->cp->platform_support.bus_scale_table != NULL)
+ return qcrypto_ce_high_bw_req(sha_ctx->cp, true);
+
return 0;
};
@@ -429,6 +461,8 @@
ahash_request_free(sha_ctx->ahash_req);
sha_ctx->ahash_req = NULL;
}
+ if (sha_ctx->cp->platform_support.bus_scale_table != NULL)
+ qcrypto_ce_high_bw_req(sha_ctx->cp, false);
};
@@ -458,6 +492,9 @@
&sha_ctx->ahash_req_complete);
crypto_ahash_clear_flags(ahash, ~0);
+ if (sha_ctx->cp->platform_support.bus_scale_table != NULL)
+ qcrypto_ce_high_bw_req(sha_ctx->cp, true);
+
return 0;
};
@@ -473,6 +510,22 @@
return _qcrypto_cipher_cra_init(tfm);
};
+static void _qcrypto_cra_ablkcipher_exit(struct crypto_tfm *tfm)
+{
+ struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ if (ctx->cp->platform_support.bus_scale_table != NULL)
+ qcrypto_ce_high_bw_req(ctx->cp, false);
+};
+
+static void _qcrypto_cra_aead_exit(struct crypto_tfm *tfm)
+{
+ struct qcrypto_cipher_ctx *ctx = crypto_tfm_ctx(tfm);
+
+ if (ctx->cp->platform_support.bus_scale_table != NULL)
+ qcrypto_ce_high_bw_req(ctx->cp, false);
+};
+
static int _disp_stats(int id)
{
struct crypto_stat *pstat;
@@ -577,6 +630,9 @@
if (!cp)
return 0;
+ if (cp->platform_support.bus_scale_table != NULL)
+ msm_bus_scale_unregister_client(cp->bus_scale_handle);
+
list_for_each_entry_safe(q_alg, n, &cp->alg_list, entry) {
if (q_alg->alg_type == QCRYPTO_ALG_CIPHER)
crypto_unregister_alg(&q_alg->cipher_alg);
@@ -2676,6 +2732,7 @@
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_ablkcipher_init,
+ .cra_exit = _qcrypto_cra_ablkcipher_exit,
.cra_u = {
.ablkcipher = {
.min_keysize = AES_MIN_KEY_SIZE,
@@ -2697,6 +2754,7 @@
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_ablkcipher_init,
+ .cra_exit = _qcrypto_cra_ablkcipher_exit,
.cra_u = {
.ablkcipher = {
.ivsize = AES_BLOCK_SIZE,
@@ -2719,6 +2777,7 @@
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_ablkcipher_init,
+ .cra_exit = _qcrypto_cra_ablkcipher_exit,
.cra_u = {
.ablkcipher = {
.ivsize = AES_BLOCK_SIZE,
@@ -2741,6 +2800,7 @@
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_ablkcipher_init,
+ .cra_exit = _qcrypto_cra_ablkcipher_exit,
.cra_u = {
.ablkcipher = {
.min_keysize = DES_KEY_SIZE,
@@ -2762,6 +2822,7 @@
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_ablkcipher_init,
+ .cra_exit = _qcrypto_cra_ablkcipher_exit,
.cra_u = {
.ablkcipher = {
.ivsize = DES_BLOCK_SIZE,
@@ -2784,6 +2845,7 @@
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_ablkcipher_init,
+ .cra_exit = _qcrypto_cra_ablkcipher_exit,
.cra_u = {
.ablkcipher = {
.min_keysize = DES3_EDE_KEY_SIZE,
@@ -2805,6 +2867,7 @@
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_ablkcipher_init,
+ .cra_exit = _qcrypto_cra_ablkcipher_exit,
.cra_u = {
.ablkcipher = {
.ivsize = DES3_EDE_BLOCK_SIZE,
@@ -2829,6 +2892,7 @@
.cra_type = &crypto_ablkcipher_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_ablkcipher_init,
+ .cra_exit = _qcrypto_cra_ablkcipher_exit,
.cra_u = {
.ablkcipher = {
.ivsize = AES_BLOCK_SIZE,
@@ -2853,6 +2917,7 @@
.cra_type = &crypto_aead_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_aead_init,
+ .cra_exit = _qcrypto_cra_aead_exit,
.cra_u = {
.aead = {
.ivsize = AES_BLOCK_SIZE,
@@ -2879,6 +2944,7 @@
.cra_type = &crypto_aead_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_aead_init,
+ .cra_exit = _qcrypto_cra_aead_exit,
.cra_u = {
.aead = {
.ivsize = AES_BLOCK_SIZE,
@@ -2904,6 +2970,7 @@
.cra_type = &crypto_aead_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_aead_init,
+ .cra_exit = _qcrypto_cra_aead_exit,
.cra_u = {
.aead = {
.ivsize = DES_BLOCK_SIZE,
@@ -2928,6 +2995,7 @@
.cra_type = &crypto_aead_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_aead_init,
+ .cra_exit = _qcrypto_cra_aead_exit,
.cra_u = {
.aead = {
.ivsize = DES3_EDE_BLOCK_SIZE,
@@ -2954,6 +3022,7 @@
.cra_type = &crypto_aead_type,
.cra_module = THIS_MODULE,
.cra_init = _qcrypto_cra_aead_init,
+ .cra_exit = _qcrypto_cra_aead_exit,
.cra_u = {
.aead = {
.ivsize = AES_BLOCK_SIZE,
@@ -3011,12 +3080,28 @@
platform_support->shared_ce_resource;
cp->platform_support.hw_key_support =
platform_support->hw_key_support;
+ cp->platform_support.bus_scale_table =
+ platform_support->bus_scale_table;
+ cp->high_bw_req_count = 0;
cp->ce_lock_count = 0;
cp->platform_support.sha_hmac = platform_support->sha_hmac;
if (cp->platform_support.ce_shared)
INIT_WORK(&cp->unlock_ce_ws, qcrypto_unlock_ce);
+ if (cp->platform_support.bus_scale_table != NULL) {
+ cp->bus_scale_handle =
+ msm_bus_scale_register_client(
+ (struct msm_bus_scale_pdata *)
+ cp->platform_support.bus_scale_table);
+ if (!cp->bus_scale_handle) {
+ printk(KERN_ERR "%s not able to get bus scale\n",
+ __func__);
+ rc = -ENOMEM;
+ goto err;
+ }
+ }
+
/* register crypto cipher algorithms the device supports */
for (i = 0; i < ARRAY_SIZE(_qcrypto_ablk_cipher_algos); i++) {
struct qcrypto_alg *q_alg;
@@ -3274,4 +3359,4 @@
MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Mona Hossain <mhossain@codeaurora.org>");
MODULE_DESCRIPTION("Qualcomm Crypto driver");
-MODULE_VERSION("1.19");
+MODULE_VERSION("1.20");
diff --git a/drivers/gpu/ion/ion_carveout_heap.c b/drivers/gpu/ion/ion_carveout_heap.c
index 9cb338a..f1d40ef 100644
--- a/drivers/gpu/ion/ion_carveout_heap.c
+++ b/drivers/gpu/ion/ion_carveout_heap.c
@@ -36,8 +36,8 @@
ion_phys_addr_t base;
unsigned long allocated_bytes;
unsigned long total_size;
- void (*request_region)(void *);
- void (*release_region)(void *);
+ int (*request_region)(void *);
+ int (*release_region)(void *);
atomic_t map_count;
void *bus_id;
};
@@ -138,10 +138,17 @@
struct ion_carveout_heap *carveout_heap =
container_of(heap, struct ion_carveout_heap, heap);
- if (atomic_inc_return(&carveout_heap->map_count) == 1)
- if (carveout_heap->request_region)
- carveout_heap->request_region(carveout_heap->bus_id);
-
+ if (atomic_inc_return(&carveout_heap->map_count) == 1) {
+ if (carveout_heap->request_region) {
+ int ret = carveout_heap->request_region(
+ carveout_heap->bus_id);
+ if (ret) {
+ pr_err("Unable to request SMI region");
+ atomic_dec(&carveout_heap->map_count);
+ return NULL;
+ }
+ }
+ }
if (ION_IS_CACHED(flags))
return ioremap_cached(buffer->priv_phys, buffer->size);
else
@@ -157,9 +164,14 @@
__arch_iounmap(buffer->vaddr);
buffer->vaddr = NULL;
- if (atomic_dec_and_test(&carveout_heap->map_count))
- if (carveout_heap->release_region)
- carveout_heap->release_region(carveout_heap->bus_id);
+ if (atomic_dec_and_test(&carveout_heap->map_count)) {
+ if (carveout_heap->release_region) {
+ int ret = carveout_heap->release_region(
+ carveout_heap->bus_id);
+ if (ret)
+ pr_err("Unable to release SMI region");
+ }
+ }
return;
}
@@ -170,9 +182,17 @@
struct ion_carveout_heap *carveout_heap =
container_of(heap, struct ion_carveout_heap, heap);
- if (atomic_inc_return(&carveout_heap->map_count) == 1)
- if (carveout_heap->request_region)
- carveout_heap->request_region(carveout_heap->bus_id);
+ if (atomic_inc_return(&carveout_heap->map_count) == 1) {
+ if (carveout_heap->request_region) {
+ int ret = carveout_heap->request_region(
+ carveout_heap->bus_id);
+ if (ret) {
+ pr_err("Unable to request SMI region");
+ atomic_dec(&carveout_heap->map_count);
+ return -EINVAL;
+ }
+ }
+ }
if (ION_IS_CACHED(flags))
return remap_pfn_range(vma, vma->vm_start,
@@ -192,9 +212,14 @@
struct ion_carveout_heap *carveout_heap =
container_of(heap, struct ion_carveout_heap, heap);
- if (atomic_dec_and_test(&carveout_heap->map_count))
- if (carveout_heap->release_region)
- carveout_heap->release_region(carveout_heap->bus_id);
+ if (atomic_dec_and_test(&carveout_heap->map_count)) {
+ if (carveout_heap->release_region) {
+ int ret = carveout_heap->release_region(
+ carveout_heap->bus_id);
+ if (ret)
+ pr_err("Unable to release SMI region");
+ }
+ }
}
int ion_carveout_cache_ops(struct ion_heap *heap, struct ion_buffer *buffer,
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index 2681263..a8a0e59 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -194,7 +194,7 @@
if (device->requested_state == KGSL_STATE_NONE) {
if (device->pwrctrl.nap_allowed == true) {
- device->requested_state = KGSL_STATE_NAP;
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
queue_work(device->work_queue, &device->idle_check_ws);
} else if (device->pwrscale.policy != NULL) {
queue_work(device->work_queue, &device->idle_check_ws);
@@ -495,8 +495,7 @@
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
int init_reftimestamp = 0x7fffffff;
- device->state = KGSL_STATE_INIT;
- device->requested_state = KGSL_STATE_NONE;
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
/* Power up the device */
kgsl_pwrctrl_enable(device);
@@ -716,33 +715,27 @@
static int
adreno_dump_and_recover(struct kgsl_device *device)
{
- static int recovery;
int result = -ETIMEDOUT;
if (device->state == KGSL_STATE_HUNG)
goto done;
- if (device->state == KGSL_STATE_DUMP_AND_RECOVER && !recovery) {
+ if (device->state == KGSL_STATE_DUMP_AND_RECOVER) {
mutex_unlock(&device->mutex);
wait_for_completion(&device->recovery_gate);
mutex_lock(&device->mutex);
- if (!(device->state & KGSL_STATE_HUNG))
- /* recovery success */
+ if (device->state != KGSL_STATE_HUNG)
result = 0;
} else {
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_DUMP_AND_RECOVER);
INIT_COMPLETION(device->recovery_gate);
/* Detected a hang - trigger an automatic dump */
adreno_postmortem_dump(device, 0);
- if (!recovery) {
- recovery = 1;
- result = adreno_recover_hang(device);
- if (result)
- device->state = KGSL_STATE_HUNG;
- recovery = 0;
- complete_all(&device->recovery_gate);
- } else
- KGSL_DRV_ERR(device,
- "Cannot recover from another hang while "
- "recovering from a hang\n");
+ result = adreno_recover_hang(device);
+ if (result)
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_HUNG);
+ else
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
+ complete_all(&device->recovery_gate);
}
done:
return result;
@@ -905,6 +898,7 @@
struct adreno_ringbuffer *rb = &adreno_dev->ringbuffer;
unsigned int rbbm_status;
+ WARN_ON(!(rb->flags & KGSL_FLAGS_STARTED));
if (rb->flags & KGSL_FLAGS_STARTED) {
/* Is the ring buffer is empty? */
GSL_RB_GET_READPTR(rb, &rb->rptr);
@@ -916,8 +910,8 @@
status = true;
}
} else {
- KGSL_DRV_ERR(device, "ringbuffer not started\n");
- BUG();
+ /* if the ringbuffer isn't started we are VERY idle */
+ status = true;
}
return status;
}
@@ -987,28 +981,27 @@
while (1) {
struct adreno_context *adreno_context = NULL;
- struct kgsl_memdesc *gpustate;
- struct kgsl_memdesc *gmemshadow;
context = idr_get_next(&device->context_idr, &next);
if (context == NULL)
break;
adreno_context = (struct adreno_context *)context->devctxt;
- if (!kgsl_mmu_pt_equal(adreno_context->pagetable, pt_base))
- continue;
+ if (kgsl_mmu_pt_equal(adreno_context->pagetable, pt_base)) {
+ struct kgsl_memdesc *desc;
- gpustate = &adreno_context->gpustate;
- if (kgsl_gpuaddr_in_memdesc(gpustate, gpuaddr, size)) {
- result = gpustate;
- return result;
- }
- gmemshadow = &adreno_context->context_gmem_shadow.gmemshadow;
- if (kgsl_gpuaddr_in_memdesc(gmemshadow, gpuaddr, size)) {
- result = gmemshadow;
- return result;
- }
+ desc = &adreno_context->gpustate;
+ if (kgsl_gpuaddr_in_memdesc(desc, gpuaddr, size)) {
+ result = desc;
+ return result;
+ }
+ desc = &adreno_context->context_gmem_shadow.gmemshadow;
+ if (kgsl_gpuaddr_in_memdesc(desc, gpuaddr, size)) {
+ result = desc;
+ return result;
+ }
+ }
next = next + 1;
}
diff --git a/drivers/gpu/msm/adreno_postmortem.c b/drivers/gpu/msm/adreno_postmortem.c
index c6b850e..aafef21 100644
--- a/drivers/gpu/msm/adreno_postmortem.c
+++ b/drivers/gpu/msm/adreno_postmortem.c
@@ -21,6 +21,7 @@
#include "adreno_postmortem.h"
#include "adreno_debugfs.h"
#include "kgsl_cffdump.h"
+#include "kgsl_pwrctrl.h"
#include "a2xx_reg.h"
@@ -470,23 +471,8 @@
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- struct kgsl_pwrctrl *pwr = &device->pwrctrl;
-
mb();
- KGSL_LOG_DUMP(device, "POWER: FLAGS = %08lX | ACTIVE POWERLEVEL = %08X",
- pwr->power_flags, pwr->active_pwrlevel);
-
- KGSL_LOG_DUMP(device, "POWER: INTERVAL TIMEOUT = %08X ",
- pwr->interval_timeout);
-
- KGSL_LOG_DUMP(device, "GRP_CLK = %lu ",
- kgsl_get_clkrate(pwr->grp_clks[0]));
-
- KGSL_LOG_DUMP(device, "BUS CLK = %lu ",
- kgsl_get_clkrate(pwr->ebi1_clk));
-
-
kgsl_regread(device, REG_RBBM_STATUS, &rbbm_status);
kgsl_regread(device, REG_RBBM_PM_OVERRIDE1, &r2);
kgsl_regread(device, REG_RBBM_PM_OVERRIDE2, &r3);
@@ -810,6 +796,7 @@
int adreno_postmortem_dump(struct kgsl_device *device, int manual)
{
bool saved_nap;
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
BUG_ON(device == NULL);
@@ -828,8 +815,23 @@
kgsl_idle(device, KGSL_TIMEOUT_DEFAULT);
}
+ KGSL_LOG_DUMP(device, "POWER: FLAGS = %08lX | ACTIVE POWERLEVEL = %08X",
+ pwr->power_flags, pwr->active_pwrlevel);
+
+ KGSL_LOG_DUMP(device, "POWER: INTERVAL TIMEOUT = %08X ",
+ pwr->interval_timeout);
+
+ KGSL_LOG_DUMP(device, "GRP_CLK = %lu ",
+ kgsl_get_clkrate(pwr->grp_clks[0]));
+
+ KGSL_LOG_DUMP(device, "BUS CLK = %lu ",
+ kgsl_get_clkrate(pwr->ebi1_clk));
+
/* Disable the idle timer so we don't get interrupted */
del_timer_sync(&device->idle_timer);
+ mutex_unlock(&device->mutex);
+ flush_workqueue(device->work_queue);
+ mutex_lock(&device->mutex);
/* Turn off napping to make sure we have the clocks full
attention through the following process */
@@ -842,21 +844,6 @@
/* Disable the irq */
kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
- /* If this is not a manual trigger, then set up the
- state to try to recover */
-
- if (!manual) {
- device->state = KGSL_STATE_DUMP_AND_RECOVER;
- KGSL_PWR_WARN(device,
- "state -> DUMP_AND_RECOVER, device %d\n",
- device->id);
- }
-
- KGSL_DRV_ERR(device,
- "wait for work in workqueue to complete\n");
- mutex_unlock(&device->mutex);
- flush_workqueue(device->work_queue);
- mutex_lock(&device->mutex);
adreno_dump(device);
/* Restore nap mode */
@@ -871,7 +858,7 @@
kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON);
/* try to go into a sleep mode until the next event */
- device->requested_state = KGSL_STATE_SLEEP;
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_SLEEP);
kgsl_pwrctrl_sleep(device);
}
diff --git a/drivers/gpu/msm/adreno_ringbuffer.c b/drivers/gpu/msm/adreno_ringbuffer.c
index a0982006..7ace67a 100644
--- a/drivers/gpu/msm/adreno_ringbuffer.c
+++ b/drivers/gpu/msm/adreno_ringbuffer.c
@@ -393,7 +393,6 @@
if (rb->flags & KGSL_FLAGS_STARTED) {
/* ME_HALT */
adreno_regwrite(rb->device, REG_CP_ME_CNTL, 0x10000000);
-
rb->flags &= ~KGSL_FLAGS_STARTED;
}
}
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 7a42dcd..3207040 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -302,7 +302,7 @@
if (device->pwrctrl.nap_allowed == true &&
device->state == KGSL_STATE_ACTIVE &&
device->requested_state == KGSL_STATE_NONE) {
- device->requested_state = KGSL_STATE_NAP;
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
if (kgsl_pwrctrl_sleep(device) != 0)
mod_timer(&device->idle_timer,
jiffies +
@@ -395,7 +395,7 @@
device->pwrctrl.nap_allowed = false;
policy_saved = device->pwrscale.policy;
device->pwrscale.policy = NULL;
- device->requested_state = KGSL_STATE_SUSPEND;
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_SUSPEND);
/* Make sure no user process is waiting for a timestamp *
* before supending */
if (device->active_cnt != 0) {
@@ -417,22 +417,18 @@
INIT_COMPLETION(device->hwaccess_gate);
device->ftbl->suspend_context(device);
device->ftbl->stop(device);
- device->state = KGSL_STATE_SUSPEND;
- KGSL_PWR_WARN(device, "state -> SUSPEND, device %d\n",
- device->id);
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_SUSPEND);
break;
case KGSL_STATE_SLUMBER:
INIT_COMPLETION(device->hwaccess_gate);
- device->state = KGSL_STATE_SUSPEND;
- KGSL_PWR_WARN(device, "state -> SUSPEND, device %d\n",
- device->id);
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_SUSPEND);
break;
default:
KGSL_PWR_ERR(device, "suspend fail, device %d\n",
device->id);
goto end;
}
- device->requested_state = KGSL_STATE_NONE;
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
device->pwrctrl.nap_allowed = nap_allowed_saved;
device->pwrscale.policy = policy_saved;
status = 0;
@@ -453,14 +449,11 @@
KGSL_PWR_WARN(device, "resume start\n");
mutex_lock(&device->mutex);
if (device->state == KGSL_STATE_SUSPEND) {
- device->state = KGSL_STATE_SLUMBER;
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER);
status = 0;
- KGSL_PWR_WARN(device,
- "state -> SLUMBER, device %d\n",
- device->id);
complete_all(&device->hwaccess_gate);
}
- device->requested_state = KGSL_STATE_NONE;
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
mutex_unlock(&device->mutex);
KGSL_PWR_WARN(device, "resume end\n");
@@ -505,7 +498,7 @@
struct kgsl_device, display_off);
KGSL_PWR_WARN(device, "early suspend start\n");
mutex_lock(&device->mutex);
- device->requested_state = KGSL_STATE_SLUMBER;
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_SLUMBER);
kgsl_pwrctrl_sleep(device);
mutex_unlock(&device->mutex);
KGSL_PWR_WARN(device, "early suspend end\n");
@@ -535,6 +528,7 @@
mutex_lock(&device->mutex);
kgsl_pwrctrl_wake(device);
device->pwrctrl.restore_slumber = 0;
+ kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
mutex_unlock(&device->mutex);
kgsl_check_idle(device);
KGSL_PWR_WARN(device, "late resume end\n");
@@ -651,8 +645,7 @@
device->open_count--;
if (device->open_count == 0) {
result = device->ftbl->stop(device);
- device->state = KGSL_STATE_INIT;
- KGSL_PWR_WARN(device, "state -> INIT, device %d\n", device->id);
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
}
/* clean up any to-be-freed entries that belong to this
* process and this device
@@ -720,9 +713,7 @@
mutex_unlock(&device->mutex);
goto err_putprocess;
}
- device->state = KGSL_STATE_ACTIVE;
- KGSL_PWR_WARN(device,
- "state -> ACTIVE, device %d\n", minor);
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
}
device->open_count++;
mutex_unlock(&device->mutex);
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 3ef11ce..f82b038 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -175,6 +175,7 @@
struct kobject pwrscale_kobj;
struct work_struct ts_expired_ws;
struct list_head events;
+ s64 on_time;
};
struct kgsl_context {
@@ -317,4 +318,6 @@
irqreturn_t (*dev_isr) (int, void*));
void kgsl_device_platform_remove(struct kgsl_device *device);
+const char *kgsl_pwrstate_to_str(unsigned int state);
+
#endif /* __KGSL_DEVICE_H */
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index dd7b1d6..f4abf99 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -18,6 +18,7 @@
#include "kgsl.h"
#include "kgsl_pwrscale.h"
#include "kgsl_device.h"
+#include "kgsl_trace.h"
#define KGSL_PWRFLAGS_POWER_ON 0
#define KGSL_PWRFLAGS_CLK_ON 1
@@ -62,24 +63,20 @@
if (new_level < (pwr->num_pwrlevels - 1) &&
new_level >= pwr->thermal_pwrlevel &&
new_level != pwr->active_pwrlevel) {
+ struct kgsl_pwrlevel *pwrlevel = &pwr->pwrlevels[new_level];
pwr->active_pwrlevel = new_level;
if ((test_bit(KGSL_PWRFLAGS_CLK_ON, &pwr->power_flags)) ||
(device->state == KGSL_STATE_NAP))
- clk_set_rate(pwr->grp_clks[0],
- pwr->pwrlevels[pwr->active_pwrlevel].
- gpu_freq);
+ clk_set_rate(pwr->grp_clks[0], pwrlevel->gpu_freq);
if (test_bit(KGSL_PWRFLAGS_AXI_ON, &pwr->power_flags)) {
if (pwr->pcl)
msm_bus_scale_client_update_request(pwr->pcl,
- pwr->pwrlevels[pwr->active_pwrlevel].
- bus_freq);
+ pwrlevel->bus_freq);
else if (pwr->ebi1_clk)
- clk_set_rate(pwr->ebi1_clk,
- pwr->pwrlevels[pwr->active_pwrlevel].
- bus_freq);
+ clk_set_rate(pwr->ebi1_clk, pwrlevel->bus_freq);
}
- KGSL_PWR_WARN(device, "kgsl pwr level changed to %d\n",
- pwr->active_pwrlevel);
+ trace_kgsl_pwrlevel(device, pwr->active_pwrlevel,
+ pwrlevel->gpu_freq);
}
}
EXPORT_SYMBOL(kgsl_pwrctrl_pwrlevel_change);
@@ -278,7 +275,7 @@
DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show, kgsl_pwrctrl_gpuclk_store);
DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show,
kgsl_pwrctrl_max_gpuclk_store);
-DEVICE_ATTR(pwrnap, 0644, kgsl_pwrctrl_pwrnap_show, kgsl_pwrctrl_pwrnap_store);
+DEVICE_ATTR(pwrnap, 0666, kgsl_pwrctrl_pwrnap_show, kgsl_pwrctrl_pwrnap_store);
DEVICE_ATTR(idle_timer, 0644, kgsl_pwrctrl_idle_timer_show,
kgsl_pwrctrl_idle_timer_store);
DEVICE_ATTR(gpubusy, 0644, kgsl_pwrctrl_gpubusy_show,
@@ -335,8 +332,7 @@
if (state == KGSL_PWRFLAGS_OFF) {
if (test_and_clear_bit(KGSL_PWRFLAGS_CLK_ON,
&pwr->power_flags)) {
- KGSL_PWR_INFO(device,
- "clocks off, device %d\n", device->id);
+ trace_kgsl_clk(device, state);
for (i = KGSL_MAX_CLKS - 1; i > 0; i--)
if (pwr->grp_clks[i])
clk_disable(pwr->grp_clks[i]);
@@ -350,9 +346,7 @@
} else if (state == KGSL_PWRFLAGS_ON) {
if (!test_and_set_bit(KGSL_PWRFLAGS_CLK_ON,
&pwr->power_flags)) {
- KGSL_PWR_INFO(device,
- "clocks on, device %d\n", device->id);
-
+ trace_kgsl_clk(device, state);
if ((pwr->pwrlevels[0].gpu_freq > 0) &&
(device->state != KGSL_STATE_NAP))
clk_set_rate(pwr->grp_clks[0],
@@ -376,8 +370,7 @@
if (state == KGSL_PWRFLAGS_OFF) {
if (test_and_clear_bit(KGSL_PWRFLAGS_AXI_ON,
&pwr->power_flags)) {
- KGSL_PWR_INFO(device,
- "axi off, device %d\n", device->id);
+ trace_kgsl_bus(device, state);
if (pwr->ebi1_clk) {
clk_set_rate(pwr->ebi1_clk, 0);
clk_disable(pwr->ebi1_clk);
@@ -389,8 +382,7 @@
} else if (state == KGSL_PWRFLAGS_ON) {
if (!test_and_set_bit(KGSL_PWRFLAGS_AXI_ON,
&pwr->power_flags)) {
- KGSL_PWR_INFO(device,
- "axi on, device %d\n", device->id);
+ trace_kgsl_bus(device, state);
if (pwr->ebi1_clk) {
clk_enable(pwr->ebi1_clk);
clk_set_rate(pwr->ebi1_clk,
@@ -412,16 +404,14 @@
if (state == KGSL_PWRFLAGS_OFF) {
if (test_and_clear_bit(KGSL_PWRFLAGS_POWER_ON,
&pwr->power_flags)) {
- KGSL_PWR_INFO(device,
- "power off, device %d\n", device->id);
+ trace_kgsl_rail(device, state);
if (pwr->gpu_reg)
regulator_disable(pwr->gpu_reg);
}
} else if (state == KGSL_PWRFLAGS_ON) {
if (!test_and_set_bit(KGSL_PWRFLAGS_POWER_ON,
&pwr->power_flags)) {
- KGSL_PWR_INFO(device,
- "power on, device %d\n", device->id);
+ trace_kgsl_rail(device, state);
if (pwr->gpu_reg)
regulator_enable(pwr->gpu_reg);
}
@@ -435,16 +425,14 @@
if (state == KGSL_PWRFLAGS_ON) {
if (!test_and_set_bit(KGSL_PWRFLAGS_IRQ_ON,
&pwr->power_flags)) {
- KGSL_PWR_INFO(device,
- "irq on, device %d\n", device->id);
+ trace_kgsl_irq(device, state);
enable_irq(pwr->interrupt_num);
device->ftbl->irqctrl(device, 1);
}
} else if (state == KGSL_PWRFLAGS_OFF) {
if (test_and_clear_bit(KGSL_PWRFLAGS_IRQ_ON,
&pwr->power_flags)) {
- KGSL_PWR_INFO(device,
- "irq off, device %d\n", device->id);
+ trace_kgsl_irq(device, state);
device->ftbl->irqctrl(device, 0);
if (in_interrupt())
disable_irq_nosync(pwr->interrupt_num);
@@ -599,6 +587,9 @@
{
struct kgsl_device *device = container_of(work, struct kgsl_device,
idle_check_ws);
+ WARN_ON(device == NULL);
+ if (device == NULL)
+ return;
mutex_lock(&device->mutex);
if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) {
@@ -620,7 +611,7 @@
}
} else if (device->state & (KGSL_STATE_HUNG |
KGSL_STATE_DUMP_AND_RECOVER)) {
- device->requested_state = KGSL_STATE_NONE;
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
}
mutex_unlock(&device->mutex);
@@ -632,7 +623,7 @@
KGSL_PWR_INFO(device, "idle timer expired device %d\n", device->id);
if (device->requested_state != KGSL_STATE_SUSPEND) {
- device->requested_state = KGSL_STATE_SLEEP;
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_SLEEP);
/* Have work run in a non-interrupt context. */
queue_work(device->work_queue, &device->idle_check_ws);
}
@@ -663,153 +654,182 @@
}
static int
-_slumber(struct kgsl_device *device)
+_nap(struct kgsl_device *device)
{
- int status = -EINVAL;
- if (!device)
- return -EINVAL;
- KGSL_PWR_WARN(device, "Slumber start\n");
-
- device->requested_state = KGSL_STATE_SLUMBER;
- del_timer(&device->idle_timer);
switch (device->state) {
case KGSL_STATE_ACTIVE:
- /* Wait for the device to become idle */
- device->ftbl->idle(device, KGSL_TIMEOUT_DEFAULT);
+ if (!device->ftbl->isidle(device)) {
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+ return -EBUSY;
+ }
+ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
+ kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF);
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_NAP);
+ if (device->idle_wakelock.name)
+ wake_unlock(&device->idle_wakelock);
case KGSL_STATE_NAP:
case KGSL_STATE_SLEEP:
- device->ftbl->suspend_context(device);
- device->ftbl->stop(device);
- device->state = KGSL_STATE_SLUMBER;
- device->pwrctrl.restore_slumber = 1;
- KGSL_PWR_WARN(device, "state -> SLUMBER, device %d\n",
- device->id);
+ case KGSL_STATE_SLUMBER:
break;
default:
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
break;
}
- status = 0;
- /* Don't set requested state to NONE
- It's done in kgsl_pwrctrl_sleep*/
- KGSL_PWR_WARN(device, "Done going to slumber\n");
- return status;
+ return 0;
+}
+
+static void
+_sleep_accounting(struct kgsl_device *device)
+{
+ kgsl_pwrctrl_busy_time(device, false);
+ device->pwrctrl.busy.start.tv_sec = 0;
+ device->pwrctrl.time = 0;
+ kgsl_pwrscale_sleep(device);
+}
+
+static int
+_sleep(struct kgsl_device *device)
+{
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+ switch (device->state) {
+ case KGSL_STATE_ACTIVE:
+ if (!device->ftbl->isidle(device)) {
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+ return -EBUSY;
+ }
+ /* fall through */
+ case KGSL_STATE_NAP:
+ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
+ kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF);
+ if (pwr->pwrlevels[0].gpu_freq > 0)
+ clk_set_rate(pwr->grp_clks[0],
+ pwr->pwrlevels[pwr->num_pwrlevels - 1].
+ gpu_freq);
+ _sleep_accounting(device);
+ kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF);
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_SLEEP);
+ if (device->idle_wakelock.name)
+ wake_unlock(&device->idle_wakelock);
+ break;
+ case KGSL_STATE_SLEEP:
+ case KGSL_STATE_SLUMBER:
+ break;
+ default:
+ KGSL_PWR_WARN(device, "unhandled state %s\n",
+ kgsl_pwrstate_to_str(device->state));
+ break;
+ }
+ return 0;
+}
+
+static int
+_slumber(struct kgsl_device *device)
+{
+ switch (device->state) {
+ case KGSL_STATE_ACTIVE:
+ if (!device->ftbl->isidle(device)) {
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+ device->pwrctrl.restore_slumber = true;
+ return -EBUSY;
+ }
+ /* fall through */
+ case KGSL_STATE_NAP:
+ case KGSL_STATE_SLEEP:
+ del_timer_sync(&device->idle_timer);
+ kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL);
+ device->ftbl->suspend_context(device);
+ device->ftbl->stop(device);
+ device->pwrctrl.restore_slumber = true;
+ _sleep_accounting(device);
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_SLUMBER);
+ if (device->idle_wakelock.name)
+ wake_unlock(&device->idle_wakelock);
+ break;
+ case KGSL_STATE_SLUMBER:
+ break;
+ default:
+ KGSL_PWR_WARN(device, "unhandled state %s\n",
+ kgsl_pwrstate_to_str(device->state));
+ break;
+ }
+ return 0;
}
/******************************************************************/
/* Caller must hold the device mutex. */
int kgsl_pwrctrl_sleep(struct kgsl_device *device)
{
- struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+ int status = 0;
KGSL_PWR_INFO(device, "sleep device %d\n", device->id);
/* Work through the legal state transitions */
- if ((device->requested_state == KGSL_STATE_NAP)) {
+ switch (device->requested_state) {
+ case KGSL_STATE_NAP:
if (device->pwrctrl.restore_slumber) {
- device->requested_state = KGSL_STATE_NONE;
- return 0;
- } else if (device->ftbl->isidle(device))
- goto nap;
- } else if (device->requested_state == KGSL_STATE_SLEEP) {
- if (device->state == KGSL_STATE_NAP ||
- device->ftbl->isidle(device)) {
- if (!device->pwrctrl.restore_slumber)
- goto sleep;
- else
- goto slumber;
- }
- } else if (device->requested_state == KGSL_STATE_SLUMBER) {
- if (device->state == KGSL_STATE_INIT)
- return 0;
- if (device->ftbl->isidle(device))
- goto slumber;
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+ break;
+ }
+ status = _nap(device);
+ break;
+ case KGSL_STATE_SLEEP:
+ if (device->pwrctrl.restore_slumber)
+ status = _slumber(device);
+ else
+ status = _sleep(device);
+ break;
+ case KGSL_STATE_SLUMBER:
+ status = _slumber(device);
+ break;
+ default:
+ KGSL_PWR_INFO(device, "bad state request 0x%x\n",
+ device->requested_state);
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+ status = -EINVAL;
+ break;
}
-
- device->requested_state = KGSL_STATE_NONE;
- return -EBUSY;
-
-
-slumber:
- _slumber(device);
-
-sleep:
- kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
- kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF);
- if (pwr->pwrlevels[0].gpu_freq > 0)
- clk_set_rate(pwr->grp_clks[0],
- pwr->pwrlevels[pwr->num_pwrlevels - 1].
- gpu_freq);
- kgsl_pwrctrl_busy_time(device, false);
- pwr->busy.start.tv_sec = 0;
- device->pwrctrl.time = 0;
-
- kgsl_pwrscale_sleep(device);
- goto clk_off;
-
-nap:
- kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
-clk_off:
- kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_OFF);
-
- device->state = device->requested_state;
- device->requested_state = KGSL_STATE_NONE;
- if (device->idle_wakelock.name)
- wake_unlock(&device->idle_wakelock);
- KGSL_PWR_WARN(device, "state -> NAP/SLEEP(%d), device %d\n",
- device->state, device->id);
-
- return 0;
-}
-EXPORT_SYMBOL(kgsl_pwrctrl_sleep);
-
-static int
-_wake_from_slumber(struct kgsl_device *device)
-{
- int status = -EINVAL;
- if (!device)
- return -EINVAL;
-
- KGSL_PWR_WARN(device, "wake from slumber start\n");
-
- device->requested_state = KGSL_STATE_ACTIVE;
- kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL);
- status = device->ftbl->start(device, 0);
- device->requested_state = KGSL_STATE_NONE;
-
- KGSL_PWR_WARN(device, "Done waking from slumber\n");
return status;
}
+EXPORT_SYMBOL(kgsl_pwrctrl_sleep);
/******************************************************************/
/* Caller must hold the device mutex. */
void kgsl_pwrctrl_wake(struct kgsl_device *device)
{
- if (device->state & (KGSL_STATE_SUSPEND | KGSL_STATE_INIT))
- return;
-
- if (device->state == KGSL_STATE_SLUMBER)
- _wake_from_slumber(device);
-
- if (device->state != KGSL_STATE_NAP) {
+ int status;
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_ACTIVE);
+ switch (device->state) {
+ case KGSL_STATE_SLUMBER:
+ status = device->ftbl->start(device, 0);
+ if (status) {
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+ KGSL_DRV_ERR(device, "start failed %d\n", status);
+ break;
+ }
+ /* fall through */
+ case KGSL_STATE_SLEEP:
kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON);
kgsl_pwrscale_wake(device);
- }
-
- /* Turn on the core clocks */
- kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON);
-
- /* Enable state before turning on irq */
- device->state = KGSL_STATE_ACTIVE;
- KGSL_PWR_WARN(device, "state -> ACTIVE, device %d\n", device->id);
- kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON);
-
- /* Re-enable HW access */
- mod_timer(&device->idle_timer,
+ /* fall through */
+ case KGSL_STATE_NAP:
+ /* Turn on the core clocks */
+ kgsl_pwrctrl_clk(device, KGSL_PWRFLAGS_ON);
+ /* Enable state before turning on irq */
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_ACTIVE);
+ kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_ON);
+ /* Re-enable HW access */
+ mod_timer(&device->idle_timer,
jiffies + device->pwrctrl.interval_timeout);
- if (device->idle_wakelock.name)
- wake_lock(&device->idle_wakelock);
-
- KGSL_PWR_INFO(device, "wake return for device %d\n", device->id);
+ if (device->idle_wakelock.name)
+ wake_lock(&device->idle_wakelock);
+ case KGSL_STATE_ACTIVE:
+ break;
+ default:
+ KGSL_PWR_WARN(device, "unhandled state %s\n",
+ kgsl_pwrstate_to_str(device->state));
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NONE);
+ break;
+ }
}
EXPORT_SYMBOL(kgsl_pwrctrl_wake);
@@ -830,3 +850,48 @@
kgsl_pwrctrl_pwrrail(device, KGSL_PWRFLAGS_OFF);
}
EXPORT_SYMBOL(kgsl_pwrctrl_disable);
+
+void kgsl_pwrctrl_set_state(struct kgsl_device *device, unsigned int state)
+{
+ trace_kgsl_pwr_set_state(device, state);
+ device->state = state;
+ device->requested_state = KGSL_STATE_NONE;
+}
+EXPORT_SYMBOL(kgsl_pwrctrl_set_state);
+
+void kgsl_pwrctrl_request_state(struct kgsl_device *device, unsigned int state)
+{
+ if (state != KGSL_STATE_NONE && state != device->requested_state)
+ trace_kgsl_pwr_request_state(device, state);
+ device->requested_state = state;
+}
+EXPORT_SYMBOL(kgsl_pwrctrl_request_state);
+
+const char *kgsl_pwrstate_to_str(unsigned int state)
+{
+ switch (state) {
+ case KGSL_STATE_NONE:
+ return "NONE";
+ case KGSL_STATE_INIT:
+ return "INIT";
+ case KGSL_STATE_ACTIVE:
+ return "ACTIVE";
+ case KGSL_STATE_NAP:
+ return "NAP";
+ case KGSL_STATE_SLEEP:
+ return "SLEEP";
+ case KGSL_STATE_SUSPEND:
+ return "SUSPEND";
+ case KGSL_STATE_HUNG:
+ return "HUNG";
+ case KGSL_STATE_DUMP_AND_RECOVER:
+ return "DNR";
+ case KGSL_STATE_SLUMBER:
+ return "SLUMBER";
+ default:
+ break;
+ }
+ return "UNKNOWN";
+}
+EXPORT_SYMBOL(kgsl_pwrstate_to_str);
+
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 8b33fcd..8ec41be 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -77,4 +77,6 @@
return (clk != NULL) ? clk_get_rate(clk) : 0;
}
+void kgsl_pwrctrl_set_state(struct kgsl_device *device, unsigned int state);
+void kgsl_pwrctrl_request_state(struct kgsl_device *device, unsigned int state);
#endif /* __KGSL_PWRCTRL_H */
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index b5c6876..928ea65 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -90,7 +90,7 @@
return ret;
}
-PWRSCALE_ATTR(policy, 0644, pwrscale_policy_show, pwrscale_policy_store);
+PWRSCALE_ATTR(policy, 0666, pwrscale_policy_show, pwrscale_policy_store);
static ssize_t pwrscale_avail_policies_show(struct kgsl_device *device,
char *buf)
@@ -267,7 +267,6 @@
sysfs_remove_group(&pwrscale->kobj, attr_group);
kobject_del(&pwrscale->kobj);
kobject_put(&pwrscale->kobj);
- pwrscale->kobj.state_initialized = 0;
}
static void _kgsl_pwrscale_detach_policy(struct kgsl_device *device)
diff --git a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
index f3e84e4..931ef48 100644
--- a/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
+++ b/drivers/gpu/msm/kgsl_pwrscale_trustzone.c
@@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/spinlock.h>
#include <mach/socinfo.h>
#include <mach/scm.h>
@@ -29,6 +30,7 @@
unsigned int no_switch_cnt;
unsigned int skip_cnt;
};
+spinlock_t tz_lock;
#define SWITCH_OFF 200
#define SWITCH_OFF_RESET_TH 40
@@ -38,13 +40,17 @@
#ifdef CONFIG_MSM_SCM
/* Trap into the TrustZone, and call funcs there. */
-static int __secure_tz_entry(u32 cmd, u32 val)
+static int __secure_tz_entry(u32 cmd, u32 val, u32 id)
{
+ int ret;
+ spin_lock(&tz_lock);
__iowmb();
- return scm_call_atomic1(SCM_SVC_IO, cmd, val);
+ ret = scm_call_atomic2(SCM_SVC_IO, cmd, val, id);
+ spin_unlock(&tz_lock);
+ return ret;
}
#else
-static int __secure_tz_entry(u32 cmd, u32 val)
+static int __secure_tz_entry(u32 cmd, u32 val, u32 id)
{
return 0;
}
@@ -107,7 +113,8 @@
{
struct tz_priv *priv = pwrscale->priv;
if (device->state != KGSL_STATE_NAP &&
- priv->governor == TZ_GOVERNOR_ONDEMAND)
+ priv->governor == TZ_GOVERNOR_ONDEMAND &&
+ device->pwrctrl.restore_slumber == 0)
kgsl_pwrctrl_pwrlevel_change(device,
device->pwrctrl.thermal_pwrlevel);
}
@@ -117,7 +124,7 @@
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
struct tz_priv *priv = pwrscale->priv;
struct kgsl_power_stats stats;
- int val;
+ int val, idle;
/* In "performance" mode the clock speed always stays
the same */
@@ -145,19 +152,26 @@
priv->no_switch_cnt = 0;
}
- val = __secure_tz_entry(TZ_UPDATE_ID,
- stats.total_time - stats.busy_time);
+ idle = stats.total_time - stats.busy_time;
+ idle = (idle > 0) ? idle : 0;
+ val = __secure_tz_entry(TZ_UPDATE_ID, idle, device->id);
if (val)
kgsl_pwrctrl_pwrlevel_change(device,
pwr->active_pwrlevel + val);
}
+static void tz_busy(struct kgsl_device *device,
+ struct kgsl_pwrscale *pwrscale)
+{
+ device->on_time = ktime_to_us(ktime_get());
+}
+
static void tz_sleep(struct kgsl_device *device,
struct kgsl_pwrscale *pwrscale)
{
struct tz_priv *priv = pwrscale->priv;
- __secure_tz_entry(TZ_RESET_ID, 0);
+ __secure_tz_entry(TZ_RESET_ID, 0, device->id);
priv->no_switch_cnt = 0;
}
@@ -174,6 +188,7 @@
return -ENOMEM;
priv->governor = TZ_GOVERNOR_ONDEMAND;
+ spin_lock_init(&tz_lock);
kgsl_pwrscale_policy_add_files(device, pwrscale, &tz_attr_group);
return 0;
@@ -189,6 +204,7 @@
struct kgsl_pwrscale_policy kgsl_pwrscale_policy_tz = {
.name = "trustzone",
.init = tz_init,
+ .busy = tz_busy,
.idle = tz_idle,
.sleep = tz_sleep,
.wake = tz_wake,
diff --git a/drivers/gpu/msm/kgsl_trace.h b/drivers/gpu/msm/kgsl_trace.h
index 7c14ac1..86a9adc 100644
--- a/drivers/gpu/msm/kgsl_trace.h
+++ b/drivers/gpu/msm/kgsl_trace.h
@@ -155,6 +155,108 @@
__entry->result
)
);
+
+DECLARE_EVENT_CLASS(kgsl_pwr_template,
+ TP_PROTO(struct kgsl_device *device, int on),
+
+ TP_ARGS(device, on),
+
+ TP_STRUCT__entry(
+ __string(device_name, device->name)
+ __field(int, on)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device_name, device->name);
+ __entry->on = on;
+ ),
+
+ TP_printk(
+ "d_name=%s %s",
+ __get_str(device_name),
+ __entry->on ? "on" : "off"
+ )
+);
+
+DEFINE_EVENT(kgsl_pwr_template, kgsl_clk,
+ TP_PROTO(struct kgsl_device *device, int on),
+ TP_ARGS(device, on)
+);
+
+DEFINE_EVENT(kgsl_pwr_template, kgsl_irq,
+ TP_PROTO(struct kgsl_device *device, int on),
+ TP_ARGS(device, on)
+);
+
+DEFINE_EVENT(kgsl_pwr_template, kgsl_bus,
+ TP_PROTO(struct kgsl_device *device, int on),
+ TP_ARGS(device, on)
+);
+
+DEFINE_EVENT(kgsl_pwr_template, kgsl_rail,
+ TP_PROTO(struct kgsl_device *device, int on),
+ TP_ARGS(device, on)
+);
+
+TRACE_EVENT(kgsl_pwrlevel,
+
+ TP_PROTO(struct kgsl_device *device, unsigned int pwrlevel,
+ unsigned int freq),
+
+ TP_ARGS(device, pwrlevel, freq),
+
+ TP_STRUCT__entry(
+ __string(device_name, device->name)
+ __field(unsigned int, pwrlevel)
+ __field(unsigned int, freq)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device_name, device->name);
+ __entry->pwrlevel = pwrlevel;
+ __entry->freq = freq;
+ ),
+
+ TP_printk(
+ "d_name=%s pwrlevel=%d freq=%d",
+ __get_str(device_name),
+ __entry->pwrlevel,
+ __entry->freq
+ )
+);
+
+DECLARE_EVENT_CLASS(kgsl_pwrstate_template,
+ TP_PROTO(struct kgsl_device *device, unsigned int state),
+
+ TP_ARGS(device, state),
+
+ TP_STRUCT__entry(
+ __string(device_name, device->name)
+ __field(unsigned int, state)
+ ),
+
+ TP_fast_assign(
+ __assign_str(device_name, device->name);
+ __entry->state = state;
+ ),
+
+ TP_printk(
+ "d_name=%s %s",
+ __get_str(device_name),
+ kgsl_pwrstate_to_str(__entry->state)
+ )
+);
+
+DEFINE_EVENT(kgsl_pwrstate_template, kgsl_pwr_set_state,
+ TP_PROTO(struct kgsl_device *device, unsigned int state),
+ TP_ARGS(device, state)
+);
+
+DEFINE_EVENT(kgsl_pwrstate_template, kgsl_pwr_request_state,
+ TP_PROTO(struct kgsl_device *device, unsigned int state),
+ TP_ARGS(device, state)
+);
+
#endif /* _KGSL_TRACE_H */
/* This part must be outside protection */
diff --git a/drivers/gpu/msm/z180.c b/drivers/gpu/msm/z180.c
index cf74e64..de7d1be 100644
--- a/drivers/gpu/msm/z180.c
+++ b/drivers/gpu/msm/z180.c
@@ -248,7 +248,7 @@
if ((device->pwrctrl.nap_allowed == true) &&
(device->requested_state == KGSL_STATE_NONE)) {
- device->requested_state = KGSL_STATE_NAP;
+ kgsl_pwrctrl_request_state(device, KGSL_STATE_NAP);
queue_work(device->work_queue, &device->idle_check_ws);
}
mod_timer(&device->idle_timer,
@@ -460,6 +460,7 @@
z180_dev->ringbuffer.prevctx = context->id;
addcmd(&z180_dev->ringbuffer, index, cmd + ofs, cnt);
+ kgsl_pwrscale_busy(device);
/* Make sure the next ringbuffer entry has a marker */
addmarker(&z180_dev->ringbuffer, nextindex);
@@ -523,6 +524,7 @@
goto error_close_ringbuffer;
kgsl_pwrscale_init(device);
+ kgsl_pwrscale_attach_policy(device, Z180_DEFAULT_PWRSCALE_POLICY);
return status;
@@ -551,9 +553,7 @@
{
int status = 0;
- device->state = KGSL_STATE_INIT;
- device->requested_state = KGSL_STATE_NONE;
- KGSL_PWR_WARN(device, "state -> INIT, device %d\n", device->id);
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_INIT);
kgsl_pwrctrl_enable(device);
@@ -834,8 +834,7 @@
status = 0;
else if (timeout == 0) {
status = -ETIMEDOUT;
- device->state = KGSL_STATE_HUNG;
- KGSL_PWR_WARN(device, "state -> HUNG, device %d\n", device->id);
+ kgsl_pwrctrl_set_state(device, KGSL_STATE_HUNG);
} else
status = timeout;
@@ -861,17 +860,17 @@
struct kgsl_power_stats *stats)
{
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+ s64 tmp = ktime_to_us(ktime_get());
if (pwr->time == 0) {
- pwr->time = ktime_to_us(ktime_get());
+ pwr->time = tmp;
stats->total_time = 0;
stats->busy_time = 0;
} else {
- s64 tmp;
- tmp = ktime_to_us(ktime_get());
stats->total_time = tmp - pwr->time;
- stats->busy_time = tmp - pwr->time;
pwr->time = tmp;
+ stats->busy_time = tmp - device->on_time;
+ device->on_time = tmp;
}
}
diff --git a/drivers/gpu/msm/z180.h b/drivers/gpu/msm/z180.h
index 28b1cc6..e5c5ef3 100644
--- a/drivers/gpu/msm/z180.h
+++ b/drivers/gpu/msm/z180.h
@@ -19,6 +19,8 @@
#define DEVICE_2D0_NAME "kgsl-2d0"
#define DEVICE_2D1_NAME "kgsl-2d1"
+#define Z180_DEFAULT_PWRSCALE_POLICY NULL
+
struct z180_ringbuffer {
unsigned int prevctx;
struct kgsl_memdesc cmdbufdesc;
diff --git a/drivers/hwmon/pm8xxx-adc-scale.c b/drivers/hwmon/pm8xxx-adc-scale.c
index 7b27f72..fb2f1d5 100644
--- a/drivers/hwmon/pm8xxx-adc-scale.c
+++ b/drivers/hwmon/pm8xxx-adc-scale.c
@@ -17,230 +17,222 @@
#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
#define KELVINMIL_DEGMIL 273160
-static const struct pm8xxx_adc_map_pt adcmap_batttherm[] = {
- {41001, -30},
- {40017, -20},
- {38721, -10},
- {37186, 0},
- {35554, 10},
- {33980, 20},
- {33253, 25},
- {32580, 30},
- {31412, 40},
- {30481, 50},
- {29759, 60},
- {29209, 70},
- {28794, 80}
-};
-
+/* Units for temperature below (on x axis) is in 0.1DegC as
+ required by the battery driver. Note the resolution used
+ here to compute the table was done for DegC to milli-volts.
+ In consideration to limit the size of the table for the given
+ temperature range below, the result is linearly interpolated
+ and provided to the battery driver in the units desired for
+ their framework which is 0.1DegC. True resolution of 0.1DegC
+ will result in the below table size to increase by 10 times */
static const struct pm8xxx_adc_map_pt adcmap_btm_threshold[] = {
- {-30, 1642},
- {-20, 1544},
- {-10, 1414},
+ {-300, 1642},
+ {-200, 1544},
+ {-100, 1414},
{0, 1260},
- {1, 1244},
- {2, 1228},
- {3, 1212},
- {4, 1195},
- {5, 1179},
- {6, 1162},
- {7, 1146},
- {8, 1129},
- {9, 1113},
- {10, 1097},
- {11, 1080},
- {12, 1064},
- {13, 1048},
- {14, 1032},
- {15, 1016},
- {16, 1000},
- {17, 985},
- {18, 969},
- {19, 954},
- {20, 939},
- {21, 924},
- {22, 909},
- {23, 894},
- {24, 880},
- {25, 866},
- {26, 852},
- {27, 838},
- {28, 824},
- {29, 811},
- {30, 798},
- {31, 785},
- {32, 773},
- {33, 760},
- {34, 748},
- {35, 736},
- {36, 725},
- {37, 713},
- {38, 702},
- {39, 691},
- {40, 681},
- {41, 670},
- {42, 660},
- {43, 650},
- {44, 640},
- {45, 631},
- {46, 622},
- {47, 613},
- {48, 604},
- {49, 595},
- {50, 587},
- {51, 579},
- {52, 571},
- {53, 563},
- {54, 556},
- {55, 548},
- {56, 541},
- {57, 534},
- {58, 527},
- {59, 521},
- {60, 514},
- {61, 508},
- {62, 502},
- {63, 496},
- {64, 490},
- {65, 485},
- {66, 281},
- {67, 274},
- {68, 267},
- {69, 260},
- {70, 254},
- {71, 247},
- {72, 241},
- {73, 235},
- {74, 229},
- {75, 224},
- {76, 218},
- {77, 213},
- {78, 208},
- {79, 203}
+ {10, 1244},
+ {20, 1228},
+ {30, 1212},
+ {40, 1195},
+ {50, 1179},
+ {60, 1162},
+ {70, 1146},
+ {80, 1129},
+ {90, 1113},
+ {100, 1097},
+ {110, 1080},
+ {120, 1064},
+ {130, 1048},
+ {140, 1032},
+ {150, 1016},
+ {160, 1000},
+ {170, 985},
+ {180, 969},
+ {190, 954},
+ {200, 939},
+ {210, 924},
+ {220, 909},
+ {230, 894},
+ {240, 880},
+ {250, 866},
+ {260, 852},
+ {270, 838},
+ {280, 824},
+ {290, 811},
+ {300, 798},
+ {310, 785},
+ {320, 773},
+ {330, 760},
+ {340, 748},
+ {350, 736},
+ {360, 725},
+ {370, 713},
+ {380, 702},
+ {390, 691},
+ {400, 681},
+ {410, 670},
+ {420, 660},
+ {430, 650},
+ {440, 640},
+ {450, 631},
+ {460, 622},
+ {470, 613},
+ {480, 604},
+ {490, 595},
+ {500, 587},
+ {510, 579},
+ {520, 571},
+ {530, 563},
+ {540, 556},
+ {550, 548},
+ {560, 541},
+ {570, 534},
+ {580, 527},
+ {590, 521},
+ {600, 514},
+ {610, 508},
+ {620, 502},
+ {630, 496},
+ {640, 490},
+ {650, 485},
+ {660, 281},
+ {670, 274},
+ {680, 267},
+ {690, 260},
+ {700, 254},
+ {710, 247},
+ {720, 241},
+ {730, 235},
+ {740, 229},
+ {750, 224},
+ {760, 218},
+ {770, 213},
+ {780, 208},
+ {790, 203}
};
static const struct pm8xxx_adc_map_pt adcmap_pa_therm[] = {
- {41350, -30},
- {41282, -29},
- {41211, -28},
- {41137, -27},
- {41060, -26},
- {40980, -25},
- {40897, -24},
- {40811, -23},
- {40721, -22},
- {40629, -21},
- {40533, -20},
- {40434, -19},
- {40331, -18},
- {40226, -17},
- {40116, -16},
- {40004, -15},
- {39888, -14},
- {39769, -13},
- {39647, -12},
- {39521, -11},
- {39392, -10},
- {39260, -9},
- {39124, -8},
- {38986, -7},
- {38845, -6},
- {38700, -5},
- {38553, -4},
- {38403, -3},
- {38250, -2},
- {38094, -1},
- {37936, 0},
- {37776, 1},
- {37613, 2},
- {37448, 3},
- {37281, 4},
- {37112, 5},
- {36942, 6},
- {36770, 7},
- {36596, 8},
- {36421, 9},
- {36245, 10},
- {36068, 11},
- {35890, 12},
- {35712, 13},
- {35532, 14},
- {35353, 15},
- {35173, 16},
- {34993, 17},
- {34813, 18},
- {34634, 19},
- {34455, 20},
- {34276, 21},
- {34098, 22},
- {33921, 23},
- {33745, 24},
- {33569, 25},
- {33395, 26},
- {33223, 27},
- {33051, 28},
- {32881, 29},
- {32713, 30},
- {32547, 31},
- {32382, 32},
- {32219, 33},
- {32058, 34},
- {31899, 35},
- {31743, 36},
- {31588, 37},
- {31436, 38},
- {31285, 39},
- {31138, 40},
- {30992, 41},
- {30849, 42},
- {30708, 43},
- {30570, 44},
- {30434, 45},
- {30300, 46},
- {30169, 47},
- {30041, 48},
- {29915, 49},
- {29791, 50},
- {29670, 51},
- {29551, 52},
- {29435, 53},
- {29321, 54},
- {29210, 55},
- {29101, 56},
- {28994, 57},
- {28890, 58},
- {28788, 59},
- {28688, 60},
- {28590, 61},
- {28495, 62},
- {28402, 63},
- {28311, 64},
- {28222, 65},
- {28136, 66},
- {28051, 67},
- {27968, 68},
- {27888, 69},
- {27809, 70},
- {27732, 71},
- {27658, 72},
- {27584, 73},
- {27513, 74},
- {27444, 75},
- {27376, 76},
- {27310, 77},
- {27245, 78},
- {27183, 79},
- {27121, 80},
- {27062, 81},
- {27004, 82},
- {26947, 83},
- {26892, 84},
- {26838, 85},
- {26785, 86},
- {26734, 87},
- {26684, 88},
- {26636, 89},
- {26588, 90}
+ {1677, -30},
+ {1671, -29},
+ {1663, -28},
+ {1656, -27},
+ {1648, -26},
+ {1640, -25},
+ {1632, -24},
+ {1623, -23},
+ {1615, -22},
+ {1605, -21},
+ {1596, -20},
+ {1586, -19},
+ {1576, -18},
+ {1565, -17},
+ {1554, -16},
+ {1543, -15},
+ {1531, -14},
+ {1519, -13},
+ {1507, -12},
+ {1494, -11},
+ {1482, -10},
+ {1468, -9},
+ {1455, -8},
+ {1441, -7},
+ {1427, -6},
+ {1412, -5},
+ {1398, -4},
+ {1383, -3},
+ {1367, -2},
+ {1352, -1},
+ {1336, 0},
+ {1320, 1},
+ {1304, 2},
+ {1287, 3},
+ {1271, 4},
+ {1254, 5},
+ {1237, 6},
+ {1219, 7},
+ {1202, 8},
+ {1185, 9},
+ {1167, 10},
+ {1149, 11},
+ {1131, 12},
+ {1114, 13},
+ {1096, 14},
+ {1078, 15},
+ {1060, 16},
+ {1042, 17},
+ {1024, 18},
+ {1006, 19},
+ {988, 20},
+ {970, 21},
+ {952, 22},
+ {934, 23},
+ {917, 24},
+ {899, 25},
+ {882, 26},
+ {865, 27},
+ {848, 28},
+ {831, 29},
+ {814, 30},
+ {797, 31},
+ {781, 32},
+ {764, 33},
+ {748, 34},
+ {732, 35},
+ {717, 36},
+ {701, 37},
+ {686, 38},
+ {671, 39},
+ {656, 40},
+ {642, 41},
+ {627, 42},
+ {613, 43},
+ {599, 44},
+ {586, 45},
+ {572, 46},
+ {559, 47},
+ {546, 48},
+ {534, 49},
+ {522, 50},
+ {509, 51},
+ {498, 52},
+ {486, 53},
+ {475, 54},
+ {463, 55},
+ {452, 56},
+ {442, 57},
+ {431, 58},
+ {421, 59},
+ {411, 60},
+ {401, 61},
+ {392, 62},
+ {383, 63},
+ {374, 64},
+ {365, 65},
+ {356, 66},
+ {348, 67},
+ {339, 68},
+ {331, 69},
+ {323, 70},
+ {316, 71},
+ {308, 72},
+ {301, 73},
+ {294, 74},
+ {287, 75},
+ {280, 76},
+ {273, 77},
+ {267, 78},
+ {261, 79},
+ {255, 80},
+ {249, 81},
+ {243, 82},
+ {237, 83},
+ {232, 84},
+ {226, 85},
+ {221, 86},
+ {216, 87},
+ {211, 88},
+ {206, 89},
+ {201, 90}
};
static const struct pm8xxx_adc_map_pt adcmap_ntcg_104ef_104fb[] = {
@@ -458,42 +450,91 @@
return 0;
}
+static int32_t pm8xxx_adc_map_batt_therm(const struct pm8xxx_adc_map_pt *pts,
+ uint32_t tablesize, int32_t input, int64_t *output)
+{
+ bool descending = 1;
+ uint32_t i = 0;
+
+ if ((pts == NULL) || (output == NULL))
+ return -EINVAL;
+
+ /* Check if table is descending or ascending */
+ if (tablesize > 1) {
+ if (pts[0].y < pts[1].y)
+ descending = 0;
+ }
+
+ while (i < tablesize) {
+ if ((descending == 1) && (pts[i].y < input)) {
+ /* table entry is less than measured
+ value and table is descending, stop */
+ break;
+ } else if ((descending == 0) && (pts[i].y > input)) {
+ /* table entry is greater than measured
+ value and table is ascending, stop */
+ break;
+ } else {
+ i++;
+ }
+ }
+
+ if (i == 0) {
+ *output = pts[0].x;
+ } else if (i == tablesize) {
+ *output = pts[tablesize-1].x;
+ } else {
+ /* result is between search_index and search_index-1 */
+ /* interpolate linearly */
+ *output = (((int32_t) ((pts[i].x - pts[i-1].x)*
+ (input - pts[i-1].y))/
+ (pts[i].y - pts[i-1].y))+
+ pts[i-1].x);
+ }
+
+ return 0;
+}
+
int32_t pm8xxx_adc_scale_default(int32_t adc_code,
const struct pm8xxx_adc_properties *adc_properties,
const struct pm8xxx_adc_chan_properties *chan_properties,
struct pm8xxx_adc_chan_result *adc_chan_result)
{
- bool negative_rawfromoffset = 0;
- int32_t rawfromoffset = 0;
+ bool negative_rawfromoffset = 0, negative_offset = 0;
+ int64_t scale_voltage = 0;
if (!chan_properties || !chan_properties->offset_gain_numerator ||
!chan_properties->offset_gain_denominator || !adc_properties
|| !adc_chan_result)
return -EINVAL;
- rawfromoffset = adc_code -
- chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].offset;
+ scale_voltage = (adc_code -
+ chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].adc_gnd)
+ * chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx;
+ if (scale_voltage < 0) {
+ negative_offset = 1;
+ scale_voltage = -scale_voltage;
+ }
+ do_div(scale_voltage,
+ chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy);
+ if (negative_offset)
+ scale_voltage = -scale_voltage;
+ scale_voltage += chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx;
- adc_chan_result->adc_code = adc_code;
- if (rawfromoffset < 0) {
+ if (scale_voltage < 0) {
if (adc_properties->bipolar) {
- rawfromoffset = -rawfromoffset;
+ scale_voltage = -scale_voltage;
negative_rawfromoffset = 1;
} else {
- rawfromoffset = 0;
+ scale_voltage = 0;
}
}
- if (rawfromoffset >= 1 << adc_properties->bitresolution)
- rawfromoffset = (1 << adc_properties->bitresolution) - 1;
-
- adc_chan_result->measurement = (int64_t)rawfromoffset *
- chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx *
+ adc_chan_result->measurement = scale_voltage *
chan_properties->offset_gain_denominator;
/* do_div only perform positive integer division! */
do_div(adc_chan_result->measurement,
- chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy *
chan_properties->offset_gain_numerator);
if (negative_rawfromoffset)
@@ -503,25 +544,52 @@
* adc_properties.adc_reference. For generic channel processing,
* channel measurement is a scale/ratio relative to the adc
* reference input */
- adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
+ adc_chan_result->physical = adc_chan_result->measurement;
return 0;
}
EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_default);
+static int64_t pm8xxx_adc_scale_ratiometric_calib(int32_t adc_code,
+ const struct pm8xxx_adc_properties *adc_properties,
+ const struct pm8xxx_adc_chan_properties *chan_properties)
+{
+ int64_t adc_voltage = 0;
+ bool negative_offset = 0;
+
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties)
+ return -EINVAL;
+
+ adc_voltage = (adc_code -
+ chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].adc_gnd)
+ * adc_properties->adc_vdd_reference;
+ if (adc_voltage < 0) {
+ negative_offset = 1;
+ adc_voltage = -adc_voltage;
+ }
+ do_div(adc_voltage,
+ chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].dy);
+ if (negative_offset)
+ adc_voltage = -adc_voltage;
+
+ return adc_voltage;
+}
+
int32_t pm8xxx_adc_scale_batt_therm(int32_t adc_code,
const struct pm8xxx_adc_properties *adc_properties,
const struct pm8xxx_adc_chan_properties *chan_properties,
struct pm8xxx_adc_chan_result *adc_chan_result)
{
- /* Note: adc_chan_result->measurement is in the unit of
- adc_properties.adc_reference */
- adc_chan_result->measurement = adc_code;
- /* convert mV ---> degC using the table */
- return pm8xxx_adc_map_linear(
- adcmap_batttherm,
- ARRAY_SIZE(adcmap_batttherm),
- adc_code,
+ int64_t bat_voltage = 0;
+
+ bat_voltage = pm8xxx_adc_scale_ratiometric_calib(adc_code,
+ adc_properties, chan_properties);
+
+ return pm8xxx_adc_map_batt_therm(
+ adcmap_btm_threshold,
+ ARRAY_SIZE(adcmap_btm_threshold),
+ bat_voltage,
&adc_chan_result->physical);
}
EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_batt_therm);
@@ -531,54 +599,74 @@
const struct pm8xxx_adc_chan_properties *chan_properties,
struct pm8xxx_adc_chan_result *adc_chan_result)
{
- /* Note: adc_chan_result->measurement is in the unit of
- adc_properties.adc_reference */
- adc_chan_result->measurement = adc_code;
- /* convert mV ---> degC using the table */
+ int64_t pa_voltage = 0;
+
+ pa_voltage = pm8xxx_adc_scale_ratiometric_calib(adc_code,
+ adc_properties, chan_properties);
+
return pm8xxx_adc_map_linear(
adcmap_pa_therm,
ARRAY_SIZE(adcmap_pa_therm),
- adc_code,
+ pa_voltage,
&adc_chan_result->physical);
}
EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_pa_therm);
+int32_t pm8xxx_adc_scale_batt_id(int32_t adc_code,
+ const struct pm8xxx_adc_properties *adc_properties,
+ const struct pm8xxx_adc_chan_properties *chan_properties,
+ struct pm8xxx_adc_chan_result *adc_chan_result)
+{
+ int64_t batt_id_voltage = 0;
+
+ batt_id_voltage = pm8xxx_adc_scale_ratiometric_calib(adc_code,
+ adc_properties, chan_properties);
+ adc_chan_result->physical = batt_id_voltage;
+ adc_chan_result->physical = adc_chan_result->measurement;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(pm8xxx_adc_scale_batt_id);
+
int32_t pm8xxx_adc_scale_pmic_therm(int32_t adc_code,
const struct pm8xxx_adc_properties *adc_properties,
const struct pm8xxx_adc_chan_properties *chan_properties,
struct pm8xxx_adc_chan_result *adc_chan_result)
{
- int32_t rawfromoffset;
+ int64_t pmic_voltage = 0;
+ bool negative_offset = 0;
if (!chan_properties || !chan_properties->offset_gain_numerator ||
!chan_properties->offset_gain_denominator || !adc_properties
|| !adc_chan_result)
return -EINVAL;
- adc_chan_result->adc_code = adc_code;
- rawfromoffset = adc_code -
- chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].offset;
- if (rawfromoffset > 0) {
- if (rawfromoffset >= 1 << adc_properties->bitresolution)
- rawfromoffset = (1 << adc_properties->bitresolution)
- - 1;
+ pmic_voltage = (adc_code -
+ chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].adc_gnd)
+ * chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx;
+ if (pmic_voltage < 0) {
+ negative_offset = 1;
+ pmic_voltage = -pmic_voltage;
+ }
+ do_div(pmic_voltage,
+ chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy);
+ if (negative_offset)
+ pmic_voltage = -pmic_voltage;
+ pmic_voltage += chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx;
+
+ if (pmic_voltage > 0) {
/* 2mV/K */
- adc_chan_result->measurement = (int64_t)rawfromoffset*
- chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx *
- chan_properties->offset_gain_denominator * 1000;
+ adc_chan_result->measurement = pmic_voltage*
+ chan_properties->offset_gain_denominator;
do_div(adc_chan_result->measurement,
- chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy *
- chan_properties->offset_gain_numerator*2);
+ chan_properties->offset_gain_numerator * 2);
} else {
adc_chan_result->measurement = 0;
}
- /* Note: adc_chan_result->measurement is in the unit of
- adc_properties.adc_reference */
- adc_chan_result->physical = (int32_t)adc_chan_result->measurement;
/* Change to .001 deg C */
- adc_chan_result->physical -= KELVINMIL_DEGMIL;
- adc_chan_result->measurement <<= 1;
+ adc_chan_result->measurement -= KELVINMIL_DEGMIL;
+ adc_chan_result->physical = (int32_t)adc_chan_result->measurement;
return 0;
}
@@ -592,14 +680,19 @@
const struct pm8xxx_adc_chan_properties *chan_properties,
struct pm8xxx_adc_chan_result *adc_chan_result)
{
- int32_t rt_r25;
- int32_t offset = chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].offset;
+ int64_t xo_thm = 0;
- rt_r25 = adc_code - offset;
+ if (!chan_properties || !chan_properties->offset_gain_numerator ||
+ !chan_properties->offset_gain_denominator || !adc_properties
+ || !adc_chan_result)
+ return -EINVAL;
+ xo_thm = pm8xxx_adc_scale_ratiometric_calib(adc_code,
+ adc_properties, chan_properties);
+ xo_thm <<= 4;
pm8xxx_adc_map_linear(adcmap_ntcg_104ef_104fb,
ARRAY_SIZE(adcmap_ntcg_104ef_104fb),
- rt_r25, &adc_chan_result->physical);
+ xo_thm, &adc_chan_result->physical);
return 0;
}
@@ -614,7 +707,7 @@
rc = pm8xxx_adc_map_linear(
adcmap_btm_threshold,
ARRAY_SIZE(adcmap_btm_threshold),
- btm_param->low_thr_temp,
+ (btm_param->low_thr_temp),
&btm_param->low_thr_voltage);
if (rc)
return rc;
@@ -628,7 +721,7 @@
rc = pm8xxx_adc_map_linear(
adcmap_btm_threshold,
ARRAY_SIZE(adcmap_btm_threshold),
- btm_param->high_thr_temp,
+ (btm_param->high_thr_temp),
&btm_param->high_thr_voltage);
if (rc)
return rc;
diff --git a/drivers/hwmon/pm8xxx-adc.c b/drivers/hwmon/pm8xxx-adc.c
index 5da80d9..57d34b9 100644
--- a/drivers/hwmon/pm8xxx-adc.c
+++ b/drivers/hwmon/pm8xxx-adc.c
@@ -122,6 +122,7 @@
#define PM8XXX_ADC_PA_THERM_VREG_UV_MAX 1800000
#define PM8XXX_ADC_PA_THERM_VREG_UA_LOAD 100000
#define PM8XXX_ADC_HWMON_NAME_LENGTH 32
+#define PM8XXX_ADC_BTM_INTERVAL_MAX 0x14
struct pm8xxx_adc {
struct device *dev;
@@ -344,12 +345,16 @@
if (rc < 0)
return rc;
+ rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_USRP_RSV, &data_arb_rsv);
+ if (rc < 0)
+ return rc;
+
data_arb_rsv &= (PM8XXX_ADC_ARB_USRP_RSV_RST |
PM8XXX_ADC_ARB_USRP_RSV_DTEST0 |
PM8XXX_ADC_ARB_USRP_RSV_DTEST1 |
- PM8XXX_ADC_ARB_USRP_RSV_OP |
- PM8XXX_ADC_ARB_USRP_RSV_TRM);
- data_arb_rsv |= chan_prop->amux_ip_rsv << PM8XXX_ADC_RSV_IP_SEL;
+ PM8XXX_ADC_ARB_USRP_RSV_OP);
+ data_arb_rsv |= (chan_prop->amux_ip_rsv << PM8XXX_ADC_RSV_IP_SEL |
+ PM8XXX_ADC_ARB_USRP_RSV_TRM);
rc = pm8xxx_adc_write_reg(PM8XXX_ADC_ARB_USRP_RSV, data_arb_rsv);
if (rc < 0)
@@ -501,7 +506,7 @@
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
struct pm8xxx_adc_amux_properties conv;
- int rc, offset_adc, slope_adc, calib_read_1, calib_read_2;
+ int rc, calib_read_1, calib_read_2;
u8 data_arb_usrp_cntrl1 = 0;
conv.amux_channel = CHANNEL_125V;
@@ -564,18 +569,14 @@
}
pm8xxx_adc_calib_first_adc = false;
- slope_adc = (((calib_read_1 - calib_read_2) << PM8XXX_ADC_MUL)/
- PM8XXX_CHANNEL_ADC_625_MV);
- offset_adc = calib_read_2 -
- ((slope_adc * PM8XXX_CHANNEL_ADC_625_MV) >>
- PM8XXX_ADC_MUL);
-
- adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].offset
- = offset_adc;
adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].dy =
(calib_read_1 - calib_read_2);
adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].dx
- = PM8XXX_CHANNEL_ADC_625_MV;
+ = PM8XXX_CHANNEL_ADC_625_UV;
+ adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].adc_vref =
+ calib_read_1;
+ adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_ABSOLUTE].adc_gnd =
+ calib_read_2;
rc = pm8xxx_adc_arb_cntrl(0, CHANNEL_NONE);
if (rc < 0) {
pr_err("%s: Configuring ADC Arbiter disable"
@@ -643,14 +644,6 @@
}
pm8xxx_adc_calib_first_adc = false;
- slope_adc = (((calib_read_1 - calib_read_2) << PM8XXX_ADC_MUL)/
- adc_pmic->adc_prop->adc_vdd_reference);
- offset_adc = calib_read_2 -
- ((slope_adc * adc_pmic->adc_prop->adc_vdd_reference)
- >> PM8XXX_ADC_MUL);
-
- adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].offset
- = offset_adc;
adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].dy =
(calib_read_1 - calib_read_2);
adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].dx =
@@ -843,6 +836,12 @@
return rc;
}
+ if (btm_param->interval > PM8XXX_ADC_BTM_INTERVAL_MAX) {
+ pr_info("Bug in PMIC BTM interval time and cannot set"
+ " a value greater than 0x14 %x\n", btm_param->interval);
+ btm_param->interval = PM8XXX_ADC_BTM_INTERVAL_MAX;
+ }
+
spin_lock_irqsave(&adc_pmic->btm_lock, flags);
data_btm_cool_thr0 = ((btm_param->low_thr_voltage << 24) >> 24);
@@ -974,22 +973,14 @@
{
struct pm8xxx_adc *adc_pmic = pmic_adc;
int i, rc;
- u8 data_arb_btm_cntrl;
+ u8 data_arb_btm_cntrl = 0;
unsigned long flags;
disable_irq_nosync(adc_pmic->btm_warm_irq);
disable_irq_nosync(adc_pmic->btm_cool_irq);
spin_lock_irqsave(&adc_pmic->btm_lock, flags);
- /* Set BTM registers to Disable mode */
- rc = pm8xxx_adc_read_reg(PM8XXX_ADC_ARB_BTM_CNTRL1,
- &data_arb_btm_cntrl);
- if (rc < 0) {
- spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
- return rc;
- }
- data_arb_btm_cntrl |= ~PM8XXX_ADC_ARB_BTM_CNTRL1_EN_BTM;
/* Write twice to the CNTRL register for the arbiter settings
to take into effect */
for (i = 0; i < 2; i++) {
diff --git a/drivers/i2c/busses/i2c-qup.c b/drivers/i2c/busses/i2c-qup.c
index 3dbbf15..2d69256 100644
--- a/drivers/i2c/busses/i2c-qup.c
+++ b/drivers/i2c/busses/i2c-qup.c
@@ -29,6 +29,7 @@
#include <linux/timer.h>
#include <linux/slab.h>
#include <mach/board.h>
+#include <mach/gpiomux.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/gpio.h>
@@ -110,6 +111,7 @@
enum {
I2C_STATUS_WR_BUFFER_FULL = 1U << 0,
I2C_STATUS_BUS_ACTIVE = 1U << 8,
+ I2C_STATUS_BUS_MASTER = 1U << 9,
I2C_STATUS_ERROR_MASK = 0x38000FC,
QUP_I2C_NACK_FLAG = 1U << 3,
QUP_IN_NOT_EMPTY = 1U << 5,
@@ -126,6 +128,12 @@
static char const * const i2c_rsrcs[] = {"i2c_clk", "i2c_sda"};
+static struct gpiomux_setting recovery_config = {
+ .func = GPIOMUX_FUNC_GPIO,
+ .drv = GPIOMUX_DRV_8MA,
+ .pull = GPIOMUX_PULL_NONE,
+};
+
struct qup_i2c_dev {
struct device *dev;
void __iomem *base; /* virtual */
@@ -639,6 +647,83 @@
return ret;
}
+
+static void qup_i2c_recover_bus_busy(struct qup_i2c_dev *dev)
+{
+ int i;
+ int gpio_clk;
+ int gpio_dat;
+ bool gpio_clk_status = false;
+ uint32_t status = readl_relaxed(dev->base + QUP_I2C_STATUS);
+ struct gpiomux_setting old_gpio_setting;
+
+ if (dev->pdata->msm_i2c_config_gpio)
+ return;
+
+ if (!(status & (I2C_STATUS_BUS_ACTIVE)) ||
+ (status & (I2C_STATUS_BUS_MASTER)))
+ return;
+
+ gpio_clk = dev->i2c_gpios[0];
+ gpio_dat = dev->i2c_gpios[1];
+
+ if ((gpio_clk == -1) && (gpio_dat == -1)) {
+ dev_err(dev->dev, "Recovery failed due to undefined GPIO's\n");
+ return;
+ }
+
+ disable_irq(dev->err_irq);
+ for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
+ if (msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE,
+ &recovery_config, &old_gpio_setting)) {
+ dev_err(dev->dev, "GPIO pins have no active setting\n");
+ goto recovery_end;
+ }
+ }
+
+ dev_warn(dev->dev, "i2c_scl: %d, i2c_sda: %d\n",
+ gpio_get_value(gpio_clk), gpio_get_value(gpio_dat));
+
+ for (i = 0; i < 9; i++) {
+ if (gpio_get_value(gpio_dat) && gpio_clk_status)
+ break;
+ gpio_direction_output(gpio_clk, 0);
+ udelay(5);
+ gpio_direction_output(gpio_dat, 0);
+ udelay(5);
+ gpio_direction_input(gpio_clk);
+ udelay(5);
+ if (!gpio_get_value(gpio_clk))
+ udelay(20);
+ if (!gpio_get_value(gpio_clk))
+ usleep_range(10000, 10000);
+ gpio_clk_status = gpio_get_value(gpio_clk);
+ gpio_direction_input(gpio_dat);
+ udelay(5);
+ }
+
+ /* Configure ALT funciton to QUP I2C*/
+ for (i = 0; i < ARRAY_SIZE(i2c_rsrcs); ++i) {
+ msm_gpiomux_write(dev->i2c_gpios[i], GPIOMUX_ACTIVE,
+ &old_gpio_setting, NULL);
+ }
+
+ udelay(10);
+
+ status = readl_relaxed(dev->base + QUP_I2C_STATUS);
+ if (!(status & I2C_STATUS_BUS_ACTIVE)) {
+ dev_info(dev->dev, "Bus busy cleared after %d clock cycles, "
+ "status %x\n",
+ i, status);
+ goto recovery_end;
+ }
+
+ dev_warn(dev->dev, "Bus still busy, status %x\n", status);
+
+recovery_end:
+ enable_irq(dev->err_irq);
+}
+
static int
qup_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
@@ -837,7 +922,8 @@
idx, rem, num, dev->mode);
qup_print_status(dev);
- timeout = wait_for_completion_timeout(&complete, HZ);
+ timeout = wait_for_completion_timeout(&complete,
+ msecs_to_jiffies(dev->out_fifo_sz));
if (!timeout) {
uint32_t istatus = readl_relaxed(dev->base +
QUP_I2C_STATUS);
@@ -846,7 +932,24 @@
uint32_t op_flgs = readl_relaxed(dev->base +
QUP_OPERATIONAL);
- dev_err(dev->dev, "Transaction timed out\n");
+ /*
+ * Dont wait for 1 sec if i2c sees the bus
+ * active and controller is not master.
+ * A slave has pulled line low. Try to recover
+ */
+ if (!(istatus & I2C_STATUS_BUS_ACTIVE) ||
+ (istatus & I2C_STATUS_BUS_MASTER)) {
+ timeout =
+ wait_for_completion_timeout(&complete,
+ HZ);
+ if (timeout)
+ goto timeout_err;
+ }
+ qup_i2c_recover_bus_busy(dev);
+ dev_err(dev->dev,
+ "Transaction timed out, SL-AD = 0x%x\n",
+ dev->msg->addr);
+
dev_err(dev->dev, "I2C Status: %x\n", istatus);
dev_err(dev->dev, "QUP Status: %x\n", qstatus);
dev_err(dev->dev, "OP Flags: %x\n", op_flgs);
@@ -858,17 +961,27 @@
ret = -ETIMEDOUT;
goto out_err;
}
+timeout_err:
if (dev->err) {
if (dev->err > 0 &&
- dev->err & QUP_I2C_NACK_FLAG)
+ dev->err & QUP_I2C_NACK_FLAG) {
dev_err(dev->dev,
"I2C slave addr:0x%x not connected\n",
dev->msg->addr);
- else if (dev->err < 0) {
+ dev->err = ENOTCONN;
+ } else if (dev->err < 0) {
dev_err(dev->dev,
"QUP data xfer error %d\n", dev->err);
ret = dev->err;
goto out_err;
+ } else if (dev->err > 0) {
+ /*
+ * ISR returns +ve error if error code
+ * is I2C related, e.g. unexpected start
+ * So you may call recover-bus-busy when
+ * this error happens
+ */
+ qup_i2c_recover_bus_busy(dev);
}
ret = -dev->err;
goto out_err;
diff --git a/drivers/input/touchscreen/cyttsp-i2c.c b/drivers/input/touchscreen/cyttsp-i2c.c
index 9df1189..bb02be6 100644
--- a/drivers/input/touchscreen/cyttsp-i2c.c
+++ b/drivers/input/touchscreen/cyttsp-i2c.c
@@ -1006,7 +1006,7 @@
/* wait for TTSP Device to complete reset back to bootloader */
tries = 0;
do {
- mdelay(1);
+ usleep_range(1000, 1000);
cyttsp_putbl(ts, 1, false, false, false);
} while (g_bl_data.bl_status != 0x10 &&
g_bl_data.bl_status != 0x11 &&
@@ -1024,7 +1024,7 @@
* switch to Operational mode */
tries = 0;
do {
- mdelay(100);
+ msleep(100);
cyttsp_putbl(ts, 2, false, false, false);
} while (GET_BOOTLOADERMODE(g_bl_data.bl_status) &&
tries++ < 100);
@@ -2001,7 +2001,7 @@
/* wait for TTSP Device to complete reset back to bootloader */
tries = 0;
do {
- mdelay(1);
+ usleep_range(1000, 1000);
cyttsp_putbl(ts, 3, false, false, false);
} while (g_bl_data.bl_status != 0x10 &&
g_bl_data.bl_status != 0x11 &&
@@ -2026,7 +2026,7 @@
i++;
tries = 0;
do {
- mdelay(100);
+ msleep(100);
cyttsp_putbl(ts, 4, false, false, false);
} while (g_bl_data.bl_status != 0x10 &&
g_bl_data.bl_status != 0x11 &&
@@ -2059,7 +2059,7 @@
!((g_bl_data.bl_status == 0x11) &&
(g_bl_data.bl_error == 0x20)) &&
(tries++ < 100)) {
- mdelay(1);
+ usleep_range(1000, 1000);
cyttsp_putbl(ts, 5, false, false, false);
}
}
@@ -2073,7 +2073,7 @@
i++;
tries = 0;
do {
- mdelay(100);
+ msleep(100);
cyttsp_putbl(ts, 6, true, false, false);
} while (g_bl_data.bl_status != 0x10 &&
g_bl_data.bl_status != 0x11 &&
@@ -2098,7 +2098,7 @@
/* wait for TTSP Device to complete reset back to bootloader */
tries = 0;
do {
- mdelay(1);
+ usleep_range(1000, 1000);
cyttsp_putbl(ts, 3, false, false, false);
} while (g_bl_data.bl_status != 0x10 &&
g_bl_data.bl_status != 0x11 &&
@@ -2132,7 +2132,7 @@
sizeof(host_reg), &host_reg);
tries = 0;
do {
- mdelay(1);
+ usleep_range(1000, 1000);
/* set arg2 to non-0 to activate */
retval = cyttsp_putbl(ts, 1, true, true, true);
@@ -2170,7 +2170,7 @@
sizeof(host_reg), &host_reg);
/* wait for TTSP Device to complete switch to
* Operational mode */
- mdelay(1000);
+ msleep(1000);
goto bypass;
}
}
@@ -2249,7 +2249,7 @@
* switch to Operational mode */
tries = 0;
do {
- mdelay(100);
+ msleep(100);
cyttsp_putbl(ts, 9, false, false, false);
} while (GET_BOOTLOADERMODE(g_bl_data.bl_status) &&
tries++ < 100);
@@ -2267,7 +2267,7 @@
retval = i2c_smbus_write_i2c_block_data(ts->client,
CY_REG_BASE, sizeof(host_reg), &host_reg);
/* wait for TTSP Device to complete switch to SysInfo mode */
- mdelay(100);
+ msleep(100);
if (!(retval < CY_OK)) {
retval = i2c_smbus_read_i2c_block_data(ts->client,
CY_REG_BASE,
@@ -2326,7 +2326,7 @@
ts->client,
CY_REG_ACT_INTRVL,
sizeof(intrvl_ray), intrvl_ray);
- mdelay(CY_DLY_SYSINFO);
+ msleep(CY_DLY_SYSINFO);
}
}
}
@@ -2339,7 +2339,7 @@
sizeof(host_reg), &host_reg);
/* wait for TTSP Device to complete
* switch to Operational mode */
- mdelay(100);
+ msleep(100);
}
}
/* init gesture setup;
@@ -2352,7 +2352,7 @@
retval = i2c_smbus_write_i2c_block_data(ts->client,
CY_REG_GEST_SET,
sizeof(gesture_setup), &gesture_setup);
- mdelay(CY_DLY_DFLT);
+ msleep(CY_DLY_DFLT);
}
if (!(retval < CY_OK))
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index 8a65df4..fd5fcd2 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -2979,9 +2979,11 @@
{
int retval;
struct iris_device *radio = video_get_drvdata(video_devdata(file));
- if (tuner->index > 0)
- return -EINVAL;
+ if (tuner->index > 0) {
+ FMDERR("Invalid Tuner Index");
+ return -EINVAL;
+ }
if (radio->mode == FM_RECV) {
retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
if (retval < 0) {
@@ -3057,16 +3059,12 @@
struct v4l2_frequency *freq)
{
struct iris_device *radio = video_get_drvdata(video_devdata(file));
- int retval;
-
- freq->type = V4L2_TUNER_RADIO;
- retval = hci_cmd(HCI_FM_GET_STATION_PARAM_CMD, radio->fm_hdev);
- if (retval < 0)
- FMDERR("get frequency failed %d\n", retval);
- else
+ if ((freq != NULL) && (radio != NULL)) {
freq->frequency =
radio->fm_st_rsp.station_rsp.station_freq * TUNE_PARAM;
- return retval;
+ } else
+ return -EINVAL;
+ return 0;
}
static int iris_vidioc_s_frequency(struct file *file, void *priv,
diff --git a/drivers/media/radio/radio-tavarua.c b/drivers/media/radio/radio-tavarua.c
index d09f89f..b73775c 100644
--- a/drivers/media/radio/radio-tavarua.c
+++ b/drivers/media/radio/radio-tavarua.c
@@ -2433,7 +2433,7 @@
.minimum = 0,
.maximum = 1,
},
- { .id = V4L2_CID_PRIVATE_TAVARUA_SET_NOTCH_FILTER,
+ { .id = V4L2_CID_PRIVATE_SET_NOTCH_FILTER,
.type = V4L2_CTRL_TYPE_INTEGER,
.name = "Notch filter",
.minimum = 0,
@@ -3297,9 +3297,20 @@
if (retval < 0)
FMDBG("write failed");
} break;
- /*This IOCTL is a place holder to keep the
+ /*These IOCTL's are place holders to keep the
driver compatible with change in frame works for IRIS */
- case V4L2_CID_PRIVATE_TAVARUA_SET_NOTCH_FILTER:
+ case V4L2_CID_PRIVATE_SOFT_MUTE:
+ case V4L2_CID_PRIVATE_RIVA_ACCS_ADDR:
+ case V4L2_CID_PRIVATE_RIVA_ACCS_LEN:
+ case V4L2_CID_PRIVATE_RIVA_PEEK:
+ case V4L2_CID_PRIVATE_RIVA_POKE:
+ case V4L2_CID_PRIVATE_SSBI_ACCS_ADDR:
+ case V4L2_CID_PRIVATE_SSBI_PEEK:
+ case V4L2_CID_PRIVATE_SSBI_POKE:
+ case V4L2_CID_PRIVATE_TX_TONE:
+ case V4L2_CID_PRIVATE_RDS_GRP_COUNTERS:
+ case V4L2_CID_PRIVATE_SET_NOTCH_FILTER:
+ case V4L2_CID_PRIVATE_TAVARUA_DO_CALIBRATION:
retval = 0;
break;
default:
diff --git a/drivers/media/video/msm/Kconfig b/drivers/media/video/msm/Kconfig
index 0d822bb..aa0e392 100644
--- a/drivers/media/video/msm/Kconfig
+++ b/drivers/media/video/msm/Kconfig
@@ -49,6 +49,11 @@
default n
---help---
Omni Vision VGA YUV Sensor for QRD Devices
+config MT9M114
+ bool "Sensor MT9M114 (YUV 1.26M)"
+ depends on MSM_CAMERA && ARCH_MSM8960
+ ---help---
+ APTINA 1.26 MP yuv Sensor
config WEBCAM_OV7692
bool "Sensor OV7692 (VGA YUV)"
depends on MSM_CAMERA && ARCH_MSM8X60 && !MSM_CAMERA_V4L2
diff --git a/drivers/media/video/msm/io/msm_camera_i2c.c b/drivers/media/video/msm/io/msm_camera_i2c.c
index 23db5f6b..a3cc012 100644
--- a/drivers/media/video/msm/io/msm_camera_i2c.c
+++ b/drivers/media/video/msm/io/msm_camera_i2c.c
@@ -250,42 +250,52 @@
int32_t rc = -EFAULT;
for (i = 0; i < size; i++) {
enum msm_camera_i2c_data_type dt;
- if (reg_conf_tbl->dt == 0)
- dt = data_type;
- else
- dt = reg_conf_tbl->dt;
+ if (reg_conf_tbl->cmd_type == MSM_CAMERA_I2C_CMD_POLL) {
+ rc = msm_camera_i2c_poll(client, reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_addr, reg_conf_tbl->dt);
+ } else {
+ if (reg_conf_tbl->dt == 0)
+ dt = data_type;
+ else
+ dt = reg_conf_tbl->dt;
- switch (dt) {
- case MSM_CAMERA_I2C_BYTE_DATA:
- case MSM_CAMERA_I2C_WORD_DATA:
- rc = msm_camera_i2c_write(
- client,
- reg_conf_tbl->reg_addr,
- reg_conf_tbl->reg_data, dt);
- break;
- case MSM_CAMERA_I2C_SET_BYTE_MASK:
- rc = msm_camera_i2c_set_mask(client,
- reg_conf_tbl->reg_addr, reg_conf_tbl->reg_data,
- MSM_CAMERA_I2C_BYTE_DATA, 1);
- break;
- case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
- rc = msm_camera_i2c_set_mask(client,
- reg_conf_tbl->reg_addr, reg_conf_tbl->reg_data,
- MSM_CAMERA_I2C_BYTE_DATA, 0);
- break;
- case MSM_CAMERA_I2C_SET_WORD_MASK:
- rc = msm_camera_i2c_set_mask(client,
- reg_conf_tbl->reg_addr, reg_conf_tbl->reg_data,
- MSM_CAMERA_I2C_WORD_DATA, 1);
- break;
- case MSM_CAMERA_I2C_UNSET_WORD_MASK:
- rc = msm_camera_i2c_set_mask(client,
- reg_conf_tbl->reg_addr, reg_conf_tbl->reg_data,
- MSM_CAMERA_I2C_WORD_DATA, 0);
- break;
- default:
- pr_err("%s: Unsupport data type: %d\n", __func__, dt);
- break;
+ switch (dt) {
+ case MSM_CAMERA_I2C_BYTE_DATA:
+ case MSM_CAMERA_I2C_WORD_DATA:
+ rc = msm_camera_i2c_write(
+ client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data, dt);
+ break;
+ case MSM_CAMERA_I2C_SET_BYTE_MASK:
+ rc = msm_camera_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA, 1);
+ break;
+ case MSM_CAMERA_I2C_UNSET_BYTE_MASK:
+ rc = msm_camera_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_BYTE_DATA, 0);
+ break;
+ case MSM_CAMERA_I2C_SET_WORD_MASK:
+ rc = msm_camera_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_WORD_DATA, 1);
+ break;
+ case MSM_CAMERA_I2C_UNSET_WORD_MASK:
+ rc = msm_camera_i2c_set_mask(client,
+ reg_conf_tbl->reg_addr,
+ reg_conf_tbl->reg_data,
+ MSM_CAMERA_I2C_WORD_DATA, 0);
+ break;
+ default:
+ pr_err("%s: Unsupport data type: %d\n",
+ __func__, dt);
+ break;
+ }
}
if (rc < 0)
break;
@@ -364,16 +374,10 @@
struct msm_camera_i2c_conf_array *array, uint16_t index)
{
int32_t rc;
- if (array[index].pre_process != NULL)
- array[index].pre_process();
rc = msm_camera_i2c_write_tbl(client,
(struct msm_camera_i2c_reg_conf *) array[index].conf,
array[index].size, array[index].data_type);
-
- if (array[index].post_process != NULL)
- array[index].post_process();
-
if (array[index].delay > 20)
msleep(array[index].delay);
else
@@ -382,6 +386,34 @@
return rc;
}
+int32_t msm_sensor_write_enum_conf_array(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_enum_conf_array *conf,
+ uint16_t enum_val)
+{
+ int32_t rc = -1, i;
+ for (i = 0; i < conf->num_enum; i++) {
+ if (conf->conf_enum[i] == enum_val)
+ break;
+ if (conf->conf_enum[i] > enum_val)
+ break;
+ }
+ if (i == conf->num_enum)
+ i = conf->num_enum - 1;
+
+ if (i >= conf->num_index)
+ return rc;
+
+ rc = msm_sensor_write_all_conf_array(client,
+ &conf->conf[i*conf->num_conf], conf->num_conf);
+
+ if (conf->delay > 20)
+ msleep(conf->delay);
+ else
+ usleep_range(conf->delay*1000,
+ (conf->delay+1)*1000);
+ return rc;
+}
+
int32_t msm_sensor_write_all_conf_array(struct msm_camera_i2c_client *client,
struct msm_camera_i2c_conf_array *array, uint16_t size)
{
diff --git a/drivers/media/video/msm/io/msm_camera_i2c.h b/drivers/media/video/msm/io/msm_camera_i2c.h
index 05b3960..2fbf5ca 100644
--- a/drivers/media/video/msm/io/msm_camera_i2c.h
+++ b/drivers/media/video/msm/io/msm_camera_i2c.h
@@ -44,10 +44,16 @@
MSM_CAMERA_I2C_UNSET_WORD_MASK,
};
+enum msm_camera_i2c_cmd_type {
+ MSM_CAMERA_I2C_CMD_WRITE,
+ MSM_CAMERA_I2C_CMD_POLL,
+};
+
struct msm_camera_i2c_reg_conf {
uint16_t reg_addr;
uint16_t reg_data;
enum msm_camera_i2c_data_type dt;
+ enum msm_camera_i2c_cmd_type cmd_type;
};
struct msm_camera_i2c_conf_array {
@@ -55,8 +61,16 @@
uint16_t size;
uint16_t delay;
enum msm_camera_i2c_data_type data_type;
- int (*pre_process) (void);
- int (*post_process) (void);
+};
+
+struct msm_camera_i2c_enum_conf_array {
+ struct msm_camera_i2c_conf_array *conf;
+ int *conf_enum;
+ uint16_t num_enum;
+ uint16_t num_index;
+ uint16_t num_conf;
+ uint16_t delay;
+ enum msm_camera_i2c_data_type data_type;
};
int32_t msm_camera_i2c_rxdata(struct msm_camera_i2c_client *client,
@@ -98,6 +112,9 @@
int32_t msm_sensor_write_conf_array(struct msm_camera_i2c_client *client,
struct msm_camera_i2c_conf_array *array, uint16_t index);
+int32_t msm_sensor_write_enum_conf_array(struct msm_camera_i2c_client *client,
+ struct msm_camera_i2c_enum_conf_array *conf, uint16_t enum_val);
+
int32_t msm_sensor_write_all_conf_array(struct msm_camera_i2c_client *client,
struct msm_camera_i2c_conf_array *array, uint16_t size);
#endif
diff --git a/drivers/media/video/msm/msm.c b/drivers/media/video/msm/msm.c
index 251f12d..579c4d5 100644
--- a/drivers/media/video/msm/msm.c
+++ b/drivers/media/video/msm/msm.c
@@ -2407,6 +2407,7 @@
adapter = NULL;
probe_fail:
actctrl->a_init_table = NULL;
+ actctrl->a_power_up = NULL;
actctrl->a_power_down = NULL;
actctrl->a_config = NULL;
actctrl->a_create_subdevice = NULL;
diff --git a/drivers/media/video/msm/msm_io_vfe31.c b/drivers/media/video/msm/msm_io_vfe31.c
index 91dbc8f4..0f93882 100644
--- a/drivers/media/video/msm/msm_io_vfe31.c
+++ b/drivers/media/video/msm/msm_io_vfe31.c
@@ -480,7 +480,6 @@
int msm_camio_enable(struct platform_device *pdev)
{
int rc = 0;
- uint32_t val;
struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
msm_camio_clk_enable(CAMIO_VFE_PBDG_CLK);
if (!sinfo->csi_if)
@@ -507,23 +506,6 @@
msm_camio_clk_enable(CAMIO_CSI0_PCLK);
msm_camio_clk_enable(CAMIO_CSI0_VFE_CLK);
msm_camio_clk_enable(CAMIO_CSI0_CLK);
-
- msleep(10);
- val = (20 <<
- MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
- (0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
- (0x0 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
- (0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
- CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
- msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
- msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
- msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
- msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
-
- val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
- (0x0 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
- CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
- msm_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
}
return 0;
csi_irq_fail:
@@ -536,42 +518,37 @@
return rc;
}
+static void msm_camio_csi_disable(void)
+{
+ uint32_t val;
+ val = 0x0;
+ CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
+ msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
+
+ CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
+ msm_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
+ usleep_range(9000, 10000);
+ free_irq(camio_ext.csiirq, 0);
+ iounmap(csibase);
+ release_mem_region(camio_ext.csiphy, camio_ext.csisz);
+}
+
void msm_camio_disable(struct platform_device *pdev)
{
struct msm_camera_sensor_info *sinfo = pdev->dev.platform_data;
- uint32_t val;
if (!sinfo->csi_if) {
msm_camio_clk_disable(CAMIO_VFE_CAMIF_CLK);
} else {
- val = (0x0 << MIPI_CALIBRATION_CONTROL_SWCAL_CAL_EN_SHFT) |
- (0x0<<MIPI_CALIBRATION_CONTROL_SWCAL_STRENGTH_OVERRIDE_EN_SHFT)|
- (0x0 << MIPI_CALIBRATION_CONTROL_CAL_SW_HW_MODE_SHFT) |
- (0x0 << MIPI_CALIBRATION_CONTROL_MANUAL_OVERRIDE_EN_SHFT);
- CDBG("%s MIPI_CALIBRATION_CONTROL val=0x%x\n", __func__, val);
- msm_io_w(val, csibase + MIPI_CALIBRATION_CONTROL);
-
- val = (20 <<
- MIPI_PHY_D0_CONTROL2_SETTLE_COUNT_SHFT) |
- (0x0F << MIPI_PHY_D0_CONTROL2_HS_TERM_IMP_SHFT) |
- (0x0 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
- (0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
- CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
- msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
- msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
- msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
- msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
- val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
- (0x0 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
- CDBG("%s MIPI_PHY_CL_CONTROL val=0x%x\n", __func__, val);
- msm_io_w(val, csibase + MIPI_PHY_CL_CONTROL);
- msleep(10);
- free_irq(camio_ext.csiirq, 0);
+ CDBG("disable mipi\n");
+ msm_camio_csi_disable();
+ CDBG("disable clocks\n");
msm_camio_clk_disable(CAMIO_CSI0_PCLK);
msm_camio_clk_disable(CAMIO_CSI0_VFE_CLK);
msm_camio_clk_disable(CAMIO_CSI0_CLK);
msm_camio_clk_disable(CAMIO_VFE_CLK);
- iounmap(csibase);
- release_mem_region(camio_ext.csiphy, camio_ext.csisz);
}
msm_camio_clk_disable(CAMIO_VFE_PBDG_CLK);
}
@@ -725,8 +702,9 @@
{
int rc = 0;
uint32_t val = 0;
+ int i;
- CDBG("msm_camio_csi_config \n");
+ CDBG("msm_camio_csi_config\n");
/* SOT_ECC_EN enable error correction for SYNC (data-lane) */
msm_io_w(0x4, csibase + MIPI_PHY_CONTROL);
@@ -763,11 +741,8 @@
(0x1 << MIPI_PHY_D0_CONTROL2_LP_REC_EN_SHFT) |
(0x1 << MIPI_PHY_D0_CONTROL2_ERR_SOT_HS_EN_SHFT);
CDBG("%s MIPI_PHY_D0_CONTROL2 val=0x%x\n", __func__, val);
- msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2);
- msm_io_w(val, csibase + MIPI_PHY_D1_CONTROL2);
- msm_io_w(val, csibase + MIPI_PHY_D2_CONTROL2);
- msm_io_w(val, csibase + MIPI_PHY_D3_CONTROL2);
-
+ for (i = 0; i < csi_params->lane_cnt; i++)
+ msm_io_w(val, csibase + MIPI_PHY_D0_CONTROL2 + i * 4);
val = (0x0F << MIPI_PHY_CL_CONTROL_HS_TERM_IMP_SHFT) |
(0x1 << MIPI_PHY_CL_CONTROL_LP_REC_EN_SHFT);
diff --git a/drivers/media/video/msm/msm_mctl.c b/drivers/media/video/msm/msm_mctl.c
index 73fd383..2adbbd2 100644
--- a/drivers/media/video/msm/msm_mctl.c
+++ b/drivers/media/video/msm/msm_mctl.c
@@ -261,6 +261,45 @@
rc = p_mctl->sync.sctrl.s_config(argp);
break;
+ case MSM_CAM_IOCTL_SENSOR_V4l2_S_CTRL: {
+ struct v4l2_control v4l2_ctrl;
+ CDBG("subdev call\n");
+ if (copy_from_user(&v4l2_ctrl,
+ (void *)argp,
+ sizeof(struct v4l2_control))) {
+ CDBG("copy fail\n");
+ return -EFAULT;
+ }
+ CDBG("subdev call ok\n");
+ rc = v4l2_subdev_call(p_mctl->sensor_sdev,
+ core, s_ctrl, &v4l2_ctrl);
+ break;
+ }
+
+ case MSM_CAM_IOCTL_SENSOR_V4l2_QUERY_CTRL: {
+ struct v4l2_queryctrl v4l2_qctrl;
+ CDBG("query called\n");
+ if (copy_from_user(&v4l2_qctrl,
+ (void *)argp,
+ sizeof(struct v4l2_queryctrl))) {
+ CDBG("copy fail\n");
+ rc = -EFAULT;
+ break;
+ }
+ rc = v4l2_subdev_call(p_mctl->sensor_sdev,
+ core, queryctrl, &v4l2_qctrl);
+ if (rc < 0) {
+ rc = -EFAULT;
+ break;
+ }
+ if (copy_to_user((void *)argp,
+ &v4l2_qctrl,
+ sizeof(struct v4l2_queryctrl))) {
+ rc = -EFAULT;
+ }
+ break;
+ }
+
case MSM_CAM_IOCTL_ACTUATOR_IO_CFG: {
struct msm_actuator_cfg_data act_data;
if (p_mctl->sync.actctrl.a_config) {
@@ -286,6 +325,19 @@
break;
}
+ case MSM_CAM_IOCTL_GET_KERNEL_SYSTEM_TIME: {
+ struct timeval timestamp;
+ if (copy_from_user(×tamp, argp, sizeof(timestamp))) {
+ ERR_COPY_FROM_USER();
+ rc = -EFAULT;
+ } else {
+ msm_mctl_gettimeofday(×tamp);
+ rc = copy_to_user((void *)argp,
+ ×tamp, sizeof(timestamp));
+ }
+ break;
+ }
+
case MSM_CAM_IOCTL_FLASH_CTRL: {
struct flash_ctrl_data flash_info;
if (copy_from_user(&flash_info, argp, sizeof(flash_info))) {
@@ -504,6 +556,15 @@
goto msm_open_done;
}
+ if (sync->actctrl.a_power_up)
+ rc = sync->actctrl.a_power_up(
+ sync->sdata->actuator_info);
+
+ if (rc < 0) {
+ pr_err("%s: act power failed:%d\n", __func__, rc);
+ goto msm_open_done;
+ }
+
pm_qos_add_request(&p_mctl->pm_qos_req_list,
PM_QOS_CPU_DMA_LATENCY,
PM_QOS_DEFAULT_VALUE);
@@ -537,7 +598,7 @@
VIDIOC_MSM_CSIPHY_RELEASE, NULL);
if (p_mctl->sync.actctrl.a_power_down)
- p_mctl->sync.actctrl.a_power_down();
+ p_mctl->sync.actctrl.a_power_down(sync->sdata->actuator_info);
if (p_mctl->sync.sctrl.s_release)
p_mctl->sync.sctrl.s_release();
@@ -551,6 +612,7 @@
PM_QOS_DEFAULT_VALUE);
pm_qos_remove_request(&p_mctl->pm_qos_req_list);
+ wake_unlock(&sync->wake_lock);
return rc;
}
diff --git a/drivers/media/video/msm/msm_vfe32.c b/drivers/media/video/msm/msm_vfe32.c
index d34d5b1..dcece44 100644
--- a/drivers/media/video/msm/msm_vfe32.c
+++ b/drivers/media/video/msm/msm_vfe32.c
@@ -444,10 +444,11 @@
static int vfe32_config_axi(int mode, uint32_t *ao)
{
- int32_t *ch_info;
+ uint32_t *ch_info;
+ uint32_t *axi_cfg = ao+V32_AXI_BUS_FMT_OFF;
/* Update the corresponding write masters for each output*/
- ch_info = ao + V32_AXI_CFG_LEN;
+ ch_info = axi_cfg + V32_AXI_CFG_LEN;
vfe32_ctrl->outpath.out0.ch0 = 0x0000FFFF & *ch_info;
vfe32_ctrl->outpath.out0.ch1 = 0x0000FFFF & (*ch_info++ >> 16);
vfe32_ctrl->outpath.out0.ch2 = 0x0000FFFF & *ch_info++;
@@ -517,9 +518,12 @@
default:
break;
}
+ msm_io_w(*ao, vfe32_ctrl->vfebase +
+ VFE_BUS_IO_FORMAT_CFG);
msm_io_memcpy(vfe32_ctrl->vfebase +
- vfe32_cmd[VFE_CMD_AXI_OUT_CFG].offset, ao,
- vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length - V32_AXI_CH_INF_LEN);
+ vfe32_cmd[VFE_CMD_AXI_OUT_CFG].offset, axi_cfg,
+ vfe32_cmd[VFE_CMD_AXI_OUT_CFG].length - V32_AXI_CH_INF_LEN
+ - V32_AXI_BUS_FMT_LEN);
return 0;
}
@@ -861,8 +865,6 @@
(0x1 << (vfe32_ctrl->outpath.out1.ch0 + 8));
msm_io_w(1, vfe32_ctrl->vfebase +
vfe32_AXI_WM_CFG[vfe32_ctrl->outpath.out1.ch0]);
- msm_io_w(0x1000, vfe32_ctrl->vfebase +
- VFE_BUS_IO_FORMAT_CFG);
}
}
msm_io_w(irq_comp_mask, vfe32_ctrl->vfebase + VFE_IRQ_COMP_MASK);
diff --git a/drivers/media/video/msm/msm_vfe32.h b/drivers/media/video/msm/msm_vfe32.h
index ecb8608..ff42c28 100644
--- a/drivers/media/video/msm/msm_vfe32.h
+++ b/drivers/media/video/msm/msm_vfe32.h
@@ -226,9 +226,11 @@
#define V32_OPERATION_CFG_LEN 32
#define V32_AXI_OUT_OFF 0x00000038
-#define V32_AXI_OUT_LEN 212
+#define V32_AXI_OUT_LEN 216
#define V32_AXI_CH_INF_LEN 24
#define V32_AXI_CFG_LEN 47
+#define V32_AXI_BUS_FMT_OFF 1
+#define V32_AXI_BUS_FMT_LEN 4
#define V32_FRAME_SKIP_OFF 0x00000504
#define V32_FRAME_SKIP_LEN 32
diff --git a/drivers/media/video/msm/msm_vpe.c b/drivers/media/video/msm/msm_vpe.c
index 2f693ea..3a7faa2 100644
--- a/drivers/media/video/msm/msm_vpe.c
+++ b/drivers/media/video/msm/msm_vpe.c
@@ -127,7 +127,7 @@
rot_flag = msm_io_r(vpe_ctrl->vpebase +
VPE_OP_MODE_OFFSET) & 0xE00;
if (pinfo != NULL) {
- pr_err("%s: Crop info in2_w = %d, in2_h = %d "
+ CDBG("%s: Crop info in2_w = %d, in2_h = %d "
"out2_w = %d out2_h = %d\n",
__func__, pcrop->src_w, pcrop->src_h,
pcrop->dst_w, pcrop->dst_h);
@@ -543,6 +543,7 @@
vpe_ctrl->fs_vpe = NULL;
vpe_fs_failed:
disable_irq(vpe_ctrl->vpeirq->start);
+ vpe_ctrl->state = VPE_STATE_IDLE;
return rc;
}
@@ -578,9 +579,11 @@
unsigned long flags;
spin_lock_irqsave(&vpe_ctrl->lock, flags);
- if (vpe_ctrl->state == VPE_STATE_ACTIVE) {
+ if (vpe_ctrl->state == VPE_STATE_ACTIVE ||
+ vpe_ctrl->state == VPE_STATE_IDLE) {
spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
- pr_err(" =====VPE is busy!!! Wrong!========\n");
+ pr_err(" =====VPE in wrong state:%d!!! Wrong!========\n",
+ vpe_ctrl->state);
return -EBUSY;
}
spin_unlock_irqrestore(&vpe_ctrl->lock, flags);
diff --git a/drivers/media/video/msm/sensors/Makefile b/drivers/media/video/msm/sensors/Makefile
index 3e42126..7b22592 100644
--- a/drivers/media/video/msm/sensors/Makefile
+++ b/drivers/media/video/msm/sensors/Makefile
@@ -4,4 +4,5 @@
EXTRA_CFLAGS += -Idrivers/media/video/msm/csi
obj-$(CONFIG_MSM_CAMERA_SENSOR) += msm_sensor.o
obj-$(CONFIG_IMX074) += imx074_v4l2.o
+obj-$(CONFIG_MT9M114) += mt9m114_v4l2.o
obj-$(CONFIG_OV2720) += ov2720.o
diff --git a/drivers/media/video/msm/sensors/msm_sensor.c b/drivers/media/video/msm/sensors/msm_sensor.c
index 7eb3160..e67f9cd 100644
--- a/drivers/media/video/msm/sensors/msm_sensor.c
+++ b/drivers/media/video/msm/sensors/msm_sensor.c
@@ -578,6 +578,74 @@
return 0;
}
+int32_t msm_sensor_v4l2_s_ctrl(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl)
+{
+ int rc = -1, i = 0;
+ struct msm_sensor_ctrl_t *s_ctrl =
+ (struct msm_sensor_ctrl_t *) sd->dev_priv;
+ struct msm_sensor_v4l2_ctrl_info_t *v4l2_ctrl =
+ s_ctrl->msm_sensor_v4l2_ctrl_info;
+
+ CDBG("%s\n", __func__);
+ CDBG("%d\n", ctrl->id);
+ if (v4l2_ctrl == NULL)
+ return rc;
+
+ for (i = 0; i < s_ctrl->num_v4l2_ctrl; i++) {
+ if (v4l2_ctrl[i].ctrl_id == ctrl->id) {
+ if (v4l2_ctrl[i].s_v4l2_ctrl != NULL) {
+ rc = v4l2_ctrl[i].s_v4l2_ctrl(
+ s_ctrl,
+ &s_ctrl->msm_sensor_v4l2_ctrl_info[i],
+ ctrl->value);
+ }
+ break;
+ }
+ }
+
+ return rc;
+}
+
+int32_t msm_sensor_v4l2_query_ctrl(
+ struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl)
+{
+ int rc = -1, i = 0;
+ struct msm_sensor_ctrl_t *s_ctrl =
+ (struct msm_sensor_ctrl_t *) sd->dev_priv;
+
+ CDBG("%s\n", __func__);
+ CDBG("%s id: %d\n", __func__, qctrl->id);
+
+ if (s_ctrl->msm_sensor_v4l2_ctrl_info == NULL)
+ return rc;
+
+ for (i = 0; i < s_ctrl->num_v4l2_ctrl; i++) {
+ if (s_ctrl->msm_sensor_v4l2_ctrl_info[i].ctrl_id == qctrl->id) {
+ qctrl->minimum =
+ s_ctrl->msm_sensor_v4l2_ctrl_info[i].min;
+ qctrl->maximum =
+ s_ctrl->msm_sensor_v4l2_ctrl_info[i].max;
+ qctrl->flags = 1;
+ rc = 0;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+int msm_sensor_s_ctrl_by_enum(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_sensor_v4l2_ctrl_info_t *ctrl_info, int value)
+{
+ int rc = 0;
+ CDBG("%s enter\n", __func__);
+ rc = msm_sensor_write_enum_conf_array(
+ s_ctrl->sensor_i2c_client,
+ ctrl_info->enum_cfg_settings, value);
+ return rc;
+}
+
static int msm_sensor_debugfs_stream_s(void *data, u64 val)
{
struct msm_sensor_ctrl_t *s_ctrl = (struct msm_sensor_ctrl_t *) data;
diff --git a/drivers/media/video/msm/sensors/msm_sensor.h b/drivers/media/video/msm/sensors/msm_sensor.h
index e4dc34f..f1a15b2 100644
--- a/drivers/media/video/msm/sensors/msm_sensor.h
+++ b/drivers/media/video/msm/sensors/msm_sensor.h
@@ -93,6 +93,17 @@
};
struct msm_sensor_ctrl_t;
+
+struct msm_sensor_v4l2_ctrl_info_t {
+ uint32_t ctrl_id;
+ int16_t min;
+ int16_t max;
+ int16_t step;
+ struct msm_camera_i2c_enum_conf_array *enum_cfg_settings;
+ int (*s_v4l2_ctrl) (struct msm_sensor_ctrl_t *,
+ struct msm_sensor_v4l2_ctrl_info_t *, int);
+};
+
struct msm_sensor_fn_t {
void (*sensor_start_stream) (struct msm_sensor_ctrl_t *);
void (*sensor_stop_stream) (struct msm_sensor_ctrl_t *);
@@ -137,6 +148,8 @@
struct msm_sensor_id_info_t *sensor_id_info;
struct msm_sensor_exp_gain_info_t *sensor_exp_gain_info;
struct msm_sensor_reg_t *msm_sensor_reg;
+ struct msm_sensor_v4l2_ctrl_info_t *msm_sensor_v4l2_ctrl_info;
+ uint16_t num_v4l2_ctrl;
uint16_t curr_line_length_pclk;
uint16_t curr_frame_length_lines;
@@ -192,6 +205,18 @@
const struct msm_camera_sensor_info *info,
struct v4l2_subdev *sdev, struct msm_sensor_ctrl *s);
+int32_t msm_sensor_v4l2_s_ctrl(struct v4l2_subdev *sd,
+ struct v4l2_control *ctrl);
+
+int32_t msm_sensor_v4l2_query_ctrl(
+ struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl);
+
+int msm_sensor_s_ctrl_by_index(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_sensor_v4l2_ctrl_info_t *ctrl_info, int value);
+
+int msm_sensor_s_ctrl_by_enum(struct msm_sensor_ctrl_t *s_ctrl,
+ struct msm_sensor_v4l2_ctrl_info_t *ctrl_info, int value);
+
int msm_sensor_v4l2_enum_fmt(struct v4l2_subdev *sd, unsigned int index,
enum v4l2_mbus_pixelcode *code);
diff --git a/drivers/media/video/msm/sensors/mt9m114_v4l2.c b/drivers/media/video/msm/sensors/mt9m114_v4l2.c
new file mode 100644
index 0000000..fc45705
--- /dev/null
+++ b/drivers/media/video/msm/sensors/mt9m114_v4l2.c
@@ -0,0 +1,1340 @@
+/* Copyright (c) 2011, 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 "msm_sensor.h"
+#define SENSOR_NAME "mt9m114"
+#define PLATFORM_DRIVER_NAME "msm_camera_mt9m114"
+#define mt9m114_obj mt9m114_##obj
+
+/* Sysctl registers */
+#define MT9M114_COMMAND_REGISTER 0x0080
+#define MT9M114_COMMAND_REGISTER_APPLY_PATCH (1 << 0)
+#define MT9M114_COMMAND_REGISTER_SET_STATE (1 << 1)
+#define MT9M114_COMMAND_REGISTER_REFRESH (1 << 2)
+#define MT9M114_COMMAND_REGISTER_WAIT_FOR_EVENT (1 << 3)
+#define MT9M114_COMMAND_REGISTER_OK (1 << 15)
+
+DEFINE_MUTEX(mt9m114_mut);
+static struct msm_sensor_ctrl_t mt9m114_s_ctrl;
+
+static struct msm_camera_i2c_reg_conf mt9m114_720p_settings[] = {
+ {0xdc00, 0x50, MSM_CAMERA_I2C_BYTE_DATA, MSM_CAMERA_I2C_CMD_WRITE},
+ {MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_SET_STATE,
+ MSM_CAMERA_I2C_UNSET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+ {MT9M114_COMMAND_REGISTER, (MT9M114_COMMAND_REGISTER_OK |
+ MT9M114_COMMAND_REGISTER_SET_STATE), MSM_CAMERA_I2C_WORD_DATA,
+ MSM_CAMERA_I2C_CMD_WRITE},
+ {MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_SET_STATE,
+ MSM_CAMERA_I2C_UNSET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+ {0xDC01, 0x52, MSM_CAMERA_I2C_BYTE_DATA, MSM_CAMERA_I2C_CMD_POLL},
+
+ {0x098E, 0, MSM_CAMERA_I2C_BYTE_DATA},
+ {0xC800, 0x007C,},/*y_addr_start = 124*/
+ {0xC802, 0x0004,},/*x_addr_start = 4*/
+ {0xC804, 0x0353,},/*y_addr_end = 851*/
+ {0xC806, 0x050B,},/*x_addr_end = 1291*/
+ {0xC808, 0x02DC,},/*pixclk = 48000000*/
+ {0xC80A, 0x6C00,},/*pixclk = 48000000*/
+ {0xC80C, 0x0001,},/*row_speed = 1*/
+ {0xC80E, 0x00DB,},/*fine_integ_time_min = 219*/
+ {0xC810, 0x05BD,},/*fine_integ_time_max = 1469*/
+ {0xC812, 0x03E8,},/*frame_length_lines = 1000*/
+ {0xC814, 0x0640,},/*line_length_pck = 1600*/
+ {0xC816, 0x0060,},/*fine_correction = 96*/
+ {0xC818, 0x02D3,},/*cpipe_last_row = 723*/
+ {0xC826, 0x0020,},/*reg_0_data = 32*/
+ {0xC834, 0x0000,},/*sensor_control_read_mode = 0*/
+ {0xC854, 0x0000,},/*crop_window_xoffset = 0*/
+ {0xC856, 0x0000,},/*crop_window_yoffset = 0*/
+ {0xC858, 0x0500,},/*crop_window_width = 1280*/
+ {0xC85A, 0x02D0,},/*crop_window_height = 720*/
+ {0xC85C, 0x03, MSM_CAMERA_I2C_BYTE_DATA}, /*crop_cropmode = 3*/
+ {0xC868, 0x0500,},/*output_width = 1280*/
+ {0xC86A, 0x02D0,},/*output_height = 720*/
+ {0xC878, 0x00, MSM_CAMERA_I2C_BYTE_DATA}, /*aet_aemode = 0*/
+ {0xC88C, 0x1E00,},/*aet_max_frame_rate = 7680*/
+ {0xC88E, 0x1E00,},/*aet_min_frame_rate = 7680*/
+ {0xC914, 0x0000,},/*stat_awb_window_xstart = 0*/
+ {0xC916, 0x0000,},/*stat_awb_window_ystart = 0*/
+ {0xC918, 0x04FF,},/*stat_awb_window_xend = 1279*/
+ {0xC91A, 0x02CF,},/*stat_awb_window_yend = 719*/
+ {0xC91C, 0x0000,},/*stat_ae_window_xstart = 0*/
+ {0xC91E, 0x0000,},/*stat_ae_window_ystart = 0*/
+ {0xC920, 0x00FF,},/*stat_ae_window_xend = 255*/
+ {0xC922, 0x008F,},/*stat_ae_window_yend = 143*/
+};
+
+static struct msm_camera_i2c_reg_conf mt9m114_recommend_settings[] = {
+ {0x301A, 0x0200, MSM_CAMERA_I2C_SET_WORD_MASK},
+ {0x098E, 0, MSM_CAMERA_I2C_BYTE_DATA},
+ /*cam_sysctl_pll_enable = 1*/
+ {0xC97E, 0x01, MSM_CAMERA_I2C_BYTE_DATA},
+ /*cam_sysctl_pll_divider_m_n = 288*/
+ {0xC980, 0x0120,},
+ /*cam_sysctl_pll_divider_p = 1792*/
+ {0xC982, 0x0700,},
+ /*output_control = 32769*/
+ {0xC984, 0x8001,},
+ /*mipi_timing_t_hs_zero = 3840*/
+ {0xC988, 0x0F00,},
+ /*mipi_timing_t_hs_exit_hs_trail = 2823*/
+ {0xC98A, 0x0B07,},
+ /*mipi_timing_t_clk_post_clk_pre = 3329*/
+ {0xC98C, 0x0D01,},
+ /*mipi_timing_t_clk_trail_clk_zero = 1821*/
+ {0xC98E, 0x071D,},
+ /*mipi_timing_t_lpx = 6*/
+ {0xC990, 0x0006,},
+ /*mipi_timing_init_timing = 2572*/
+ {0xC992, 0x0A0C,},
+ {0xC800, 0x007C,},/*y_addr_start = 124*/
+ {0xC802, 0x0004,},/*x_addr_start = 4*/
+ {0xC804, 0x0353,},/*y_addr_end = 851*/
+ {0xC806, 0x050B,},/*x_addr_end = 1291*/
+ {0xC808, 0x02DC,},/*pixclk = 48000000*/
+ {0xC80A, 0x6C00,},/*pixclk = 48000000*/
+ {0xC80C, 0x0001,},/*row_speed = 1*/
+ {0xC80E, 0x00DB,},/*fine_integ_time_min = 219*/
+ {0xC810, 0x05BD,},/*fine_integ_time_max = 1469*/
+ {0xC812, 0x03E8,},/*frame_length_lines = 1000*/
+ {0xC814, 0x0640,},/*line_length_pck = 1600*/
+ {0xC816, 0x0060,},/*fine_correction = 96*/
+ {0xC818, 0x02D3,},/*cpipe_last_row = 723*/
+ {0xC826, 0x0020,},/*reg_0_data = 32*/
+ {0xC834, 0x0000,},/*sensor_control_read_mode = 0*/
+ {0xC854, 0x0000,},/*crop_window_xoffset = 0*/
+ {0xC856, 0x0000,},/*crop_window_yoffset = 0*/
+ {0xC858, 0x0500,},/*crop_window_width = 1280*/
+ {0xC85A, 0x02D0,},/*crop_window_height = 720*/
+ {0xC85C, 0x03, MSM_CAMERA_I2C_BYTE_DATA}, /*crop_cropmode = 3*/
+ {0xC868, 0x0500,},/*output_width = 1280*/
+ {0xC86A, 0x02D0,},/*output_height = 720*/
+ {0xC878, 0x00, MSM_CAMERA_I2C_BYTE_DATA}, /*aet_aemode = 0*/
+ {0xC88C, 0x1E00,},/*aet_max_frame_rate = 7680*/
+ {0xC88E, 0x1E00,},/*aet_min_frame_rate = 7680*/
+ {0xC914, 0x0000,},/*stat_awb_window_xstart = 0*/
+ {0xC916, 0x0000,},/*stat_awb_window_ystart = 0*/
+ {0xC918, 0x04FF,},/*stat_awb_window_xend = 1279*/
+ {0xC91A, 0x02CF,},/*stat_awb_window_yend = 719*/
+ {0xC91C, 0x0000,},/*stat_ae_window_xstart = 0*/
+ {0xC91E, 0x0000,},/*stat_ae_window_ystart = 0*/
+ {0xC920, 0x00FF,},/*stat_ae_window_xend = 255*/
+ {0xC922, 0x008F,},/*stat_ae_window_yend = 143*/
+
+ /*Sensor optimization*/
+ {0x316A, 0x8270,},
+ {0x316C, 0x8270,},
+ {0x3ED0, 0x2305,},
+ {0x3ED2, 0x77CF,},
+ {0x316E, 0x8202,},
+ {0x3180, 0x87FF,},
+ {0x30D4, 0x6080,},
+ {0xA802, 0x0008,},/*AE_TRACK_MODE*/
+ {0x3E14, 0xFF39,},
+ {0x0982, 0x0001,},/*ACCESS_CTL_STAT*/
+ {0x098A, 0x5000,},/*PHYSICAL_ADDRESS_ACCESS*/
+ {0xD000, 0x70CF,},
+ {0xD002, 0xFFFF,},
+ {0xD004, 0xC5D4,},
+ {0xD006, 0x903A,},
+ {0xD008, 0x2144,},
+ {0xD00A, 0x0C00,},
+ {0xD00C, 0x2186,},
+ {0xD00E, 0x0FF3,},
+ {0xD010, 0xB844,},
+ {0xD012, 0xB948,},
+ {0xD014, 0xE082,},
+ {0xD016, 0x20CC,},
+ {0xD018, 0x80E2,},
+ {0xD01A, 0x21CC,},
+ {0xD01C, 0x80A2,},
+ {0xD01E, 0x21CC,},
+ {0xD020, 0x80E2,},
+ {0xD022, 0xF404,},
+ {0xD024, 0xD801,},
+ {0xD026, 0xF003,},
+ {0xD028, 0xD800,},
+ {0xD02A, 0x7EE0,},
+ {0xD02C, 0xC0F1,},
+ {0xD02E, 0x08BA,},
+ {0xD030, 0x0600,},
+ {0xD032, 0xC1A1,},
+ {0xD034, 0x76CF,},
+ {0xD036, 0xFFFF,},
+ {0xD038, 0xC130,},
+ {0xD03A, 0x6E04,},
+ {0xD03C, 0xC040,},
+ {0xD03E, 0x71CF,},
+ {0xD040, 0xFFFF,},
+ {0xD042, 0xC790,},
+ {0xD044, 0x8103,},
+ {0xD046, 0x77CF,},
+ {0xD048, 0xFFFF,},
+ {0xD04A, 0xC7C0,},
+ {0xD04C, 0xE001,},
+ {0xD04E, 0xA103,},
+ {0xD050, 0xD800,},
+ {0xD052, 0x0C6A,},
+ {0xD054, 0x04E0,},
+ {0xD056, 0xB89E,},
+ {0xD058, 0x7508,},
+ {0xD05A, 0x8E1C,},
+ {0xD05C, 0x0809,},
+ {0xD05E, 0x0191,},
+ {0xD060, 0xD801,},
+ {0xD062, 0xAE1D,},
+ {0xD064, 0xE580,},
+ {0xD066, 0x20CA,},
+ {0xD068, 0x0022,},
+ {0xD06A, 0x20CF,},
+ {0xD06C, 0x0522,},
+ {0xD06E, 0x0C5C,},
+ {0xD070, 0x04E2,},
+ {0xD072, 0x21CA,},
+ {0xD074, 0x0062,},
+ {0xD076, 0xE580,},
+ {0xD078, 0xD901,},
+ {0xD07A, 0x79C0,},
+ {0xD07C, 0xD800,},
+ {0xD07E, 0x0BE6,},
+ {0xD080, 0x04E0,},
+ {0xD082, 0xB89E,},
+ {0xD084, 0x70CF,},
+ {0xD086, 0xFFFF,},
+ {0xD088, 0xC8D4,},
+ {0xD08A, 0x9002,},
+ {0xD08C, 0x0857,},
+ {0xD08E, 0x025E,},
+ {0xD090, 0xFFDC,},
+ {0xD092, 0xE080,},
+ {0xD094, 0x25CC,},
+ {0xD096, 0x9022,},
+ {0xD098, 0xF225,},
+ {0xD09A, 0x1700,},
+ {0xD09C, 0x108A,},
+ {0xD09E, 0x73CF,},
+ {0xD0A0, 0xFF00,},
+ {0xD0A2, 0x3174,},
+ {0xD0A4, 0x9307,},
+ {0xD0A6, 0x2A04,},
+ {0xD0A8, 0x103E,},
+ {0xD0AA, 0x9328,},
+ {0xD0AC, 0x2942,},
+ {0xD0AE, 0x7140,},
+ {0xD0B0, 0x2A04,},
+ {0xD0B2, 0x107E,},
+ {0xD0B4, 0x9349,},
+ {0xD0B6, 0x2942,},
+ {0xD0B8, 0x7141,},
+ {0xD0BA, 0x2A04,},
+ {0xD0BC, 0x10BE,},
+ {0xD0BE, 0x934A,},
+ {0xD0C0, 0x2942,},
+ {0xD0C2, 0x714B,},
+ {0xD0C4, 0x2A04,},
+ {0xD0C6, 0x10BE,},
+ {0xD0C8, 0x130C,},
+ {0xD0CA, 0x010A,},
+ {0xD0CC, 0x2942,},
+ {0xD0CE, 0x7142,},
+ {0xD0D0, 0x2250,},
+ {0xD0D2, 0x13CA,},
+ {0xD0D4, 0x1B0C,},
+ {0xD0D6, 0x0284,},
+ {0xD0D8, 0xB307,},
+ {0xD0DA, 0xB328,},
+ {0xD0DC, 0x1B12,},
+ {0xD0DE, 0x02C4,},
+ {0xD0E0, 0xB34A,},
+ {0xD0E2, 0xED88,},
+ {0xD0E4, 0x71CF,},
+ {0xD0E6, 0xFF00,},
+ {0xD0E8, 0x3174,},
+ {0xD0EA, 0x9106,},
+ {0xD0EC, 0xB88F,},
+ {0xD0EE, 0xB106,},
+ {0xD0F0, 0x210A,},
+ {0xD0F2, 0x8340,},
+ {0xD0F4, 0xC000,},
+ {0xD0F6, 0x21CA,},
+ {0xD0F8, 0x0062,},
+ {0xD0FA, 0x20F0,},
+ {0xD0FC, 0x0040,},
+ {0xD0FE, 0x0B02,},
+ {0xD100, 0x0320,},
+ {0xD102, 0xD901,},
+ {0xD104, 0x07F1,},
+ {0xD106, 0x05E0,},
+ {0xD108, 0xC0A1,},
+ {0xD10A, 0x78E0,},
+ {0xD10C, 0xC0F1,},
+ {0xD10E, 0x71CF,},
+ {0xD110, 0xFFFF,},
+ {0xD112, 0xC7C0,},
+ {0xD114, 0xD840,},
+ {0xD116, 0xA900,},
+ {0xD118, 0x71CF,},
+ {0xD11A, 0xFFFF,},
+ {0xD11C, 0xD02C,},
+ {0xD11E, 0xD81E,},
+ {0xD120, 0x0A5A,},
+ {0xD122, 0x04E0,},
+ {0xD124, 0xDA00,},
+ {0xD126, 0xD800,},
+ {0xD128, 0xC0D1,},
+ {0xD12A, 0x7EE0,},
+ {0x098E, 0x0000,},
+
+ {0x0982, 0x0001,},
+ {0x098A, 0x5C10,},
+ {0xDC10, 0xC0F1,},
+ {0xDC12, 0x0CDA,},
+ {0xDC14, 0x0580,},
+ {0xDC16, 0x76CF,},
+ {0xDC18, 0xFF00,},
+ {0xDC1A, 0x2184,},
+ {0xDC1C, 0x9624,},
+ {0xDC1E, 0x218C,},
+ {0xDC20, 0x8FC3,},
+ {0xDC22, 0x75CF,},
+ {0xDC24, 0xFFFF,},
+ {0xDC26, 0xE058,},
+ {0xDC28, 0xF686,},
+ {0xDC2A, 0x1550,},
+ {0xDC2C, 0x1080,},
+ {0xDC2E, 0xE001,},
+ {0xDC30, 0x1D50,},
+ {0xDC32, 0x1002,},
+ {0xDC34, 0x1552,},
+ {0xDC36, 0x1100,},
+ {0xDC38, 0x6038,},
+ {0xDC3A, 0x1D52,},
+ {0xDC3C, 0x1004,},
+ {0xDC3E, 0x1540,},
+ {0xDC40, 0x1080,},
+ {0xDC42, 0x081B,},
+ {0xDC44, 0x00D1,},
+ {0xDC46, 0x8512,},
+ {0xDC48, 0x1000,},
+ {0xDC4A, 0x00C0,},
+ {0xDC4C, 0x7822,},
+ {0xDC4E, 0x2089,},
+ {0xDC50, 0x0FC1,},
+ {0xDC52, 0x2008,},
+ {0xDC54, 0x0F81,},
+ {0xDC56, 0xFFFF,},
+ {0xDC58, 0xFF80,},
+ {0xDC5A, 0x8512,},
+ {0xDC5C, 0x1801,},
+ {0xDC5E, 0x0052,},
+ {0xDC60, 0xA512,},
+ {0xDC62, 0x1544,},
+ {0xDC64, 0x1080,},
+ {0xDC66, 0xB861,},
+ {0xDC68, 0x262F,},
+ {0xDC6A, 0xF007,},
+ {0xDC6C, 0x1D44,},
+ {0xDC6E, 0x1002,},
+ {0xDC70, 0x20CA,},
+ {0xDC72, 0x0021,},
+ {0xDC74, 0x20CF,},
+ {0xDC76, 0x04E1,},
+ {0xDC78, 0x0850,},
+ {0xDC7A, 0x04A1,},
+ {0xDC7C, 0x21CA,},
+ {0xDC7E, 0x0021,},
+ {0xDC80, 0x1542,},
+ {0xDC82, 0x1140,},
+ {0xDC84, 0x8D2C,},
+ {0xDC86, 0x6038,},
+ {0xDC88, 0x1D42,},
+ {0xDC8A, 0x1004,},
+ {0xDC8C, 0x1542,},
+ {0xDC8E, 0x1140,},
+ {0xDC90, 0xB601,},
+ {0xDC92, 0x046D,},
+ {0xDC94, 0x0580,},
+ {0xDC96, 0x78E0,},
+ {0xDC98, 0xD800,},
+ {0xDC9A, 0xB893,},
+ {0xDC9C, 0x002D,},
+ {0xDC9E, 0x04A0,},
+ {0xDCA0, 0xD900,},
+ {0xDCA2, 0x78E0,},
+ {0xDCA4, 0x72CF,},
+ {0xDCA6, 0xFFFF,},
+ {0xDCA8, 0xE058,},
+ {0xDCAA, 0x2240,},
+ {0xDCAC, 0x0340,},
+ {0xDCAE, 0xA212,},
+ {0xDCB0, 0x208A,},
+ {0xDCB2, 0x0FFF,},
+ {0xDCB4, 0x1A42,},
+ {0xDCB6, 0x0004,},
+ {0xDCB8, 0xD830,},
+ {0xDCBA, 0x1A44,},
+ {0xDCBC, 0x0002,},
+ {0xDCBE, 0xD800,},
+ {0xDCC0, 0x1A50,},
+ {0xDCC2, 0x0002,},
+ {0xDCC4, 0x1A52,},
+ {0xDCC6, 0x0004,},
+ {0xDCC8, 0x1242,},
+ {0xDCCA, 0x0140,},
+ {0xDCCC, 0x8A2C,},
+ {0xDCCE, 0x6038,},
+ {0xDCD0, 0x1A42,},
+ {0xDCD2, 0x0004,},
+ {0xDCD4, 0x1242,},
+ {0xDCD6, 0x0141,},
+ {0xDCD8, 0x70CF,},
+ {0xDCDA, 0xFF00,},
+ {0xDCDC, 0x2184,},
+ {0xDCDE, 0xB021,},
+ {0xDCE0, 0xD800,},
+ {0xDCE2, 0xB893,},
+ {0xDCE4, 0x07E5,},
+ {0xDCE6, 0x0460,},
+ {0xDCE8, 0xD901,},
+ {0xDCEA, 0x78E0,},
+ {0xDCEC, 0xC0F1,},
+ {0xDCEE, 0x0BFA,},
+ {0xDCF0, 0x05A0,},
+ {0xDCF2, 0x216F,},
+ {0xDCF4, 0x0043,},
+ {0xDCF6, 0xC1A4,},
+ {0xDCF8, 0x220A,},
+ {0xDCFA, 0x1F80,},
+ {0xDCFC, 0xFFFF,},
+ {0xDCFE, 0xE058,},
+ {0xDD00, 0x2240,},
+ {0xDD02, 0x134F,},
+ {0xDD04, 0x1A48,},
+ {0xDD06, 0x13C0,},
+ {0xDD08, 0x1248,},
+ {0xDD0A, 0x1002,},
+ {0xDD0C, 0x70CF,},
+ {0xDD0E, 0x7FFF,},
+ {0xDD10, 0xFFFF,},
+ {0xDD12, 0xE230,},
+ {0xDD14, 0xC240,},
+ {0xDD16, 0xDA00,},
+ {0xDD18, 0xF00C,},
+ {0xDD1A, 0x1248,},
+ {0xDD1C, 0x1003,},
+ {0xDD1E, 0x1301,},
+ {0xDD20, 0x04CB,},
+ {0xDD22, 0x7261,},
+ {0xDD24, 0x2108,},
+ {0xDD26, 0x0081,},
+ {0xDD28, 0x2009,},
+ {0xDD2A, 0x0080,},
+ {0xDD2C, 0x1A48,},
+ {0xDD2E, 0x10C0,},
+ {0xDD30, 0x1248,},
+ {0xDD32, 0x100B,},
+ {0xDD34, 0xC300,},
+ {0xDD36, 0x0BE7,},
+ {0xDD38, 0x90C4,},
+ {0xDD3A, 0x2102,},
+ {0xDD3C, 0x0003,},
+ {0xDD3E, 0x238C,},
+ {0xDD40, 0x8FC3,},
+ {0xDD42, 0xF6C7,},
+ {0xDD44, 0xDAFF,},
+ {0xDD46, 0x1A05,},
+ {0xDD48, 0x1082,},
+ {0xDD4A, 0xC241,},
+ {0xDD4C, 0xF005,},
+ {0xDD4E, 0x7A6F,},
+ {0xDD50, 0xC241,},
+ {0xDD52, 0x1A05,},
+ {0xDD54, 0x10C2,},
+ {0xDD56, 0x2000,},
+ {0xDD58, 0x8040,},
+ {0xDD5A, 0xDA00,},
+ {0xDD5C, 0x20C0,},
+ {0xDD5E, 0x0064,},
+ {0xDD60, 0x781C,},
+ {0xDD62, 0xC042,},
+ {0xDD64, 0x1C0E,},
+ {0xDD66, 0x3082,},
+ {0xDD68, 0x1A48,},
+ {0xDD6A, 0x13C0,},
+ {0xDD6C, 0x7548,},
+ {0xDD6E, 0x7348,},
+ {0xDD70, 0x7148,},
+ {0xDD72, 0x7648,},
+ {0xDD74, 0xF002,},
+ {0xDD76, 0x7608,},
+ {0xDD78, 0x1248,},
+ {0xDD7A, 0x1000,},
+ {0xDD7C, 0x1400,},
+ {0xDD7E, 0x300B,},
+ {0xDD80, 0x084D,},
+ {0xDD82, 0x02C5,},
+ {0xDD84, 0x1248,},
+ {0xDD86, 0x1000,},
+ {0xDD88, 0xE101,},
+ {0xDD8A, 0x1001,},
+ {0xDD8C, 0x04CB,},
+ {0xDD8E, 0x1A48,},
+ {0xDD90, 0x1000,},
+ {0xDD92, 0x7361,},
+ {0xDD94, 0x1408,},
+ {0xDD96, 0x300B,},
+ {0xDD98, 0x2302,},
+ {0xDD9A, 0x02C0,},
+ {0xDD9C, 0x780D,},
+ {0xDD9E, 0x2607,},
+ {0xDDA0, 0x903E,},
+ {0xDDA2, 0x07D6,},
+ {0xDDA4, 0xFFE3,},
+ {0xDDA6, 0x792F,},
+ {0xDDA8, 0x09CF,},
+ {0xDDAA, 0x8152,},
+ {0xDDAC, 0x1248,},
+ {0xDDAE, 0x100E,},
+ {0xDDB0, 0x2400,},
+ {0xDDB2, 0x334B,},
+ {0xDDB4, 0xE501,},
+ {0xDDB6, 0x7EE2,},
+ {0xDDB8, 0x0DBF,},
+ {0xDDBA, 0x90F2,},
+ {0xDDBC, 0x1B0C,},
+ {0xDDBE, 0x1382,},
+ {0xDDC0, 0xC123,},
+ {0xDDC2, 0x140E,},
+ {0xDDC4, 0x3080,},
+ {0xDDC6, 0x7822,},
+ {0xDDC8, 0x1A07,},
+ {0xDDCA, 0x1002,},
+ {0xDDCC, 0x124C,},
+ {0xDDCE, 0x1000,},
+ {0xDDD0, 0x120B,},
+ {0xDDD2, 0x1081,},
+ {0xDDD4, 0x1207,},
+ {0xDDD6, 0x1083,},
+ {0xDDD8, 0x2142,},
+ {0xDDDA, 0x004B,},
+ {0xDDDC, 0x781B,},
+ {0xDDDE, 0x0B21,},
+ {0xDDE0, 0x02E2,},
+ {0xDDE2, 0x1A4C,},
+ {0xDDE4, 0x1000,},
+ {0xDDE6, 0xE101,},
+ {0xDDE8, 0x0915,},
+ {0xDDEA, 0x00C2,},
+ {0xDDEC, 0xC101,},
+ {0xDDEE, 0x1204,},
+ {0xDDF0, 0x1083,},
+ {0xDDF2, 0x090D,},
+ {0xDDF4, 0x00C2,},
+ {0xDDF6, 0xE001,},
+ {0xDDF8, 0x1A4C,},
+ {0xDDFA, 0x1000,},
+ {0xDDFC, 0x1A06,},
+ {0xDDFE, 0x1002,},
+ {0xDE00, 0x234A,},
+ {0xDE02, 0x1000,},
+ {0xDE04, 0x7169,},
+ {0xDE06, 0xF008,},
+ {0xDE08, 0x2053,},
+ {0xDE0A, 0x0003,},
+ {0xDE0C, 0x6179,},
+ {0xDE0E, 0x781C,},
+ {0xDE10, 0x2340,},
+ {0xDE12, 0x104B,},
+ {0xDE14, 0x1203,},
+ {0xDE16, 0x1083,},
+ {0xDE18, 0x0BF1,},
+ {0xDE1A, 0x90C2,},
+ {0xDE1C, 0x1202,},
+ {0xDE1E, 0x1080,},
+ {0xDE20, 0x091D,},
+ {0xDE22, 0x0004,},
+ {0xDE24, 0x70CF,},
+ {0xDE26, 0xFFFF,},
+ {0xDE28, 0xC644,},
+ {0xDE2A, 0x881B,},
+ {0xDE2C, 0xE0B2,},
+ {0xDE2E, 0xD83C,},
+ {0xDE30, 0x20CA,},
+ {0xDE32, 0x0CA2,},
+ {0xDE34, 0x1A01,},
+ {0xDE36, 0x1002,},
+ {0xDE38, 0x1A4C,},
+ {0xDE3A, 0x1080,},
+ {0xDE3C, 0x02B9,},
+ {0xDE3E, 0x05A0,},
+ {0xDE40, 0xC0A4,},
+ {0xDE42, 0x78E0,},
+ {0xDE44, 0xC0F1,},
+ {0xDE46, 0xFF95,},
+ {0xDE48, 0xD800,},
+ {0xDE4A, 0x71CF,},
+ {0xDE4C, 0xFF00,},
+ {0xDE4E, 0x1FE0,},
+ {0xDE50, 0x19D0,},
+ {0xDE52, 0x001C,},
+ {0xDE54, 0x19D1,},
+ {0xDE56, 0x001C,},
+ {0xDE58, 0x70CF,},
+ {0xDE5A, 0xFFFF,},
+ {0xDE5C, 0xE058,},
+ {0xDE5E, 0x901F,},
+ {0xDE60, 0xB861,},
+ {0xDE62, 0x19D2,},
+ {0xDE64, 0x001C,},
+ {0xDE66, 0xC0D1,},
+ {0xDE68, 0x7EE0,},
+ {0xDE6A, 0x78E0,},
+ {0xDE6C, 0xC0F1,},
+ {0xDE6E, 0x0A7A,},
+ {0xDE70, 0x0580,},
+ {0xDE72, 0x70CF,},
+ {0xDE74, 0xFFFF,},
+ {0xDE76, 0xC5D4,},
+ {0xDE78, 0x9041,},
+ {0xDE7A, 0x9023,},
+ {0xDE7C, 0x75CF,},
+ {0xDE7E, 0xFFFF,},
+ {0xDE80, 0xE058,},
+ {0xDE82, 0x7942,},
+ {0xDE84, 0xB967,},
+ {0xDE86, 0x7F30,},
+ {0xDE88, 0xB53F,},
+ {0xDE8A, 0x71CF,},
+ {0xDE8C, 0xFFFF,},
+ {0xDE8E, 0xC84C,},
+ {0xDE90, 0x91D3,},
+ {0xDE92, 0x108B,},
+ {0xDE94, 0x0081,},
+ {0xDE96, 0x2615,},
+ {0xDE98, 0x1380,},
+ {0xDE9A, 0x090F,},
+ {0xDE9C, 0x0C91,},
+ {0xDE9E, 0x0A8E,},
+ {0xDEA0, 0x05A0,},
+ {0xDEA2, 0xD906,},
+ {0xDEA4, 0x7E10,},
+ {0xDEA6, 0x2615,},
+ {0xDEA8, 0x1380,},
+ {0xDEAA, 0x0A82,},
+ {0xDEAC, 0x05A0,},
+ {0xDEAE, 0xD960,},
+ {0xDEB0, 0x790F,},
+ {0xDEB2, 0x090D,},
+ {0xDEB4, 0x0133,},
+ {0xDEB6, 0xAD0C,},
+ {0xDEB8, 0xD904,},
+ {0xDEBA, 0xAD2C,},
+ {0xDEBC, 0x79EC,},
+ {0xDEBE, 0x2941,},
+ {0xDEC0, 0x7402,},
+ {0xDEC2, 0x71CF,},
+ {0xDEC4, 0xFF00,},
+ {0xDEC6, 0x2184,},
+ {0xDEC8, 0xB142,},
+ {0xDECA, 0x1906,},
+ {0xDECC, 0x0E44,},
+ {0xDECE, 0xFFDE,},
+ {0xDED0, 0x70C9,},
+ {0xDED2, 0x0A5A,},
+ {0xDED4, 0x05A0,},
+ {0xDED6, 0x8D2C,},
+ {0xDED8, 0xAD0B,},
+ {0xDEDA, 0xD800,},
+ {0xDEDC, 0xAD01,},
+ {0xDEDE, 0x0219,},
+ {0xDEE0, 0x05A0,},
+ {0xDEE2, 0xA513,},
+ {0xDEE4, 0xC0F1,},
+ {0xDEE6, 0x71CF,},
+ {0xDEE8, 0xFFFF,},
+ {0xDEEA, 0xC644,},
+ {0xDEEC, 0xA91B,},
+ {0xDEEE, 0xD902,},
+ {0xDEF0, 0x70CF,},
+ {0xDEF2, 0xFFFF,},
+ {0xDEF4, 0xC84C,},
+ {0xDEF6, 0x093E,},
+ {0xDEF8, 0x03A0,},
+ {0xDEFA, 0xA826,},
+ {0xDEFC, 0xFFDC,},
+ {0xDEFE, 0xF1B5,},
+ {0xDF00, 0xC0F1,},
+ {0xDF02, 0x09EA,},
+ {0xDF04, 0x0580,},
+ {0xDF06, 0x75CF,},
+ {0xDF08, 0xFFFF,},
+ {0xDF0A, 0xE058,},
+ {0xDF0C, 0x1540,},
+ {0xDF0E, 0x1080,},
+ {0xDF10, 0x08A7,},
+ {0xDF12, 0x0010,},
+ {0xDF14, 0x8D00,},
+ {0xDF16, 0x0813,},
+ {0xDF18, 0x009E,},
+ {0xDF1A, 0x1540,},
+ {0xDF1C, 0x1081,},
+ {0xDF1E, 0xE181,},
+ {0xDF20, 0x20CA,},
+ {0xDF22, 0x00A1,},
+ {0xDF24, 0xF24B,},
+ {0xDF26, 0x1540,},
+ {0xDF28, 0x1081,},
+ {0xDF2A, 0x090F,},
+ {0xDF2C, 0x0050,},
+ {0xDF2E, 0x1540,},
+ {0xDF30, 0x1081,},
+ {0xDF32, 0x0927,},
+ {0xDF34, 0x0091,},
+ {0xDF36, 0x1550,},
+ {0xDF38, 0x1081,},
+ {0xDF3A, 0xDE00,},
+ {0xDF3C, 0xAD2A,},
+ {0xDF3E, 0x1D50,},
+ {0xDF40, 0x1382,},
+ {0xDF42, 0x1552,},
+ {0xDF44, 0x1101,},
+ {0xDF46, 0x1D52,},
+ {0xDF48, 0x1384,},
+ {0xDF4A, 0xB524,},
+ {0xDF4C, 0x082D,},
+ {0xDF4E, 0x015F,},
+ {0xDF50, 0xFF55,},
+ {0xDF52, 0xD803,},
+ {0xDF54, 0xF033,},
+ {0xDF56, 0x1540,},
+ {0xDF58, 0x1081,},
+ {0xDF5A, 0x0967,},
+ {0xDF5C, 0x00D1,},
+ {0xDF5E, 0x1550,},
+ {0xDF60, 0x1081,},
+ {0xDF62, 0xDE00,},
+ {0xDF64, 0xAD2A,},
+ {0xDF66, 0x1D50,},
+ {0xDF68, 0x1382,},
+ {0xDF6A, 0x1552,},
+ {0xDF6C, 0x1101,},
+ {0xDF6E, 0x1D52,},
+ {0xDF70, 0x1384,},
+ {0xDF72, 0xB524,},
+ {0xDF74, 0x0811,},
+ {0xDF76, 0x019E,},
+ {0xDF78, 0xB8A0,},
+ {0xDF7A, 0xAD00,},
+ {0xDF7C, 0xFF47,},
+ {0xDF7E, 0x1D40,},
+ {0xDF80, 0x1382,},
+ {0xDF82, 0xF01F,},
+ {0xDF84, 0xFF5A,},
+ {0xDF86, 0x8D01,},
+ {0xDF88, 0x8D40,},
+ {0xDF8A, 0xE812,},
+ {0xDF8C, 0x71CF,},
+ {0xDF8E, 0xFFFF,},
+ {0xDF90, 0xC644,},
+ {0xDF92, 0x893B,},
+ {0xDF94, 0x7030,},
+ {0xDF96, 0x22D1,},
+ {0xDF98, 0x8062,},
+ {0xDF9A, 0xF20A,},
+ {0xDF9C, 0x0A0F,},
+ {0xDF9E, 0x009E,},
+ {0xDFA0, 0x71CF,},
+ {0xDFA2, 0xFFFF,},
+ {0xDFA4, 0xC84C,},
+ {0xDFA6, 0x893B,},
+ {0xDFA8, 0xE902,},
+ {0xDFAA, 0xFFCF,},
+ {0xDFAC, 0x8D00,},
+ {0xDFAE, 0xB8E7,},
+ {0xDFB0, 0x26CA,},
+ {0xDFB2, 0x1022,},
+ {0xDFB4, 0xF5E2,},
+ {0xDFB6, 0xFF3C,},
+ {0xDFB8, 0xD801,},
+ {0xDFBA, 0x1D40,},
+ {0xDFBC, 0x1002,},
+ {0xDFBE, 0x0141,},
+ {0xDFC0, 0x0580,},
+ {0xDFC2, 0x78E0,},
+ {0xDFC4, 0xC0F1,},
+ {0xDFC6, 0xC5E1,},
+ {0xDFC8, 0xFF34,},
+ {0xDFCA, 0xDD00,},
+ {0xDFCC, 0x70CF,},
+ {0xDFCE, 0xFFFF,},
+ {0xDFD0, 0xE090,},
+ {0xDFD2, 0xA8A8,},
+ {0xDFD4, 0xD800,},
+ {0xDFD6, 0xB893,},
+ {0xDFD8, 0x0C8A,},
+ {0xDFDA, 0x0460,},
+ {0xDFDC, 0xD901,},
+ {0xDFDE, 0x71CF,},
+ {0xDFE0, 0xFFFF,},
+ {0xDFE2, 0xDC10,},
+ {0xDFE4, 0xD813,},
+ {0xDFE6, 0x0B96,},
+ {0xDFE8, 0x0460,},
+ {0xDFEA, 0x72A9,},
+ {0xDFEC, 0x0119,},
+ {0xDFEE, 0x0580,},
+ {0xDFF0, 0xC0F1,},
+ {0xDFF2, 0x71CF,},
+ {0xDFF4, 0x0000,},
+ {0xDFF6, 0x5BAE,},
+ {0xDFF8, 0x7940,},
+ {0xDFFA, 0xFF9D,},
+ {0xDFFC, 0xF135,},
+ {0xDFFE, 0x78E0,},
+ {0xE000, 0xC0F1,},
+ {0xE002, 0x70CF,},
+ {0xE004, 0x0000,},
+ {0xE006, 0x5CBA,},
+ {0xE008, 0x7840,},
+ {0xE00A, 0x70CF,},
+ {0xE00C, 0xFFFF,},
+ {0xE00E, 0xE058,},
+ {0xE010, 0x8800,},
+ {0xE012, 0x0815,},
+ {0xE014, 0x001E,},
+ {0xE016, 0x70CF,},
+ {0xE018, 0xFFFF,},
+ {0xE01A, 0xC84C,},
+ {0xE01C, 0x881A,},
+ {0xE01E, 0xE080,},
+ {0xE020, 0x0EE0,},
+ {0xE022, 0xFFC1,},
+ {0xE024, 0xF121,},
+ {0xE026, 0x78E0,},
+ {0xE028, 0xC0F1,},
+ {0xE02A, 0xD900,},
+ {0xE02C, 0xF009,},
+ {0xE02E, 0x70CF,},
+ {0xE030, 0xFFFF,},
+ {0xE032, 0xE0AC,},
+ {0xE034, 0x7835,},
+ {0xE036, 0x8041,},
+ {0xE038, 0x8000,},
+ {0xE03A, 0xE102,},
+ {0xE03C, 0xA040,},
+ {0xE03E, 0x09F3,},
+ {0xE040, 0x8114,},
+ {0xE042, 0x71CF,},
+ {0xE044, 0xFFFF,},
+ {0xE046, 0xE058,},
+ {0xE048, 0x70CF,},
+ {0xE04A, 0xFFFF,},
+ {0xE04C, 0xC594,},
+ {0xE04E, 0xB030,},
+ {0xE050, 0xFFDD,},
+ {0xE052, 0xD800,},
+ {0xE054, 0xF109,},
+ {0xE056, 0x0000,},
+ {0xE058, 0x0300,},
+ {0xE05A, 0x0204,},
+ {0xE05C, 0x0700,},
+ {0xE05E, 0x0000,},
+ {0xE060, 0x0000,},
+ {0xE062, 0x0000,},
+ {0xE064, 0x0000,},
+ {0xE066, 0x0000,},
+ {0xE068, 0x0000,},
+ {0xE06A, 0x0000,},
+ {0xE06C, 0x0000,},
+ {0xE06E, 0x0000,},
+ {0xE070, 0x0000,},
+ {0xE072, 0x0000,},
+ {0xE074, 0x0000,},
+ {0xE076, 0x0000,},
+ {0xE078, 0x0000,},
+ {0xE07A, 0x0000,},
+ {0xE07C, 0x0000,},
+ {0xE07E, 0x0000,},
+ {0xE080, 0x0000,},
+ {0xE082, 0x0000,},
+ {0xE084, 0x0000,},
+ {0xE086, 0x0000,},
+ {0xE088, 0x0000,},
+ {0xE08A, 0x0000,},
+ {0xE08C, 0x0000,},
+ {0xE08E, 0x0000,},
+ {0xE090, 0x0000,},
+ {0xE092, 0x0000,},
+ {0xE094, 0x0000,},
+ {0xE096, 0x0000,},
+ {0xE098, 0x0000,},
+ {0xE09A, 0x0000,},
+ {0xE09C, 0x0000,},
+ {0xE09E, 0x0000,},
+ {0xE0A0, 0x0000,},
+ {0xE0A2, 0x0000,},
+ {0xE0A4, 0x0000,},
+ {0xE0A6, 0x0000,},
+ {0xE0A8, 0x0000,},
+ {0xE0AA, 0x0000,},
+ {0xE0AC, 0xFFFF,},
+ {0xE0AE, 0xCB68,},
+ {0xE0B0, 0xFFFF,},
+ {0xE0B2, 0xDFF0,},
+ {0xE0B4, 0xFFFF,},
+ {0xE0B6, 0xCB6C,},
+ {0xE0B8, 0xFFFF,},
+ {0xE0BA, 0xE000,},
+ {0x098E, 0x0000,},
+
+ /*MIPI setting for SOC1040*/
+ {0x3C5A, 0x0009,},
+ {0x3C44, 0x0080,},/*MIPI_CUSTOM_SHORT_PKT*/
+
+ /*[Tuning_settings]*/
+
+ /*[CCM]*/
+ {0xC892, 0x0267,},/*CAM_AWB_CCM_L_0*/
+ {0xC894, 0xFF1A,},/*CAM_AWB_CCM_L_1*/
+ {0xC896, 0xFFB3,},/*CAM_AWB_CCM_L_2*/
+ {0xC898, 0xFF80,},/*CAM_AWB_CCM_L_3*/
+ {0xC89A, 0x0166,},/*CAM_AWB_CCM_L_4*/
+ {0xC89C, 0x0003,},/*CAM_AWB_CCM_L_5*/
+ {0xC89E, 0xFF9A,},/*CAM_AWB_CCM_L_6*/
+ {0xC8A0, 0xFEB4,},/*CAM_AWB_CCM_L_7*/
+ {0xC8A2, 0x024D,},/*CAM_AWB_CCM_L_8*/
+ {0xC8A4, 0x01BF,},/*CAM_AWB_CCM_M_0*/
+ {0xC8A6, 0xFF01,},/*CAM_AWB_CCM_M_1*/
+ {0xC8A8, 0xFFF3,},/*CAM_AWB_CCM_M_2*/
+ {0xC8AA, 0xFF75,},/*CAM_AWB_CCM_M_3*/
+ {0xC8AC, 0x0198,},/*CAM_AWB_CCM_M_4*/
+ {0xC8AE, 0xFFFD,},/*CAM_AWB_CCM_M_5*/
+ {0xC8B0, 0xFF9A,},/*CAM_AWB_CCM_M_6*/
+ {0xC8B2, 0xFEE7,},/*CAM_AWB_CCM_M_7*/
+ {0xC8B4, 0x02A8,},/*CAM_AWB_CCM_M_8*/
+ {0xC8B6, 0x01D9,},/*CAM_AWB_CCM_R_0*/
+ {0xC8B8, 0xFF26,},/*CAM_AWB_CCM_R_1*/
+ {0xC8BA, 0xFFF3,},/*CAM_AWB_CCM_R_2*/
+ {0xC8BC, 0xFFB3,},/*CAM_AWB_CCM_R_3*/
+ {0xC8BE, 0x0132,},/*CAM_AWB_CCM_R_4*/
+ {0xC8C0, 0xFFE8,},/*CAM_AWB_CCM_R_5*/
+ {0xC8C2, 0xFFDA,},/*CAM_AWB_CCM_R_6*/
+ {0xC8C4, 0xFECD,},/*CAM_AWB_CCM_R_7*/
+ {0xC8C6, 0x02C2,},/*CAM_AWB_CCM_R_8*/
+ {0xC8C8, 0x0075,},/*CAM_AWB_CCM_L_RG_GAIN*/
+ {0xC8CA, 0x011C,},/*CAM_AWB_CCM_L_BG_GAIN*/
+ {0xC8CC, 0x009A,},/*CAM_AWB_CCM_M_RG_GAIN*/
+ {0xC8CE, 0x0105,},/*CAM_AWB_CCM_M_BG_GAIN*/
+ {0xC8D0, 0x00A4,},/*CAM_AWB_CCM_R_RG_GAIN*/
+ {0xC8D2, 0x00AC,},/*CAM_AWB_CCM_R_BG_GAIN*/
+ {0xC8D4, 0x0A8C,},/*CAM_AWB_CCM_L_CTEMP*/
+ {0xC8D6, 0x0F0A,},/*CAM_AWB_CCM_M_CTEMP*/
+ {0xC8D8, 0x1964,},/*CAM_AWB_CCM_R_CTEMP*/
+
+ /*[AWB]*/
+ {0xC914, 0x0000,},/*CAM_STAT_AWB_CLIP_WINDOW_XSTART*/
+ {0xC916, 0x0000,},/*CAM_STAT_AWB_CLIP_WINDOW_YSTART*/
+ {0xC918, 0x04FF,},/*CAM_STAT_AWB_CLIP_WINDOW_XEND*/
+ {0xC91A, 0x02CF,},/*CAM_STAT_AWB_CLIP_WINDOW_YEND*/
+ {0xC904, 0x0033,},/*CAM_AWB_AWB_XSHIFT_PRE_ADJ*/
+ {0xC906, 0x0040,},/*CAM_AWB_AWB_YSHIFT_PRE_ADJ*/
+ {0xC8F2, 0x03, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_AWB_XSCALE*/
+ {0xC8F3, 0x02, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_AWB_YSCALE*/
+ {0xC906, 0x003C,},/*CAM_AWB_AWB_YSHIFT_PRE_ADJ*/
+ {0xC8F4, 0x0000,},/*CAM_AWB_AWB_WEIGHTS_0*/
+ {0xC8F6, 0x0000,},/*CAM_AWB_AWB_WEIGHTS_1*/
+ {0xC8F8, 0x0000,},/*CAM_AWB_AWB_WEIGHTS_2*/
+ {0xC8FA, 0xE724,},/*CAM_AWB_AWB_WEIGHTS_3*/
+ {0xC8FC, 0x1583,},/*CAM_AWB_AWB_WEIGHTS_4*/
+ {0xC8FE, 0x2045,},/*CAM_AWB_AWB_WEIGHTS_5*/
+ {0xC900, 0x03FF,},/*CAM_AWB_AWB_WEIGHTS_6*/
+ {0xC902, 0x007C,},/*CAM_AWB_AWB_WEIGHTS_7*/
+ {0xC90C, 0x80, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_K_R_L*/
+ {0xC90D, 0x80, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_K_G_L*/
+ {0xC90E, 0x80, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_K_B_L*/
+ {0xC90F, 0x88, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_K_R_R*/
+ {0xC910, 0x80, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_K_G_R*/
+ {0xC911, 0x80, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AWB_K_B_R*/
+
+ /*[Step7-CPIPE_Preference]*/
+ {0xC926, 0x0020,},/*CAM_LL_START_BRIGHTNESS*/
+ {0xC928, 0x009A,},/*CAM_LL_STOP_BRIGHTNESS*/
+ {0xC946, 0x0070,},/*CAM_LL_START_GAIN_METRIC*/
+ {0xC948, 0x00F3,},/*CAM_LL_STOP_GAIN_METRIC*/
+ {0xC952, 0x0020,},/*CAM_LL_START_TARGET_LUMA_BM*/
+ {0xC954, 0x009A,},/*CAM_LL_STOP_TARGET_LUMA_BM*/
+ {0xC92A, 0x80, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_SATURATION*/
+ {0xC92B, 0x4B, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_END_SATURATION*/
+ {0xC92C, 0x00, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_DESATURATION*/
+ {0xC92D, 0xFF, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_END_DESATURATION*/
+ {0xC92E, 0x3C, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_DEMOSAIC*/
+ {0xC92F, 0x02, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_AP_GAIN*/
+ {0xC930, 0x06, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_AP_THRESH*/
+ {0xC931, 0x64, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_DEMOSAIC*/
+ {0xC932, 0x01, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_AP_GAIN*/
+ {0xC933, 0x0C, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_AP_THRESH*/
+ {0xC934, 0x3C, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_NR_RED*/
+ {0xC935, 0x3C, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_NR_GREEN*/
+ {0xC936, 0x3C, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_NR_BLUE*/
+ {0xC937, 0x0F, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_NR_THRESH*/
+ {0xC938, 0x64, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_NR_RED*/
+ {0xC939, 0x64, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_NR_GREEN*/
+ {0xC93A, 0x64, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_NR_BLUE*/
+ {0xC93B, 0x32, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_NR_THRESH*/
+ {0xC93C, 0x0020,},/*CAM_LL_START_CONTRAST_BM*/
+ {0xC93E, 0x009A,},/*CAM_LL_STOP_CONTRAST_BM*/
+ {0xC940, 0x00DC,},/*CAM_LL_GAMMA*/
+ /*CAM_LL_START_CONTRAST_GRADIENT*/
+ {0xC942, 0x38, MSM_CAMERA_I2C_BYTE_DATA},
+ /*CAM_LL_STOP_CONTRAST_GRADIENT*/
+ {0xC943, 0x30, MSM_CAMERA_I2C_BYTE_DATA},
+ {0xC944, 0x50, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_START_CONTRAST_LUMA*/
+ {0xC945, 0x19, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_STOP_CONTRAST_LUMA*/
+ {0xC94A, 0x0230,},/*CAM_LL_START_FADE_TO_BLACK_LUMA*/
+ {0xC94C, 0x0010,},/*CAM_LL_STOP_FADE_TO_BLACK_LUMA*/
+ {0xC94E, 0x01CD,},/*CAM_LL_CLUSTER_DC_TH_BM*/
+ {0xC950, 0x05, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_CLUSTER_DC_GATE*/
+ {0xC951, 0x40, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_LL_SUMMING_SENSITIVITY*/
+ /*CAM_AET_TARGET_AVERAGE_LUMA_DARK*/
+ {0xC87B, 0x1B, MSM_CAMERA_I2C_BYTE_DATA},
+ {0xC878, 0x0E, MSM_CAMERA_I2C_BYTE_DATA},/*CAM_AET_AEMODE*/
+ {0xC890, 0x0080,},/*CAM_AET_TARGET_GAIN*/
+ {0xC886, 0x0100,},/*CAM_AET_AE_MAX_VIRT_AGAIN*/
+ {0xC87C, 0x005A,},/*CAM_AET_BLACK_CLIPPING_TARGET*/
+ {0xB42A, 0x05, MSM_CAMERA_I2C_BYTE_DATA},/*CCM_DELTA_GAIN*/
+ /*AE_TRACK_AE_TRACKING_DAMPENING*/
+ {0xA80A, 0x20, MSM_CAMERA_I2C_BYTE_DATA},
+ {0x3C44, 0x0080,},
+ {0x3C40, 0x0004, MSM_CAMERA_I2C_UNSET_WORD_MASK},
+ {0xA802, 0x08, MSM_CAMERA_I2C_SET_BYTE_MASK},
+ {0xC908, 0x01, MSM_CAMERA_I2C_BYTE_DATA},
+ {0xC879, 0x01, MSM_CAMERA_I2C_BYTE_DATA},
+ {0xC909, 0x01, MSM_CAMERA_I2C_UNSET_BYTE_MASK},
+ {0xA80A, 0x18, MSM_CAMERA_I2C_BYTE_DATA},
+ {0xA80B, 0x18, MSM_CAMERA_I2C_BYTE_DATA},
+ {0xAC16, 0x18, MSM_CAMERA_I2C_BYTE_DATA},
+ {0xC878, 0x08, MSM_CAMERA_I2C_SET_BYTE_MASK},
+ {0xBC02, 0x08, MSM_CAMERA_I2C_UNSET_BYTE_MASK},
+};
+
+static struct v4l2_subdev_info mt9m114_subdev_info[] = {
+ {
+ .code = V4L2_MBUS_FMT_YUYV8_2X8,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .fmt = 1,
+ .order = 0,
+ },
+ /* more can be supported, to be added later */
+};
+
+static struct msm_camera_i2c_reg_conf mt9m114_config_change_settings[] = {
+ {0xdc00, 0x28, MSM_CAMERA_I2C_BYTE_DATA, MSM_CAMERA_I2C_CMD_WRITE},
+ {MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_SET_STATE,
+ MSM_CAMERA_I2C_UNSET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+ {MT9M114_COMMAND_REGISTER, (MT9M114_COMMAND_REGISTER_OK |
+ MT9M114_COMMAND_REGISTER_SET_STATE), MSM_CAMERA_I2C_WORD_DATA,
+ MSM_CAMERA_I2C_CMD_WRITE},
+ {MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_SET_STATE,
+ MSM_CAMERA_I2C_UNSET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+ {0xDC01, 0x31, MSM_CAMERA_I2C_BYTE_DATA},
+};
+
+static void mt9m114_stop_stream(struct msm_sensor_ctrl_t *s_ctrl) {}
+
+static struct msm_camera_i2c_conf_array mt9m114_init_conf[] = {
+ {mt9m114_recommend_settings,
+ ARRAY_SIZE(mt9m114_recommend_settings), 0, MSM_CAMERA_I2C_WORD_DATA},
+ {mt9m114_config_change_settings,
+ ARRAY_SIZE(mt9m114_config_change_settings),
+ 0, MSM_CAMERA_I2C_WORD_DATA},
+};
+
+static struct msm_camera_i2c_conf_array mt9m114_confs[] = {
+ {mt9m114_720p_settings,
+ ARRAY_SIZE(mt9m114_720p_settings), 0, MSM_CAMERA_I2C_WORD_DATA},
+};
+
+static struct msm_camera_i2c_reg_conf mt9m114_saturation[][1] = {
+ {{0xCC12, 0x00},},
+ {{0xCC12, 0x1A},},
+ {{0xCC12, 0x34},},
+ {{0xCC12, 0x4E},},
+ {{0xCC12, 0x68},},
+ {{0xCC12, 0x80},},
+ {{0xCC12, 0x9A},},
+ {{0xCC12, 0xB4},},
+ {{0xCC12, 0xCE},},
+ {{0xCC12, 0xE8},},
+ {{0xCC12, 0xFF},},
+};
+
+static struct msm_camera_i2c_reg_conf mt9m114_refresh[] = {
+ {MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_REFRESH,
+ MSM_CAMERA_I2C_UNSET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+ {MT9M114_COMMAND_REGISTER, (MT9M114_COMMAND_REGISTER_OK |
+ MT9M114_COMMAND_REGISTER_REFRESH), MSM_CAMERA_I2C_WORD_DATA,
+ MSM_CAMERA_I2C_CMD_WRITE},
+ {MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_REFRESH,
+ MSM_CAMERA_I2C_UNSET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+ {MT9M114_COMMAND_REGISTER, MT9M114_COMMAND_REGISTER_OK,
+ MSM_CAMERA_I2C_SET_WORD_MASK, MSM_CAMERA_I2C_CMD_POLL},
+};
+
+static struct msm_camera_i2c_conf_array mt9m114_saturation_confs[][2] = {
+ {{mt9m114_saturation[0],
+ ARRAY_SIZE(mt9m114_saturation[0]), 0, MSM_CAMERA_I2C_WORD_DATA},
+ {mt9m114_refresh,
+ ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+ {{mt9m114_saturation[1],
+ ARRAY_SIZE(mt9m114_saturation[1]), 0, MSM_CAMERA_I2C_WORD_DATA},
+ {mt9m114_refresh,
+ ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+ {{mt9m114_saturation[2],
+ ARRAY_SIZE(mt9m114_saturation[2]), 0, MSM_CAMERA_I2C_WORD_DATA},
+ {mt9m114_refresh,
+ ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+ {{mt9m114_saturation[3],
+ ARRAY_SIZE(mt9m114_saturation[3]), 0, MSM_CAMERA_I2C_WORD_DATA},
+ {mt9m114_refresh,
+ ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+ {{mt9m114_saturation[4],
+ ARRAY_SIZE(mt9m114_saturation[4]), 0, MSM_CAMERA_I2C_WORD_DATA},
+ {mt9m114_refresh,
+ ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+ {{mt9m114_saturation[5],
+ ARRAY_SIZE(mt9m114_saturation[5]), 0, MSM_CAMERA_I2C_WORD_DATA},
+ {mt9m114_refresh,
+ ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+ {{mt9m114_saturation[6],
+ ARRAY_SIZE(mt9m114_saturation[6]), 0, MSM_CAMERA_I2C_WORD_DATA},
+ {mt9m114_refresh,
+ ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+ {{mt9m114_saturation[7],
+ ARRAY_SIZE(mt9m114_saturation[7]), 0, MSM_CAMERA_I2C_WORD_DATA},
+ {mt9m114_refresh,
+ ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+ {{mt9m114_saturation[8],
+ ARRAY_SIZE(mt9m114_saturation[8]), 0, MSM_CAMERA_I2C_WORD_DATA},
+ {mt9m114_refresh,
+ ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+ {{mt9m114_saturation[9],
+ ARRAY_SIZE(mt9m114_saturation[9]), 0, MSM_CAMERA_I2C_WORD_DATA},
+ {mt9m114_refresh,
+ ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+ {{mt9m114_saturation[10],
+ ARRAY_SIZE(mt9m114_saturation[10]),
+ 0, MSM_CAMERA_I2C_WORD_DATA},
+ {mt9m114_refresh,
+ ARRAY_SIZE(mt9m114_refresh), 0, MSM_CAMERA_I2C_WORD_DATA},},
+};
+
+static int mt9m114_saturation_enum_map[] = {
+ MSM_V4L2_SATURATION_L0,
+ MSM_V4L2_SATURATION_L1,
+ MSM_V4L2_SATURATION_L2,
+ MSM_V4L2_SATURATION_L3,
+ MSM_V4L2_SATURATION_L4,
+ MSM_V4L2_SATURATION_L5,
+ MSM_V4L2_SATURATION_L6,
+ MSM_V4L2_SATURATION_L7,
+ MSM_V4L2_SATURATION_L8,
+ MSM_V4L2_SATURATION_L9,
+ MSM_V4L2_SATURATION_L10,
+};
+
+static struct msm_camera_i2c_enum_conf_array mt9m114_saturation_enum_confs = {
+ .conf = &mt9m114_saturation_confs[0][0],
+ .conf_enum = mt9m114_saturation_enum_map,
+ .num_enum = ARRAY_SIZE(mt9m114_saturation_enum_map),
+ .num_index = ARRAY_SIZE(mt9m114_saturation_confs),
+ .num_conf = ARRAY_SIZE(mt9m114_saturation_confs[0]),
+ .data_type = MSM_CAMERA_I2C_WORD_DATA,
+};
+
+struct msm_sensor_v4l2_ctrl_info_t mt9m114_v4l2_ctrl_info[] = {
+ {
+ .ctrl_id = V4L2_CID_SATURATION,
+ .min = MSM_V4L2_SATURATION_L0,
+ .max = MSM_V4L2_SATURATION_L10,
+ .step = 1,
+ .enum_cfg_settings = &mt9m114_saturation_enum_confs,
+ .s_v4l2_ctrl = msm_sensor_s_ctrl_by_enum,
+ },
+};
+
+static struct msm_sensor_output_info_t mt9m114_dimensions[] = {
+ {
+ .x_output = 0x500,
+ .y_output = 0x2D0,
+ .line_length_pclk = 0x500,
+ .frame_length_lines = 0x2D0,
+ .vt_pixel_clk = 48000000,
+ .op_pixel_clk = 128000000,
+ .binning_factor = 1,
+ },
+};
+
+static struct msm_camera_csid_vc_cfg mt9m114_cid_cfg[] = {
+ {0, CSI_YUV422_8, CSI_DECODE_8BIT},
+ {1, CSI_EMBED_DATA, CSI_DECODE_8BIT},
+};
+
+static struct msm_camera_csi2_params mt9m114_csi_params = {
+ .csid_params = {
+ .lane_assign = 0xe4,
+ .lane_cnt = 1,
+ .lut_params = {
+ .num_cid = 2,
+ .vc_cfg = mt9m114_cid_cfg,
+ },
+ },
+ .csiphy_params = {
+ .lane_cnt = 1,
+ .settle_cnt = 0x14,
+ },
+};
+
+static struct msm_camera_csi2_params *mt9m114_csi_params_array[] = {
+ &mt9m114_csi_params,
+ &mt9m114_csi_params,
+};
+
+static struct msm_sensor_output_reg_addr_t mt9m114_reg_addr = {
+ .x_output = 0xC868,
+ .y_output = 0xC86A,
+ .line_length_pclk = 0xC868,
+ .frame_length_lines = 0xC86A,
+};
+
+static struct msm_sensor_id_info_t mt9m114_id_info = {
+ .sensor_id_reg_addr = 0x0,
+ .sensor_id = 0x2481,
+};
+
+static int mt9m114_sensor_config(void __user *argp)
+{
+ return msm_sensor_config(&mt9m114_s_ctrl, argp);
+}
+
+static int mt9m114_sensor_open_init(const struct msm_camera_sensor_info *data)
+{
+ return msm_sensor_open_init(&mt9m114_s_ctrl, data);
+}
+
+static int mt9m114_sensor_release(void)
+{
+ return msm_sensor_release(&mt9m114_s_ctrl);
+}
+
+static const struct i2c_device_id mt9m114_i2c_id[] = {
+ {SENSOR_NAME, (kernel_ulong_t)&mt9m114_s_ctrl},
+ { }
+};
+
+static struct i2c_driver mt9m114_i2c_driver = {
+ .id_table = mt9m114_i2c_id,
+ .probe = msm_sensor_i2c_probe,
+ .driver = {
+ .name = SENSOR_NAME,
+ },
+};
+
+static struct msm_camera_i2c_client mt9m114_sensor_i2c_client = {
+ .addr_type = MSM_CAMERA_I2C_WORD_ADDR,
+};
+
+static int mt9m114_sensor_v4l2_probe(const struct msm_camera_sensor_info *info,
+ struct v4l2_subdev *sdev, struct msm_sensor_ctrl *s)
+{
+ return msm_sensor_v4l2_probe(&mt9m114_s_ctrl, info, sdev, s);
+}
+
+static int mt9m114_probe(struct platform_device *pdev)
+{
+ return msm_sensor_register(pdev, mt9m114_sensor_v4l2_probe);
+}
+
+struct platform_driver mt9m114_driver = {
+ .probe = mt9m114_probe,
+ .driver = {
+ .name = PLATFORM_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init msm_sensor_init_module(void)
+{
+ return platform_driver_register(&mt9m114_driver);
+}
+
+static struct v4l2_subdev_core_ops mt9m114_subdev_core_ops = {
+ .s_ctrl = msm_sensor_v4l2_s_ctrl,
+ .queryctrl = msm_sensor_v4l2_query_ctrl,
+};
+
+static struct v4l2_subdev_video_ops mt9m114_subdev_video_ops = {
+ .enum_mbus_fmt = msm_sensor_v4l2_enum_fmt,
+};
+
+static struct v4l2_subdev_ops mt9m114_subdev_ops = {
+ .core = &mt9m114_subdev_core_ops,
+ .video = &mt9m114_subdev_video_ops,
+};
+
+static struct msm_sensor_fn_t mt9m114_func_tbl = {
+ .sensor_start_stream = msm_sensor_start_stream,
+ .sensor_stop_stream = mt9m114_stop_stream,
+ .sensor_setting = msm_sensor_setting,
+ .sensor_set_sensor_mode = msm_sensor_set_sensor_mode,
+ .sensor_mode_init = msm_sensor_mode_init,
+ .sensor_get_output_info = msm_sensor_get_output_info,
+ .sensor_config = mt9m114_sensor_config,
+ .sensor_open_init = mt9m114_sensor_open_init,
+ .sensor_release = mt9m114_sensor_release,
+ .sensor_power_up = msm_sensor_power_up,
+ .sensor_power_down = msm_sensor_power_down,
+ .sensor_probe = msm_sensor_probe,
+};
+
+static struct msm_sensor_reg_t mt9m114_regs = {
+ .default_data_type = MSM_CAMERA_I2C_BYTE_DATA,
+ .start_stream_conf = mt9m114_config_change_settings,
+ .start_stream_conf_size = ARRAY_SIZE(mt9m114_config_change_settings),
+ .init_settings = &mt9m114_init_conf[0],
+ .init_size = ARRAY_SIZE(mt9m114_init_conf),
+ .mode_settings = &mt9m114_confs[0],
+ .output_settings = &mt9m114_dimensions[0],
+ .num_conf = ARRAY_SIZE(mt9m114_confs),
+};
+
+static struct msm_sensor_ctrl_t mt9m114_s_ctrl = {
+ .msm_sensor_reg = &mt9m114_regs,
+ .msm_sensor_v4l2_ctrl_info = mt9m114_v4l2_ctrl_info,
+ .num_v4l2_ctrl = ARRAY_SIZE(mt9m114_v4l2_ctrl_info),
+ .sensor_i2c_client = &mt9m114_sensor_i2c_client,
+ .sensor_i2c_addr = 0x90,
+ .sensor_output_reg_addr = &mt9m114_reg_addr,
+ .sensor_id_info = &mt9m114_id_info,
+ .cam_mode = MSM_SENSOR_MODE_INVALID,
+ .csi_params = &mt9m114_csi_params_array[0],
+ .msm_sensor_mutex = &mt9m114_mut,
+ .sensor_i2c_driver = &mt9m114_i2c_driver,
+ .sensor_v4l2_subdev_info = mt9m114_subdev_info,
+ .sensor_v4l2_subdev_info_size = ARRAY_SIZE(mt9m114_subdev_info),
+ .sensor_v4l2_subdev_ops = &mt9m114_subdev_ops,
+ .func_tbl = &mt9m114_func_tbl,
+};
+
+module_init(msm_sensor_init_module);
+MODULE_DESCRIPTION("Aptina 1.26MP YUV sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/pm8xxx-misc.c b/drivers/mfd/pm8xxx-misc.c
index a73a695..7314c7e 100644
--- a/drivers/mfd/pm8xxx-misc.c
+++ b/drivers/mfd/pm8xxx-misc.c
@@ -18,6 +18,7 @@
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
+#include <linux/delay.h>
#include <linux/mfd/pm8xxx/core.h>
#include <linux/mfd/pm8xxx/misc.h>
@@ -131,6 +132,10 @@
#define UART_PATH_SEL_MASK 0x60
#define UART_PATH_SEL_SHIFT 0x5
+/* Shutdown/restart delays to allow for LDO 7/dVdd regulator load settling. */
+#define PM8901_DELAY_AFTER_REG_DISABLE_MS 4
+#define PM8901_DELAY_BEFORE_SHUTDOWN_MS 8
+
struct pm8xxx_misc_chip {
struct list_head link;
struct pm8xxx_misc_platform_data pdata;
@@ -420,10 +425,12 @@
"rc=%d\n", rc);
goto read_write_err;
}
+ mdelay(PM8901_DELAY_AFTER_REG_DISABLE_MS);
}
}
read_write_err:
+ mdelay(PM8901_DELAY_BEFORE_SHUTDOWN_MS);
return rc;
}
diff --git a/drivers/mfd/pmic8058.c b/drivers/mfd/pmic8058.c
index ae721e4..814b59c 100644
--- a/drivers/mfd/pmic8058.c
+++ b/drivers/mfd/pmic8058.c
@@ -42,15 +42,6 @@
#define REG_TEMP_ALRM_CTRL 0x1B
#define REG_TEMP_ALRM_PWM 0x9B
-/* PON CNTL 1 register */
-#define SSBI_REG_ADDR_PON_CNTL_1 0x01C
-
-#define PM8058_PON_PUP_MASK 0xF0
-
-#define PM8058_PON_WD_EN_MASK 0x08
-#define PM8058_PON_WD_EN_RESET 0x08
-#define PM8058_PON_WD_EN_PWR_OFF 0x00
-
/* PON CNTL 4 register */
#define SSBI_REG_ADDR_PON_CNTL_4 0x98
#define PM8058_PON_RESET_EN_MASK 0x01
@@ -59,60 +50,6 @@
#define SSBI_REG_ADDR_PON_CNTL_5 0x7B
#define PM8058_HARD_RESET_EN_MASK 0x08
-/* Regulator master enable addresses */
-#define SSBI_REG_ADDR_VREG_EN_MSM 0x018
-#define SSBI_REG_ADDR_VREG_EN_GRP_5_4 0x1C8
-
-/* Regulator control registers for shutdown/reset */
-#define SSBI_REG_ADDR_S0_CTRL 0x004
-#define SSBI_REG_ADDR_S1_CTRL 0x005
-#define SSBI_REG_ADDR_S3_CTRL 0x111
-#define SSBI_REG_ADDR_L21_CTRL 0x120
-#define SSBI_REG_ADDR_L22_CTRL 0x121
-
-#define REGULATOR_ENABLE_MASK 0x80
-#define REGULATOR_ENABLE 0x80
-#define REGULATOR_DISABLE 0x00
-#define REGULATOR_PULL_DOWN_MASK 0x40
-#define REGULATOR_PULL_DOWN_EN 0x40
-#define REGULATOR_PULL_DOWN_DIS 0x00
-
-/* Buck CTRL register */
-#define SMPS_LEGACY_VREF_SEL 0x20
-#define SMPS_LEGACY_VPROG_MASK 0x1F
-#define SMPS_ADVANCED_BAND_MASK 0xC0
-#define SMPS_ADVANCED_BAND_SHIFT 6
-#define SMPS_ADVANCED_VPROG_MASK 0x3F
-
-/* Buck TEST2 registers for shutdown/reset */
-#define SSBI_REG_ADDR_S0_TEST2 0x084
-#define SSBI_REG_ADDR_S1_TEST2 0x085
-#define SSBI_REG_ADDR_S3_TEST2 0x11A
-
-#define REGULATOR_BANK_WRITE 0x80
-#define REGULATOR_BANK_MASK 0x70
-#define REGULATOR_BANK_SHIFT 4
-#define REGULATOR_BANK_SEL(n) ((n) << REGULATOR_BANK_SHIFT)
-
-/* Buck TEST2 register bank 1 */
-#define SMPS_LEGACY_VLOW_SEL 0x01
-
-/* Buck TEST2 register bank 7 */
-#define SMPS_ADVANCED_MODE_MASK 0x02
-#define SMPS_ADVANCED_MODE 0x02
-#define SMPS_LEGACY_MODE 0x00
-
-/* SLEEP CNTL register */
-#define SSBI_REG_ADDR_SLEEP_CNTL 0x02B
-
-#define PM8058_SLEEP_SMPL_EN_MASK 0x04
-#define PM8058_SLEEP_SMPL_EN_RESET 0x04
-#define PM8058_SLEEP_SMPL_EN_PWR_OFF 0x00
-
-#define PM8058_SLEEP_SMPL_SEL_MASK 0x03
-#define PM8058_SLEEP_SMPL_SEL_MIN 0
-#define PM8058_SLEEP_SMPL_SEL_MAX 3
-
/* GP_TEST1 register */
#define SSBI_REG_ADDR_GP_TEST_1 0x07A
@@ -136,435 +73,8 @@
struct mfd_cell *mfd_regulators, *mfd_xo_buffers;
u8 revision;
-
- struct mutex pm_lock;
};
-static struct pm8058_chip *pmic_chip;
-
-static inline int
-ssbi_read(struct device *dev, u16 addr, u8 *buf, size_t len)
-{
- return msm_ssbi_read(dev->parent, addr, buf, len);
-}
-
-static inline int
-ssbi_write(struct device *dev, u16 addr, u8 *buf, size_t len)
-{
- return msm_ssbi_write(dev->parent, addr, buf, len);
-}
-
-static int pm8058_masked_write(u16 addr, u8 val, u8 mask)
-{
- int rc;
- u8 reg;
-
- if (pmic_chip == NULL)
- return -ENODEV;
-
- rc = ssbi_read(pmic_chip->dev, addr, ®, 1);
- if (rc) {
- pr_err("%s: ssbi_read(0x%03X) failed: rc=%d\n", __func__, addr,
- rc);
- goto done;
- }
-
- reg &= ~mask;
- reg |= val & mask;
-
- rc = ssbi_write(pmic_chip->dev, addr, ®, 1);
- if (rc)
- pr_err("%s: ssbi_write(0x%03X)=0x%02X failed: rc=%d\n",
- __func__, addr, reg, rc);
-done:
- return rc;
-}
-
-/**
- * pm8058_smpl_control - enables/disables SMPL detection
- * @enable: 0 = shutdown PMIC on power loss, 1 = reset PMIC on power loss
- *
- * This function enables or disables the Sudden Momentary Power Loss detection
- * module. If SMPL detection is enabled, then when a sufficiently long power
- * loss event occurs, the PMIC will automatically reset itself. If SMPL
- * detection is disabled, then the PMIC will shutdown when power loss occurs.
- *
- * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
- */
-int pm8058_smpl_control(int enable)
-{
- return pm8058_masked_write(SSBI_REG_ADDR_SLEEP_CNTL,
- (enable ? PM8058_SLEEP_SMPL_EN_RESET
- : PM8058_SLEEP_SMPL_EN_PWR_OFF),
- PM8058_SLEEP_SMPL_EN_MASK);
-}
-EXPORT_SYMBOL(pm8058_smpl_control);
-
-/**
- * pm8058_smpl_set_delay - sets the SMPL detection time delay
- * @delay: enum value corresponding to delay time
- *
- * This function sets the time delay of the SMPL detection module. If power
- * is reapplied within this interval, then the PMIC reset automatically. The
- * SMPL detection module must be enabled for this delay time to take effect.
- *
- * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
- */
-int pm8058_smpl_set_delay(enum pm8058_smpl_delay delay)
-{
- if (delay < PM8058_SLEEP_SMPL_SEL_MIN
- || delay > PM8058_SLEEP_SMPL_SEL_MAX) {
- pr_err("%s: invalid delay specified: %d\n", __func__, delay);
- return -EINVAL;
- }
-
- return pm8058_masked_write(SSBI_REG_ADDR_SLEEP_CNTL, delay,
- PM8058_SLEEP_SMPL_SEL_MASK);
-}
-EXPORT_SYMBOL(pm8058_smpl_set_delay);
-
-/**
- * pm8058_watchdog_reset_control - enables/disables watchdog reset detection
- * @enable: 0 = shutdown when PS_HOLD goes low, 1 = reset when PS_HOLD goes low
- *
- * This function enables or disables the PMIC watchdog reset detection feature.
- * If watchdog reset detection is enabled, then the PMIC will reset itself
- * when PS_HOLD goes low. If it is not enabled, then the PMIC will shutdown
- * when PS_HOLD goes low.
- *
- * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
- */
-int pm8058_watchdog_reset_control(int enable)
-{
- return pm8058_masked_write(SSBI_REG_ADDR_PON_CNTL_1,
- (enable ? PM8058_PON_WD_EN_RESET
- : PM8058_PON_WD_EN_PWR_OFF),
- PM8058_PON_WD_EN_MASK);
-}
-EXPORT_SYMBOL(pm8058_watchdog_reset_control);
-
-/*
- * Set an SMPS regulator to be disabled in its CTRL register, but enabled
- * in the master enable register. Also set it's pull down enable bit.
- * Take care to make sure that the output voltage doesn't change if switching
- * from advanced mode to legacy mode.
- */
-static int disable_smps_locally_set_pull_down(u16 ctrl_addr, u16 test2_addr,
- u16 master_enable_addr, u8 master_enable_bit)
-{
- int rc = 0;
- u8 vref_sel, vlow_sel, band, vprog, bank, reg;
-
- if (pmic_chip == NULL)
- return -ENODEV;
-
- bank = REGULATOR_BANK_SEL(7);
- rc = ssbi_write(pmic_chip->dev, test2_addr, &bank, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_write(0x%03X): rc=%d\n", __func__,
- test2_addr, rc);
- goto done;
- }
-
- rc = ssbi_read(pmic_chip->dev, test2_addr, ®, 1);
- if (rc) {
- pr_err("%s: FAIL pm8058_read(0x%03X): rc=%d\n",
- __func__, test2_addr, rc);
- goto done;
- }
-
- /* Check if in advanced mode. */
- if ((reg & SMPS_ADVANCED_MODE_MASK) == SMPS_ADVANCED_MODE) {
- /* Determine current output voltage. */
- rc = ssbi_read(pmic_chip->dev, ctrl_addr, ®, 1);
- if (rc) {
- pr_err("%s: FAIL pm8058_read(0x%03X): rc=%d\n",
- __func__, ctrl_addr, rc);
- goto done;
- }
-
- band = (reg & SMPS_ADVANCED_BAND_MASK)
- >> SMPS_ADVANCED_BAND_SHIFT;
- switch (band) {
- case 3:
- vref_sel = 0;
- vlow_sel = 0;
- break;
- case 2:
- vref_sel = SMPS_LEGACY_VREF_SEL;
- vlow_sel = 0;
- break;
- case 1:
- vref_sel = SMPS_LEGACY_VREF_SEL;
- vlow_sel = SMPS_LEGACY_VLOW_SEL;
- break;
- default:
- pr_err("%s: regulator already disabled\n", __func__);
- return -EPERM;
- }
- vprog = (reg & SMPS_ADVANCED_VPROG_MASK);
- /* Round up if fine step is in use. */
- vprog = (vprog + 1) >> 1;
- if (vprog > SMPS_LEGACY_VPROG_MASK)
- vprog = SMPS_LEGACY_VPROG_MASK;
-
- /* Set VLOW_SEL bit. */
- bank = REGULATOR_BANK_SEL(1);
- rc = ssbi_write(pmic_chip->dev, test2_addr, &bank, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_write(0x%03X): rc=%d\n",
- __func__, test2_addr, rc);
- goto done;
- }
- rc = pm8058_masked_write(test2_addr,
- REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(1)
- | vlow_sel,
- REGULATOR_BANK_WRITE | REGULATOR_BANK_MASK
- | SMPS_LEGACY_VLOW_SEL);
- if (rc)
- goto done;
-
- /* Switch to legacy mode */
- bank = REGULATOR_BANK_SEL(7);
- rc = ssbi_write(pmic_chip->dev, test2_addr, &bank, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_write(0x%03X): rc=%d\n", __func__,
- test2_addr, rc);
- goto done;
- }
- rc = pm8058_masked_write(test2_addr,
- REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(7)
- | SMPS_LEGACY_MODE,
- REGULATOR_BANK_WRITE | REGULATOR_BANK_MASK
- | SMPS_ADVANCED_MODE_MASK);
- if (rc)
- goto done;
-
- /* Enable locally, enable pull down, keep voltage the same. */
- rc = pm8058_masked_write(ctrl_addr,
- REGULATOR_ENABLE | REGULATOR_PULL_DOWN_EN
- | vref_sel | vprog,
- REGULATOR_ENABLE_MASK | REGULATOR_PULL_DOWN_MASK
- | SMPS_LEGACY_VREF_SEL | SMPS_LEGACY_VPROG_MASK);
- if (rc)
- goto done;
- }
-
- /* Enable in master control register. */
- rc = pm8058_masked_write(master_enable_addr, master_enable_bit,
- master_enable_bit);
- if (rc)
- goto done;
-
- /* Disable locally and enable pull down. */
- rc = pm8058_masked_write(ctrl_addr,
- REGULATOR_DISABLE | REGULATOR_PULL_DOWN_EN,
- REGULATOR_ENABLE_MASK | REGULATOR_PULL_DOWN_MASK);
-
-done:
- return rc;
-}
-
-static int disable_ldo_locally_set_pull_down(u16 ctrl_addr,
- u16 master_enable_addr, u8 master_enable_bit)
-{
- int rc;
-
- /* Enable LDO in master control register. */
- rc = pm8058_masked_write(master_enable_addr, master_enable_bit,
- master_enable_bit);
- if (rc)
- goto done;
-
- /* Disable LDO in CTRL register and set pull down */
- rc = pm8058_masked_write(ctrl_addr,
- REGULATOR_DISABLE | REGULATOR_PULL_DOWN_EN,
- REGULATOR_ENABLE_MASK | REGULATOR_PULL_DOWN_MASK);
-
-done:
- return rc;
-}
-
-int pm8058_reset_pwr_off(int reset)
-{
- int rc;
- u8 pon, ctrl, smpl;
-
- if (pmic_chip == NULL)
- return -ENODEV;
-
- /* When shutting down, enable active pulldowns on important rails. */
- if (!reset) {
- /* Disable SMPS's 0,1,3 locally and set pulldown enable bits. */
- disable_smps_locally_set_pull_down(SSBI_REG_ADDR_S0_CTRL,
- SSBI_REG_ADDR_S0_TEST2, SSBI_REG_ADDR_VREG_EN_MSM, BIT(7));
- disable_smps_locally_set_pull_down(SSBI_REG_ADDR_S1_CTRL,
- SSBI_REG_ADDR_S1_TEST2, SSBI_REG_ADDR_VREG_EN_MSM, BIT(6));
- disable_smps_locally_set_pull_down(SSBI_REG_ADDR_S3_CTRL,
- SSBI_REG_ADDR_S3_TEST2, SSBI_REG_ADDR_VREG_EN_GRP_5_4,
- BIT(7) | BIT(4));
- /* Disable LDO 21 locally and set pulldown enable bit. */
- disable_ldo_locally_set_pull_down(SSBI_REG_ADDR_L21_CTRL,
- SSBI_REG_ADDR_VREG_EN_GRP_5_4, BIT(1));
- }
-
- /* Set regulator L22 to 1.225V in high power mode. */
- rc = ssbi_read(pmic_chip->dev, SSBI_REG_ADDR_L22_CTRL, &ctrl, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n", __func__,
- SSBI_REG_ADDR_L22_CTRL, rc);
- goto get_out3;
- }
- /* Leave pull-down state intact. */
- ctrl &= 0x40;
- ctrl |= 0x93;
- rc = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_L22_CTRL, &ctrl, 1);
- if (rc)
- pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n", __func__,
- SSBI_REG_ADDR_L22_CTRL, ctrl, rc);
-
-get_out3:
- if (!reset) {
- /* Only modify the SLEEP_CNTL reg if shutdown is desired. */
- rc = ssbi_read(pmic_chip->dev, SSBI_REG_ADDR_SLEEP_CNTL,
- &smpl, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n",
- __func__, SSBI_REG_ADDR_SLEEP_CNTL, rc);
- goto get_out2;
- }
-
- smpl &= ~PM8058_SLEEP_SMPL_EN_MASK;
- smpl |= PM8058_SLEEP_SMPL_EN_PWR_OFF;
-
- rc = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_SLEEP_CNTL,
- &smpl, 1);
- if (rc)
- pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n",
- __func__, SSBI_REG_ADDR_SLEEP_CNTL, smpl, rc);
- }
-
-get_out2:
- rc = ssbi_read(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_1, &pon, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n",
- __func__, SSBI_REG_ADDR_PON_CNTL_1, rc);
- goto get_out;
- }
-
- pon &= ~PM8058_PON_WD_EN_MASK;
- pon |= reset ? PM8058_PON_WD_EN_RESET : PM8058_PON_WD_EN_PWR_OFF;
-
- /* Enable all pullups */
- pon |= PM8058_PON_PUP_MASK;
-
- rc = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_1, &pon, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n",
- __func__, SSBI_REG_ADDR_PON_CNTL_1, pon, rc);
- goto get_out;
- }
-
-get_out:
- return rc;
-}
-EXPORT_SYMBOL(pm8058_reset_pwr_off);
-
-/**
- * pm8058_stay_on - enables stay_on feature
- *
- * PMIC stay-on feature allows PMIC to ignore MSM PS_HOLD=low
- * signal so that some special functions like debugging could be
- * performed.
- *
- * This feature should not be used in any product release.
- *
- * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
- */
-int pm8058_stay_on(void)
-{
- u8 ctrl = 0x92;
- int rc;
-
- rc = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_GP_TEST_1, &ctrl, 1);
- pr_info("%s: set stay-on: rc = %d\n", __func__, rc);
- return rc;
-}
-EXPORT_SYMBOL(pm8058_stay_on);
-
-/*
- power on hard reset configuration
- config = DISABLE_HARD_RESET to disable hard reset
- = SHUTDOWN_ON_HARD_RESET to turn off the system on hard reset
- = RESTART_ON_HARD_RESET to restart the system on hard reset
- */
-int pm8058_hard_reset_config(enum pon_config config)
-{
- int rc, ret;
- u8 pon, pon_5;
-
- if (config >= MAX_PON_CONFIG)
- return -EINVAL;
-
- if (pmic_chip == NULL)
- return -ENODEV;
-
- mutex_lock(&pmic_chip->pm_lock);
-
- rc = ssbi_read(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_5, &pon, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n",
- __func__, SSBI_REG_ADDR_PON_CNTL_5, rc);
- mutex_unlock(&pmic_chip->pm_lock);
- return rc;
- }
-
- pon_5 = pon;
- (config != DISABLE_HARD_RESET) ? (pon |= PM8058_HARD_RESET_EN_MASK) :
- (pon &= ~PM8058_HARD_RESET_EN_MASK);
-
- rc = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_5, &pon, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n",
- __func__, SSBI_REG_ADDR_PON_CNTL_5, pon, rc);
- mutex_unlock(&pmic_chip->pm_lock);
- return rc;
- }
-
- if (config == DISABLE_HARD_RESET) {
- mutex_unlock(&pmic_chip->pm_lock);
- return 0;
- }
-
- rc = ssbi_read(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_4, &pon, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n",
- __func__, SSBI_REG_ADDR_PON_CNTL_4, rc);
- goto err_restore_pon_5;
- }
-
- (config == RESTART_ON_HARD_RESET) ? (pon |= PM8058_PON_RESET_EN_MASK) :
- (pon &= ~PM8058_PON_RESET_EN_MASK);
-
- rc = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_4, &pon, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n",
- __func__, SSBI_REG_ADDR_PON_CNTL_4, pon, rc);
- goto err_restore_pon_5;
- }
- mutex_unlock(&pmic_chip->pm_lock);
- return 0;
-
-err_restore_pon_5:
- ret = ssbi_write(pmic_chip->dev, SSBI_REG_ADDR_PON_CNTL_5, &pon_5, 1);
- if (ret)
- pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d\n",
- __func__, SSBI_REG_ADDR_PON_CNTL_5, pon, ret);
- mutex_unlock(&pmic_chip->pm_lock);
- return rc;
-}
-EXPORT_SYMBOL(pm8058_hard_reset_config);
-
static int pm8058_readb(const struct device *dev, u16 addr, u8 *val)
{
const struct pm8xxx_drvdata *pm8058_drvdata = dev_get_drvdata(dev);
@@ -1202,9 +712,6 @@
pm8058_drvdata.pm_chip_data = pmic;
platform_set_drvdata(pdev, &pm8058_drvdata);
- mutex_init(&pmic->pm_lock);
- pmic_chip = pmic;
-
/* Read PMIC chip revision */
rc = pm8058_readb(pmic->dev, PM8058_REG_REV, &pmic->revision);
if (rc)
@@ -1222,7 +729,7 @@
goto err;
}
- rc = pm8058_hard_reset_config(SHUTDOWN_ON_HARD_RESET);
+ rc = pm8xxx_hard_reset_config(PM8XXX_SHUTDOWN_ON_HARD_RESET);
if (rc < 0)
pr_err("%s: failed to config shutdown on hard reset: %d\n",
__func__, rc);
@@ -1249,7 +756,6 @@
mfd_remove_devices(pmic->dev);
if (pmic->irq_chip)
pm8xxx_irq_exit(pmic->irq_chip);
- mutex_destroy(&pmic->pm_lock);
kfree(pmic->mfd_regulators);
kfree(pmic);
}
diff --git a/drivers/mfd/pmic8901.c b/drivers/mfd/pmic8901.c
index 9e8786e..080a3e3 100644
--- a/drivers/mfd/pmic8901.c
+++ b/drivers/mfd/pmic8901.c
@@ -13,7 +13,6 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/delay.h>
#include <linux/mfd/core.h>
#include <linux/msm_ssbi.h>
#include <linux/mfd/pmic8901.h>
@@ -31,19 +30,6 @@
#define REG_TEMP_ALRM_CTRL 0x23
#define REG_TEMP_ALRM_PWM 0x24
-/* FTS regulator PMR registers */
-#define SSBI_REG_ADDR_S1_PMR (0xA7)
-#define SSBI_REG_ADDR_S2_PMR (0xA8)
-#define SSBI_REG_ADDR_S3_PMR (0xA9)
-#define SSBI_REG_ADDR_S4_PMR (0xAA)
-
-#define REGULATOR_PMR_STATE_MASK 0x60
-#define REGULATOR_PMR_STATE_OFF 0x20
-
-/* Shutdown/restart delays to allow for LDO 7/dVdd regulator load settling. */
-#define DELAY_AFTER_REG_DISABLE_MS 4
-#define DELAY_BEFORE_SHUTDOWN_MS 8
-
#define SINGLE_IRQ_RESOURCE(_name, _irq) \
{ \
.name = _name, \
@@ -60,63 +46,6 @@
u8 revision;
};
-static struct pm8901_chip *pmic_chip;
-
-static inline int
-ssbi_read(struct device *dev, u16 addr, u8 *buf, size_t len)
-{
- return msm_ssbi_read(dev->parent, addr, buf, len);
-}
-
-static inline int
-ssbi_write(struct device *dev, u16 addr, u8 *buf, size_t len)
-{
- return msm_ssbi_write(dev->parent, addr, buf, len);
-}
-
-int pm8901_reset_pwr_off(int reset)
-{
- int rc = 0, i;
- u8 pmr;
- u8 pmr_addr[4] = {
- SSBI_REG_ADDR_S2_PMR,
- SSBI_REG_ADDR_S3_PMR,
- SSBI_REG_ADDR_S4_PMR,
- SSBI_REG_ADDR_S1_PMR,
- };
-
- if (pmic_chip == NULL)
- return -ENODEV;
-
- /* Turn off regulators S1, S2, S3, S4 when shutting down. */
- if (!reset) {
- for (i = 0; i < 4; i++) {
- rc = ssbi_read(pmic_chip->dev, pmr_addr[i], &pmr, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_read(0x%x): rc=%d\n",
- __func__, pmr_addr[i], rc);
- goto get_out;
- }
-
- pmr &= ~REGULATOR_PMR_STATE_MASK;
- pmr |= REGULATOR_PMR_STATE_OFF;
-
- rc = ssbi_write(pmic_chip->dev, pmr_addr[i], &pmr, 1);
- if (rc) {
- pr_err("%s: FAIL ssbi_write(0x%x)=0x%x: rc=%d"
- "\n", __func__, pmr_addr[i], pmr, rc);
- goto get_out;
- }
- mdelay(DELAY_AFTER_REG_DISABLE_MS);
- }
- }
-
-get_out:
- mdelay(DELAY_BEFORE_SHUTDOWN_MS);
- return rc;
-}
-EXPORT_SYMBOL(pm8901_reset_pwr_off);
-
static int pm8901_readb(const struct device *dev, u16 addr, u8 *val)
{
const struct pm8xxx_drvdata *pm8901_drvdata = dev_get_drvdata(dev);
@@ -191,6 +120,11 @@
.pmic_get_revision = pm8901_get_revision,
};
+static struct mfd_cell misc_cell = {
+ .name = PM8XXX_MISC_DEV_NAME,
+ .id = 1,
+};
+
static struct mfd_cell debugfs_cell = {
.name = "pm8xxx-debug",
.id = 1,
@@ -315,6 +249,17 @@
goto bail;
}
+ if (pdata->misc_pdata) {
+ misc_cell.platform_data = pdata->misc_pdata;
+ misc_cell.pdata_size = sizeof(struct pm8xxx_misc_platform_data);
+ rc = mfd_add_devices(pmic->dev, 0, &misc_cell, 1, NULL,
+ irq_base);
+ if (rc) {
+ pr_err("Failed to add misc subdevice ret=%d\n", rc);
+ goto bail;
+ }
+ }
+
return rc;
bail:
@@ -346,7 +291,6 @@
pm8901_drvdata.pm_chip_data = pmic;
platform_set_drvdata(pdev, &pm8901_drvdata);
- pmic_chip = pmic;
/* Read PMIC chip revision */
rc = pm8901_readb(pmic->dev, PM8901_REG_REV, &pmic->revision);
diff --git a/drivers/mfd/wcd9310-core.c b/drivers/mfd/wcd9310-core.c
index aabc2cc..4b81e99 100644
--- a/drivers/mfd/wcd9310-core.c
+++ b/drivers/mfd/wcd9310-core.c
@@ -27,6 +27,7 @@
#define TABLA_SLIM_GLA_MAX_RETRIES 5
#define TABLA_REGISTER_START_OFFSET 0x800
+#define TABLA_SLIM_RW_MAX_TRIES 3
#define MAX_TABLA_DEVICE 4
#define TABLA_I2C_MODE 0x03
@@ -183,21 +184,25 @@
{
int ret;
struct slim_ele_access msg;
+ int slim_read_tries = TABLA_SLIM_RW_MAX_TRIES;
msg.start_offset = TABLA_REGISTER_START_OFFSET + reg;
msg.num_bytes = bytes;
msg.comp = NULL;
- mutex_lock(&tabla->xfer_lock);
- if (interface)
- ret = slim_request_val_element(tabla->slim_slave, &msg, dest,
- bytes);
- else
- ret = slim_request_val_element(tabla->slim, &msg, dest, bytes);
+ while (1) {
+ mutex_lock(&tabla->xfer_lock);
+ ret = slim_request_val_element(interface ?
+ tabla->slim_slave : tabla->slim,
+ &msg, dest, bytes);
+ mutex_unlock(&tabla->xfer_lock);
+ if (likely(ret == 0) || (--slim_read_tries == 0))
+ break;
+ usleep_range(5000, 5000);
+ }
if (ret)
- pr_err("%s: Error, Tabla read failed\n", __func__);
+ pr_err("%s: Error, Tabla read failed (%d)\n", __func__, ret);
- mutex_unlock(&tabla->xfer_lock);
return ret;
}
/* Interface specifies whether the write is to the interface or general
@@ -208,20 +213,25 @@
{
int ret;
struct slim_ele_access msg;
+ int slim_write_tries = TABLA_SLIM_RW_MAX_TRIES;
msg.start_offset = TABLA_REGISTER_START_OFFSET + reg;
msg.num_bytes = bytes;
msg.comp = NULL;
- mutex_lock(&tabla->xfer_lock);
- if (interface)
- ret = slim_change_val_element(tabla->slim_slave, &msg, src,
- bytes);
- else
- ret = slim_change_val_element(tabla->slim, &msg, src, bytes);
- if (ret)
- pr_err("%s: Error, Tabla write failed\n", __func__);
+ while (1) {
+ mutex_lock(&tabla->xfer_lock);
+ ret = slim_change_val_element(interface ?
+ tabla->slim_slave : tabla->slim,
+ &msg, src, bytes);
+ mutex_unlock(&tabla->xfer_lock);
+ if (likely(ret == 0) || (--slim_write_tries == 0))
+ break;
+ usleep_range(5000, 5000);
+ }
- mutex_unlock(&tabla->xfer_lock);
+ if (ret)
+ pr_err("%s: Error, Tabla write failed (%d)\n", __func__, ret);
+
return ret;
}
diff --git a/drivers/mfd/wcd9310-irq.c b/drivers/mfd/wcd9310-irq.c
index 9cc4761..fef1323 100644
--- a/drivers/mfd/wcd9310-irq.c
+++ b/drivers/mfd/wcd9310-irq.c
@@ -189,17 +189,37 @@
ret = request_threaded_irq(tabla->irq, NULL, tabla_irq_thread,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"tabla", tabla);
-
- if (ret != 0) {
+ if (ret != 0)
dev_err(tabla->dev, "Failed to request IRQ %d: %d\n",
tabla->irq, ret);
- return ret;
+ else {
+ ret = enable_irq_wake(tabla->irq);
+ if (ret == 0) {
+ ret = device_init_wakeup(tabla->dev, 1);
+ if (ret) {
+ dev_err(tabla->dev, "Failed to init device"
+ "wakeup : %d\n", ret);
+ disable_irq_wake(tabla->irq);
+ }
+ } else
+ dev_err(tabla->dev, "Failed to set wake interrupt on"
+ " IRQ %d: %d\n", tabla->irq, ret);
+ if (ret)
+ free_irq(tabla->irq, tabla);
}
- return 0;
+
+ if (ret)
+ mutex_destroy(&tabla->irq_lock);
+
+ return ret;
}
+
void tabla_irq_exit(struct tabla *tabla)
{
- if (tabla->irq)
+ if (tabla->irq) {
+ disable_irq_wake(tabla->irq);
free_irq(tabla->irq, tabla);
+ device_init_wakeup(tabla->dev, 0);
+ }
mutex_destroy(&tabla->irq_lock);
}
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 0c579e2..88fa13a 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -561,6 +561,14 @@
---help---
This turns on debugging information for the tsif driver
+config TSPP
+ depends on ARCH_MSM
+ tristate "TSPP (Transport Stream Packet Processor) Support"
+ ---help---
+ Transport Stream Packet Processor is used to offload the
+ processing of MPEG transport streams from the main processor.
+ This can also be compiled as a loadable module.
+
config HAPTIC_ISA1200
tristate "ISA1200 haptic support"
depends on I2C
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 385f8ff..df35737 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -54,6 +54,7 @@
obj-$(CONFIG_TSIF) += msm_tsif.o
msm_tsif-objs := tsif.o
obj-$(CONFIG_TSIF_CHRDEV) += tsif_chrdev.o
+obj-$(CONFIG_TSPP) += tspp.o
obj-$(CONFIG_HAPTIC_ISA1200) += isa1200.o
obj-$(CONFIG_PMIC8058_PWM) += pmic8058-pwm.o
obj-$(CONFIG_PMIC8058_VIBRATOR) += pmic8058-vibrator.o
diff --git a/drivers/misc/pmem.c b/drivers/misc/pmem.c
index 692849a..7ee7c11 100644
--- a/drivers/misc/pmem.c
+++ b/drivers/misc/pmem.c
@@ -18,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/fs.h>
#include <linux/file.h>
+#include <linux/fmem.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/debugfs.h>
@@ -229,12 +230,12 @@
* request function for a region when the allocation count goes
* from 0 -> 1
*/
- void (*mem_request)(void *);
+ int (*mem_request)(void *);
/*
* release function for a region when the allocation count goes
* from 1 -> 0
*/
- void (*mem_release)(void *);
+ int (*mem_release)(void *);
/*
* private data for the request/release callback
*/
@@ -243,6 +244,10 @@
* map and unmap as needed
*/
int map_on_demand;
+ /*
+ * memory will be reused through fmem
+ */
+ int reusable;
};
#define to_pmem_info_id(a) (container_of(a, struct pmem_info, kobj)->id)
@@ -582,8 +587,13 @@
atomic_inc(&pmem[id].allocation_cnt);
if (!pmem[id].vbase) {
DLOG("PMEMDEBUG: mapping for %s", pmem[id].name);
- if (pmem[id].mem_request)
- pmem[id].mem_request(pmem[id].region_data);
+ if (pmem[id].mem_request) {
+ int ret = pmem[id].mem_request(pmem[id].region_data);
+ if (ret) {
+ atomic_dec(&pmem[id].allocation_cnt);
+ return 1;
+ }
+ }
ioremap_pmem(id);
}
@@ -610,8 +620,11 @@
unmap_kernel_range((unsigned long)pmem[id].vbase,
pmem[id].size);
pmem[id].vbase = NULL;
- if (pmem[id].mem_release)
- pmem[id].mem_release(pmem[id].region_data);
+ if (pmem[id].mem_release) {
+ int ret = pmem[id].mem_release(
+ pmem[id].region_data);
+ WARN(ret, "mem_release failed");
+ }
}
}
@@ -2785,19 +2798,26 @@
pr_info("allocating %lu bytes at %p (%lx physical) for %s\n",
pmem[id].size, pmem[id].vbase, pmem[id].base, pmem[id].name);
- pmem[id].map_on_demand = pdata->map_on_demand;
+ pmem[id].reusable = pdata->reusable;
+ /* reusable pmem requires map on demand */
+ pmem[id].map_on_demand = pdata->map_on_demand || pdata->reusable;
if (pmem[id].map_on_demand) {
- pmem_vma = get_vm_area(pmem[id].size, VM_IOREMAP);
- if (!pmem_vma) {
- pr_err("pmem: Failed to allocate virtual space for "
+ if (pmem[id].reusable) {
+ const struct fmem_data *fmem_info = fmem_get_info();
+ pmem[id].area = fmem_info->area;
+ } else {
+ pmem_vma = get_vm_area(pmem[id].size, VM_IOREMAP);
+ if (!pmem_vma) {
+ pr_err("pmem: Failed to allocate virtual space for "
"%s\n", pdata->name);
- goto out_put_kobj;
- }
- pr_err("pmem: Reserving virtual address range %lx - %lx for"
+ goto out_put_kobj;
+ }
+ pr_err("pmem: Reserving virtual address range %lx - %lx for"
" %s\n", (unsigned long) pmem_vma->addr,
(unsigned long) pmem_vma->addr + pmem[id].size,
pdata->name);
- pmem[id].area = pmem_vma;
+ pmem[id].area = pmem_vma;
+ }
} else
pmem[id].area = NULL;
diff --git a/drivers/misc/tspp.c b/drivers/misc/tspp.c
new file mode 100644
index 0000000..81c6b65
--- /dev/null
+++ b/drivers/misc/tspp.c
@@ -0,0 +1,2019 @@
+/* Copyright (c) 2011, 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> /* Needed by all modules */
+#include <linux/kernel.h> /* Needed for KERN_INFO */
+#include <linux/init.h> /* Needed for the macros */
+#include <linux/cdev.h>
+#include <linux/err.h> /* IS_ERR */
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/sched.h> /* TASK_INTERRUPTIBLE */
+#include <linux/uaccess.h> /* copy_to_user */
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h> /* kfree, kzalloc */
+#include <linux/wakelock.h>
+#include <linux/io.h> /* ioXXX */
+#include <linux/ioport.h> /* XXX_ mem_region */
+#include <linux/dma-mapping.h> /* dma_XXX */
+#include <linux/delay.h> /* msleep */
+#include <linux/clk.h>
+#include <linux/poll.h> /* poll() file op */
+#include <linux/wait.h> /* wait() macros, sleeping */
+#include <linux/tspp.h> /* tspp functions */
+#include <linux/bitops.h> /* BIT() macro */
+#include <mach/sps.h> /* BAM stuff */
+#include <mach/gpio.h>
+#include <mach/dma.h>
+#include <mach/msm_tspp.h>
+
+#define TSPP_USE_DEBUGFS
+#ifdef TSPP_USE_DEBUGFS
+#include <linux/debugfs.h>
+#endif /* TSPP_USE_DEBUGFS */
+
+/*
+ * General defines
+ */
+#define TSPP_USE_DMA_ALLOC_COHERENT
+#define TSPP_TSIF_INSTANCES 2
+#define TSPP_FILTER_TABLES 3
+#define TSPP_MAX_DEVICES 3
+#define TSPP_NUM_CHANNELS 16
+#define TSPP_NUM_PRIORITIES 16
+#define TSPP_NUM_KEYS 8
+#define INVALID_CHANNEL 0xFFFFFFFF
+#define TSPP_SPS_DESCRIPTOR_COUNT 32
+#define TSPP_PACKET_LENGTH 188
+#define TSPP_MIN_BUFFER_SIZE (TSPP_PACKET_LENGTH)
+#define TSPP_MAX_BUFFER_SIZE (16 * 1024) /* maybe allow 64K? */
+#define TSPP_NUM_BUFFERS 16
+#define TSPP_TSIF_DEFAULT_TIME_LIMIT 60
+#define SPS_DESCRIPTOR_SIZE 8
+#define MIN_ACCEPTABLE_BUFFER_COUNT 2
+#define TSPP_DEBUG(msg...) pr_info(msg)
+
+/*
+ * TSIF register offsets
+ */
+#define TSIF_STS_CTL_OFF (0x0)
+#define TSIF_TIME_LIMIT_OFF (0x4)
+#define TSIF_CLK_REF_OFF (0x8)
+#define TSIF_LPBK_FLAGS_OFF (0xc)
+#define TSIF_LPBK_DATA_OFF (0x10)
+#define TSIF_TEST_CTL_OFF (0x14)
+#define TSIF_TEST_MODE_OFF (0x18)
+#define TSIF_TEST_RESET_OFF (0x1c)
+#define TSIF_TEST_EXPORT_OFF (0x20)
+#define TSIF_TEST_CURRENT_OFF (0x24)
+
+#define TSIF_DATA_PORT_OFF (0x100)
+
+/* bits for TSIF_STS_CTL register */
+#define TSIF_STS_CTL_EN_IRQ BIT(28)
+#define TSIF_STS_CTL_PACK_AVAIL BIT(27)
+#define TSIF_STS_CTL_1ST_PACKET BIT(26)
+#define TSIF_STS_CTL_OVERFLOW BIT(25)
+#define TSIF_STS_CTL_LOST_SYNC BIT(24)
+#define TSIF_STS_CTL_TIMEOUT BIT(23)
+#define TSIF_STS_CTL_INV_SYNC BIT(21)
+#define TSIF_STS_CTL_INV_NULL BIT(20)
+#define TSIF_STS_CTL_INV_ERROR BIT(19)
+#define TSIF_STS_CTL_INV_ENABLE BIT(18)
+#define TSIF_STS_CTL_INV_DATA BIT(17)
+#define TSIF_STS_CTL_INV_CLOCK BIT(16)
+#define TSIF_STS_CTL_SPARE BIT(15)
+#define TSIF_STS_CTL_EN_NULL BIT(11)
+#define TSIF_STS_CTL_EN_ERROR BIT(10)
+#define TSIF_STS_CTL_LAST_BIT BIT(9)
+#define TSIF_STS_CTL_EN_TIME_LIM BIT(8)
+#define TSIF_STS_CTL_EN_TCR BIT(7)
+#define TSIF_STS_CTL_TEST_MODE BIT(6)
+#define TSIF_STS_CTL_EN_DM BIT(4)
+#define TSIF_STS_CTL_STOP BIT(3)
+#define TSIF_STS_CTL_START BIT(0)
+
+/*
+ * TSPP register offsets
+ */
+#define TSPP_RST 0x00
+#define TSPP_CLK_CONTROL 0x04
+#define TSPP_CONFIG 0x08
+#define TSPP_CONTROL 0x0C
+#define TSPP_PS_DISABLE 0x10
+#define TSPP_MSG_IRQ_STATUS 0x14
+#define TSPP_MSG_IRQ_MASK 0x18
+#define TSPP_IRQ_STATUS 0x1C
+#define TSPP_IRQ_MASK 0x20
+#define TSPP_IRQ_CLEAR 0x24
+#define TSPP_PIPE_ERROR_STATUS(_n) (0x28 + (_n << 2))
+#define TSPP_STATUS 0x68
+#define TSPP_CURR_TSP_HEADER 0x6C
+#define TSPP_CURR_PID_FILTER 0x70
+#define TSPP_SYSTEM_KEY(_n) (0x74 + (_n << 2))
+#define TSPP_CBC_INIT_VAL(_n) (0x94 + (_n << 2))
+#define TSPP_DATA_KEY_RESET 0x9C
+#define TSPP_KEY_VALID 0xA0
+#define TSPP_KEY_ERROR 0xA4
+#define TSPP_TEST_CTRL 0xA8
+#define TSPP_VERSION 0xAC
+#define TSPP_GENERICS 0xB0
+#define TSPP_NOP 0xB4
+
+/*
+ * Register bit definitions
+ */
+/* TSPP_RST */
+#define TSPP_RST_RESET BIT(0)
+
+/* TSPP_CLK_CONTROL */
+#define TSPP_CLK_CONTROL_FORCE_CRYPTO BIT(9)
+#define TSPP_CLK_CONTROL_FORCE_PES_PL BIT(8)
+#define TSPP_CLK_CONTROL_FORCE_PES_AF BIT(7)
+#define TSPP_CLK_CONTROL_FORCE_RAW_CTRL BIT(6)
+#define TSPP_CLK_CONTROL_FORCE_PERF_CNT BIT(5)
+#define TSPP_CLK_CONTROL_FORCE_CTX_SEARCH BIT(4)
+#define TSPP_CLK_CONTROL_FORCE_TSP_PROC BIT(3)
+#define TSPP_CLK_CONTROL_FORCE_CONS_AHB2MEM BIT(2)
+#define TSPP_CLK_CONTROL_FORCE_TS_AHB2MEM BIT(1)
+#define TSPP_CLK_CONTROL_SET_CLKON BIT(0)
+
+/* TSPP_CONFIG */
+#define TSPP_CONFIG_SET_PACKET_LENGTH(_a, _b) (_a = (_a & 0xF0) | \
+((_b & 0xF) << 8))
+#define TSPP_CONFIG_GET_PACKET_LENGTH(_a) ((_a >> 8) & 0xF)
+#define TSPP_CONFIG_DUP_WITH_DISC_EN BIT(7)
+#define TSPP_CONFIG_PES_SYNC_ERROR_MASK BIT(6)
+#define TSPP_CONFIG_PS_LEN_ERR_MASK BIT(5)
+#define TSPP_CONFIG_PS_CONT_ERR_UNSP_MASK BIT(4)
+#define TSPP_CONFIG_PS_CONT_ERR_MASK BIT(3)
+#define TSPP_CONFIG_PS_DUP_TSP_MASK BIT(2)
+#define TSPP_CONFIG_TSP_ERR_IND_MASK BIT(1)
+#define TSPP_CONFIG_TSP_SYNC_ERR_MASK BIT(0)
+
+/* TSPP_CONTROL */
+#define TSPP_CONTROL_PID_FILTER_LOCK BIT(5)
+#define TSPP_CONTROL_FORCE_KEY_CALC BIT(4)
+#define TSPP_CONTROL_TSP_CONS_SRC_DIS BIT(3)
+#define TSPP_CONTROL_TSP_TSIF1_SRC_DIS BIT(2)
+#define TSPP_CONTROL_TSP_TSIF0_SRC_DIS BIT(1)
+#define TSPP_CONTROL_PERF_COUNT_INIT BIT(0)
+
+/* TSPP_MSG_IRQ_STATUS + TSPP_MSG_IRQ_MASK */
+#define TSPP_MSG_TSPP_IRQ BIT(2)
+#define TSPP_MSG_TSIF_1_IRQ BIT(1)
+#define TSPP_MSG_TSIF_0_IRQ BIT(0)
+
+/* TSPP_IRQ_STATUS + TSPP_IRQ_MASK + TSPP_IRQ_CLEAR */
+#define TSPP_IRQ_STATUS_TSP_RD_CMPL BIT(19)
+#define TSPP_IRQ_STATUS_KEY_ERROR BIT(18)
+#define TSPP_IRQ_STATUS_KEY_SWITCHED_BAD BIT(17)
+#define TSPP_IRQ_STATUS_KEY_SWITCHED BIT(16)
+#define TSPP_IRQ_STATUS_PS_BROKEN(_n) BIT((_n))
+
+/* TSPP_PIPE_ERROR_STATUS */
+#define TSPP_PIPE_PES_SYNC_ERROR BIT(3)
+#define TSPP_PIPE_PS_LENGTH_ERROR BIT(2)
+#define TSPP_PIPE_PS_CONTINUITY_ERROR BIT(1)
+#define TSPP_PIP_PS_LOST_START BIT(0)
+
+/* TSPP_STATUS */
+#define TSPP_STATUS_TSP_PKT_AVAIL BIT(10)
+#define TSPP_STATUS_TSIF1_DM_REQ BIT(6)
+#define TSPP_STATUS_TSIF0_DM_REQ BIT(2)
+#define TSPP_CURR_FILTER_TABLE BIT(0)
+
+/* TSPP_GENERICS */
+#define TSPP_GENERICS_CRYPTO_GEN BIT(12)
+#define TSPP_GENERICS_MAX_CONS_PIPES BIT(7)
+#define TSPP_GENERICS_MAX_PIPES BIT(2)
+#define TSPP_GENERICS_TSIF_1_GEN BIT(1)
+#define TSPP_GENERICS_TSIF_0_GEN BIT(0)
+
+/*
+ * TSPP memory regions
+ */
+#define TSPP_PID_FILTER_TABLE0 0x800
+#define TSPP_PID_FILTER_TABLE1 0x880
+#define TSPP_PID_FILTER_TABLE2 0x900
+#define TSPP_GLOBAL_PERFORMANCE 0x980 /* see tspp_global_performance */
+#define TSPP_PIPE_CONTEXT 0x990 /* see tspp_pipe_context */
+#define TSPP_PIPE_PERFORMANCE 0x998 /* see tspp_pipe_performance */
+#define TSPP_TSP_BUFF_WORD(_n) (0xC10 + (_n << 2))
+#define TSPP_DATA_KEY 0xCD0
+
+#ifdef TSPP_USE_DEBUGFS
+struct debugfs_entry {
+ const char *name;
+ mode_t mode;
+ int offset;
+};
+
+static const struct debugfs_entry debugfs_tsif_regs[] = {
+ {"sts_ctl", S_IRUGO | S_IWUSR, TSIF_STS_CTL_OFF},
+ {"time_limit", S_IRUGO | S_IWUSR, TSIF_TIME_LIMIT_OFF},
+ {"clk_ref", S_IRUGO | S_IWUSR, TSIF_CLK_REF_OFF},
+ {"lpbk_flags", S_IRUGO | S_IWUSR, TSIF_LPBK_FLAGS_OFF},
+ {"lpbk_data", S_IRUGO | S_IWUSR, TSIF_LPBK_DATA_OFF},
+ {"test_ctl", S_IRUGO | S_IWUSR, TSIF_TEST_CTL_OFF},
+ {"test_mode", S_IRUGO | S_IWUSR, TSIF_TEST_MODE_OFF},
+ {"test_reset", S_IWUSR, TSIF_TEST_RESET_OFF},
+ {"test_export", S_IRUGO | S_IWUSR, TSIF_TEST_EXPORT_OFF},
+ {"test_current", S_IRUGO, TSIF_TEST_CURRENT_OFF},
+ {"data_port", S_IRUSR, TSIF_DATA_PORT_OFF},
+};
+
+static const struct debugfs_entry debugfs_tspp_regs[] = {
+ {"rst", S_IRUGO | S_IWUSR, TSPP_RST},
+ {"clk_control", S_IRUGO | S_IWUSR, TSPP_CLK_CONTROL},
+ {"config", S_IRUGO | S_IWUSR, TSPP_CONFIG},
+ {"control", S_IRUGO | S_IWUSR, TSPP_CONTROL},
+ {"ps_disable", S_IRUGO | S_IWUSR, TSPP_PS_DISABLE},
+ {"msg_irq_status", S_IRUGO | S_IWUSR, TSPP_MSG_IRQ_STATUS},
+ {"msg_irq_mask", S_IRUGO | S_IWUSR, TSPP_MSG_IRQ_MASK},
+ {"irq_status", S_IRUGO | S_IWUSR, TSPP_IRQ_STATUS},
+ {"irq_mask", S_IRUGO | S_IWUSR, TSPP_IRQ_MASK},
+ {"irq_clear", S_IRUGO | S_IWUSR, TSPP_IRQ_CLEAR},
+ /* {"pipe_error_status",S_IRUGO | S_IWUSR, TSPP_PIPE_ERROR_STATUS}, */
+ {"status", S_IRUGO | S_IWUSR, TSPP_STATUS},
+ {"curr_tsp_header", S_IRUGO | S_IWUSR, TSPP_CURR_TSP_HEADER},
+ {"curr_pid_filter", S_IRUGO | S_IWUSR, TSPP_CURR_PID_FILTER},
+ /* {"system_key", S_IRUGO | S_IWUSR, TSPP_SYSTEM_KEY}, */
+ /* {"cbc_init_val", S_IRUGO | S_IWUSR, TSPP_CBC_INIT_VAL}, */
+ {"data_key_reset", S_IRUGO | S_IWUSR, TSPP_DATA_KEY_RESET},
+ {"key_valid", S_IRUGO | S_IWUSR, TSPP_KEY_VALID},
+ {"key_error", S_IRUGO | S_IWUSR, TSPP_KEY_ERROR},
+ {"test_ctrl", S_IRUGO | S_IWUSR, TSPP_TEST_CTRL},
+ {"version", S_IRUGO | S_IWUSR, TSPP_VERSION},
+ {"generics", S_IRUGO | S_IWUSR, TSPP_GENERICS},
+ {"pid_filter_table0", S_IRUGO | S_IWUSR, TSPP_PID_FILTER_TABLE0},
+ {"pid_filter_table1", S_IRUGO | S_IWUSR, TSPP_PID_FILTER_TABLE1},
+ {"pid_filter_table2", S_IRUGO | S_IWUSR, TSPP_PID_FILTER_TABLE2},
+ {"global_performance", S_IRUGO | S_IWUSR, TSPP_GLOBAL_PERFORMANCE},
+ {"pipe_context", S_IRUGO | S_IWUSR, TSPP_PIPE_CONTEXT},
+ {"pipe_performance", S_IRUGO | S_IWUSR, TSPP_PIPE_PERFORMANCE},
+ {"data_key", S_IRUGO | S_IWUSR, TSPP_DATA_KEY}
+};
+
+#endif /* TSPP_USE_DEBUGFS */
+
+struct tspp_pid_filter {
+ u32 filter; /* see FILTER_ macros */
+ u32 config; /* see FILTER_ macros */
+};
+
+/* tsp_info */
+#define FILTER_HEADER_ERROR_MASK BIT(7)
+#define FILTER_TRANS_END_DISABLE BIT(6)
+#define FILTER_DEC_ON_ERROR_EN BIT(5)
+#define FILTER_DECRYPT BIT(4)
+#define FILTER_HAS_ENCRYPTION(_p) (_p->config & FILTER_DECRYPT)
+#define FILTER_GET_PIPE_NUMBER0(_p) (_p->config & 0xF)
+#define FILTER_SET_PIPE_NUMBER0(_p, _b) (_p->config = \
+ (_p->config & ~0xF) | (_b & 0xF))
+#define FILTER_GET_PIPE_PROCESS0(_p) ((_p->filter >> 30) & 0x3)
+#define FILTER_SET_PIPE_PROCESS0(_p, _b) (_p->filter = \
+ (_p->filter & ~(0x3<<30)) | ((_b & 0x3) << 30))
+#define FILTER_GET_PIPE_PID(_p) ((_p->filter >> 13) & 0x1FFF)
+#define FILTER_SET_PIPE_PID(_p, _b) (_p->filter = \
+ (_p->filter & ~(0x1FFF<<13)) | ((_b & 0x1FFF) << 13))
+#define FILTER_GET_PID_MASK(_p) (_p->filter & 0x1FFF)
+#define FILTER_SET_PID_MASK(_p, _b) (_p->filter = \
+ (_p->filter & ~0x1FFF) | (_b & 0x1FFF))
+#define FILTER_GET_PIPE_PROCESS1(_p) ((_p->config >> 30) & 0x3)
+#define FILTER_SET_PIPE_PROCESS1(_p, _b) (_p->config = \
+ (_p->config & ~(0x3<<30)) | ((_b & 0x3) << 30))
+#define FILTER_GET_KEY_NUMBER(_p) ((_p->config >> 8) & 0x7)
+#define FILTER_SET_KEY_NUMBER(_p, _b) (_p->config = \
+ (_p->config & ~(0x7<<8)) | ((_b & 0x7) << 8))
+
+struct tspp_global_performance_regs {
+ u32 tsp_total;
+ u32 tsp_ignored;
+ u32 tsp_error;
+ u32 tsp_sync;
+};
+
+struct tspp_pipe_context_regs {
+ u16 pes_bytes_left;
+ u16 count;
+ u32 tsif_suffix;
+} __packed;
+#define CONTEXT_GET_STATE(_a) (_a & 0x3)
+#define CONTEXT_UNSPEC_LENGTH BIT(11)
+#define CONTEXT_GET_CONT_COUNT(_a) ((_a >> 12) & 0xF)
+
+struct tspp_pipe_performance_regs {
+ u32 tsp_total;
+ u32 ps_duplicate_tsp;
+ u32 tsp_no_payload;
+ u32 tsp_broken_ps;
+ u32 ps_total_num;
+ u32 ps_continuity_error;
+ u32 ps_length_error;
+ u32 pes_sync_error;
+};
+
+struct tspp_tsif_device {
+ void __iomem *base;
+ u32 time_limit;
+ u32 ref_count;
+
+ /* debugfs */
+#ifdef TSPP_USE_DEBUGFS
+ struct dentry *dent_tsif;
+ struct dentry *debugfs_tsif_regs[ARRAY_SIZE(debugfs_tsif_regs)];
+#endif /* TSPP_USE_DEBUGFS */
+};
+
+/* this represents the actual hardware device */
+struct tspp_device {
+ struct platform_device *pdev;
+ void __iomem *base;
+ unsigned int tspp_irq;
+ unsigned int bam_irq;
+ u32 bam_handle;
+ struct sps_bam_props bam_props;
+ struct wake_lock wake_lock;
+ spinlock_t spinlock;
+ struct tasklet_struct tlet;
+ struct tspp_tsif_device tsif[TSPP_TSIF_INSTANCES];
+ /* clocks */
+ struct clk *tsif_pclk;
+ struct clk *tsif_ref_clk;
+
+#ifdef TSPP_USE_DEBUGFS
+ struct dentry *dent;
+ struct dentry *debugfs_regs[ARRAY_SIZE(debugfs_tspp_regs)];
+#endif /* TSPP_USE_DEBUGFS */
+};
+
+enum tspp_buf_state {
+ TSPP_BUF_STATE_EMPTY, /* buffer has been allocated, but not waiting */
+ TSPP_BUF_STATE_WAITING, /* buffer is waiting to be filled */
+ TSPP_BUF_STATE_DATA /* buffer is not empty and can be read */
+};
+
+struct tspp_mem_buffer {
+ struct sps_mem_buffer mem;
+ enum tspp_buf_state state;
+ size_t filled; /* how much data this buffer is holding */
+ int read_index; /* where to start reading data from */
+};
+
+/* this represents each char device 'channel' */
+struct tspp_channel {
+ struct cdev cdev;
+ struct device *dd;
+ struct tspp_device *pdev;
+ struct sps_pipe *pipe;
+ struct sps_connect config;
+ struct sps_register_event event;
+ struct tspp_mem_buffer buffer[TSPP_NUM_BUFFERS];
+ wait_queue_head_t in_queue; /* set when data is received */
+ int read; /* index into mem showing buffers ready to be read by user */
+ int waiting; /* index into mem showing outstanding transfers */
+ int id; /* channel id (0-15) */
+ int used; /* is this channel in use? */
+ int key; /* which encryption key index is used */
+ u32 bufsize; /* size of the sps transfer buffers */
+ int buffer_count; /* how many buffers are actually allocated */
+ int filter_count; /* how many filters have been added to this channel */
+ enum tspp_source src;
+ enum tspp_mode mode;
+};
+
+struct tspp_pid_filter_table {
+ struct tspp_pid_filter filter[TSPP_NUM_PRIORITIES];
+};
+
+struct tspp_key_entry {
+ u32 even_lsb;
+ u32 even_msb;
+ u32 odd_lsb;
+ u32 odd_msb;
+};
+
+struct tspp_key_table {
+ struct tspp_key_entry entry[TSPP_NUM_KEYS];
+};
+
+static struct tspp_pid_filter_table *tspp_filter_table[TSPP_FILTER_TABLES];
+static struct tspp_channel channel_list[TSPP_NUM_CHANNELS];
+static struct tspp_key_table *tspp_key_table;
+static struct tspp_global_performance_regs *tspp_global_performance;
+static struct tspp_pipe_context_regs *tspp_pipe_context;
+static struct tspp_pipe_performance_regs *tspp_pipe_performance;
+static struct class *tspp_class;
+static int tspp_key_entry;
+static dev_t tspp_minor; /* next minor number to assign */
+static int loopback_mode; /* put tsif interfaces into loopback mode */
+
+/*** IRQ ***/
+static irqreturn_t tspp_isr(int irq, void *dev_id)
+{
+ struct tspp_device *device = dev_id;
+ u32 status, mask;
+ u32 data;
+
+ status = readl_relaxed(device->base + TSPP_IRQ_STATUS);
+ mask = readl_relaxed(device->base + TSPP_IRQ_MASK);
+ status &= mask;
+
+ if (!status) {
+ dev_warn(&device->pdev->dev, "Spurious interrupt");
+ return IRQ_NONE;
+ }
+
+ /* if (status & TSPP_IRQ_STATUS_TSP_RD_CMPL) */
+
+ if (status & TSPP_IRQ_STATUS_KEY_ERROR) {
+ /* read the key error info */
+ data = readl_relaxed(device->base + TSPP_KEY_ERROR);
+ dev_info(&device->pdev->dev, "key error 0x%x", data);
+ }
+ if (status & TSPP_IRQ_STATUS_KEY_SWITCHED_BAD) {
+ data = readl_relaxed(device->base + TSPP_KEY_VALID);
+ dev_info(&device->pdev->dev, "key invalidated: 0x%x", data);
+ }
+ if (status & TSPP_IRQ_STATUS_KEY_SWITCHED)
+ dev_info(&device->pdev->dev, "key switched");
+
+ if (status & 0xffff)
+ dev_info(&device->pdev->dev, "broken pipe");
+
+ writel_relaxed(status, device->base + TSPP_IRQ_CLEAR);
+ wmb();
+ return IRQ_HANDLED;
+}
+
+/*** callbacks ***/
+static void tspp_sps_complete_cb(struct sps_event_notify *notify)
+{
+ struct tspp_channel *channel = notify->user;
+ tasklet_schedule(&channel->pdev->tlet);
+}
+
+/*** tasklet ***/
+static void tspp_sps_complete_tlet(unsigned long data)
+{
+ int i;
+ int complete;
+ unsigned long flags;
+ struct sps_iovec iovec;
+ struct tspp_channel *channel;
+ struct tspp_device *device = (struct tspp_device *)data;
+ struct tspp_mem_buffer *buffer;
+
+ spin_lock_irqsave(&device->spinlock, flags);
+
+ for (i = 0; i < TSPP_NUM_CHANNELS; i++) {
+ complete = 0;
+ channel = &channel_list[i];
+ buffer = &channel->buffer[channel->waiting];
+
+ /* get completions */
+ if (buffer->state == TSPP_BUF_STATE_WAITING) {
+ if (sps_get_iovec(channel->pipe, &iovec) != 0) {
+ pr_err("tspp: Error in iovec on channel %i",
+ channel->id);
+ break;
+ }
+ if (iovec.size == 0)
+ break;
+
+ if (iovec.addr != buffer->mem.phys_base)
+ pr_err("tspp: buffer mismatch 0x%08x",
+ buffer->mem.phys_base);
+
+ complete = 1;
+ buffer->state = TSPP_BUF_STATE_DATA;
+ buffer->filled = iovec.size;
+ buffer->read_index = 0;
+ channel->waiting++;
+ if (channel->waiting == TSPP_NUM_BUFFERS)
+ channel->waiting = 0;
+ }
+
+ if (complete) {
+ /* wake any waiting processes */
+ wake_up_interruptible(&channel->in_queue);
+ }
+ }
+
+ spin_unlock_irqrestore(&device->spinlock, flags);
+}
+
+/*** GPIO functions ***/
+static void tspp_gpios_free(const struct msm_gpio *table, int size)
+{
+ int i;
+ const struct msm_gpio *g;
+ for (i = size-1; i >= 0; i--) {
+ g = table + i;
+ gpio_free(GPIO_PIN(g->gpio_cfg));
+ }
+}
+
+static int tspp_gpios_request(const struct msm_gpio *table, int size)
+{
+ int rc;
+ int i;
+ const struct msm_gpio *g;
+ for (i = 0; i < size; i++) {
+ g = table + i;
+ rc = gpio_request(GPIO_PIN(g->gpio_cfg), g->label);
+ if (rc) {
+ pr_err("tspp: gpio_request(%d) <%s> failed: %d\n",
+ GPIO_PIN(g->gpio_cfg), g->label ?: "?", rc);
+ goto err;
+ }
+ }
+ return 0;
+err:
+ tspp_gpios_free(table, i);
+ return rc;
+}
+
+static int tspp_gpios_disable(const struct msm_gpio *table, int size)
+{
+ int rc = 0;
+ int i;
+ const struct msm_gpio *g;
+ for (i = size-1; i >= 0; i--) {
+ int tmp;
+ g = table + i;
+ tmp = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_DISABLE);
+ if (tmp) {
+ pr_err("tspp_gpios_disable(0x%08x, GPIO_CFG_DISABLE)"
+ " <%s> failed: %d\n",
+ g->gpio_cfg, g->label ?: "?", rc);
+ pr_err("tspp: pin %d func %d dir %d pull %d drvstr %d\n",
+ GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+ GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+ GPIO_DRVSTR(g->gpio_cfg));
+ if (!rc)
+ rc = tmp;
+ }
+ }
+
+ return rc;
+}
+
+static int tspp_gpios_enable(const struct msm_gpio *table, int size)
+{
+ int rc;
+ int i;
+ const struct msm_gpio *g;
+ for (i = 0; i < size; i++) {
+ g = table + i;
+ rc = gpio_tlmm_config(g->gpio_cfg, GPIO_CFG_ENABLE);
+ if (rc) {
+ pr_err("tspp: gpio_tlmm_config(0x%08x, GPIO_CFG_ENABLE)"
+ " <%s> failed: %d\n",
+ g->gpio_cfg, g->label ?: "?", rc);
+ pr_err("tspp: pin %d func %d dir %d pull %d drvstr %d\n",
+ GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg),
+ GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg),
+ GPIO_DRVSTR(g->gpio_cfg));
+ goto err;
+ }
+ }
+ return 0;
+err:
+ tspp_gpios_disable(table, i);
+ return rc;
+}
+
+static int tspp_gpios_request_enable(const struct msm_gpio *table, int size)
+{
+ int rc = tspp_gpios_request(table, size);
+ if (rc)
+ return rc;
+ rc = tspp_gpios_enable(table, size);
+ if (rc)
+ tspp_gpios_free(table, size);
+ return rc;
+}
+
+static void tspp_gpios_disable_free(const struct msm_gpio *table, int size)
+{
+ tspp_gpios_disable(table, size);
+ tspp_gpios_free(table, size);
+}
+
+static int tspp_start_gpios(struct tspp_device *device)
+{
+ struct msm_tspp_platform_data *pdata =
+ device->pdev->dev.platform_data;
+ return tspp_gpios_request_enable(pdata->gpios, pdata->num_gpios);
+}
+
+static void tspp_stop_gpios(struct tspp_device *device)
+{
+ struct msm_tspp_platform_data *pdata =
+ device->pdev->dev.platform_data;
+ tspp_gpios_disable_free(pdata->gpios, pdata->num_gpios);
+}
+
+/*** TSIF functions ***/
+static int tspp_start_tsif(struct tspp_tsif_device *tsif_device)
+{
+ int start_hardware = 0;
+ u32 ctl;
+
+ if (tsif_device->ref_count == 0) {
+ start_hardware = 1;
+ } else if (tsif_device->ref_count > 0) {
+ ctl = readl_relaxed(tsif_device->base + TSIF_STS_CTL_OFF);
+ if ((ctl & TSIF_STS_CTL_START) != 1) {
+ /* this hardware should already be running */
+ pr_warn("tspp: tsif hw not started but ref count > 0");
+ start_hardware = 1;
+ }
+ }
+
+ if (start_hardware) {
+ if (loopback_mode) {
+ ctl = TSIF_STS_CTL_EN_IRQ |
+ TSIF_STS_CTL_EN_NULL |
+ TSIF_STS_CTL_EN_ERROR |
+ TSIF_STS_CTL_TEST_MODE |
+ TSIF_STS_CTL_EN_DM;
+ TSPP_DEBUG("tspp: starting tsif hw in loopback mode 0x%x", ctl);
+ } else {
+ ctl = TSIF_STS_CTL_EN_IRQ |
+ TSIF_STS_CTL_EN_TIME_LIM |
+ TSIF_STS_CTL_EN_TCR |
+ TSIF_STS_CTL_EN_DM;
+ }
+ writel_relaxed(ctl, tsif_device->base + TSIF_STS_CTL_OFF);
+ writel_relaxed(tsif_device->time_limit,
+ tsif_device->base + TSIF_TIME_LIMIT_OFF);
+ wmb();
+ writel_relaxed(ctl | TSIF_STS_CTL_START,
+ tsif_device->base + TSIF_STS_CTL_OFF);
+ wmb();
+ ctl = readl_relaxed(tsif_device->base + TSIF_STS_CTL_OFF);
+ }
+
+ tsif_device->ref_count++;
+
+ return (ctl & TSIF_STS_CTL_START) ? 0 : -EFAULT;
+}
+
+static void tspp_stop_tsif(struct tspp_tsif_device *tsif_device)
+{
+ if (tsif_device->ref_count == 0)
+ return;
+
+ tsif_device->ref_count--;
+
+ if (tsif_device->ref_count == 0) {
+ writel_relaxed(TSIF_STS_CTL_STOP,
+ tsif_device->base + TSIF_STS_CTL_OFF);
+ wmb();
+ }
+}
+
+/*** TSPP functions ***/
+static int tspp_get_key_entry(void)
+{
+ int i;
+ for (i = 0; i < TSPP_NUM_KEYS; i++) {
+ if (!(tspp_key_entry & (1 << i))) {
+ tspp_key_entry |= (1 << i);
+ return i;
+ }
+ }
+ return 1;
+}
+
+static void tspp_free_key_entry(int entry)
+{
+ if (entry > TSPP_NUM_KEYS) {
+ pr_err("tspp_free_key_entry: index out of bounds");
+ return;
+ }
+
+ tspp_key_entry &= ~(1 << entry);
+}
+
+static int tspp_alloc_buffer(struct sps_mem_buffer *mem,
+ struct tspp_channel *channel)
+{
+ if (channel->bufsize < TSPP_MIN_BUFFER_SIZE ||
+ channel->bufsize > TSPP_MAX_BUFFER_SIZE) {
+ pr_err("tspp: bad buffer size");
+ return 1;
+ }
+
+ switch (channel->mode) {
+ case TSPP_MODE_DISABLED:
+ mem->size = 0;
+ pr_err("tspp: channel is disabled");
+ return 1;
+
+ case TSPP_MODE_PES:
+ /* give the user what he asks for */
+ mem->size = channel->bufsize;
+ break;
+
+ case TSPP_MODE_RAW:
+ /* must be a multiple of 192 */
+ if (channel->bufsize < (TSPP_PACKET_LENGTH+4))
+ mem->size = (TSPP_PACKET_LENGTH+4);
+ else
+ mem->size = (channel->bufsize /
+ (TSPP_PACKET_LENGTH+4)) *
+ (TSPP_PACKET_LENGTH+4);
+ break;
+
+ case TSPP_MODE_RAW_NO_SUFFIX:
+ /* must be a multiple of 188 */
+ mem->size = (channel->bufsize / TSPP_PACKET_LENGTH) *
+ TSPP_PACKET_LENGTH;
+ break;
+ }
+
+#ifdef TSPP_USE_DMA_ALLOC_COHERENT
+ mem->base = dma_alloc_coherent(NULL, mem->size,
+ &mem->phys_base, GFP_KERNEL);
+ if (mem->base == 0) {
+ pr_err("tspp dma alloc coherent failed %i", mem->size);
+ return -ENOMEM;
+ }
+#else
+ mem->base = kmalloc(mem->size, GFP_KERNEL);
+ if (mem->base == 0) {
+ pr_err("tspp buffer allocation failed %i", mem->size);
+ return -ENOMEM;
+ }
+ mem->phys_base = dma_map_single(NULL,
+ mem->base,
+ mem->size,
+ DMA_FROM_DEVICE);
+#endif
+
+ return 0;
+}
+
+static int tspp_global_reset(struct tspp_device *pdev)
+{
+ u32 i, val;
+
+ /* stop all TSIFs */
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++) {
+ pdev->tsif[i].ref_count = 1; /* allows stopping hw */
+ tspp_stop_tsif(&pdev->tsif[i]); /* will reset ref_count to 0 */
+ pdev->tsif[i].time_limit = TSPP_TSIF_DEFAULT_TIME_LIMIT;
+ }
+ writel_relaxed(TSPP_RST_RESET, pdev->base + TSPP_RST);
+ wmb();
+
+ /* BAM */
+ if (sps_device_reset(pdev->bam_handle) != 0) {
+ pr_err("tspp: error resetting bam");
+ return 1;
+ }
+
+ /* TSPP tables */
+ for (i = 0; i < TSPP_FILTER_TABLES; i++)
+ memset(tspp_filter_table[i],
+ 0, sizeof(struct tspp_pid_filter_table));
+
+ /* disable all filters */
+ val = (2 << TSPP_NUM_CHANNELS) - 1;
+ writel_relaxed(val, pdev->base + TSPP_PS_DISABLE);
+
+ /* TSPP registers */
+ val = readl_relaxed(pdev->base + TSPP_CONTROL);
+ writel_relaxed(val | TSPP_CLK_CONTROL_FORCE_PERF_CNT,
+ pdev->base + TSPP_CONTROL);
+ wmb();
+ memset(tspp_global_performance, 0,
+ sizeof(struct tspp_global_performance_regs));
+ memset(tspp_pipe_context, 0,
+ sizeof(struct tspp_pipe_context_regs));
+ memset(tspp_pipe_performance, 0,
+ sizeof(struct tspp_pipe_performance_regs));
+ wmb();
+ writel_relaxed(val & ~TSPP_CLK_CONTROL_FORCE_PERF_CNT,
+ pdev->base + TSPP_CONTROL);
+ wmb();
+
+ val = readl_relaxed(pdev->base + TSPP_CONFIG);
+ val &= ~(TSPP_CONFIG_PS_LEN_ERR_MASK |
+ TSPP_CONFIG_PS_CONT_ERR_UNSP_MASK |
+ TSPP_CONFIG_PS_CONT_ERR_MASK);
+ TSPP_CONFIG_SET_PACKET_LENGTH(val, TSPP_PACKET_LENGTH);
+ writel_relaxed(val, pdev->base + TSPP_CONFIG);
+ writel_relaxed(0x000fffff, pdev->base + TSPP_IRQ_MASK);
+ writel_relaxed(0x000fffff, pdev->base + TSPP_IRQ_CLEAR);
+ writel_relaxed(0, pdev->base + TSPP_RST);
+ wmb();
+
+ tspp_key_entry = 0;
+
+ return 0;
+}
+
+int tspp_open_stream(struct tspp_channel *channel, enum tspp_source src)
+{
+ u32 val;
+ struct tspp_device *pdev;
+
+ if (!channel)
+ return 1;
+
+ pdev = channel->pdev;
+
+ switch (src) {
+ case TSPP_SOURCE_TSIF0:
+ /* make sure TSIF0 is running & enabled */
+ if (tspp_start_tsif(&pdev->tsif[0]) != 0) {
+ pr_err("tspp: error starting tsif0");
+ return 1;
+ }
+ val = readl_relaxed(pdev->base + TSPP_CONTROL);
+ writel_relaxed(val & ~TSPP_CONTROL_TSP_TSIF0_SRC_DIS,
+ pdev->base + TSPP_CONTROL);
+ wmb();
+ break;
+ case TSPP_SOURCE_TSIF1:
+ /* make sure TSIF1 is running & enabled */
+ if (tspp_start_tsif(&pdev->tsif[1]) != 0) {
+ pr_err("tspp: error starting tsif1");
+ return 1;
+ }
+ val = readl_relaxed(pdev->base + TSPP_CONTROL);
+ writel_relaxed(val & ~TSPP_CONTROL_TSP_TSIF1_SRC_DIS,
+ pdev->base + TSPP_CONTROL);
+ wmb();
+ break;
+ case TSPP_SOURCE_MEM:
+ break;
+ default:
+ pr_warn("tspp: channel %i invalid source %i", channel->id, src);
+ return 1;
+ }
+
+ channel->src = src;
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_open_stream);
+
+int tspp_close_stream(struct tspp_channel *channel)
+{
+ u32 val;
+ struct tspp_device *pdev;
+
+ pdev = channel->pdev;
+
+ switch (channel->src) {
+ case TSPP_SOURCE_TSIF0:
+ tspp_stop_tsif(&pdev->tsif[0]);
+ val = readl_relaxed(pdev->base + TSPP_CONTROL);
+ writel_relaxed(val | TSPP_CONTROL_TSP_TSIF0_SRC_DIS,
+ pdev->base + TSPP_CONTROL);
+ wmb();
+ break;
+ case TSPP_SOURCE_TSIF1:
+ tspp_stop_tsif(&pdev->tsif[1]);
+ val = readl_relaxed(pdev->base + TSPP_CONTROL);
+ writel_relaxed(val | TSPP_CONTROL_TSP_TSIF1_SRC_DIS,
+ pdev->base + TSPP_CONTROL);
+ break;
+ case TSPP_SOURCE_MEM:
+ break;
+ case TSPP_SOURCE_NONE:
+ break;
+ }
+
+ channel->src = -1;
+ return 0;
+}
+EXPORT_SYMBOL(tspp_close_stream);
+
+int tspp_open_channel(struct tspp_channel *channel)
+{
+ int rc = 0;
+ struct sps_connect *config = &channel->config;
+ struct sps_register_event *event = &channel->event;
+
+ if (channel->used) {
+ pr_err("tspp channel already in use");
+ return 1;
+ }
+
+ /* mark it as used */
+ channel->used = 1;
+
+ /* start the bam */
+ channel->pipe = sps_alloc_endpoint();
+ if (channel->pipe == 0) {
+ pr_err("tspp: error allocating endpoint");
+ rc = -ENOMEM;
+ goto err_sps_alloc;
+ }
+
+ /* get default configuration */
+ sps_get_config(channel->pipe, config);
+
+ config->source = channel->pdev->bam_handle;
+ config->destination = SPS_DEV_HANDLE_MEM;
+ config->mode = SPS_MODE_SRC;
+ config->options = SPS_O_AUTO_ENABLE |
+ SPS_O_EOT | SPS_O_ACK_TRANSFERS;
+ config->src_pipe_index = channel->id;
+ config->desc.size =
+ (TSPP_SPS_DESCRIPTOR_COUNT + 1) * SPS_DESCRIPTOR_SIZE;
+ config->desc.base = dma_alloc_coherent(NULL,
+ config->desc.size,
+ &config->desc.phys_base,
+ GFP_KERNEL);
+ if (config->desc.base == 0) {
+ pr_err("tspp: error allocating sps descriptors");
+ rc = -ENOMEM;
+ goto err_desc_alloc;
+ }
+
+ memset(config->desc.base, 0, config->desc.size);
+
+ rc = sps_connect(channel->pipe, config);
+ if (rc) {
+ pr_err("tspp: error connecting bam");
+ goto err_connect;
+ }
+
+ event->mode = SPS_TRIGGER_CALLBACK;
+ event->options = SPS_O_EOT;
+ event->callback = tspp_sps_complete_cb;
+ event->xfer_done = NULL;
+ event->user = channel;
+
+ rc = sps_register_event(channel->pipe, event);
+ if (rc) {
+ pr_err("tspp: error registering event");
+ goto err_event;
+ }
+
+ rc = pm_runtime_get(&channel->pdev->pdev->dev);
+ if (rc < 0) {
+ dev_err(&channel->pdev->pdev->dev,
+ "Runtime PM: Unable to wake up tspp device, rc = %d",
+ rc);
+ }
+
+ wake_lock(&channel->pdev->wake_lock);
+ return 0;
+
+err_event:
+ sps_disconnect(channel->pipe);
+err_connect:
+ dma_free_coherent(NULL, config->desc.size, config->desc.base,
+ config->desc.phys_base);
+err_desc_alloc:
+ sps_free_endpoint(channel->pipe);
+err_sps_alloc:
+ return rc;
+}
+EXPORT_SYMBOL(tspp_open_channel);
+
+int tspp_close_channel(struct tspp_channel *channel)
+{
+ int i;
+ int id;
+ u32 val;
+ struct sps_connect *config = &channel->config;
+ struct tspp_device *pdev = channel->pdev;
+
+ TSPP_DEBUG("tspp_close_channel");
+ channel->used = 0;
+
+ /* disable pipe (channel) */
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+ writel_relaxed(val | channel->id, pdev->base + TSPP_PS_DISABLE);
+ wmb();
+
+ /* unregister all filters for this channel */
+ for (i = 0; i < TSPP_NUM_PRIORITIES; i++) {
+ struct tspp_pid_filter *tspp_filter =
+ &tspp_filter_table[channel->src]->filter[i];
+ id = FILTER_GET_PIPE_NUMBER0(tspp_filter);
+ if (id == channel->id) {
+ if (FILTER_HAS_ENCRYPTION(tspp_filter))
+ tspp_free_key_entry(
+ FILTER_GET_KEY_NUMBER(tspp_filter));
+ tspp_filter->config = 0;
+ tspp_filter->filter = 0;
+ }
+ }
+ channel->filter_count = 0;
+
+ /* stop the stream */
+ tspp_close_stream(channel);
+
+ /* disconnect the bam */
+ if (sps_disconnect(channel->pipe) != 0)
+ pr_warn("tspp: Error freeing sps endpoint (%i)", channel->id);
+
+ /* destroy the buffers */
+ dma_free_coherent(NULL, config->desc.size, config->desc.base,
+ config->desc.phys_base);
+
+ for (i = 0; i < TSPP_NUM_BUFFERS; i++) {
+ if (channel->buffer[i].mem.phys_base) {
+#ifdef TSPP_USE_DMA_ALLOC_COHERENT
+ dma_free_coherent(NULL,
+ channel->buffer[i].mem.size,
+ channel->buffer[i].mem.base,
+ channel->buffer[i].mem.phys_base);
+#else
+ dma_unmap_single(channel->dd,
+ channel->buffer[i].mem.phys_base,
+ channel->buffer[i].mem.size,
+ 0);
+ kfree(channel->buffer[i].mem.base);
+#endif
+ channel->buffer[i].mem.phys_base = 0;
+ }
+ channel->buffer[i].mem.base = 0;
+ channel->buffer[i].state = TSPP_BUF_STATE_EMPTY;
+ }
+ channel->buffer_count = 0;
+
+ wake_unlock(&channel->pdev->wake_lock);
+ return 0;
+}
+EXPORT_SYMBOL(tspp_close_channel);
+
+/* picks a stream for this channel */
+int tspp_select_source(struct tspp_channel *channel,
+ struct tspp_select_source *src)
+{
+ /* make sure the requested src id is in bounds */
+ if (src->source > TSPP_SOURCE_MEM) {
+ pr_err("tspp source out of bounds");
+ return 1;
+ }
+
+ /* open the stream */
+ tspp_open_stream(channel, src->source);
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_select_source);
+
+int tspp_add_filter(struct tspp_channel *channel,
+ struct tspp_filter *filter)
+{
+ int i;
+ int other_channel;
+ int entry;
+ u32 val, pid, enabled;
+ struct tspp_device *pdev = channel->pdev;
+ struct tspp_pid_filter p;
+
+ TSPP_DEBUG("tspp_add_filter");
+ if (filter->source > TSPP_SOURCE_MEM) {
+ pr_err("tspp invalid source");
+ return 1;
+ }
+
+ if (filter->priority >= TSPP_NUM_PRIORITIES) {
+ pr_err("tspp invalid source");
+ return 1;
+ }
+
+ /* make sure this filter mode matches the channel mode */
+ switch (channel->mode) {
+ case TSPP_MODE_DISABLED:
+ channel->mode = filter->mode;
+ break;
+ case TSPP_MODE_RAW:
+ case TSPP_MODE_PES:
+ case TSPP_MODE_RAW_NO_SUFFIX:
+ if (filter->mode != channel->mode) {
+ pr_err("tspp: wrong filter mode");
+ return 1;
+ }
+ }
+
+ if (filter->mode == TSPP_MODE_PES) {
+ for (i = 0; i < TSPP_NUM_PRIORITIES; i++) {
+ struct tspp_pid_filter *tspp_filter =
+ &tspp_filter_table[channel->src]->filter[i];
+ pid = FILTER_GET_PIPE_PID((tspp_filter));
+ enabled = FILTER_GET_PIPE_PROCESS0(tspp_filter);
+ if (enabled && (pid == filter->pid)) {
+ other_channel =
+ FILTER_GET_PIPE_NUMBER0(tspp_filter);
+ pr_err("tspp: pid 0x%x already in use by channel %i",
+ filter->pid, other_channel);
+ return 1;
+ }
+ }
+ }
+
+ /* make sure this priority is not already in use */
+ enabled = FILTER_GET_PIPE_PROCESS0(
+ (&(tspp_filter_table[channel->src]->filter[filter->priority])));
+ if (enabled) {
+ pr_err("tspp: filter priority %i source %i is already enabled\n",
+ filter->priority, channel->src);
+ return 1;
+ }
+
+ if (channel->mode == TSPP_MODE_PES) {
+ /* if we are already processing in PES mode, disable pipe
+ (channel) and filter to be updated */
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+ writel_relaxed(val | (1 << channel->id),
+ pdev->base + TSPP_PS_DISABLE);
+ wmb();
+ }
+
+ /* update entry */
+ p.filter = 0;
+ p.config = 0;
+ FILTER_SET_PIPE_PROCESS0((&p), filter->mode);
+ FILTER_SET_PIPE_PID((&p), filter->pid);
+ FILTER_SET_PID_MASK((&p), filter->mask);
+ FILTER_SET_PIPE_NUMBER0((&p), channel->id);
+ FILTER_SET_PIPE_PROCESS1((&p), TSPP_MODE_DISABLED);
+ if (filter->decrypt) {
+ entry = tspp_get_key_entry();
+ if (entry == -1) {
+ pr_err("tspp: no more keys available!");
+ } else {
+ p.config |= FILTER_DECRYPT;
+ FILTER_SET_KEY_NUMBER((&p), entry);
+ }
+ }
+ TSPP_DEBUG("tspp_add_filter: mode=%i pid=%i mask=%i channel=%i",
+ filter->mode, filter->pid, filter->mask, channel->id);
+
+ tspp_filter_table[channel->src]->
+ filter[filter->priority].config = p.config;
+ tspp_filter_table[channel->src]->
+ filter[filter->priority].filter = p.filter;
+
+ /* reenable pipe */
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+ writel_relaxed(val & ~(1 << channel->id), pdev->base + TSPP_PS_DISABLE);
+ wmb();
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+
+ /* allocate buffers if needed */
+ if (channel->buffer_count == 0) {
+ TSPP_DEBUG("tspp: no buffers need %i", TSPP_NUM_BUFFERS);
+ for (i = 0; i < TSPP_NUM_BUFFERS; i++) {
+ if (tspp_alloc_buffer(&channel->buffer[i].mem,
+ channel) != 0) {
+ pr_warn("tspp: Can't allocate buffer %i", i);
+ } else {
+ channel->buffer[i].filled = 0;
+ channel->buffer[i].read_index = 0;
+ channel->buffer_count++;
+
+ /* start the transfer */
+ if (sps_transfer_one(channel->pipe,
+ channel->buffer[i].mem.phys_base,
+ channel->buffer[i].mem.size,
+ channel,
+ SPS_IOVEC_FLAG_INT |
+ SPS_IOVEC_FLAG_EOB))
+ pr_err("tspp: can't submit transfer");
+ else
+ channel->buffer[i].state =
+ TSPP_BUF_STATE_WAITING;
+ }
+ }
+ }
+
+ if (channel->buffer_count < MIN_ACCEPTABLE_BUFFER_COUNT) {
+ pr_err("failed to allocate at least %i buffers",
+ MIN_ACCEPTABLE_BUFFER_COUNT);
+ return -ENOMEM;
+ }
+
+ channel->filter_count++;
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_add_filter);
+
+int tspp_remove_filter(struct tspp_channel *channel,
+ struct tspp_filter *filter)
+{
+ int entry;
+ u32 val;
+ struct tspp_device *pdev = channel->pdev;
+ int src = channel->src;
+ struct tspp_pid_filter *tspp_filter =
+ &(tspp_filter_table[src]->filter[filter->priority]);
+
+ /* disable pipe (channel) */
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+ writel_relaxed(val | channel->id, pdev->base + TSPP_PS_DISABLE);
+ wmb();
+
+ /* update data keys */
+ if (tspp_filter->config & FILTER_DECRYPT) {
+ entry = FILTER_GET_KEY_NUMBER(tspp_filter);
+ tspp_free_key_entry(entry);
+ }
+
+ /* update pid table */
+ tspp_filter->config = 0;
+ tspp_filter->filter = 0;
+
+ channel->filter_count--;
+
+ /* reenable pipe */
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+ writel_relaxed(val & ~(1 << channel->id),
+ pdev->base + TSPP_PS_DISABLE);
+ wmb();
+ val = readl_relaxed(pdev->base + TSPP_PS_DISABLE);
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_remove_filter);
+
+int tspp_set_key(struct tspp_channel *channel, struct tspp_key* key)
+{
+ int i;
+ int id;
+ int key_index;
+ int data;
+
+ /* read the key index used by this channel */
+ for (i = 0; i < TSPP_NUM_PRIORITIES; i++) {
+ struct tspp_pid_filter *tspp_filter =
+ &(tspp_filter_table[channel->src]->filter[i]);
+ id = FILTER_GET_PIPE_NUMBER0(tspp_filter);
+ if (id == channel->id) {
+ if (FILTER_HAS_ENCRYPTION(tspp_filter)) {
+ key_index = FILTER_GET_KEY_NUMBER(tspp_filter);
+ break;
+ }
+ }
+ }
+ if (i == TSPP_NUM_PRIORITIES) {
+ pr_err("tspp: no encryption on this channel");
+ return 1;
+ }
+
+ if (key->parity == TSPP_KEY_PARITY_EVEN) {
+ tspp_key_table->entry[key_index].even_lsb = key->lsb;
+ tspp_key_table->entry[key_index].even_msb = key->msb;
+ } else {
+ tspp_key_table->entry[key_index].odd_lsb = key->lsb;
+ tspp_key_table->entry[key_index].odd_msb = key->msb;
+ }
+ data = readl_relaxed(channel->pdev->base + TSPP_KEY_VALID);
+
+ return 0;
+}
+EXPORT_SYMBOL(tspp_set_key);
+
+static int tspp_set_iv(struct tspp_channel *channel, struct tspp_iv *iv)
+{
+ struct tspp_device *pdev = channel->pdev;
+
+ writel_relaxed(iv->data[0], pdev->base + TSPP_CBC_INIT_VAL(0));
+ writel_relaxed(iv->data[1], pdev->base + TSPP_CBC_INIT_VAL(1));
+ return 0;
+}
+
+static int tspp_set_system_keys(struct tspp_channel *channel,
+ struct tspp_system_keys *keys)
+{
+ int i;
+ struct tspp_device *pdev = channel->pdev;
+
+ for (i = 0; i < TSPP_NUM_SYSTEM_KEYS; i++)
+ writel_relaxed(keys->data[i], pdev->base + TSPP_SYSTEM_KEY(i));
+
+ return 0;
+}
+
+static int tspp_set_buffer_size(struct tspp_channel *channel,
+ struct tspp_buffer *buf)
+{
+ if (buf->size < TSPP_MIN_BUFFER_SIZE)
+ channel->bufsize = TSPP_MIN_BUFFER_SIZE;
+ else if (buf->size > TSPP_MAX_BUFFER_SIZE)
+ channel->bufsize = TSPP_MAX_BUFFER_SIZE;
+ else
+ channel->bufsize = buf->size;
+
+ TSPP_DEBUG("tspp channel %i buffer size %i",
+ channel->id, channel->bufsize);
+
+ return 0;
+}
+
+/*** File Operations ***/
+static ssize_t tspp_open(struct inode *inode, struct file *filp)
+{
+ struct tspp_channel *channel;
+ channel = container_of(inode->i_cdev, struct tspp_channel, cdev);
+ filp->private_data = channel;
+
+ /* if this channel is already in use, quit */
+ if (channel->used) {
+ pr_err("tspp channel %i already in use",
+ MINOR(channel->cdev.dev));
+ return -EACCES;
+ }
+
+ if (tspp_open_channel(channel) != 0) {
+ pr_err("tspp: error opening channel");
+ return -EACCES;
+ }
+
+ return 0;
+}
+
+static unsigned int tspp_poll(struct file *filp, struct poll_table_struct *p)
+{
+ unsigned long flags;
+ unsigned int mask = 0;
+ struct tspp_channel *channel;
+ channel = filp->private_data;
+
+ /* register the wait queue for this channel */
+ poll_wait(filp, &channel->in_queue, p);
+
+ spin_lock_irqsave(&channel->pdev->spinlock, flags);
+ if (channel->buffer[channel->read].state == TSPP_BUF_STATE_DATA)
+ mask = POLLIN | POLLRDNORM;
+
+ spin_unlock_irqrestore(&channel->pdev->spinlock, flags);
+
+ return mask;
+}
+
+static ssize_t tspp_release(struct inode *inode, struct file *filp)
+{
+ struct tspp_channel *channel;
+ channel = filp->private_data;
+
+ pr_info("tspp_release");
+ tspp_close_channel(channel);
+
+ return 0;
+}
+
+static ssize_t tspp_read(struct file *filp, char __user *buf, size_t count,
+ loff_t *f_pos)
+{
+ size_t size = 0;
+ size_t transferred = 0;
+ struct tspp_channel *channel;
+ struct tspp_mem_buffer *buffer;
+ channel = filp->private_data;
+
+ TSPP_DEBUG("tspp_read");
+ buffer = &channel->buffer[channel->read];
+ /* see if we have any buffers ready to read */
+ while (buffer->state != TSPP_BUF_STATE_DATA) {
+ if (filp->f_flags & O_NONBLOCK) {
+ pr_warn("tspp: nothing to read on channel %i!",
+ channel->id);
+ return -EAGAIN;
+ }
+ /* go to sleep if there is nothing to read */
+ TSPP_DEBUG("tspp: sleeping");
+ if (wait_event_interruptible(channel->in_queue,
+ (buffer->state == TSPP_BUF_STATE_DATA))) {
+ pr_err("tspp: rude awakening\n");
+ return -ERESTARTSYS;
+ }
+ }
+
+ while (buffer->state == TSPP_BUF_STATE_DATA) {
+ size = min(count, buffer->filled);
+ TSPP_DEBUG("tspp: reading channel %i buffer %i size %i",
+ channel->id, channel->read, size);
+ if (size == 0)
+ break;
+
+#ifndef TSPP_USE_DMA_ALLOC_COHERENT
+ /* unmap buffer (invalidates processor cache) */
+ if (buffer->mem.phys_base) {
+ dma_unmap_single(NULL,
+ buffer->mem.phys_base,
+ buffer->mem.size,
+ DMA_FROM_DEVICE);
+ buffer->mem.phys_base = 0;
+ }
+#endif
+
+ if (copy_to_user(buf, buffer->mem.base +
+ buffer->read_index, size)) {
+ pr_err("tspp: error copying to user buffer");
+ return -EFAULT;
+ }
+ buf += size;
+ count -= size;
+ transferred += size;
+ buffer->read_index += size;
+
+ /* after reading the end of the buffer, requeue it,
+ and set up for reading the next one */
+ if (buffer->read_index ==
+ channel->buffer[channel->read].filled) {
+ buffer->state = TSPP_BUF_STATE_WAITING;
+#ifndef TSPP_USE_DMA_ALLOC_COHERENT
+ buffer->mem.phys_base = dma_map_single(NULL,
+ buffer->mem.base,
+ buffer->mem.size,
+ DMA_FROM_DEVICE);
+ if (!dma_mapping_error(NULL,
+ buffer->mem.phys_base)) {
+#endif
+ if (sps_transfer_one(channel->pipe,
+ buffer->mem.phys_base,
+ buffer->mem.size,
+ channel,
+ SPS_IOVEC_FLAG_INT |
+ SPS_IOVEC_FLAG_EOT))
+ pr_err("tspp: can't submit transfer");
+ else {
+ channel->read++;
+ if (channel->read == TSPP_NUM_BUFFERS)
+ channel->read = 0;
+ }
+#ifndef TSPP_USE_DMA_ALLOC_COHERENT
+ }
+#endif
+ }
+ }
+
+ return transferred;
+}
+
+static long tspp_ioctl(struct file *filp,
+ unsigned int param0, unsigned long param1)
+{
+ int rc = -1;
+ struct tspp_channel *channel;
+ channel = filp->private_data;
+
+ if (!param1)
+ return -EINVAL;
+
+ switch (param0) {
+ case TSPP_IOCTL_SELECT_SOURCE:
+ rc = tspp_select_source(channel,
+ (struct tspp_select_source *)param1);
+ break;
+ case TSPP_IOCTL_ADD_FILTER:
+ rc = tspp_add_filter(channel,
+ (struct tspp_filter *)param1);
+ break;
+ case TSPP_IOCTL_REMOVE_FILTER:
+ rc = tspp_remove_filter(channel,
+ (struct tspp_filter *)param1);
+ break;
+ case TSPP_IOCTL_SET_KEY:
+ rc = tspp_set_key(channel,
+ (struct tspp_key *)param1);
+ break;
+ case TSPP_IOCTL_SET_IV:
+ rc = tspp_set_iv(channel,
+ (struct tspp_iv *)param1);
+ break;
+ case TSPP_IOCTL_SET_SYSTEM_KEYS:
+ rc = tspp_set_system_keys(channel,
+ (struct tspp_system_keys *)param1);
+ break;
+ case TSPP_IOCTL_BUFFER_SIZE:
+ rc = tspp_set_buffer_size(channel,
+ (struct tspp_buffer *)param1);
+ break;
+ case TSPP_IOCTL_LOOPBACK:
+ loopback_mode = param1;
+ rc = 0;
+ break;
+ default:
+ pr_err("tspp: Unknown ioctl %i", param0);
+ }
+
+ /* normalize the return code in case one of the subfunctions does
+ something weird */
+ if (rc != 0)
+ rc = 1;
+
+ return rc;
+}
+
+/*** debugfs ***/
+#ifdef TSPP_USE_DEBUGFS
+static int debugfs_iomem_x32_set(void *data, u64 val)
+{
+ writel_relaxed(val, data);
+ wmb();
+ return 0;
+}
+
+static int debugfs_iomem_x32_get(void *data, u64 *val)
+{
+ *val = readl_relaxed(data);
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_iomem_x32, debugfs_iomem_x32_get,
+ debugfs_iomem_x32_set, "0x%08llx");
+
+static void tsif_debugfs_init(struct tspp_tsif_device *tsif_device,
+ int instance)
+{
+ char name[10];
+ snprintf(name, 10, "tsif%i", instance);
+ tsif_device->dent_tsif = debugfs_create_dir(
+ name, NULL);
+ if (tsif_device->dent_tsif) {
+ int i;
+ void __iomem *base = tsif_device->base;
+ for (i = 0; i < ARRAY_SIZE(debugfs_tsif_regs); i++) {
+ tsif_device->debugfs_tsif_regs[i] =
+ debugfs_create_file(
+ debugfs_tsif_regs[i].name,
+ debugfs_tsif_regs[i].mode,
+ tsif_device->dent_tsif,
+ base + debugfs_tsif_regs[i].offset,
+ &fops_iomem_x32);
+ }
+ }
+}
+
+static void tsif_debugfs_exit(struct tspp_tsif_device *tsif_device)
+{
+ if (tsif_device->dent_tsif) {
+ int i;
+ debugfs_remove_recursive(tsif_device->dent_tsif);
+ tsif_device->dent_tsif = NULL;
+ for (i = 0; i < ARRAY_SIZE(debugfs_tsif_regs); i++)
+ tsif_device->debugfs_tsif_regs[i] = NULL;
+ }
+}
+
+static void tspp_debugfs_init(struct tspp_device *device, int instance)
+{
+ char name[10];
+ snprintf(name, 10, "tspp%i", instance);
+ device->dent = debugfs_create_dir(
+ name, NULL);
+ if (device->dent) {
+ int i;
+ void __iomem *base = device->base;
+ for (i = 0; i < ARRAY_SIZE(debugfs_tspp_regs); i++) {
+ device->debugfs_regs[i] =
+ debugfs_create_file(
+ debugfs_tspp_regs[i].name,
+ debugfs_tspp_regs[i].mode,
+ device->dent,
+ base + debugfs_tspp_regs[i].offset,
+ &fops_iomem_x32);
+ }
+ }
+}
+
+static void tspp_debugfs_exit(struct tspp_device *device)
+{
+ if (device->dent) {
+ int i;
+ debugfs_remove_recursive(device->dent);
+ device->dent = NULL;
+ for (i = 0; i < ARRAY_SIZE(debugfs_tspp_regs); i++)
+ device->debugfs_regs[i] = NULL;
+ }
+}
+#endif /* TSPP_USE_DEBUGFS */
+
+static const struct file_operations tspp_fops = {
+ .owner = THIS_MODULE,
+ .read = tspp_read,
+ .open = tspp_open,
+ .poll = tspp_poll,
+ .release = tspp_release,
+ .unlocked_ioctl = tspp_ioctl,
+};
+
+static int tspp_channel_init(struct tspp_channel *channel)
+{
+ channel->bufsize = TSPP_MIN_BUFFER_SIZE;
+ channel->read = 0;
+ channel->waiting = 0;
+ cdev_init(&channel->cdev, &tspp_fops);
+ channel->cdev.owner = THIS_MODULE;
+ channel->id = MINOR(tspp_minor);
+ init_waitqueue_head(&channel->in_queue);
+ channel->buffer_count = 0;
+ channel->filter_count = 0;
+
+ if (cdev_add(&channel->cdev, tspp_minor++, 1) != 0) {
+ pr_err("tspp: cdev_add failed");
+ return 1;
+ }
+
+ channel->dd = device_create(tspp_class, NULL, channel->cdev.dev,
+ channel, "tspp%02d", channel->id);
+ if (IS_ERR(channel->dd)) {
+ pr_err("tspp: device_create failed: %i",
+ (int)PTR_ERR(channel->dd));
+ cdev_del(&channel->cdev);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int __devinit msm_tspp_probe(struct platform_device *pdev)
+{
+ int rc = -ENODEV;
+ u32 version;
+ u32 i;
+ struct msm_tspp_platform_data *data;
+ struct tspp_device *device;
+ struct resource *mem_tsif0;
+ struct resource *mem_tsif1;
+ struct resource *mem_tspp;
+ struct resource *mem_bam;
+ struct tspp_channel *channel;
+
+ /* must have platform data */
+ data = pdev->dev.platform_data;
+ if (!data) {
+ pr_err("tspp: Platform data not available");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* check for valid device id */
+ if ((pdev->id < 0) || (pdev->id >= 1)) {
+ pr_err("tspp: Invalid device ID %d", pdev->id);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* OK, we will use this device */
+ device = kzalloc(sizeof(struct tspp_device), GFP_KERNEL);
+ if (!device) {
+ pr_err("tspp: Failed to allocate memory for device");
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ /* set up references */
+ device->pdev = pdev;
+ platform_set_drvdata(pdev, device);
+
+ /* map clocks */
+ if (data->tsif_pclk) {
+ device->tsif_pclk = clk_get(NULL, data->tsif_pclk);
+ if (IS_ERR(device->tsif_pclk)) {
+ pr_err("tspp: failed to get %s",
+ data->tsif_pclk);
+ rc = PTR_ERR(device->tsif_pclk);
+ device->tsif_pclk = NULL;
+ goto err_pclock;
+ }
+ }
+ if (data->tsif_ref_clk) {
+ device->tsif_ref_clk = clk_get(NULL, data->tsif_ref_clk);
+ if (IS_ERR(device->tsif_ref_clk)) {
+ pr_err("tspp: failed to get %s",
+ data->tsif_ref_clk);
+ rc = PTR_ERR(device->tsif_ref_clk);
+ device->tsif_ref_clk = NULL;
+ goto err_refclock;
+ }
+ }
+
+ /* map I/O memory */
+ mem_tsif0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem_tsif0) {
+ pr_err("tspp: Missing tsif0 MEM resource");
+ rc = -ENXIO;
+ goto err_res_tsif0;
+ }
+ device->tsif[0].base = ioremap(mem_tsif0->start,
+ resource_size(mem_tsif0));
+ if (!device->tsif[0].base) {
+ pr_err("tspp: ioremap failed");
+ goto err_map_tsif0;
+ }
+
+ mem_tsif1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!mem_tsif1) {
+ dev_err(&pdev->dev, "Missing tsif1 MEM resource");
+ rc = -ENXIO;
+ goto err_res_tsif1;
+ }
+ device->tsif[1].base = ioremap(mem_tsif1->start,
+ resource_size(mem_tsif1));
+ if (!device->tsif[1].base) {
+ dev_err(&pdev->dev, "ioremap failed");
+ goto err_map_tsif1;
+ }
+
+ mem_tspp = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ if (!mem_tspp) {
+ dev_err(&pdev->dev, "Missing MEM resource");
+ rc = -ENXIO;
+ goto err_res_dev;
+ }
+ device->base = ioremap(mem_tspp->start, resource_size(mem_tspp));
+ if (!device->base) {
+ dev_err(&pdev->dev, "ioremap failed");
+ goto err_map_dev;
+ }
+
+ mem_bam = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+ if (!mem_bam) {
+ pr_err("tspp: Missing bam MEM resource");
+ rc = -ENXIO;
+ goto err_res_bam;
+ }
+ memset(&device->bam_props, 0, sizeof(device->bam_props));
+ device->bam_props.phys_addr = mem_bam->start;
+ device->bam_props.virt_addr = ioremap(mem_bam->start,
+ resource_size(mem_bam));
+ if (!device->bam_props.virt_addr) {
+ dev_err(&pdev->dev, "ioremap failed");
+ goto err_map_bam;
+ }
+
+ /* map TSPP IRQ */
+ rc = platform_get_irq(pdev, 0);
+ if (rc > 0) {
+ device->tspp_irq = rc;
+ rc = request_irq(device->tspp_irq, tspp_isr, IRQF_SHARED,
+ dev_name(&pdev->dev), device);
+ if (rc) {
+ dev_err(&pdev->dev, "failed to request IRQ %d : %d",
+ device->tspp_irq, rc);
+ goto err_irq;
+ }
+ } else {
+ dev_err(&pdev->dev, "failed to get tspp IRQ");
+ goto err_irq;
+ }
+
+ /* BAM IRQ */
+ device->bam_irq = TSIF_BAM_IRQ;
+
+ /* GPIOs */
+ rc = tspp_start_gpios(device);
+ if (rc)
+ goto err_gpio;
+
+ /* power management */
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+#ifdef TSPP_USE_DEBUGFS
+ tspp_debugfs_init(device, 0);
+
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+ tsif_debugfs_init(&device->tsif[i], i);
+#endif /* TSPP_USE_DEBUGFS */
+
+ wake_lock_init(&device->wake_lock, WAKE_LOCK_SUSPEND,
+ dev_name(&pdev->dev));
+
+ /* set up pointers to ram-based 'registers' */
+ tspp_filter_table[0] = TSPP_PID_FILTER_TABLE0 + device->base;
+ tspp_filter_table[1] = TSPP_PID_FILTER_TABLE1 + device->base;
+ tspp_filter_table[2] = TSPP_PID_FILTER_TABLE2 + device->base;
+ tspp_key_table = TSPP_DATA_KEY + device->base;
+ tspp_global_performance = TSPP_GLOBAL_PERFORMANCE + device->base;
+ tspp_pipe_context = TSPP_PIPE_CONTEXT + device->base;
+ tspp_pipe_performance = TSPP_PIPE_PERFORMANCE + device->base;
+
+ device->bam_props.summing_threshold = 0x10;
+ device->bam_props.irq = device->bam_irq;
+ device->bam_props.manage = SPS_BAM_MGR_LOCAL;
+
+ if (sps_register_bam_device(&device->bam_props,
+ &device->bam_handle) != 0) {
+ pr_err("tspp: failed to register bam");
+ goto err_bam;
+ }
+
+ if (device->tsif_pclk && clk_enable(device->tsif_pclk) != 0) {
+ dev_err(&pdev->dev, "Can't start pclk");
+ goto err_pclk;
+ }
+ if (device->tsif_ref_clk && clk_enable(device->tsif_ref_clk) != 0) {
+ dev_err(&pdev->dev, "Can't start ref clk");
+ goto err_refclk;
+ }
+
+ spin_lock_init(&device->spinlock);
+ tasklet_init(&device->tlet, tspp_sps_complete_tlet,
+ (unsigned long)device);
+
+ /* initialize everything to a known state */
+ tspp_global_reset(device);
+
+ version = readl_relaxed(device->base + TSPP_VERSION);
+ if (version != 1)
+ pr_warn("tspp: unrecognized hw version=%i", version);
+
+ /* update the channels with the device */
+ for (i = 0; i < TSPP_NUM_CHANNELS; i++) {
+ channel = &channel_list[i];
+ channel->pdev = device;
+ }
+
+ /* everything is ok */
+ return 0;
+
+err_refclk:
+ if (device->tsif_pclk)
+ clk_disable(device->tsif_pclk);
+err_pclk:
+ sps_deregister_bam_device(device->bam_handle);
+err_bam:
+#ifdef TSPP_USE_DEBUGFS
+ tspp_debugfs_exit(device);
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+ tsif_debugfs_exit(&device->tsif[i]);
+#endif /* TSPP_USE_DEBUGFS */
+err_gpio:
+err_irq:
+ tspp_stop_gpios(device);
+ iounmap(device->bam_props.virt_addr);
+err_map_bam:
+err_res_bam:
+ iounmap(device->base);
+err_map_dev:
+err_res_dev:
+ iounmap(device->tsif[1].base);
+err_map_tsif1:
+err_res_tsif1:
+ iounmap(device->tsif[0].base);
+err_map_tsif0:
+err_res_tsif0:
+ if (device->tsif_ref_clk)
+ clk_put(device->tsif_ref_clk);
+err_refclock:
+ if (device->tsif_pclk)
+ clk_put(device->tsif_pclk);
+err_pclock:
+ kfree(device);
+
+out:
+ return rc;
+}
+
+static int __devexit msm_tspp_remove(struct platform_device *pdev)
+{
+#ifdef TSPP_USE_DEBUGFS
+ u32 i;
+#endif /* TSPP_USE_DEBUGFS */
+
+ struct tspp_device *device = platform_get_drvdata(pdev);
+
+ sps_deregister_bam_device(device->bam_handle);
+
+#ifdef TSPP_USE_DEBUGFS
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+ tsif_debugfs_exit(&device->tsif[i]);
+#endif /* TSPP_USE_DEBUGFS */
+
+ wake_lock_destroy(&device->wake_lock);
+ free_irq(device->tspp_irq, device);
+ tspp_stop_gpios(device);
+
+ iounmap(device->bam_props.virt_addr);
+ iounmap(device->base);
+ for (i = 0; i < TSPP_TSIF_INSTANCES; i++)
+ iounmap(device->tsif[i].base);
+
+ if (device->tsif_ref_clk)
+ clk_put(device->tsif_ref_clk);
+
+ if (device->tsif_pclk)
+ clk_put(device->tsif_pclk);
+
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_put(&pdev->dev);
+ kfree(device);
+
+ return 0;
+}
+
+/*** power management ***/
+
+static int tspp_runtime_suspend(struct device *dev)
+{
+ dev_dbg(dev, "pm_runtime: suspending...");
+ return 0;
+}
+
+static int tspp_runtime_resume(struct device *dev)
+{
+ dev_dbg(dev, "pm_runtime: resuming...");
+ return 0;
+}
+
+static const struct dev_pm_ops tspp_dev_pm_ops = {
+ .runtime_suspend = tspp_runtime_suspend,
+ .runtime_resume = tspp_runtime_resume,
+};
+
+static struct platform_driver msm_tspp_driver = {
+ .probe = msm_tspp_probe,
+ .remove = __exit_p(msm_tspp_remove),
+ .driver = {
+ .name = "msm_tspp",
+ .pm = &tspp_dev_pm_ops,
+ },
+};
+
+
+static int __init mod_init(void)
+{
+ u32 i;
+ int rc;
+
+ /* first register the driver, and check hardware */
+ rc = platform_driver_register(&msm_tspp_driver);
+ if (rc) {
+ pr_err("tspp: platform_driver_register failed: %d", rc);
+ goto err_register;
+ }
+
+ /* now make the char devs (channels) */
+ rc = alloc_chrdev_region(&tspp_minor, 0, TSPP_NUM_CHANNELS, "tspp");
+ if (rc) {
+ pr_err("tspp: alloc_chrdev_region failed: %d", rc);
+ goto err_devrgn;
+ }
+
+ tspp_class = class_create(THIS_MODULE, "tspp");
+ if (IS_ERR(tspp_class)) {
+ rc = PTR_ERR(tspp_class);
+ pr_err("tspp: Error creating class: %d", rc);
+ goto err_class;
+ }
+
+ for (i = 0; i < TSPP_NUM_CHANNELS; i++) {
+ if (tspp_channel_init(&channel_list[i]) != 0) {
+ pr_err("tspp_channel_init failed");
+ break;
+ }
+ }
+
+ return 0;
+
+err_class:
+ unregister_chrdev_region(0, TSPP_NUM_CHANNELS);
+err_devrgn:
+ platform_driver_unregister(&msm_tspp_driver);
+err_register:
+ return rc;
+}
+
+static void __exit mod_exit(void)
+{
+ u32 i;
+ struct tspp_channel *channel;
+
+ /* first delete upper layer interface */
+ for (i = 0; i < TSPP_NUM_CHANNELS; i++) {
+ channel = &channel_list[i];
+ device_destroy(tspp_class, channel->cdev.dev);
+ cdev_del(&channel->cdev);
+ }
+ class_destroy(tspp_class);
+ unregister_chrdev_region(0, TSPP_NUM_CHANNELS);
+
+ /* now delete low level driver */
+ platform_driver_unregister(&msm_tspp_driver);
+}
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_DESCRIPTION("TSPP character device interface");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index eeca25a..0a239da 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -138,8 +138,7 @@
unsigned short ret = NR_SG;
if (host->is_sps_mode) {
- if (NR_SG > MAX_NR_SG_SPS)
- ret = MAX_NR_SG_SPS;
+ ret = SPS_MAX_DESCS;
} else { /* DMA or PIO mode */
if (NR_SG > MAX_NR_SG_DMA_PIO)
ret = MAX_NR_SG_DMA_PIO;
@@ -148,28 +147,6 @@
return ret;
}
-static inline unsigned int msmsdcc_get_max_seg_size(struct msmsdcc_host *host)
-{
- unsigned int max_seg_size;
-
- /*
- * SPS BAM has limitation of max. number of descriptors.
- * max. # of descriptors = SPS_MAX_DESCS
- * each descriptor can point to SPS_MAX_DESC_SIZE (16KB)
- * So (nr_sg * max_seg_size) should be limited to the
- * max. size that all of the descriptors can point to.
- * i.e., (nr_sg * max_seg_size) = (SPS_MAX_DESCS * SPS_MAX_DESC_SIZE).
- */
- if (host->is_sps_mode) {
- max_seg_size = (SPS_MAX_DESCS * SPS_MAX_DESC_SIZE) /
- msmsdcc_get_nr_sg(host);
- } else { /* DMA or PIO mode */
- max_seg_size = MMC_MAX_REQ_SIZE;
- }
-
- return max_seg_size;
-}
-
#ifdef CONFIG_MMC_MSM_SPS_SUPPORT
static int msmsdcc_sps_reset_ep(struct msmsdcc_host *host,
struct msmsdcc_sps_ep_conn_data *ep);
@@ -2920,7 +2897,7 @@
} else {
status = gpio_direction_input(gpio_no);
if (!status)
- status = !gpio_get_value_cansleep(gpio_no);
+ status = gpio_get_value_cansleep(gpio_no);
gpio_free(gpio_no);
}
return status;
@@ -2933,15 +2910,36 @@
unsigned int status;
if (host->plat->status || host->plat->status_gpio) {
- if (host->plat->status)
+ if (host->plat->status) {
status = host->plat->status(mmc_dev(host->mmc));
- else
+ host->eject = !status;
+ } else {
status = msmsdcc_slot_status(host);
- host->eject = !status;
+ if (host->plat->is_status_gpio_active_low)
+ host->eject = status;
+ else
+ host->eject = !status;
+ }
+
if (status ^ host->oldstat) {
- pr_info("%s: Slot status change detected (%d -> %d)\n",
- mmc_hostname(host->mmc), host->oldstat, status);
+ if (host->plat->status)
+ pr_info("%s: Slot status change detected "
+ "(%d -> %d)\n",
+ mmc_hostname(host->mmc),
+ host->oldstat, status);
+ else if (host->plat->is_status_gpio_active_low)
+ pr_info("%s: Slot status change detected "
+ "(%d -> %d) and the card detect GPIO"
+ " is ACTIVE_LOW\n",
+ mmc_hostname(host->mmc),
+ host->oldstat, status);
+ else
+ pr_info("%s: Slot status change detected "
+ "(%d -> %d) and the card detect GPIO"
+ " is ACTIVE_HIGH\n",
+ mmc_hostname(host->mmc),
+ host->oldstat, status);
mmc_detect_change(host->mmc, 0);
}
host->oldstat = status;
@@ -4009,7 +4007,7 @@
mmc->max_blk_count = MMC_MAX_BLK_CNT;
mmc->max_req_size = MMC_MAX_REQ_SIZE;
- mmc->max_seg_size = msmsdcc_get_max_seg_size(host);
+ mmc->max_seg_size = mmc->max_req_size;
writel_relaxed(0, host->base + MMCIMASK0);
writel_relaxed(MCI_CLEAR_STATIC_MASK, host->base + MMCICLEAR);
@@ -4072,11 +4070,17 @@
*/
if (plat->status || plat->status_gpio) {
- if (plat->status)
+ if (plat->status) {
host->oldstat = plat->status(mmc_dev(host->mmc));
- else
+ host->eject = !host->oldstat;
+ } else {
host->oldstat = msmsdcc_slot_status(host);
- host->eject = !host->oldstat;
+
+ if (host->plat->is_status_gpio_active_low)
+ host->eject = host->oldstat;
+ else
+ host->eject = !host->oldstat;
+ }
}
if (plat->status_irq) {
@@ -4661,7 +4665,7 @@
size_t count, loff_t *ppos)
{
struct msmsdcc_host *host = (struct msmsdcc_host *) file->private_data;
- char buf[1024];
+ char buf[200];
int max, i;
i = 0;
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index a8332e51..d128984 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -242,8 +242,6 @@
/* Each descriptor is of length 8 bytes */
#define SPS_MAX_DESC_LENGTH 8
#define SPS_MAX_DESCS (SPS_MAX_DESC_FIFO_SIZE / SPS_MAX_DESC_LENGTH)
-#define SPS_MAX_SG_DESCS (MAX_SG_SIZE / SPS_MAX_DESC_SIZE)
-#define MAX_NR_SG_SPS (SPS_MAX_DESCS / SPS_MAX_SG_DESCS)
/*
* DMA limitations
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index 3f92731..f1af222 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -1192,7 +1192,7 @@
static void __exit cleanup_mtdchar(void)
{
unregister_mtd_user(&mtdchar_notifier);
- mntput(mtd_inode_mnt);
+ kern_unmount(mtd_inode_mnt);
unregister_filesystem(&mtd_inodefs_type);
__unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd");
}
diff --git a/drivers/net/qfec.c b/drivers/net/qfec.c
index d554fec..fc27837 100644
--- a/drivers/net/qfec.c
+++ b/drivers/net/qfec.c
@@ -33,17 +33,20 @@
#include "qfec.h"
#define QFEC_NAME "qfec"
-#define QFEC_DRV_VER "July 14 2011"
+#define QFEC_DRV_VER "Nov 29 2011"
#define ETH_BUF_SIZE 0x600
#define MAX_N_BD 50
#define MAC_ADDR_SIZE 6
#define RX_TX_BD_RATIO 8
-#define RX_BD_NUM 32
-#define TX_BD_NUM (RX_BD_NUM * RX_TX_BD_RATIO)
+#define TX_BD_NUM 256
+#define RX_BD_NUM 256
#define TX_BD_TI_RATIO 4
+#define MAX_MDIO_REG 32
+#define H_DPLX 0
+#define F_DPLX 1
/*
* logging macros
*/
@@ -52,6 +55,8 @@
#define QFEC_LOG_DBG2 4
#define QFEC_LOG_MDIO_W 8
#define QFEC_LOG_MDIO_R 16
+#define QFEC_MII_EXP_MASK (EXPANSION_LCWP | EXPANSION_ENABLENPAGE \
+ | EXPANSION_NPCAPABLE)
static int qfec_debug = QFEC_LOG_PR;
@@ -297,13 +302,15 @@
static inline void qfec_ring_head_adv(struct ring *p_ring)
{
- p_ring->head = ++p_ring->head % p_ring->len;
+ if (++p_ring->head == p_ring->len)
+ p_ring->head = 0;
p_ring->n_free--;
};
static inline void qfec_ring_tail_adv(struct ring *p_ring)
{
- p_ring->tail = ++p_ring->tail % p_ring->len;
+ if (++p_ring->tail == p_ring->len)
+ p_ring->tail = 0;
p_ring->n_free++;
};
@@ -809,6 +816,13 @@
qfec_reg_write(priv, STATUS_REG, INTRP_EN_REG_NIE | INTRP_EN_REG_RIE
| INTRP_EN_REG_TIE | INTRP_EN_REG_TUE | INTRP_EN_REG_ETE);
+ if (priv->mii.supports_gmii) {
+ /* Clear RGMII */
+ qfec_reg_read(priv, SG_RG_SMII_STATUS_REG);
+ /* Disable RGMII int */
+ qfec_reg_write(priv, INTRP_MASK_REG, 1);
+ }
+
return res;
}
@@ -874,9 +888,9 @@
* configure the PHY interface and clock routing and signal bits
*/
enum phy_intfc {
- intfc_mii = 0,
- intfc_rgmii = 1,
- intfc_revmii = 2,
+ INTFC_MII = 0,
+ INTFC_RGMII = 1,
+ INTFC_REVMII = 2,
};
static int qfec_intf_sel(struct qfec_priv *priv, unsigned int intfc)
@@ -885,7 +899,7 @@
QFEC_LOG(QFEC_LOG_DBG2, "%s: %d\n", __func__, intfc);
- if (intfc > intfc_revmii) {
+ if (intfc > INTFC_REVMII) {
QFEC_LOG_ERR("%s: range\n", __func__);
return -ENXIO;
}
@@ -972,9 +986,9 @@
};
enum speed {
- spd_10 = 0,
- spd_100 = 1,
- spd_1000 = 2,
+ SPD_10 = 0,
+ SPD_100 = 1,
+ SPD_1000 = 2,
};
/*
@@ -988,7 +1002,7 @@
QFEC_LOG(QFEC_LOG_DBG2, "%s: %d spd, %d dplx\n", __func__, spd, dplx);
- if (spd > spd_1000) {
+ if (spd > SPD_1000) {
QFEC_LOG_ERR("%s: range\n", __func__);
return -ENODEV;
}
@@ -999,7 +1013,7 @@
qfec_reg_write(priv, MAC_CONFIG_REG,
(qfec_reg_read(priv, MAC_CONFIG_REG)
& ~(MAC_CONFIG_REG_SPD | MAC_CONFIG_REG_DM))
- | p->spd | (dplx ? MAC_CONFIG_REG_DM : 0));
+ | p->spd | (dplx ? MAC_CONFIG_REG_DM : H_DPLX));
qfec_clkreg_write(priv, ETH_MD_REG, p->eth_md);
qfec_clkreg_write(priv, ETH_NS_REG, p->eth_ns);
@@ -1146,9 +1160,33 @@
}
/*
+ * MDIO show
+ */
+static int qfec_mdio_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct qfec_priv *priv = netdev_priv(to_net_dev(dev));
+ int n;
+ int l = 0;
+ int count = PAGE_SIZE;
+
+ QFEC_LOG(QFEC_LOG_DBG2, "%s:\n", __func__);
+
+ for (n = 0; n < MAX_MDIO_REG; n++) {
+ if (!(n % 8))
+ l += snprintf(&buf[l], count - l, "\n %02x: ", n);
+
+ l += snprintf(&buf[l], count - l, " %04x",
+ qfec_mdio_read(to_net_dev(dev), priv->phy_id, n));
+ }
+ l += snprintf(&buf[l], count - l, "\n");
+
+ return l;
+}
+
+/*
* get auto-negotiation results
*/
-
#define QFEC_100 (LPA_100HALF | LPA_100FULL | LPA_100HALF)
#define QFEC_100_FD (LPA_100FULL | LPA_100BASE4)
#define QFEC_10 (LPA_10HALF | LPA_10FULL)
@@ -1157,28 +1195,45 @@
static void qfec_get_an(struct net_device *dev, uint32_t *spd, uint32_t *dplx)
{
struct qfec_priv *priv = netdev_priv(dev);
- uint32_t status;
- uint32_t advert;
- uint32_t lpa;
- uint32_t flow;
+ uint32_t advert = qfec_mdio_read(dev, priv->phy_id, MII_ADVERTISE);
+ uint32_t lpa = qfec_mdio_read(dev, priv->phy_id, MII_LPA);
+ uint32_t mastCtrl = qfec_mdio_read(dev, priv->phy_id, MII_CTRL1000);
+ uint32_t mastStat = qfec_mdio_read(dev, priv->phy_id, MII_STAT1000);
+ uint32_t anExp = qfec_mdio_read(dev, priv->phy_id, MII_EXPANSION);
+ uint32_t status = advert & lpa;
+ uint32_t flow;
- advert = qfec_mdio_read(dev, priv->phy_id, MII_ADVERTISE);
- lpa = qfec_mdio_read(dev, priv->phy_id, MII_LPA);
- status = advert & lpa;
+ if (priv->mii.supports_gmii) {
+ if (((anExp & QFEC_MII_EXP_MASK) == QFEC_MII_EXP_MASK)
+ && (mastCtrl & ADVERTISE_1000FULL)
+ && (mastStat & LPA_1000FULL)) {
+ *spd = SPD_1000;
+ *dplx = F_DPLX;
+ goto pause;
+ }
- /* todo: check extended status register for 1G abilities */
+ else if (((anExp & QFEC_MII_EXP_MASK) == QFEC_MII_EXP_MASK)
+ && (mastCtrl & ADVERTISE_1000HALF)
+ && (mastStat & LPA_1000HALF)) {
+ *spd = SPD_1000;
+ *dplx = H_DPLX;
+ goto pause;
+ }
+ }
+ /* mii speeds */
if (status & QFEC_100) {
- *spd = spd_100;
- *dplx = status & QFEC_100_FD ? 1 : 0;
+ *spd = SPD_100;
+ *dplx = status & QFEC_100_FD ? F_DPLX : H_DPLX;
}
else if (status & QFEC_10) {
- *spd = spd_10;
- *dplx = status & QFEC_10_FD ? 1 : 0;
+ *spd = SPD_10;
+ *dplx = status & QFEC_10_FD ? F_DPLX : H_DPLX;
}
/* check pause */
+pause:
flow = qfec_reg_read(priv, FLOW_CONTROL_REG);
flow &= ~(FLOW_CONTROL_TFE | FLOW_CONTROL_RFE);
@@ -1202,8 +1257,8 @@
{
struct net_device *dev = (struct net_device *) data;
struct qfec_priv *priv = netdev_priv(dev);
- unsigned int spd = 0;
- unsigned int dplx = 1;
+ unsigned int spd = H_DPLX;
+ unsigned int dplx = F_DPLX;
mod_timer(&priv->phy_tmr, jiffies + HZ);
@@ -1609,15 +1664,8 @@
CNTR_INC(priv, rx_int);
/* check that valid interrupt occurred */
- if (unlikely(desc_status & BUF_OWN)) {
- char s[100];
-
- qfec_bd_fmt(s, sizeof(s), p_bd);
- QFEC_LOG_ERR("%s: owned by DMA, %08x, %s\n", __func__,
- qfec_reg_read(priv, CUR_HOST_RX_DES_REG), s);
- CNTR_INC(priv, rx_owned);
+ if (unlikely(desc_status & BUF_OWN))
return;
- }
/* accumulate missed-frame count (reg reset when read) */
priv->stats.rx_missed_errors += mis_fr_reg
@@ -1691,6 +1739,8 @@
break;
qfec_ring_tail_adv(p_ring);
}
+
+ qfec_reg_write(priv, STATUS_REG, STATUS_REG_RI);
}
/*
@@ -1735,7 +1785,7 @@
CNTR_INC(priv, norm_int);
/* receive interrupt */
- if (status & STATUS_REG_RI) {
+ if (status & STATUS_REG_RI) {
CNTR_INC(priv, rx_isr);
qfec_rx_int(dev);
}
@@ -1748,8 +1798,10 @@
/* gmac interrupt */
if (status & (STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI)) {
+ status &= ~(STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI);
CNTR_INC(priv, gmac_isr);
- int_bits |= STATUS_REG_GMI;
+ int_bits |= STATUS_REG_GPI | STATUS_REG_GMI | STATUS_REG_GLI;
+ qfec_reg_read(priv, SG_RG_SMII_STATUS_REG);
}
/* clear interrupts */
@@ -1823,7 +1875,14 @@
qfec_ptp_cfg(priv);
/* configure PHY - must be set before reset/hw_init */
- qfec_intf_sel(priv, intfc_mii);
+ priv->mii.supports_gmii = mii_check_gmii_support(&priv->mii);
+ if (priv->mii.supports_gmii) {
+ QFEC_LOG_ERR("%s: RGMII\n", __func__);
+ qfec_intf_sel(priv, INTFC_RGMII);
+ } else {
+ QFEC_LOG_ERR("%s: MII\n", __func__);
+ qfec_intf_sel(priv, INTFC_MII);
+ }
/* initialize controller after BDs allocated */
res = qfec_hw_init(priv);
@@ -1923,6 +1982,10 @@
spin_lock_irqsave(&priv->xmit_lock, flags);
+ /* If there is no room, on the ring try to free some up */
+ if (qfec_ring_room(p_ring) == 0)
+ qfec_tx_replenish(dev);
+
/* stop queuing if no resources available */
if (qfec_ring_room(p_ring) == 0) {
qfec_queue_stop(dev);
@@ -2477,6 +2540,7 @@
static DEVICE_ATTR(cmd, 0222, NULL, qfec_cmd);
static DEVICE_ATTR(cntrs, 0444, qfec_cntrs_show, NULL);
static DEVICE_ATTR(reg, 0444, qfec_reg_show, NULL);
+static DEVICE_ATTR(mdio, 0444, qfec_mdio_show, NULL);
static DEVICE_ATTR(stats, 0444, qfec_stats_show, NULL);
static DEVICE_ATTR(tstamp, 0444, qfec_tstamp_show, NULL);
@@ -2488,6 +2552,7 @@
device_create_file(&(dev->dev), &dev_attr_clk_reg) ||
device_create_file(&(dev->dev), &dev_attr_cmd) ||
device_create_file(&(dev->dev), &dev_attr_cntrs) ||
+ device_create_file(&(dev->dev), &dev_attr_mdio) ||
device_create_file(&(dev->dev), &dev_attr_reg) ||
device_create_file(&(dev->dev), &dev_attr_stats) ||
device_create_file(&(dev->dev), &dev_attr_tstamp))
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 84d4608..4e40468 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -458,4 +458,14 @@
http://ubuntuforums.org/showpost.php?p=10589647&postcount=17
+config MSM_RMNET_USB
+ tristate "RMNET USB Driver"
+ depends on USB_USBNET
+ help
+ Select this if you have a Qualcomm modem device connected via USB
+ supporting RMNET network interface.
+
+ To compile this driver as a module, choose M here: the module
+ will be called rmnet_usb. If unsure, choose N.
+
endmenu
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index c203fa2..84228c1 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -29,4 +29,5 @@
obj-$(CONFIG_USB_NET_CX82310_ETH) += cx82310_eth.o
obj-$(CONFIG_USB_NET_CDC_NCM) += cdc_ncm.o
obj-$(CONFIG_USB_VL600) += lg-vl600.o
-
+rmnet_usb-y := rmnet_usb_ctrl.o rmnet_usb_data.o
+obj-$(CONFIG_MSM_RMNET_USB) += rmnet_usb.o
diff --git a/drivers/net/usb/rmnet_usb_ctrl.c b/drivers/net/usb/rmnet_usb_ctrl.c
new file mode 100644
index 0000000..b693b18
--- /dev/null
+++ b/drivers/net/usb/rmnet_usb_ctrl.c
@@ -0,0 +1,1071 @@
+/* Copyright (c) 2011, 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/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/uaccess.h>
+#include <linux/termios.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include "rmnet_usb_ctrl.h"
+
+#define DEVICE_NAME "hsicctl"
+#define NUM_CTRL_CHANNELS 4
+#define DEFAULT_READ_URB_LENGTH 0x1000
+
+/*Output control lines.*/
+#define ACM_CTRL_DTR BIT(0)
+#define ACM_CTRL_RTS BIT(1)
+
+
+/*Input control lines.*/
+#define ACM_CTRL_DSR BIT(0)
+#define ACM_CTRL_CTS BIT(1)
+#define ACM_CTRL_RI BIT(2)
+#define ACM_CTRL_CD BIT(3)
+
+/* polling interval for Interrupt ep */
+#define HS_INTERVAL 7
+#define FS_LS_INTERVAL 3
+
+/*echo modem_wait > /sys/class/hsicctl/hsicctlx/modem_wait*/
+static ssize_t modem_wait_store(struct device *d, struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned int mdm_wait;
+ struct rmnet_ctrl_dev *dev = dev_get_drvdata(d);
+
+ if (!dev)
+ return -ENODEV;
+
+ sscanf(buf, "%u", &mdm_wait);
+
+ dev->mdm_wait_timeout = mdm_wait;
+
+ return n;
+}
+
+static ssize_t modem_wait_show(struct device *d, struct device_attribute *attr,
+ char *buf)
+{
+ struct rmnet_ctrl_dev *dev = dev_get_drvdata(d);
+
+ if (!dev)
+ return -ENODEV;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", dev->mdm_wait_timeout);
+}
+
+static DEVICE_ATTR(modem_wait, 0666, modem_wait_show, modem_wait_store);
+
+static int ctl_msg_dbg_mask;
+module_param_named(dump_ctrl_msg, ctl_msg_dbg_mask, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+
+enum {
+ MSM_USB_CTL_DEBUG = 1U << 0,
+ MSM_USB_CTL_DUMP_BUFFER = 1U << 1,
+};
+
+#define DUMP_BUFFER(prestr, cnt, buf) \
+do { \
+ if (ctl_msg_dbg_mask & MSM_USB_CTL_DUMP_BUFFER) \
+ print_hex_dump(KERN_INFO, prestr, DUMP_PREFIX_NONE, \
+ 16, 1, buf, cnt, false); \
+} while (0)
+
+#define DBG(x...) \
+ do { \
+ if (ctl_msg_dbg_mask & MSM_USB_CTL_DEBUG) \
+ pr_info(x); \
+ } while (0)
+
+struct rmnet_ctrl_dev *ctrl_dev[NUM_CTRL_CHANNELS];
+struct class *ctrldev_classp;
+static dev_t ctrldev_num;
+
+struct ctrl_pkt {
+ size_t data_size;
+ void *data;
+};
+
+struct ctrl_pkt_list_elem {
+ struct list_head list;
+ struct ctrl_pkt cpkt;
+};
+
+static void resp_avail_cb(struct urb *);
+
+static int is_dev_connected(struct rmnet_ctrl_dev *dev)
+{
+ if (dev) {
+ mutex_lock(&dev->dev_lock);
+ if (!dev->intf) {
+ mutex_unlock(&dev->dev_lock);
+ return 0;
+ }
+ mutex_unlock(&dev->dev_lock);
+ return 1;
+ }
+ return 0;
+}
+
+static void notification_available_cb(struct urb *urb)
+{
+ int status;
+ struct usb_cdc_notification *ctrl;
+ struct usb_device *udev;
+ struct rmnet_ctrl_dev *dev = urb->context;
+
+ udev = interface_to_usbdev(dev->intf);
+
+ switch (urb->status) {
+ case 0:
+ /*success*/
+ break;
+
+ /*do not resubmit*/
+ case -ESHUTDOWN:
+ case -ENOENT:
+ case -ECONNRESET:
+ case -EPROTO:
+ return;
+ case -EPIPE:
+ pr_err_ratelimited("%s: Stall on int endpoint\n", __func__);
+ /* TBD : halt to be cleared in work */
+ return;
+
+ /*resubmit*/
+ case -EOVERFLOW:
+ pr_err_ratelimited("%s: Babble error happened\n", __func__);
+ default:
+ pr_debug_ratelimited("%s: Non zero urb status = %d\n",
+ __func__, urb->status);
+ goto resubmit_int_urb;
+ }
+
+ ctrl = urb->transfer_buffer;
+
+ switch (ctrl->bNotificationType) {
+ case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
+ dev->resp_avail_cnt++;
+ usb_fill_control_urb(dev->rcvurb, udev,
+ usb_rcvctrlpipe(udev, 0),
+ (unsigned char *)dev->in_ctlreq,
+ dev->rcvbuf,
+ DEFAULT_READ_URB_LENGTH,
+ resp_avail_cb, dev);
+
+ status = usb_submit_urb(dev->rcvurb, GFP_ATOMIC);
+ if (status) {
+ dev_err(dev->devicep,
+ "%s: Error submitting Read URB %d\n", __func__, status);
+ goto resubmit_int_urb;
+ }
+
+ if (!dev->resp_available) {
+ dev->resp_available = true;
+ wake_up(&dev->open_wait_queue);
+ }
+
+ return;
+ default:
+ dev_err(dev->devicep,
+ "%s:Command not implemented\n", __func__);
+ }
+
+resubmit_int_urb:
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status)
+ dev_err(dev->devicep, "%s: Error re-submitting Int URB %d\n",
+ __func__, status);
+
+ return;
+}
+
+static void resp_avail_cb(struct urb *urb)
+{
+ struct usb_device *udev;
+ struct ctrl_pkt_list_elem *list_elem = NULL;
+ struct rmnet_ctrl_dev *dev = urb->context;
+ void *cpkt;
+ int status = 0;
+ size_t cpkt_size = 0;
+
+ udev = interface_to_usbdev(dev->intf);
+
+ switch (urb->status) {
+ case 0:
+ /*success*/
+ dev->get_encap_resp_cnt++;
+ break;
+
+ /*do not resubmit*/
+ case -ESHUTDOWN:
+ case -ENOENT:
+ case -ECONNRESET:
+ case -EPROTO:
+ return;
+
+ /*resubmit*/
+ case -EOVERFLOW:
+ pr_err_ratelimited("%s: Babble error happened\n", __func__);
+ default:
+ pr_debug_ratelimited("%s: Non zero urb status = %d\n",
+ __func__, urb->status);
+ goto resubmit_int_urb;
+ }
+
+ dev_dbg(dev->devicep, "Read %d bytes for %s\n",
+ urb->actual_length, dev->name);
+
+ cpkt = urb->transfer_buffer;
+ cpkt_size = urb->actual_length;
+
+ list_elem = kmalloc(sizeof(struct ctrl_pkt_list_elem), GFP_ATOMIC);
+ if (!list_elem) {
+ dev_err(dev->devicep, "%s: list_elem alloc failed\n", __func__);
+ return;
+ }
+ list_elem->cpkt.data = kmalloc(cpkt_size, GFP_ATOMIC);
+ if (!list_elem->cpkt.data) {
+ dev_err(dev->devicep, "%s: list_elem->data alloc failed\n",
+ __func__);
+ kfree(list_elem);
+ return;
+ }
+ memcpy(list_elem->cpkt.data, cpkt, cpkt_size);
+ list_elem->cpkt.data_size = cpkt_size;
+ spin_lock(&dev->rx_lock);
+ list_add_tail(&list_elem->list, &dev->rx_list);
+ spin_unlock(&dev->rx_lock);
+
+ wake_up(&dev->read_wait_queue);
+
+resubmit_int_urb:
+ /*re-submit int urb to check response available*/
+ status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
+ if (status)
+ dev_err(dev->devicep, "%s: Error re-submitting Int URB %d\n",
+ __func__, status);
+}
+
+static int rmnet_usb_ctrl_start_rx(struct rmnet_ctrl_dev *dev)
+{
+ int retval = 0;
+
+ retval = usb_autopm_get_interface(dev->intf);
+ if (retval < 0) {
+ dev_err(dev->devicep, "%s Resumption fail\n", __func__);
+ goto done_nopm;
+ }
+
+ retval = usb_submit_urb(dev->inturb, GFP_KERNEL);
+ if (retval < 0)
+ dev_err(dev->devicep, "%s Intr submit %d\n", __func__, retval);
+
+ usb_autopm_put_interface(dev->intf);
+
+done_nopm:
+ return retval;
+}
+
+int rmnet_usb_ctrl_stop_rx(struct rmnet_ctrl_dev *dev)
+{
+ if (!is_dev_connected(dev)) {
+ dev_dbg(dev->devicep, "%s: Ctrl device disconnected\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ dev_dbg(dev->devicep, "%s\n", __func__);
+
+ usb_kill_urb(dev->rcvurb);
+ usb_kill_urb(dev->inturb);
+
+ return 0;
+}
+
+int rmnet_usb_ctrl_start(struct rmnet_ctrl_dev *dev)
+{
+ int status = 0;
+
+ mutex_lock(&dev->dev_lock);
+ if (dev->is_opened)
+ status = rmnet_usb_ctrl_start_rx(dev);
+ mutex_unlock(&dev->dev_lock);
+
+ return status;
+}
+
+static int rmnet_usb_ctrl_alloc_rx(struct rmnet_ctrl_dev *dev)
+{
+ int retval = -ENOMEM;
+
+ dev->rcvurb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->rcvurb) {
+ pr_err("%s: Error allocating read urb\n", __func__);
+ goto nomem;
+ }
+
+ dev->rcvbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
+ if (!dev->rcvbuf) {
+ pr_err("%s: Error allocating read buffer\n", __func__);
+ goto nomem;
+ }
+
+ dev->in_ctlreq = kmalloc(sizeof(*dev->in_ctlreq), GFP_KERNEL);
+ if (!dev->in_ctlreq) {
+ pr_err("%s: Error allocating setup packet buffer\n", __func__);
+ goto nomem;
+ }
+
+ return 0;
+
+nomem:
+ usb_free_urb(dev->rcvurb);
+ kfree(dev->rcvbuf);
+ kfree(dev->in_ctlreq);
+
+ return retval;
+
+}
+static int rmnet_usb_ctrl_write_cmd(struct rmnet_ctrl_dev *dev)
+{
+ int retval = 0;
+ struct usb_device *udev;
+
+ if (!is_dev_connected(dev))
+ return -ENODEV;
+
+ udev = interface_to_usbdev(dev->intf);
+ retval = usb_autopm_get_interface(dev->intf);
+ if (retval < 0) {
+ dev_err(dev->devicep, "%s: Unable to resume interface: %d\n",
+ __func__, retval);
+
+ /*
+ * Revisit if (retval == -EPERM)
+ * rmnet_usb_suspend(dev->intf, PMSG_SUSPEND);
+ */
+
+ return retval;
+ }
+ dev->set_ctrl_line_state_cnt++;
+ retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+ (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE),
+ dev->cbits_tomdm,
+ dev->intf->cur_altsetting->desc.bInterfaceNumber,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+ usb_autopm_put_interface(dev->intf);
+
+ return retval;
+}
+
+static void ctrl_write_callback(struct urb *urb)
+{
+ struct rmnet_ctrl_dev *dev = urb->context;
+
+ if (urb->status) {
+ dev->tx_ctrl_err_cnt++;
+ pr_debug_ratelimited("Write status/size %d/%d\n",
+ urb->status, urb->actual_length);
+ }
+
+ kfree(urb->setup_packet);
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ usb_autopm_put_interface_async(dev->intf);
+}
+
+static int rmnet_usb_ctrl_write(struct rmnet_ctrl_dev *dev, char *buf,
+ size_t size)
+{
+ int result;
+ struct urb *sndurb;
+ struct usb_ctrlrequest *out_ctlreq;
+ struct usb_device *udev;
+
+ if (!is_dev_connected(dev))
+ return -ENETRESET;
+
+ udev = interface_to_usbdev(dev->intf);
+
+ sndurb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!sndurb) {
+ dev_err(dev->devicep, "Error allocating read urb\n");
+ return -ENOMEM;
+ }
+
+ out_ctlreq = kmalloc(sizeof(*out_ctlreq), GFP_KERNEL);
+ if (!out_ctlreq) {
+ usb_free_urb(sndurb);
+ dev_err(dev->devicep, "Error allocating setup packet buffer\n");
+ return -ENOMEM;
+ }
+
+ /* CDC Send Encapsulated Request packet */
+ out_ctlreq->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE);
+ out_ctlreq->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
+ out_ctlreq->wValue = 0;
+ out_ctlreq->wIndex = dev->intf->cur_altsetting->desc.bInterfaceNumber;
+ out_ctlreq->wLength = cpu_to_le16(size);
+
+ usb_fill_control_urb(sndurb, udev,
+ usb_sndctrlpipe(udev, 0),
+ (unsigned char *)out_ctlreq, (void *)buf, size,
+ ctrl_write_callback, dev);
+
+ result = usb_autopm_get_interface_async(dev->intf);
+ if (result < 0) {
+ dev_err(dev->devicep, "%s: Unable to resume interface: %d\n",
+ __func__, result);
+
+ /*
+ * Revisit: if (result == -EPERM)
+ * rmnet_usb_suspend(dev->intf, PMSG_SUSPEND);
+ */
+
+ usb_free_urb(sndurb);
+ kfree(out_ctlreq);
+ return result;
+ }
+
+ usb_anchor_urb(sndurb, &dev->tx_submitted);
+ dev->snd_encap_cmd_cnt++;
+ result = usb_submit_urb(sndurb, GFP_KERNEL);
+ if (result < 0) {
+ dev_err(dev->devicep, "%s: Submit URB error %d\n",
+ __func__, result);
+ dev->snd_encap_cmd_cnt--;
+ usb_autopm_put_interface_async(dev->intf);
+ usb_unanchor_urb(sndurb);
+ usb_free_urb(sndurb);
+ kfree(out_ctlreq);
+ return result;
+ }
+
+ return size;
+}
+
+static int rmnet_ctl_open(struct inode *inode, struct file *file)
+{
+ int retval = 0;
+ struct rmnet_ctrl_dev *dev =
+ container_of(inode->i_cdev, struct rmnet_ctrl_dev, cdev);
+
+ if (!dev)
+ return -ENODEV;
+
+ if (dev->is_opened)
+ goto already_opened;
+
+ctrl_open:
+ if (!is_dev_connected(dev)) {
+ dev_dbg(dev->devicep, "%s: Device not connected\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ /*block open to get first response available from mdm*/
+ if (dev->mdm_wait_timeout && !dev->resp_available) {
+ retval = wait_event_interruptible_timeout(
+ dev->open_wait_queue,
+ dev->resp_available ||
+ !is_dev_connected(dev),
+ msecs_to_jiffies(dev->mdm_wait_timeout *
+ 1000));
+ if (retval == 0) {
+ dev_err(dev->devicep, "%s: Timeout opening %s\n",
+ __func__, dev->name);
+ return -ETIMEDOUT;
+ } else if (retval < 0) {
+ dev_err(dev->devicep, "%s: Error waiting for %s\n",
+ __func__, dev->name);
+ return retval;
+ }
+
+ goto ctrl_open;
+ }
+
+ if (!dev->resp_available) {
+ dev_dbg(dev->devicep, "%s: Connection timedout opening %s\n",
+ __func__, dev->name);
+ return -ETIMEDOUT;
+ }
+
+ mutex_lock(&dev->dev_lock);
+ dev->is_opened = 1;
+ mutex_unlock(&dev->dev_lock);
+
+ file->private_data = dev;
+
+already_opened:
+ DBG("%s: Open called for %s\n", __func__, dev->name);
+
+ return 0;
+}
+
+static int rmnet_ctl_release(struct inode *inode, struct file *file)
+{
+ struct ctrl_pkt_list_elem *list_elem = NULL;
+ struct rmnet_ctrl_dev *dev;
+ unsigned long flag;
+
+ dev = file->private_data;
+ if (!dev)
+ return -ENODEV;
+
+ DBG("%s Called on %s device\n", __func__, dev->name);
+
+ spin_lock_irqsave(&dev->rx_lock, flag);
+ while (!list_empty(&dev->rx_list)) {
+ list_elem = list_first_entry(
+ &dev->rx_list,
+ struct ctrl_pkt_list_elem,
+ list);
+ list_del(&list_elem->list);
+ kfree(list_elem->cpkt.data);
+ kfree(list_elem);
+ }
+ spin_unlock_irqrestore(&dev->rx_lock, flag);
+
+ mutex_lock(&dev->dev_lock);
+ dev->is_opened = 0;
+ mutex_unlock(&dev->dev_lock);
+
+ rmnet_usb_ctrl_stop_rx(dev);
+
+ if (is_dev_connected(dev))
+ usb_kill_anchored_urbs(&dev->tx_submitted);
+
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static ssize_t rmnet_ctl_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ int retval = 0;
+ int bytes_to_read;
+ struct rmnet_ctrl_dev *dev;
+ struct ctrl_pkt_list_elem *list_elem = NULL;
+ unsigned long flags;
+
+ dev = file->private_data;
+ if (!dev)
+ return -ENODEV;
+
+ DBG("%s: Read from %s\n", __func__, dev->name);
+
+ctrl_read:
+ if (!is_dev_connected(dev)) {
+ dev_err(dev->devicep, "%s: Device not connected\n",
+ __func__);
+ return -ENETRESET;
+ }
+ spin_lock_irqsave(&dev->rx_lock, flags);
+ if (list_empty(&dev->rx_list)) {
+ spin_unlock_irqrestore(&dev->rx_lock, flags);
+
+ retval = wait_event_interruptible(dev->read_wait_queue,
+ !list_empty(&dev->rx_list) ||
+ !is_dev_connected(dev));
+ if (retval < 0)
+ return retval;
+
+ goto ctrl_read;
+ }
+
+ list_elem = list_first_entry(&dev->rx_list,
+ struct ctrl_pkt_list_elem, list);
+ bytes_to_read = (uint32_t)(list_elem->cpkt.data_size);
+ if (bytes_to_read > count) {
+ spin_unlock_irqrestore(&dev->rx_lock, flags);
+ dev_err(dev->devicep, "%s: Packet size %d > buf size %d\n",
+ __func__, bytes_to_read, count);
+ return -ENOMEM;
+ }
+ spin_unlock_irqrestore(&dev->rx_lock, flags);
+
+ if (copy_to_user(buf, list_elem->cpkt.data, bytes_to_read)) {
+ dev_err(dev->devicep,
+ "%s: copy_to_user failed for %s\n",
+ __func__, dev->name);
+ return -EFAULT;
+ }
+ spin_lock_irqsave(&dev->rx_lock, flags);
+ list_del(&list_elem->list);
+ spin_unlock_irqrestore(&dev->rx_lock, flags);
+
+ kfree(list_elem->cpkt.data);
+ kfree(list_elem);
+ DBG("%s: Returning %d bytes to %s\n", __func__, bytes_to_read,
+ dev->name);
+ DUMP_BUFFER("Read: ", bytes_to_read, buf);
+
+ return bytes_to_read;
+}
+
+static ssize_t rmnet_ctl_write(struct file *file, const char __user * buf,
+ size_t size, loff_t *pos)
+{
+ int status;
+ void *wbuf;
+ struct rmnet_ctrl_dev *dev = file->private_data;
+
+ if (!dev)
+ return -ENODEV;
+
+ if (size <= 0)
+ return -EINVAL;
+
+ if (!is_dev_connected(dev))
+ return -ENETRESET;
+
+ DBG("%s: Writing %i bytes on %s\n", __func__, size, dev->name);
+
+ wbuf = kmalloc(size , GFP_KERNEL);
+ if (!wbuf)
+ return -ENOMEM;
+
+ status = copy_from_user(wbuf , buf, size);
+ if (status) {
+ dev_err(dev->devicep,
+ "%s: Unable to copy data from userspace %d\n",
+ __func__, status);
+ kfree(wbuf);
+ return status;
+ }
+ DUMP_BUFFER("Write: ", size, buf);
+
+ status = rmnet_usb_ctrl_write(dev, wbuf, size);
+ if (status == size)
+ return size;
+
+ return status;
+}
+
+static int rmnet_ctrl_tiocmset(struct rmnet_ctrl_dev *dev, unsigned int set,
+ unsigned int clear)
+{
+ mutex_lock(&dev->dev_lock);
+ if (set & TIOCM_DTR)
+ dev->cbits_tomdm |= ACM_CTRL_DTR;
+
+ /*
+ * TBD if (set & TIOCM_RTS)
+ * dev->cbits_tomdm |= ACM_CTRL_RTS;
+ */
+
+ if (clear & TIOCM_DTR)
+ dev->cbits_tomdm &= ~ACM_CTRL_DTR;
+
+ /*
+ * (clear & TIOCM_RTS)
+ * dev->cbits_tomdm &= ~ACM_CTRL_RTS;
+ */
+
+ mutex_unlock(&dev->dev_lock);
+
+ return rmnet_usb_ctrl_write_cmd(dev);
+}
+
+static int rmnet_ctrl_tiocmget(struct rmnet_ctrl_dev *dev)
+{
+ int ret;
+
+ mutex_lock(&dev->dev_lock);
+ ret =
+ /*
+ * TBD(dev->cbits_tolocal & ACM_CTRL_DSR ? TIOCM_DSR : 0) |
+ * (dev->cbits_tolocal & ACM_CTRL_CTS ? TIOCM_CTS : 0) |
+ */
+ (dev->cbits_tolocal & ACM_CTRL_CD ? TIOCM_CD : 0) |
+ /*
+ * TBD (dev->cbits_tolocal & ACM_CTRL_RI ? TIOCM_RI : 0) |
+ *(dev->cbits_tomdm & ACM_CTRL_RTS ? TIOCM_RTS : 0) |
+ */
+ (dev->cbits_tomdm & ACM_CTRL_DTR ? TIOCM_DTR : 0);
+ mutex_unlock(&dev->dev_lock);
+
+ return ret;
+}
+
+static long rmnet_ctrl_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret;
+ struct rmnet_ctrl_dev *dev;
+
+ dev = file->private_data;
+ if (!dev)
+ return -ENODEV;
+
+ switch (cmd) {
+ case TIOCMGET:
+
+ ret = rmnet_ctrl_tiocmget(dev);
+ break;
+ case TIOCMSET:
+ ret = rmnet_ctrl_tiocmset(dev, arg, ~arg);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct file_operations ctrldev_fops = {
+ .owner = THIS_MODULE,
+ .read = rmnet_ctl_read,
+ .write = rmnet_ctl_write,
+ .unlocked_ioctl = rmnet_ctrl_ioctl,
+ .open = rmnet_ctl_open,
+ .release = rmnet_ctl_release,
+};
+
+int rmnet_usb_ctrl_probe(struct usb_interface *intf,
+ struct usb_host_endpoint *int_in, struct rmnet_ctrl_dev *dev)
+{
+ u16 wMaxPacketSize;
+ struct usb_endpoint_descriptor *ep;
+ struct usb_device *udev;
+ int interval;
+ int ret = 0;
+
+ udev = interface_to_usbdev(intf);
+
+ if (!dev) {
+ pr_err("%s: Ctrl device not found\n", __func__);
+ return -ENODEV;
+ }
+ dev->int_pipe = usb_rcvintpipe(udev,
+ int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+ mutex_lock(&dev->dev_lock);
+ dev->intf = intf;
+
+ /*TBD: for now just update CD status*/
+ dev->cbits_tolocal = ACM_CTRL_CD;
+
+ /*send DTR high to modem*/
+ dev->cbits_tomdm = ACM_CTRL_DTR;
+ mutex_unlock(&dev->dev_lock);
+
+ dev->resp_available = false;
+ dev->snd_encap_cmd_cnt = 0;
+ dev->get_encap_resp_cnt = 0;
+ dev->resp_avail_cnt = 0;
+ dev->tx_ctrl_err_cnt = 0;
+ dev->set_ctrl_line_state_cnt = 0;
+
+ dev->inturb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->inturb) {
+ dev_err(dev->devicep, "Error allocating int urb\n");
+ return -ENOMEM;
+ }
+
+ /*use max pkt size from ep desc*/
+ ep = &dev->intf->cur_altsetting->endpoint[0].desc;
+ wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
+
+ dev->intbuf = kmalloc(wMaxPacketSize, GFP_KERNEL);
+ if (!dev->intbuf) {
+ usb_free_urb(dev->inturb);
+ dev_err(dev->devicep, "Error allocating int buffer\n");
+ return -ENOMEM;
+ }
+
+ dev->in_ctlreq->bRequestType =
+ (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
+ dev->in_ctlreq->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
+ dev->in_ctlreq->wValue = 0;
+ dev->in_ctlreq->wIndex =
+ dev->intf->cur_altsetting->desc.bInterfaceNumber;
+ dev->in_ctlreq->wLength = cpu_to_le16(DEFAULT_READ_URB_LENGTH);
+
+ interval =
+ max((int)int_in->desc.bInterval,
+ (udev->speed == USB_SPEED_HIGH) ? HS_INTERVAL : FS_LS_INTERVAL);
+
+ usb_fill_int_urb(dev->inturb, udev,
+ dev->int_pipe,
+ dev->intbuf, wMaxPacketSize,
+ notification_available_cb, dev, interval);
+
+ ret = rmnet_usb_ctrl_write_cmd(dev);
+ if (ret < 0)
+ return ret;
+
+ return rmnet_usb_ctrl_start_rx(dev);
+}
+
+void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *dev)
+{
+ rmnet_usb_ctrl_stop_rx(dev);
+
+ mutex_lock(&dev->dev_lock);
+
+ /*TBD: for now just update CD status*/
+ dev->cbits_tolocal = ~ACM_CTRL_CD;
+
+ dev->cbits_tomdm = ~ACM_CTRL_DTR;
+ dev->intf = NULL;
+ mutex_unlock(&dev->dev_lock);
+
+ usb_free_urb(dev->inturb);
+ dev->inturb = NULL;
+
+ kfree(dev->intbuf);
+ dev->intbuf = NULL;
+
+ usb_kill_anchored_urbs(&dev->tx_submitted);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE 1024
+static ssize_t rmnet_usb_ctrl_read_stats(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct rmnet_ctrl_dev *dev;
+ char *buf;
+ int ret;
+ int i;
+ int temp = 0;
+
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < NUM_CTRL_CHANNELS; i++) {
+ dev = ctrl_dev[i];
+ if (!dev)
+ continue;
+
+ temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ "\n#ctrl_dev: %p Name: %s#\n"
+ "snd encap cmd cnt %u\n"
+ "resp avail cnt: %u\n"
+ "get encap resp cnt: %u\n"
+ "set ctrl line state cnt: %u\n"
+ "tx_err_cnt: %u\n"
+ "cbits_tolocal: %d\n"
+ "cbits_tomdm: %d\n"
+ "mdm_wait_timeout: %u\n"
+ "dev opened: %s\n",
+ dev, dev->name,
+ dev->snd_encap_cmd_cnt,
+ dev->resp_avail_cnt,
+ dev->get_encap_resp_cnt,
+ dev->set_ctrl_line_state_cnt,
+ dev->tx_ctrl_err_cnt,
+ dev->cbits_tolocal,
+ dev->cbits_tomdm,
+ dev->mdm_wait_timeout,
+ dev->is_opened ? "OPEN" : "CLOSE");
+
+ }
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static ssize_t rmnet_usb_ctrl_reset_stats(struct file *file, const char __user *
+ buf, size_t count, loff_t *ppos)
+{
+ struct rmnet_ctrl_dev *dev;
+ int i;
+
+ for (i = 0; i < NUM_CTRL_CHANNELS; i++) {
+ dev = ctrl_dev[i];
+ if (!dev)
+ continue;
+
+ dev->snd_encap_cmd_cnt = 0;
+ dev->resp_avail_cnt = 0;
+ dev->get_encap_resp_cnt = 0;
+ dev->set_ctrl_line_state_cnt = 0;
+ dev->tx_ctrl_err_cnt = 0;
+ }
+ return count;
+}
+
+const struct file_operations rmnet_usb_ctrl_stats_ops = {
+ .read = rmnet_usb_ctrl_read_stats,
+ .write = rmnet_usb_ctrl_reset_stats,
+};
+
+struct dentry *usb_ctrl_dent;
+struct dentry *usb_ctrl_dfile;
+static void rmnet_usb_ctrl_debugfs_init(void)
+{
+ usb_ctrl_dent = debugfs_create_dir("rmnet_usb_ctrl", 0);
+ if (IS_ERR(usb_ctrl_dent))
+ return;
+
+ usb_ctrl_dfile = debugfs_create_file("status", 0644, usb_ctrl_dent, 0,
+ &rmnet_usb_ctrl_stats_ops);
+ if (!usb_ctrl_dfile || IS_ERR(usb_ctrl_dfile))
+ debugfs_remove(usb_ctrl_dent);
+}
+
+static void rmnet_usb_ctrl_debugfs_exit(void)
+{
+ debugfs_remove(usb_ctrl_dfile);
+ debugfs_remove(usb_ctrl_dent);
+}
+
+#else
+static void rmnet_usb_ctrl_debugfs_init(void) { }
+static void rmnet_usb_ctrl_debugfs_exit(void) { }
+#endif
+
+int rmnet_usb_ctrl_init(void)
+{
+ struct rmnet_ctrl_dev *dev;
+ int n;
+ int status;
+
+ for (n = 0; n < NUM_CTRL_CHANNELS; ++n) {
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ status = -ENOMEM;
+ goto error0;
+ }
+ /*for debug purpose*/
+ snprintf(dev->name, CTRL_DEV_MAX_LEN, "hsicctl%d", n);
+
+ mutex_init(&dev->dev_lock);
+ spin_lock_init(&dev->rx_lock);
+ init_waitqueue_head(&dev->read_wait_queue);
+ init_waitqueue_head(&dev->open_wait_queue);
+ INIT_LIST_HEAD(&dev->rx_list);
+ init_usb_anchor(&dev->tx_submitted);
+
+ status = rmnet_usb_ctrl_alloc_rx(dev);
+ if (status < 0) {
+ kfree(dev);
+ goto error0;
+ }
+
+ ctrl_dev[n] = dev;
+ }
+
+ status = alloc_chrdev_region(&ctrldev_num, 0, NUM_CTRL_CHANNELS,
+ DEVICE_NAME);
+ if (IS_ERR_VALUE(status)) {
+ pr_err("ERROR:%s: alloc_chrdev_region() ret %i.\n",
+ __func__, status);
+ goto error0;
+ }
+
+ ctrldev_classp = class_create(THIS_MODULE, DEVICE_NAME);
+ if (IS_ERR(ctrldev_classp)) {
+ pr_err("ERROR:%s: class_create() ENOMEM\n", __func__);
+ status = -ENOMEM;
+ goto error1;
+ }
+ for (n = 0; n < NUM_CTRL_CHANNELS; ++n) {
+ cdev_init(&ctrl_dev[n]->cdev, &ctrldev_fops);
+ ctrl_dev[n]->cdev.owner = THIS_MODULE;
+
+ status = cdev_add(&ctrl_dev[n]->cdev, (ctrldev_num + n), 1);
+
+ if (IS_ERR_VALUE(status)) {
+ pr_err("%s: cdev_add() ret %i\n", __func__, status);
+ kfree(ctrl_dev[n]);
+ goto error2;
+ }
+
+ ctrl_dev[n]->devicep =
+ device_create(ctrldev_classp, NULL,
+ (ctrldev_num + n), NULL,
+ DEVICE_NAME "%d", n);
+
+ if (IS_ERR(ctrl_dev[n]->devicep)) {
+ pr_err("%s: device_create() ENOMEM\n", __func__);
+ status = -ENOMEM;
+ cdev_del(&ctrl_dev[n]->cdev);
+ kfree(ctrl_dev[n]);
+ goto error2;
+ }
+ /*create /sys/class/hsicctl/hsicctlx/modem_wait*/
+ status = device_create_file(ctrl_dev[n]->devicep,
+ &dev_attr_modem_wait);
+ if (status) {
+ device_destroy(ctrldev_classp,
+ MKDEV(MAJOR(ctrldev_num), n));
+ cdev_del(&ctrl_dev[n]->cdev);
+ kfree(ctrl_dev[n]);
+ goto error2;
+ }
+ dev_set_drvdata(ctrl_dev[n]->devicep, ctrl_dev[n]);
+ }
+
+ rmnet_usb_ctrl_debugfs_init();
+ pr_info("rmnet usb ctrl Initialized.\n");
+ return 0;
+
+error2:
+ while (--n >= 0) {
+ cdev_del(&ctrl_dev[n]->cdev);
+ device_destroy(ctrldev_classp,
+ MKDEV(MAJOR(ctrldev_num), n));
+ }
+
+ class_destroy(ctrldev_classp);
+ n = NUM_CTRL_CHANNELS;
+error1:
+ unregister_chrdev_region(MAJOR(ctrldev_num), NUM_CTRL_CHANNELS);
+error0:
+ while (--n >= 0)
+ kfree(ctrl_dev[n]);
+
+ return status;
+}
+
+void rmnet_usb_ctrl_exit(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_CTRL_CHANNELS; ++i) {
+ if (!ctrl_dev[i])
+ return;
+
+ kfree(ctrl_dev[i]->in_ctlreq);
+ kfree(ctrl_dev[i]->rcvbuf);
+ kfree(ctrl_dev[i]->intbuf);
+ usb_free_urb(ctrl_dev[i]->rcvurb);
+ usb_free_urb(ctrl_dev[i]->inturb);
+#if defined(DEBUG)
+ device_remove_file(ctrl_dev[i]->devicep, &dev_attr_modem_wait);
+#endif
+ cdev_del(&ctrl_dev[i]->cdev);
+ kfree(ctrl_dev[i]);
+ ctrl_dev[i] = NULL;
+ device_destroy(ctrldev_classp, MKDEV(MAJOR(ctrldev_num), i));
+ }
+
+ class_destroy(ctrldev_classp);
+ unregister_chrdev_region(MAJOR(ctrldev_num), NUM_CTRL_CHANNELS);
+ rmnet_usb_ctrl_debugfs_exit();
+}
diff --git a/drivers/net/usb/rmnet_usb_ctrl.h b/drivers/net/usb/rmnet_usb_ctrl.h
new file mode 100644
index 0000000..f6e5876
--- /dev/null
+++ b/drivers/net/usb/rmnet_usb_ctrl.h
@@ -0,0 +1,82 @@
+/* Copyright (c) 2011, 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 __RMNET_USB_CTRL_H
+#define __RMNET_USB_CTRL_H
+
+#include <linux/mutex.h>
+#include <linux/usb.h>
+#include <linux/cdev.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
+
+#define CTRL_DEV_MAX_LEN 10
+
+struct rmnet_ctrl_dev {
+
+ /*for debugging purpose*/
+ char name[CTRL_DEV_MAX_LEN];
+
+ struct cdev cdev;
+ struct device *devicep;
+
+ struct usb_interface *intf;
+ unsigned int int_pipe;
+ struct urb *rcvurb;
+ struct urb *inturb;
+ struct usb_anchor tx_submitted;
+ void *rcvbuf;
+ void *intbuf;
+ struct usb_ctrlrequest *in_ctlreq;
+
+ spinlock_t rx_lock;
+ struct mutex dev_lock;
+ struct list_head rx_list;
+ wait_queue_head_t read_wait_queue;
+ wait_queue_head_t open_wait_queue;
+
+ unsigned is_opened;
+
+ /*input control lines (DSR, CTS, CD, RI)*/
+ unsigned int cbits_tolocal;
+
+ /*output control lines (DTR, RTS)*/
+ unsigned int cbits_tomdm;
+
+ /*
+ * track first resp available from mdm when it boots up
+ * to avoid bigger timeout value used by qmuxd
+ */
+ bool resp_available;
+
+ unsigned int mdm_wait_timeout;
+
+ /*counters*/
+ unsigned int snd_encap_cmd_cnt;
+ unsigned int get_encap_resp_cnt;
+ unsigned int resp_avail_cnt;
+ unsigned int set_ctrl_line_state_cnt;
+ unsigned int tx_ctrl_err_cnt;
+};
+
+extern struct rmnet_ctrl_dev *ctrl_dev[];
+
+extern int rmnet_usb_ctrl_start(struct rmnet_ctrl_dev *);
+extern int rmnet_usb_ctrl_stop_rx(struct rmnet_ctrl_dev *);
+extern int rmnet_usb_ctrl_init(void);
+extern void rmnet_usb_ctrl_exit(void);
+extern int rmnet_usb_ctrl_probe(struct usb_interface *intf,
+ struct usb_host_endpoint *status,
+ struct rmnet_ctrl_dev *dev);
+extern void rmnet_usb_ctrl_disconnect(struct rmnet_ctrl_dev *);
+
+#endif /* __RMNET_USB_H*/
diff --git a/drivers/net/usb/rmnet_usb_data.c b/drivers/net/usb/rmnet_usb_data.c
new file mode 100644
index 0000000..6df586d
--- /dev/null
+++ b/drivers/net/usb/rmnet_usb_data.c
@@ -0,0 +1,564 @@
+/* Copyright (c) 2011, 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/mii.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/usb.h>
+#include <linux/usb/usbnet.h>
+#include <linux/msm_rmnet.h>
+
+#include "rmnet_usb_ctrl.h"
+
+#define RMNET_DATA_LEN 2000
+#define HEADROOM_FOR_QOS 8
+
+#define FIRST_RMNET_USB_INTERFACE 4
+#define NUM_EMBEDDED_RMNET_IFACE 4
+
+static int data_msg_dbg_mask;
+
+enum {
+ DEBUG_MASK_LVL0 = 1U << 0,
+ DEBUG_MASK_LVL1 = 1U << 1,
+ DEBUG_MASK_LVL2 = 1U << 2,
+};
+
+#define DBG(m, x...) do { \
+ if (data_msg_dbg_mask & m) \
+ pr_info(x); \
+} while (0)
+
+/*echo dbg_mask > /sys/class/net/rmnet_usbx/dbg_mask*/
+static ssize_t dbg_mask_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ unsigned int dbg_mask;
+ struct net_device *dev = to_net_dev(d);
+ struct usbnet *unet = netdev_priv(dev);
+
+ if (!dev)
+ return -ENODEV;
+
+ sscanf(buf, "%u", &dbg_mask);
+ /*enable dbg msgs for data driver*/
+ data_msg_dbg_mask = dbg_mask;
+
+ /*set default msg level*/
+ unet->msg_enable = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK;
+
+ /*enable netif_xxx msgs*/
+ if (dbg_mask & DEBUG_MASK_LVL0)
+ unet->msg_enable |= NETIF_MSG_IFUP | NETIF_MSG_IFDOWN;
+ if (dbg_mask & DEBUG_MASK_LVL1)
+ unet->msg_enable |= NETIF_MSG_TX_ERR | NETIF_MSG_RX_ERR
+ | NETIF_MSG_TX_QUEUED | NETIF_MSG_TX_DONE
+ | NETIF_MSG_RX_STATUS;
+
+ return n;
+}
+
+static ssize_t dbg_mask_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", data_msg_dbg_mask);
+}
+
+static DEVICE_ATTR(dbg_mask, 0644, dbg_mask_show, dbg_mask_store);
+
+#define DBG0(x...) DBG(DEBUG_MASK_LVL0, x)
+#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
+#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
+
+static void rmnet_usb_setup(struct net_device *);
+static int rmnet_ioctl(struct net_device *, struct ifreq *, int);
+
+static int rmnet_usb_suspend(struct usb_interface *iface, pm_message_t message)
+{
+ struct usbnet *unet;
+ struct rmnet_ctrl_dev *dev;
+ int time = 0;
+ int retval = 0;
+
+ unet = usb_get_intfdata(iface);
+ if (!unet) {
+ pr_err("%s:data device not found\n", __func__);
+ retval = -ENODEV;
+ goto fail;
+ }
+
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ if (!dev) {
+ dev_err(&unet->udev->dev, "%s: ctrl device not found\n",
+ __func__);
+ retval = -ENODEV;
+ goto fail;
+ }
+
+ retval = usbnet_suspend(iface, message);
+ if (!retval) {
+ if (message.event & PM_EVENT_SUSPEND) {
+ time = usb_wait_anchor_empty_timeout(&dev->tx_submitted,
+ 1000);
+ if (!time)
+ usb_kill_anchored_urbs(&dev->tx_submitted);
+
+ retval = rmnet_usb_ctrl_stop_rx(dev);
+ iface->dev.power.power_state.event = message.event;
+ }
+ /* TBD : do we need to set/clear usbnet->udev->reset_resume*/
+ } else
+ dev_dbg(&unet->udev->dev,
+ "%s: device is busy can not suspend\n", __func__);
+
+fail:
+ return retval;
+}
+
+static int rmnet_usb_resume(struct usb_interface *iface)
+{
+ int retval = 0;
+ int oldstate;
+ struct usbnet *unet;
+ struct rmnet_ctrl_dev *dev;
+
+ unet = usb_get_intfdata(iface);
+ if (!unet) {
+ pr_err("%s:data device not found\n", __func__);
+ retval = -ENODEV;
+ goto fail;
+ }
+
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ if (!dev) {
+ dev_err(&unet->udev->dev, "%s: ctrl device not found\n",
+ __func__);
+ retval = -ENODEV;
+ goto fail;
+ }
+ oldstate = iface->dev.power.power_state.event;
+ iface->dev.power.power_state.event = PM_EVENT_ON;
+
+ retval = usbnet_resume(iface);
+ if (!retval) {
+
+ if (oldstate & PM_EVENT_SUSPEND)
+ retval = rmnet_usb_ctrl_start(dev);
+ }
+fail:
+ return retval;
+}
+
+static int rmnet_usb_bind(struct usbnet *usbnet, struct usb_interface *iface)
+{
+ struct usb_host_endpoint *endpoint = NULL;
+ struct usb_host_endpoint *bulk_in = NULL;
+ struct usb_host_endpoint *bulk_out = NULL;
+ struct usb_host_endpoint *int_in = NULL;
+ struct usb_device *udev;
+ int status = 0;
+ int i;
+ int numends;
+
+ udev = interface_to_usbdev(iface);
+ numends = iface->cur_altsetting->desc.bNumEndpoints;
+ for (i = 0; i < numends; i++) {
+ endpoint = iface->cur_altsetting->endpoint + i;
+ if (!endpoint) {
+ dev_err(&udev->dev, "%s: invalid endpoint %u\n",
+ __func__, i);
+ status = -EINVAL;
+ goto out;
+ }
+ if (usb_endpoint_is_bulk_in(&endpoint->desc))
+ bulk_in = endpoint;
+ else if (usb_endpoint_is_bulk_out(&endpoint->desc))
+ bulk_out = endpoint;
+ else if (usb_endpoint_is_int_in(&endpoint->desc))
+ int_in = endpoint;
+ }
+
+ if (!bulk_in || !bulk_out || !int_in) {
+ dev_err(&udev->dev, "%s: invalid endpoints\n", __func__);
+ status = -EINVAL;
+ goto out;
+ }
+ usbnet->in = usb_rcvbulkpipe(usbnet->udev,
+ bulk_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ usbnet->out = usb_sndbulkpipe(usbnet->udev,
+ bulk_out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ usbnet->status = int_in;
+
+ /*change name of net device to rmnet_usbx here*/
+ strlcpy(usbnet->net->name, "rmnet_usb%d", IFNAMSIZ);
+
+ /*TBD: update rx_urb_size, curently set to eth frame len by usbnet*/
+out:
+ return status;
+}
+
+static struct sk_buff *rmnet_usb_tx_fixup(struct usbnet *dev,
+ struct sk_buff *skb, gfp_t flags)
+{
+ struct QMI_QOS_HDR_S *qmih;
+
+ if (test_bit(RMNET_MODE_QOS, &dev->data[0])) {
+ qmih = (struct QMI_QOS_HDR_S *)
+ skb_push(skb, sizeof(struct QMI_QOS_HDR_S));
+ qmih->version = 1;
+ qmih->flags = 0;
+ qmih->flow_id = skb->mark;
+ }
+
+ DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
+ dev->net->name, dev->net->stats.tx_packets, skb->len, skb->mark);
+
+ return skb;
+}
+
+static __be16 rmnet_ip_type_trans(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ __be16 protocol = 0;
+
+ skb->dev = dev;
+
+ switch (skb->data[0] & 0xf0) {
+ case 0x40:
+ protocol = htons(ETH_P_IP);
+ break;
+ case 0x60:
+ protocol = htons(ETH_P_IPV6);
+ break;
+ default:
+ pr_err("[%s] rmnet_recv() L3 protocol decode error: 0x%02x",
+ dev->name, skb->data[0] & 0xf0);
+ }
+
+ return protocol;
+}
+
+static int rmnet_usb_rx_fixup(struct usbnet *dev,
+ struct sk_buff *skb)
+{
+
+ if (test_bit(RMNET_MODE_LLP_IP, &dev->data[0]))
+ skb->protocol = rmnet_ip_type_trans(skb, dev->net);
+ else /*set zero for eth mode*/
+ skb->protocol = 0;
+
+ DBG1("[%s] Rx packet #%lu len=%d\n",
+ dev->net->name, dev->net->stats.rx_packets, skb->len);
+
+ return 1;
+}
+
+static int rmnet_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if (0 > new_mtu || RMNET_DATA_LEN < new_mtu)
+ return -EINVAL;
+
+ DBG0("[%s] MTU change: old=%d new=%d\n", dev->name, dev->mtu, new_mtu);
+
+ dev->mtu = new_mtu;
+
+ return 0;
+}
+
+static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
+{
+ return &dev->stats;
+}
+
+static const struct net_device_ops rmnet_usb_ops_ether = {
+ .ndo_open = usbnet_open,
+ .ndo_stop = usbnet_stop,
+ .ndo_start_xmit = usbnet_start_xmit,
+ .ndo_get_stats = rmnet_get_stats,
+ /*.ndo_set_multicast_list = rmnet_set_multicast_list,*/
+ .ndo_tx_timeout = usbnet_tx_timeout,
+ .ndo_do_ioctl = rmnet_ioctl,
+ .ndo_change_mtu = usbnet_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static const struct net_device_ops rmnet_usb_ops_ip = {
+ .ndo_open = usbnet_open,
+ .ndo_stop = usbnet_stop,
+ .ndo_start_xmit = usbnet_start_xmit,
+ .ndo_get_stats = rmnet_get_stats,
+ /*.ndo_set_multicast_list = rmnet_set_multicast_list,*/
+ .ndo_tx_timeout = usbnet_tx_timeout,
+ .ndo_do_ioctl = rmnet_ioctl,
+ .ndo_change_mtu = rmnet_change_mtu,
+ .ndo_set_mac_address = 0,
+ .ndo_validate_addr = 0,
+};
+
+
+static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct usbnet *unet = netdev_priv(dev);
+ u32 old_opmode;
+ int prev_mtu = dev->mtu;
+ int rc = 0;
+
+ old_opmode = unet->data[0]; /*data[0] saves operation mode*/
+ /* Process IOCTL command */
+ switch (cmd) {
+ case RMNET_IOCTL_SET_LLP_ETHERNET: /*Set Ethernet protocol*/
+ /* Perform Ethernet config only if in IP mode currently*/
+ if (test_bit(RMNET_MODE_LLP_IP, &unet->data[0])) {
+ ether_setup(dev);
+ random_ether_addr(dev->dev_addr);
+ dev->mtu = prev_mtu;
+ dev->netdev_ops = &rmnet_usb_ops_ether;
+ clear_bit(RMNET_MODE_LLP_IP, &unet->data[0]);
+ set_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
+ DBG0("[%s] rmnet_ioctl(): set Ethernet protocol mode\n",
+ dev->name);
+ }
+ break;
+
+ case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol*/
+ /* Perform IP config only if in Ethernet mode currently*/
+ if (test_bit(RMNET_MODE_LLP_ETH, &unet->data[0])) {
+
+ /* Undo config done in ether_setup() */
+ dev->header_ops = 0; /* No header */
+ dev->type = ARPHRD_RAWIP;
+ dev->hard_header_len = 0;
+ dev->mtu = prev_mtu;
+ dev->addr_len = 0;
+ dev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
+ dev->needed_headroom = HEADROOM_FOR_QOS;
+ dev->netdev_ops = &rmnet_usb_ops_ip;
+ clear_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
+ set_bit(RMNET_MODE_LLP_IP, &unet->data[0]);
+ DBG0("[%s] rmnet_ioctl(): set IP protocol mode\n",
+ dev->name);
+ }
+ break;
+
+ case RMNET_IOCTL_GET_LLP: /* Get link protocol state */
+ ifr->ifr_ifru.ifru_data = (void *)(unet->data[0]
+ & (RMNET_MODE_LLP_ETH
+ | RMNET_MODE_LLP_IP));
+ break;
+
+ case RMNET_IOCTL_SET_QOS_ENABLE: /* Set QoS header enabled*/
+ set_bit(RMNET_MODE_QOS, &unet->data[0]);
+ DBG0("[%s] rmnet_ioctl(): set QMI QOS header enable\n",
+ dev->name);
+ break;
+
+ case RMNET_IOCTL_SET_QOS_DISABLE: /* Set QoS header disabled */
+ clear_bit(RMNET_MODE_QOS, &unet->data[0]);
+ DBG0("[%s] rmnet_ioctl(): set QMI QOS header disable\n",
+ dev->name);
+ break;
+
+ case RMNET_IOCTL_GET_QOS: /* Get QoS header state */
+ ifr->ifr_ifru.ifru_data = (void *)(unet->data[0]
+ & RMNET_MODE_QOS);
+ break;
+
+ case RMNET_IOCTL_GET_OPMODE: /* Get operation mode*/
+ ifr->ifr_ifru.ifru_data = (void *)unet->data[0];
+ break;
+
+ case RMNET_IOCTL_OPEN: /* Open transport port */
+ rc = usbnet_open(dev);
+ DBG0("[%s] rmnet_ioctl(): open transport port\n", dev->name);
+ break;
+
+ case RMNET_IOCTL_CLOSE: /* Close transport port*/
+ rc = usbnet_stop(dev);
+ DBG0("[%s] rmnet_ioctl(): close transport port\n", dev->name);
+ break;
+
+ default:
+ dev_err(&unet->udev->dev, "[%s] error: "
+ "rmnet_ioct called for unsupported cmd[%d]",
+ dev->name, cmd);
+ return -EINVAL;
+ }
+
+ DBG2("[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08lx\n",
+ dev->name, __func__, cmd, old_opmode, unet->data[0]);
+
+ return rc;
+}
+
+static void rmnet_usb_setup(struct net_device *dev)
+{
+ /* Using Ethernet mode by default */
+ dev->netdev_ops = &rmnet_usb_ops_ether;
+
+ /* set this after calling ether_setup */
+ dev->mtu = RMNET_DATA_LEN;
+
+ dev->needed_headroom = HEADROOM_FOR_QOS;
+ random_ether_addr(dev->dev_addr);
+ dev->watchdog_timeo = 1000; /* 10 seconds? */
+}
+
+static int rmnet_usb_probe(struct usb_interface *iface,
+ const struct usb_device_id *prod)
+{
+ struct usbnet *unet;
+ struct usb_device *udev;
+ int iface_num;
+ int last_rmnet_iface_num;
+ int status = -ENODEV;
+
+ udev = interface_to_usbdev(iface);
+ iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+ if (iface->num_altsetting != 1) {
+ dev_err(&udev->dev, "%s invalid num_altsetting %u\n",
+ __func__, iface->num_altsetting);
+ status = -EINVAL;
+ goto out;
+ }
+
+ last_rmnet_iface_num = (FIRST_RMNET_USB_INTERFACE +
+ NUM_EMBEDDED_RMNET_IFACE - 1);
+
+ if (iface_num >= FIRST_RMNET_USB_INTERFACE &&
+ iface_num <= last_rmnet_iface_num) {
+ status = usbnet_probe(iface, prod);
+ if (status < 0) {
+ dev_err(&udev->dev, "usbnet_probe failed %d\n",
+ status);
+ goto out;
+ }
+ unet = usb_get_intfdata(iface);
+
+ /*set rmnet operation mode to eth by default*/
+ set_bit(RMNET_MODE_LLP_ETH, &unet->data[0]);
+
+ /*update net device*/
+ rmnet_usb_setup(unet->net);
+
+ /*create /sys/class/net/rmnet_usbx/dbg_mask*/
+ status = device_create_file(&unet->net->dev,
+ &dev_attr_dbg_mask);
+ if (status)
+ goto out;
+
+ /*save control device intstance */
+ unet->data[1] = (unsigned long)ctrl_dev \
+ [iface_num - FIRST_RMNET_USB_INTERFACE];
+
+ status = rmnet_usb_ctrl_probe(iface, unet->status,
+ (struct rmnet_ctrl_dev *)unet->data[1]);
+ }
+out:
+ return status;
+}
+
+static void rmnet_usb_disconnect(struct usb_interface *intf)
+{
+ struct usbnet *unet;
+ struct usb_device *udev;
+ struct rmnet_ctrl_dev *dev;
+ int iface_num;
+ int last_rmnet_iface_num;
+
+ udev = interface_to_usbdev(intf);
+ iface_num = intf->cur_altsetting->desc.bInterfaceNumber;
+
+ last_rmnet_iface_num = (FIRST_RMNET_USB_INTERFACE +
+ NUM_EMBEDDED_RMNET_IFACE - 1);
+
+ if (iface_num >= FIRST_RMNET_USB_INTERFACE &&
+ iface_num <= last_rmnet_iface_num) {
+ unet = usb_get_intfdata(intf);
+ if (!unet) {
+ dev_err(&udev->dev, "%s:data device not found\n",
+ __func__);
+ return;
+ }
+ dev = (struct rmnet_ctrl_dev *)unet->data[1];
+ if (!dev) {
+ dev_err(&udev->dev, "%s:ctrl device not found\n",
+ __func__);
+ return;
+ }
+ unet->data[0] = 0;
+ unet->data[1] = 0;
+ rmnet_usb_ctrl_disconnect(dev);
+ device_remove_file(&unet->net->dev, &dev_attr_dbg_mask);
+ usbnet_disconnect(intf);
+ }
+}
+
+static const struct driver_info rmnet_info = {
+ .description = "RmNET net device",
+ .bind = rmnet_usb_bind,
+ .tx_fixup = rmnet_usb_tx_fixup,
+ .rx_fixup = rmnet_usb_rx_fixup,
+ .data = 0,
+};
+
+static const struct usb_device_id vidpids[] = {
+ {
+ USB_DEVICE(0x05c6, 0x9034), /* MDM9x15*/
+ .driver_info = (unsigned long)&rmnet_info,
+ },
+ { },
+};
+
+MODULE_DEVICE_TABLE(usb, vidpids);
+
+static struct usb_driver rmnet_usb = {
+ .name = "rmnet_usb",
+ .id_table = vidpids,
+ .probe = rmnet_usb_probe,
+ .disconnect = rmnet_usb_disconnect,
+ .suspend = rmnet_usb_suspend,
+ .resume = rmnet_usb_resume,
+ .supports_autosuspend = true,
+};
+
+static int __init rmnet_usb_init(void)
+{
+ int retval;
+
+ retval = usb_register(&rmnet_usb);
+ if (retval) {
+ err("usb_register failed: %d", retval);
+ return retval;
+ }
+ /* initialize rmnet ctrl device here*/
+ retval = rmnet_usb_ctrl_init();
+ if (retval) {
+ usb_deregister(&rmnet_usb);
+ err("rmnet_usb_cmux_init failed: %d", retval);
+ return retval;
+ }
+
+ return 0;
+}
+module_init(rmnet_usb_init);
+
+static void __exit rmnet_usb_exit(void)
+{
+ rmnet_usb_ctrl_exit();
+ usb_deregister(&rmnet_usb);
+}
+module_exit(rmnet_usb_exit);
+
+MODULE_DESCRIPTION("msm rmnet usb device");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c
index ce395fe..dce8d6d 100644
--- a/drivers/net/usb/usbnet.c
+++ b/drivers/net/usb/usbnet.c
@@ -231,6 +231,9 @@
return;
}
+ if (!skb->protocol)
+ skb->protocol = eth_type_trans(skb, dev->net);
+
skb->protocol = eth_type_trans (skb, dev->net);
dev->net->stats.rx_packets++;
dev->net->stats.rx_bytes += skb->len;
diff --git a/drivers/net/wireless/libra/Makefile b/drivers/net/wireless/libra/Makefile
index 3c606ba..849de05 100644
--- a/drivers/net/wireless/libra/Makefile
+++ b/drivers/net/wireless/libra/Makefile
@@ -1,7 +1,7 @@
# Makefile for wlan sdio if driver
-librasdioif-objs += libra_sdioif.o
+librasdioif-objs += libra_sdioif.o ../wcnss/qcomwlan_secif.o
ifdef CONFIG_ARCH_MSM8X60
librasdioif-objs += qcomwlan_pwrif.o
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index 26441cd..23efb00 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -23,6 +23,14 @@
2. Peripheral-to-Memory.
3. Memory-to-Memory.
+config USB_BAM
+ boolean "USB BAM Driver"
+ depends on SPS && USB_GADGET
+ help
+ Enabling this option adds USB BAM Driver.
+ USB BAM driver was added to supports SPS Peripheral-to-Peripheral
+ transfers between the USB and other peripheral.
+
config SPS_SUPPORT_BAMDMA
bool "SPS support BAM DMA"
depends on SPS
diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile
index f6f212e..92eb492 100644
--- a/drivers/platform/msm/Makefile
+++ b/drivers/platform/msm/Makefile
@@ -2,4 +2,5 @@
# Makefile for the MSM specific device drivers.
#
obj-$(CONFIG_MSM_SSBI) += ssbi.o
+obj-$(CONFIG_USB_BAM) += usb_bam.o
obj-$(CONFIG_SPS) += sps/
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
index b002657..c2a4b6a 100644
--- a/drivers/platform/msm/sps/sps.c
+++ b/drivers/platform/msm/sps/sps.c
@@ -1203,11 +1203,11 @@
mutex_unlock(&sps->lock);
if (result) {
- if (virt_addr != NULL)
- iounmap(bam->props.virt_addr);
-
- if (bam != NULL)
+ if (bam != NULL) {
+ if (virt_addr != NULL)
+ iounmap(bam->props.virt_addr);
kfree(bam);
+ }
return result;
}
diff --git a/drivers/platform/msm/sps/sps_bam.c b/drivers/platform/msm/sps/sps_bam.c
index edd789e..cb5a0ee 100644
--- a/drivers/platform/msm/sps/sps_bam.c
+++ b/drivers/platform/msm/sps/sps_bam.c
@@ -634,9 +634,16 @@
SPS_ERR("Disconnect BAM 0x%x pipe %d with events pending",
BAM_ID(dev), pipe_index);
- list_for_each_entry(sps_event, &pipe->sys.events_q, list) {
+ sps_event = list_entry((&pipe->sys.events_q)->next,
+ typeof(*sps_event), list);
+
+ while (&sps_event->list != (&pipe->sys.events_q)) {
+ struct sps_q_event *sps_event_delete = sps_event;
+
list_del(&sps_event->list);
- kfree(sps_event);
+ sps_event = list_entry(sps_event->list.next,
+ typeof(*sps_event), list);
+ kfree(sps_event_delete);
}
}
@@ -1086,11 +1093,17 @@
continue; /* No */
index = SPS_EVENT_INDEX(opt_event_table[n].event_id);
- event_reg = &pipe->sys.event_regs[index];
- event_reg->xfer_done = reg->xfer_done;
- event_reg->callback = reg->callback;
- event_reg->mode = reg->mode;
- event_reg->user = reg->user;
+ if (index < 0)
+ SPS_ERR("Negative event index: "
+ "BAM 0x%x pipe %d mode %d",
+ BAM_ID(dev), pipe_index, reg->mode);
+ else {
+ event_reg = &pipe->sys.event_regs[index];
+ event_reg->xfer_done = reg->xfer_done;
+ event_reg->callback = reg->callback;
+ event_reg->mode = reg->mode;
+ event_reg->user = reg->user;
+ }
}
return 0;
diff --git a/drivers/platform/msm/sps/sps_dma.c b/drivers/platform/msm/sps/sps_dma.c
index b650098..48c7ffd 100644
--- a/drivers/platform/msm/sps/sps_dma.c
+++ b/drivers/platform/msm/sps/sps_dma.c
@@ -291,6 +291,11 @@
dev->h = h;
dev->bam = sps_h2bam(h);
+ if (dev->bam == NULL) {
+ SPS_ERR("BAM-DMA BAM device is not found from the handle.");
+ goto exit_err;
+ }
+
/* Map the BAM DMA device into virtual space, if necessary */
props = &dev->bam->props;
dev->phys_addr = props->periph_phys_addr;
diff --git a/drivers/platform/msm/sps/sps_mem.c b/drivers/platform/msm/sps/sps_mem.c
index 31c1314..358dbd3 100644
--- a/drivers/platform/msm/sps/sps_mem.c
+++ b/drivers/platform/msm/sps/sps_mem.c
@@ -131,6 +131,12 @@
#endif
pool = gen_pool_create(min_alloc_order, nid);
+
+ if (!pool) {
+ SPS_ERR("sps:Failed to create a new memory pool.\n");
+ return -ENOMEM;
+ }
+
#ifndef CONFIG_SPS_SUPPORT_NDP_BAM
res = gen_pool_add(pool, (u32) iomem_virt, iomem_size, nid);
if (res)
diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c
new file mode 100644
index 0000000..b34c35e
--- /dev/null
+++ b/drivers/platform/msm/usb_bam.c
@@ -0,0 +1,238 @@
+/* Copyright (c) 2011, 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 <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/usb/msm_hsusb.h>
+#include <mach/usb_bam.h>
+#include <mach/sps.h>
+
+#define USB_SUMMING_THRESHOLD 512
+#define CONNECTIONS_NUM 4
+
+static struct sps_bam_props usb_props;
+static struct sps_pipe *sps_pipes[CONNECTIONS_NUM][2];
+static struct sps_connect sps_connections[CONNECTIONS_NUM][2];
+static struct sps_mem_buffer data_mem_buf[CONNECTIONS_NUM][2];
+static struct sps_mem_buffer desc_mem_buf[CONNECTIONS_NUM][2];
+static struct platform_device *usb_bam_pdev;
+
+struct usb_bam_connect_info {
+ u8 idx;
+ u8 *src_pipe;
+ u8 *dst_pipe;
+ bool enabled;
+};
+
+static struct usb_bam_connect_info usb_bam_connections[CONNECTIONS_NUM];
+
+static int connect_pipe(u8 connection_idx, enum usb_bam_pipe_dir pipe_dir,
+ u8 *usb_pipe_idx)
+{
+ int ret;
+ struct sps_pipe *pipe = sps_pipes[connection_idx][pipe_dir];
+ struct sps_connect *connection =
+ &sps_connections[connection_idx][pipe_dir];
+ struct msm_usb_bam_platform_data *pdata =
+ (struct msm_usb_bam_platform_data *)
+ (usb_bam_pdev->dev.platform_data);
+ struct usb_bam_pipe_connect *pipe_connection =
+ (struct usb_bam_pipe_connect *)(pdata->connections +
+ (2*connection_idx+pipe_dir));
+
+ pipe = sps_alloc_endpoint();
+ if (pipe == NULL) {
+ pr_err("%s: sps_alloc_endpoint failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ ret = sps_get_config(pipe, connection);
+ if (ret) {
+ pr_err("%s: tx get config failed %d\n", __func__, ret);
+ goto get_config_failed;
+ }
+
+ ret = sps_phy2h(pipe_connection->src_phy_addr, &(connection->source));
+ if (ret) {
+ pr_err("%s: sps_phy2h failed (src BAM) %d\n", __func__, ret);
+ goto get_config_failed;
+ }
+
+ connection->src_pipe_index = pipe_connection->src_pipe_index;
+ ret = sps_phy2h(pipe_connection->dst_phy_addr,
+ &(connection->destination));
+ if (ret) {
+ pr_err("%s: sps_phy2h failed (dst BAM) %d\n", __func__, ret);
+ goto get_config_failed;
+ }
+ connection->dest_pipe_index = pipe_connection->dst_pipe_index;
+
+ if (pipe_dir == USB_TO_PEER_PERIPHERAL) {
+ connection->mode = SPS_MODE_SRC;
+ *usb_pipe_idx = connection->src_pipe_index;
+ } else {
+ connection->mode = SPS_MODE_DEST;
+ *usb_pipe_idx = connection->dest_pipe_index;
+ }
+
+ ret = sps_setup_bam2bam_fifo(
+ &data_mem_buf[connection_idx][pipe_dir],
+ pipe_connection->data_fifo_base_offset,
+ pipe_connection->data_fifo_size, 1);
+ if (ret) {
+ pr_err("%s: data fifo setup failure %d\n", __func__, ret);
+ goto fifo_setup_error;
+ }
+ connection->data = data_mem_buf[connection_idx][pipe_dir];
+
+ ret = sps_setup_bam2bam_fifo(
+ &desc_mem_buf[connection_idx][pipe_dir],
+ pipe_connection->desc_fifo_base_offset,
+ pipe_connection->desc_fifo_size, 1);
+ if (ret) {
+ pr_err("%s: desc. fifo setup failure %d\n", __func__, ret);
+ goto fifo_setup_error;
+ }
+ connection->desc = desc_mem_buf[connection_idx][pipe_dir];
+ connection->event_thresh = 512;
+
+ ret = sps_connect(pipe, connection);
+ if (ret < 0) {
+ pr_err("%s: tx connect error %d\n", __func__, ret);
+ goto error;
+ }
+ return 0;
+
+error:
+ sps_disconnect(pipe);
+fifo_setup_error:
+get_config_failed:
+ sps_free_endpoint(pipe);
+ return ret;
+}
+
+int usb_bam_connect(u8 idx, u8 *src_pipe_idx, u8 *dst_pipe_idx)
+{
+ struct usb_bam_connect_info *connection = &usb_bam_connections[idx];
+ int ret;
+
+ if (idx >= CONNECTIONS_NUM) {
+ pr_err("%s: Invalid connection index\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (connection->enabled) {
+ pr_info("%s: connection %d was already established\n",
+ __func__, idx);
+ return 0;
+ }
+ connection->src_pipe = src_pipe_idx;
+ connection->dst_pipe = dst_pipe_idx;
+ connection->idx = idx;
+
+ /* open USB -> Peripheral pipe */
+ ret = connect_pipe(connection->idx, USB_TO_PEER_PERIPHERAL,
+ connection->src_pipe);
+ if (ret) {
+ pr_err("%s: src pipe connection failure\n", __func__);
+ return ret;
+ }
+ /* open Peripheral -> USB pipe */
+ ret = connect_pipe(connection->idx, PEER_PERIPHERAL_TO_USB,
+ connection->dst_pipe);
+ if (ret) {
+ pr_err("%s: dst pipe connection failure\n", __func__);
+ return ret;
+ }
+ connection->enabled = 1;
+
+ return 0;
+}
+static int usb_bam_init(void)
+{
+ u32 h_usb;
+ int ret;
+ void *usb_virt_addr;
+ struct msm_usb_bam_platform_data *pdata =
+ (struct msm_usb_bam_platform_data *)
+ (usb_bam_pdev->dev.platform_data);
+
+ usb_virt_addr = ioremap_nocache(
+ pdata->usb_bam_phy_base,
+ pdata->usb_bam_phy_size);
+ if (!usb_virt_addr) {
+ pr_err("%s: ioremap failed\n", __func__);
+ return -ENOMEM;
+ }
+ usb_props.phys_addr = pdata->usb_bam_phy_base;
+ usb_props.virt_addr = usb_virt_addr;
+ usb_props.virt_size = pdata->usb_bam_phy_size;
+ usb_props.irq = USB1_HS_BAM_IRQ;
+ usb_props.num_pipes = pdata->usb_bam_num_pipes;
+ usb_props.summing_threshold = USB_SUMMING_THRESHOLD;
+ ret = sps_register_bam_device(&usb_props, &h_usb);
+ if (ret < 0) {
+ pr_err("%s: register bam error %d\n", __func__, ret);
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+static int usb_bam_probe(struct platform_device *pdev)
+{
+ int ret, i;
+
+ dev_dbg(&pdev->dev, "usb_bam_probe\n");
+
+ for (i = 0; i < CONNECTIONS_NUM; i++)
+ usb_bam_connections[i].enabled = 0;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "missing platform_data\n");
+ return -ENODEV;
+ }
+ usb_bam_pdev = pdev;
+
+ ret = usb_bam_init();
+ if (ret) {
+ dev_err(&pdev->dev, "failed to get platform resource mem\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver usb_bam_driver = {
+ .probe = usb_bam_probe,
+ .driver = { .name = "usb_bam", },
+};
+
+static int __init init(void)
+{
+ return platform_driver_register(&usb_bam_driver);
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+ platform_driver_unregister(&usb_bam_driver);
+}
+module_exit(cleanup);
+
+MODULE_DESCRIPTION("MSM USB BAM DRIVER");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index 85d8a08..5f63cd7 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -52,6 +52,26 @@
PM_BMS_MAX_INTS,
};
+struct pm8921_soc_params {
+ uint16_t ocv_for_rbatt_raw;
+ uint16_t vsense_for_rbatt_raw;
+ uint16_t vbatt_for_rbatt_raw;
+ uint16_t last_good_ocv_raw;
+ int cc;
+
+ int ocv_for_rbatt_uv;
+ int vsense_for_rbatt_uv;
+ int vbatt_for_rbatt_uv;
+ int last_good_ocv_uv;
+};
+
+/**
+ * struct pm8921_bms_chip -
+ * @bms_output_lock: lock to prevent concurrent bms reads
+ * @bms_100_lock: lock to prevent concurrent updates to values that force
+ * 100% charge
+ *
+ */
struct pm8921_bms_chip {
struct device *dev;
struct dentry *dent;
@@ -77,6 +97,7 @@
unsigned int pmic_bms_irq[PM_BMS_MAX_INTS];
DECLARE_BITMAP(enabled_irqs, PM_BMS_MAX_INTS);
spinlock_t bms_output_lock;
+ spinlock_t bms_100_lock;
struct single_row_lut *adjusted_fcc_temp_lut;
unsigned int charging_began;
unsigned int start_percent;
@@ -84,6 +105,7 @@
uint16_t ocv_reading_at_100;
int cc_reading_at_100;
+ int max_voltage_uv;
};
static struct pm8921_bms_chip *the_chip;
@@ -129,6 +151,31 @@
static int bms_fake_battery;
module_param(bms_fake_battery, int, 0644);
+/* bms_start_XXX and bms_end_XXX are read only */
+static int bms_start_percent;
+static int bms_start_ocv_uv;
+static int bms_start_cc_mah;
+static int bms_end_percent;
+static int bms_end_ocv_uv;
+static int bms_end_cc_mah;
+
+static int bms_ro_ops_set(const char *val, const struct kernel_param *kp)
+{
+ return -EINVAL;
+}
+
+static struct kernel_param_ops bms_ro_param_ops = {
+ .set = bms_ro_ops_set,
+ .get = param_get_int,
+};
+module_param_cb(bms_start_percent, &bms_ro_param_ops, &bms_start_percent, 0644);
+module_param_cb(bms_start_ocv_uv, &bms_ro_param_ops, &bms_start_ocv_uv, 0644);
+module_param_cb(bms_start_cc_mah, &bms_ro_param_ops, &bms_start_cc_mah, 0644);
+
+module_param_cb(bms_end_percent, &bms_ro_param_ops, &bms_end_percent, 0644);
+module_param_cb(bms_end_ocv_uv, &bms_ro_param_ops, &bms_end_ocv_uv, 0644);
+module_param_cb(bms_end_cc_mah, &bms_ro_param_ops, &bms_end_cc_mah, 0644);
+
static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp);
static void readjust_fcc_table(void)
{
@@ -314,9 +361,6 @@
return rc;
}
- /* Output register data must be held (locked) while reading output */
- WARN_ON(!(reg && HOLD_OREG_DATA));
-
rc = pm_bms_masked_write(chip, BMS_CONTROL, SELECT_OUTPUT_DATA,
type << SELECT_OUTPUT_TYPE_SHIFT);
if (rc) {
@@ -425,96 +469,28 @@
}
*result = msw << 16 | lsw;
pr_debug("msw = %04x lsw = %04x cc = %d\n", msw, lsw, *result);
- *result = *result - chip->cc_reading_at_100;
pr_debug("cc = %d after subtracting %d\n",
*result, chip->cc_reading_at_100);
return 0;
}
-static int read_last_good_ocv(struct pm8921_bms_chip *chip, uint *result)
+static int convert_vbatt_raw_to_uv(struct pm8921_bms_chip *chip,
+ uint16_t reading, int *result)
{
- int rc;
- uint16_t reading;
-
- rc = pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &reading);
- if (rc) {
- pr_err("fail to read LAST_GOOD_OCV_VALUE rc = %d\n", rc);
- return rc;
- }
-
- if (chip->ocv_reading_at_100 != reading) {
- chip->ocv_reading_at_100 = 0;
- chip->cc_reading_at_100 = 0;
- *result = xoadc_reading_to_microvolt(reading);
- pr_debug("raw = %04x ocv_uV = %u\n", reading, *result);
- *result = adjust_xo_vbatt_reading(chip, *result);
- pr_debug("after adj ocv_uV = %u\n", *result);
- if (*result != 0)
- last_ocv_uv = *result;
- } else {
- /*
- * force 100% ocv by selecting the highest profiled ocv
- * This is the first row last column entry in the ocv
- * lookup table
- */
- int cols = chip->pc_temp_ocv_lut->cols;
-
- pr_debug("Forcing max voltage %d\n",
- 1000 * chip->pc_temp_ocv_lut->ocv[0][cols-1]);
- *result = 1000 * chip->pc_temp_ocv_lut->ocv[0][cols-1];
- }
-
- return 0;
-}
-
-static int read_vbatt_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
-{
- int rc;
- uint16_t reading;
-
- rc = pm_bms_read_output_data(chip, VBATT_FOR_RBATT, &reading);
- if (rc) {
- pr_err("fail to read VBATT_FOR_RBATT rc = %d\n", rc);
- return rc;
- }
*result = xoadc_reading_to_microvolt(reading);
- pr_debug("raw = %04x vbatt_for_r_microV = %u\n", reading, *result);
+ pr_debug("raw = %04x vbatt = %u\n", reading, *result);
*result = adjust_xo_vbatt_reading(chip, *result);
- pr_debug("after adj vbatt_for_r_uV = %u\n", *result);
+ pr_debug("after adj vbatt = %u\n", *result);
return 0;
}
-static int read_vsense_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
+static int convert_vsense_to_uv(struct pm8921_bms_chip *chip,
+ int16_t reading, int *result)
{
- int rc;
- uint16_t reading;
-
- rc = pm_bms_read_output_data(chip, VSENSE_FOR_RBATT, &reading);
- if (rc) {
- pr_err("fail to read VSENSE_FOR_RBATT rc = %d\n", rc);
- return rc;
- }
*result = pm8xxx_ccadc_reading_to_microvolt(chip->revision, reading);
- pr_debug("raw = %04x vsense_for_r_uV = %u\n", reading, *result);
+ pr_debug("raw = %04x vsense = %d\n", reading, *result);
*result = pm8xxx_cc_adjust_for_gain(*result);
- pr_debug("after adj vsense_for_r_uV = %u\n", *result);
- return 0;
-}
-
-static int read_ocv_for_rbatt(struct pm8921_bms_chip *chip, uint *result)
-{
- int rc;
- uint16_t reading;
-
- rc = pm_bms_read_output_data(chip, OCV_FOR_RBATT, &reading);
- if (rc) {
- pr_err("fail to read OCV_FOR_RBATT rc = %d\n", rc);
- return rc;
- }
- *result = xoadc_reading_to_microvolt(reading);
- pr_debug("raw = %04x ocv_for_r_uV = %u\n", reading, *result);
- *result = adjust_xo_vbatt_reading(chip, *result);
- pr_debug("after adj ocv_for_r_uV = %u\n", *result);
+ pr_debug("after adj vsense = %d\n", *result);
return 0;
}
@@ -528,11 +504,8 @@
pr_err("fail to read VSENSE_AVG rc = %d\n", rc);
return rc;
}
- *result = pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
- reading);
- pr_debug("raw = %04x vsense = %d\n", reading, *result);
- *result = pm8xxx_cc_adjust_for_gain((s64)*result);
- pr_debug("after adj vsense = %d\n", *result);
+
+ convert_vsense_to_uv(chip, reading, result);
return 0;
}
@@ -579,11 +552,15 @@
static int interpolate_fcc(struct pm8921_bms_chip *chip, int batt_temp)
{
+ /* batt_temp is in tenths of degC - convert it to degC for lookups */
+ batt_temp = batt_temp/10;
return interpolate_single_lut(chip->fcc_temp_lut, batt_temp);
}
static int interpolate_fcc_adjusted(struct pm8921_bms_chip *chip, int batt_temp)
{
+ /* batt_temp is in tenths of degC - convert it to degC for lookups */
+ batt_temp = batt_temp/10;
return interpolate_single_lut(chip->adjusted_fcc_temp_lut, batt_temp);
}
@@ -699,6 +676,9 @@
int rows = chip->pc_temp_ocv_lut->rows;
int cols = chip->pc_temp_ocv_lut->cols;
+ /* batt_temp is in tenths of degC - convert it to degC for lookups */
+ batt_temp = batt_temp/10;
+
if (batt_temp < chip->pc_temp_ocv_lut->temp[0]) {
pr_debug("batt_temp %d < known temp range for pc\n", batt_temp);
batt_temp = chip->pc_temp_ocv_lut->temp[0];
@@ -790,37 +770,59 @@
return 100;
}
-static int calculate_rbatt(struct pm8921_bms_chip *chip)
+static int read_soc_params_raw(struct pm8921_bms_chip *chip,
+ struct pm8921_soc_params *raw)
{
- int rc;
- unsigned int ocv, vsense, vbatt, r_batt;
+ unsigned long flags;
- rc = read_ocv_for_rbatt(chip, &ocv);
- if (rc) {
- pr_err("fail to read ocv_for_rbatt rc = %d\n", rc);
- ocv = 0;
- }
+ spin_lock_irqsave(&chip->bms_output_lock, flags);
+ pm_bms_lock_output_data(chip);
- rc = read_vbatt_for_rbatt(chip, &vbatt);
- if (rc) {
- pr_err("fail to read vbatt_for_rbatt rc = %d\n", rc);
- ocv = 0;
- }
+ pm_bms_read_output_data(chip,
+ OCV_FOR_RBATT, &raw->ocv_for_rbatt_raw);
+ pm_bms_read_output_data(chip,
+ VBATT_FOR_RBATT, &raw->vbatt_for_rbatt_raw);
+ pm_bms_read_output_data(chip,
+ VSENSE_FOR_RBATT, &raw->vsense_for_rbatt_raw);
+ pm_bms_read_output_data(chip,
+ LAST_GOOD_OCV_VALUE, &raw->last_good_ocv_raw);
+ read_cc(chip, &raw->cc);
- rc = read_vsense_for_rbatt(chip, &vsense);
- if (rc) {
- pr_err("fail to read vsense_for_rbatt rc = %d\n", rc);
- ocv = 0;
- }
- if (ocv == 0
- || ocv == vbatt
- || vsense == 0) {
+ pm_bms_unlock_output_data(chip);
+ spin_unlock_irqrestore(&chip->bms_output_lock, flags);
+
+ convert_vbatt_raw_to_uv(chip,
+ raw->vbatt_for_rbatt_raw, &raw->vbatt_for_rbatt_uv);
+ convert_vbatt_raw_to_uv(chip,
+ raw->ocv_for_rbatt_raw, &raw->ocv_for_rbatt_uv);
+ convert_vbatt_raw_to_uv(chip,
+ raw->last_good_ocv_raw, &raw->last_good_ocv_uv);
+ convert_vsense_to_uv(chip,
+ raw->vsense_for_rbatt_raw, &raw->vsense_for_rbatt_uv);
+
+ if (raw->last_good_ocv_uv)
+ last_ocv_uv = raw->last_good_ocv_uv;
+
+ return 0;
+}
+
+static int calculate_rbatt(struct pm8921_bms_chip *chip,
+ struct pm8921_soc_params *raw)
+{
+ unsigned int r_batt;
+
+ if (raw->ocv_for_rbatt_uv == 0
+ || raw->ocv_for_rbatt_uv == raw->vbatt_for_rbatt_uv
+ || raw->vsense_for_rbatt_raw == 0) {
pr_debug("rbatt readings unavailable ocv = %d, vbatt = %d,"
"vsen = %d\n",
- ocv, vbatt, vsense);
+ raw->ocv_for_rbatt_uv,
+ raw->vbatt_for_rbatt_uv,
+ raw->vsense_for_rbatt_raw);
return -EINVAL;
}
- r_batt = ((ocv - vbatt) * chip->r_sense) / vsense;
+ r_batt = ((raw->ocv_for_rbatt_uv - raw->vbatt_for_rbatt_uv)
+ * chip->r_sense) / raw->vsense_for_rbatt_uv;
last_rbatt = r_batt;
pr_debug("r_batt = %umilliOhms", r_batt);
return r_batt;
@@ -859,13 +861,13 @@
pr_debug("mvolts phy = %lld meas = 0x%llx", result.physical,
result.measurement);
*uvolts = (int)result.physical;
- *uvolts = *uvolts * 1000;
return 0;
}
static int adc_based_ocv(struct pm8921_bms_chip *chip, int *ocv)
{
- int vbatt, rbatt, ibatt, rc;
+ int vbatt, rbatt, ibatt_ua, rc;
+ struct pm8921_soc_params raw;
rc = get_battery_uvolts(chip, &vbatt);
if (rc) {
@@ -873,16 +875,18 @@
return rc;
}
- rc = pm8921_bms_get_battery_current(&ibatt);
+ rc = pm8921_bms_get_battery_current(&ibatt_ua);
if (rc) {
pr_err("failed to read batt current rc = %d\n", rc);
return rc;
}
- rbatt = calculate_rbatt(the_chip);
+ read_soc_params_raw(chip, &raw);
+
+ rbatt = calculate_rbatt(the_chip, &raw);
if (rbatt < 0)
rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
- *ocv = vbatt + ibatt * rbatt;
+ *ocv = vbatt + (ibatt_ua * rbatt)/1000;
return 0;
}
@@ -903,13 +907,13 @@
return pc;
}
-static void calculate_cc_mah(struct pm8921_bms_chip *chip, int64_t *val,
+static void calculate_cc_mah(struct pm8921_bms_chip *chip, int cc, int *val,
int *coulumb_counter)
{
- int rc;
int64_t cc_voltage_uv, cc_uvh, cc_mah;
- rc = read_cc(the_chip, coulumb_counter);
+ *coulumb_counter = cc;
+ *coulumb_counter -= chip->cc_reading_at_100;
cc_voltage_uv = (int64_t)*coulumb_counter;
cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
@@ -921,11 +925,12 @@
}
static int calculate_unusable_charge_mah(struct pm8921_bms_chip *chip,
+ struct pm8921_soc_params *raw,
int fcc, int batt_temp, int chargecycles)
{
int rbatt, voltage_unusable_uv, pc_unusable;
- rbatt = calculate_rbatt(chip);
+ rbatt = calculate_rbatt(chip, raw);
if (rbatt < 0) {
rbatt = (last_rbatt < 0) ? DEFAULT_RBATT_MOHMS : last_rbatt;
pr_debug("rbatt unavailable assuming %d\n", rbatt);
@@ -942,16 +947,26 @@
}
/* calculate remainging charge at the time of ocv */
-static int calculate_remaining_charge_mah(struct pm8921_bms_chip *chip, int fcc,
- int batt_temp, int chargecycles)
+static int calculate_remaining_charge_mah(struct pm8921_bms_chip *chip,
+ struct pm8921_soc_params *raw,
+ int fcc, int batt_temp,
+ int chargecycles)
{
- int rc, ocv, pc;
+ int ocv, pc;
/* calculate remainging charge */
ocv = 0;
- rc = read_last_good_ocv(chip, &ocv);
- if (rc)
- pr_debug("failed to read ocv rc = %d\n", rc);
+ if (chip->ocv_reading_at_100 != raw->last_good_ocv_raw) {
+ chip->ocv_reading_at_100 = 0;
+ chip->cc_reading_at_100 = 0;
+ ocv = raw->last_good_ocv_uv;
+ } else {
+ /*
+ * force 100% ocv by selecting the highest voltage the
+ * battery could every reach
+ */
+ ocv = chip->max_voltage_uv;
+ }
if (ocv == 0) {
ocv = last_ocv_uv;
@@ -964,11 +979,12 @@
}
static void calculate_charging_params(struct pm8921_bms_chip *chip,
+ struct pm8921_soc_params *raw,
int batt_temp, int chargecycles,
int *fcc,
int *unusable_charge,
int *remaining_charge,
- int64_t *cc_mah)
+ int *cc_mah)
{
int coulumb_counter;
unsigned long flags;
@@ -977,38 +993,35 @@
pr_debug("FCC = %umAh batt_temp = %d, cycles = %d",
*fcc, batt_temp, chargecycles);
- /* fcc doesnt need to be read from hardware, lock the bms now */
- spin_lock_irqsave(&chip->bms_output_lock, flags);
- pm_bms_lock_output_data(chip);
-
- *unusable_charge = calculate_unusable_charge_mah(chip, *fcc,
+ *unusable_charge = calculate_unusable_charge_mah(chip, raw, *fcc,
batt_temp, chargecycles);
pr_debug("UUC = %umAh", *unusable_charge);
+ spin_lock_irqsave(&chip->bms_100_lock, flags);
/* calculate remainging charge */
- *remaining_charge = calculate_remaining_charge_mah(chip, *fcc,
+ *remaining_charge = calculate_remaining_charge_mah(chip, raw, *fcc,
batt_temp, chargecycles);
pr_debug("RC = %umAh\n", *remaining_charge);
/* calculate cc milli_volt_hour */
- calculate_cc_mah(chip, cc_mah, &coulumb_counter);
- pr_debug("cc_mah = %lldmAh cc = %d\n", *cc_mah, coulumb_counter);
-
- pm_bms_unlock_output_data(chip);
- spin_unlock_irqrestore(&chip->bms_output_lock, flags);
+ calculate_cc_mah(chip, raw->cc, cc_mah, &coulumb_counter);
+ pr_debug("cc_mah = %dmAh raw->cc = %x cc = %x\n",
+ *cc_mah, raw->cc, coulumb_counter);
+ spin_unlock_irqrestore(&chip->bms_100_lock, flags);
}
static int calculate_real_fcc(struct pm8921_bms_chip *chip,
- int batt_temp, int chargecycles,
- int *ret_fcc)
+ struct pm8921_soc_params *raw,
+ int batt_temp, int chargecycles,
+ int *ret_fcc)
{
int fcc, unusable_charge;
int remaining_charge;
- int64_t cc_mah;
+ int cc_mah;
int real_fcc;
- calculate_charging_params(chip, batt_temp, chargecycles,
+ calculate_charging_params(chip, raw, batt_temp, chargecycles,
&fcc,
&unusable_charge,
&remaining_charge,
@@ -1016,7 +1029,7 @@
real_fcc = remaining_charge - cc_mah;
*ret_fcc = fcc;
- pr_debug("real_fcc = %d, RC = %d CC = %lld fcc = %d\n",
+ pr_debug("real_fcc = %d, RC = %d CC = %d fcc = %d\n",
real_fcc, remaining_charge, cc_mah, fcc);
return real_fcc;
}
@@ -1028,14 +1041,15 @@
*/
#define BATTERY_POWER_SUPPLY_SOC 53
static int calculate_state_of_charge(struct pm8921_bms_chip *chip,
- int batt_temp, int chargecycles)
+ struct pm8921_soc_params *raw,
+ int batt_temp, int chargecycles)
{
int remaining_usable_charge, fcc, unusable_charge;
int remaining_charge, soc;
int update_userspace = 1;
- int64_t cc_mah;
+ int cc_mah;
- calculate_charging_params(chip, batt_temp, chargecycles,
+ calculate_charging_params(chip, raw, batt_temp, chargecycles,
&fcc,
&unusable_charge,
&remaining_charge,
@@ -1049,15 +1063,9 @@
soc = 100;
pr_debug("SOC = %u%%\n", soc);
- if (bms_fake_battery) {
- soc = BATTERY_POWER_SUPPLY_SOC;
- pr_debug("setting SOC = %u%% bms_fake_battery = %d\n", soc,
- bms_fake_battery);
- }
-
if (soc < 0) {
pr_err("bad rem_usb_chg = %d rem_chg %d,"
- "cc_mah %lld, unusb_chg %d\n",
+ "cc_mah %d, unusb_chg %d\n",
remaining_usable_charge, remaining_charge,
cc_mah, unusable_charge);
pr_err("for bad rem_usb_chg last_ocv_uv = %d"
@@ -1071,7 +1079,7 @@
if (last_soc == -EINVAL || soc <= last_soc) {
last_soc = update_userspace ? soc : last_soc;
- return soc;
+ return bms_fake_battery ? BATTERY_POWER_SUPPLY_SOC : soc;
}
/*
@@ -1086,7 +1094,7 @@
soc = last_soc;
}
- return soc;
+ return bms_fake_battery ? BATTERY_POWER_SUPPLY_SOC : soc;
}
#define XOADC_MAX_1P25V 1312500
@@ -1130,7 +1138,7 @@
return;
}
voltage = calib_hkadc_convert_microvolt(result.adc_code);
- pr_debug("result 0.625V = 0x%x, voltage = %duV adc_mead = %lld\n",
+ pr_debug("result 0.625V = 0x%x, voltage = %duV adc_meas = %lld\n",
result.adc_code, voltage, result.measurement);
/* check for valid range */
if (voltage > XOADC_MAX_0P625V)
@@ -1178,7 +1186,7 @@
}
EXPORT_SYMBOL(pm8921_bms_get_vsense_avg);
-int pm8921_bms_get_battery_current(int *result)
+int pm8921_bms_get_battery_current(int *result_ua)
{
unsigned long flags;
int vsense;
@@ -1199,7 +1207,7 @@
spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
pr_debug("vsense=%d\n", vsense);
/* cast for signed division */
- *result = vsense / (int)the_chip->r_sense;
+ *result_ua = vsense * 1000 / (int)the_chip->r_sense;
return 0;
}
@@ -1209,6 +1217,7 @@
{
int batt_temp, rc;
struct pm8xxx_adc_chan_result result;
+ struct pm8921_soc_params raw;
if (!the_chip) {
pr_err("called before initialization\n");
@@ -1224,7 +1233,10 @@
pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
result.measurement);
batt_temp = (int)result.physical;
- return calculate_state_of_charge(the_chip,
+
+ read_soc_params_raw(the_chip, &raw);
+
+ return calculate_state_of_charge(the_chip, &raw,
batt_temp, last_chargecycles);
}
EXPORT_SYMBOL_GPL(pm8921_bms_get_percent_charge);
@@ -1254,7 +1266,28 @@
void pm8921_bms_charging_began(void)
{
- the_chip->start_percent = pm8921_bms_get_percent_charge();
+ int batt_temp, coulumb_counter, rc;
+ struct pm8xxx_adc_chan_result result;
+ struct pm8921_soc_params raw;
+
+ rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+ if (rc) {
+ pr_err("error reading adc channel = %d, rc = %d\n",
+ the_chip->batt_temp_channel, rc);
+ return;
+ }
+ pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+ result.measurement);
+ batt_temp = (int)result.physical;
+
+ read_soc_params_raw(the_chip, &raw);
+
+ the_chip->start_percent = calculate_state_of_charge(the_chip, &raw,
+ batt_temp, last_chargecycles);
+ bms_start_percent = the_chip->start_percent;
+ bms_start_ocv_uv = raw.last_good_ocv_uv;
+ calculate_cc_mah(the_chip, raw.cc, &bms_start_cc_mah, &coulumb_counter);
+
pr_debug("start_percent = %u%%\n", the_chip->start_percent);
}
EXPORT_SYMBOL_GPL(pm8921_bms_charging_began);
@@ -1262,22 +1295,27 @@
#define DELTA_FCC_PERCENT 3
void pm8921_bms_charging_end(int is_battery_full)
{
+ int batt_temp, coulumb_counter, rc;
+ struct pm8xxx_adc_chan_result result;
+ struct pm8921_soc_params raw;
+
+ rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
+ if (rc) {
+ pr_err("error reading adc channel = %d, rc = %d\n",
+ the_chip->batt_temp_channel, rc);
+ return;
+ }
+ pr_debug("batt_temp phy = %lld meas = 0x%llx\n", result.physical,
+ result.measurement);
+ batt_temp = (int)result.physical;
+
+ read_soc_params_raw(the_chip, &raw);
+
if (is_battery_full && the_chip != NULL) {
unsigned long flags;
- int batt_temp, rc, cc_reading;
int fcc, new_fcc, delta_fcc;
- struct pm8xxx_adc_chan_result result;
- rc = pm8xxx_adc_read(the_chip->batt_temp_channel, &result);
- if (rc) {
- pr_err("error reading adc channel = %d, rc = %d\n",
- the_chip->batt_temp_channel, rc);
- goto charge_cycle_calculation;
- }
- pr_debug("batt_temp phy = %lld meas = 0x%llx", result.physical,
- result.measurement);
- batt_temp = (int)result.physical;
- new_fcc = calculate_real_fcc(the_chip,
+ new_fcc = calculate_real_fcc(the_chip, &raw,
batt_temp, last_chargecycles,
&fcc);
delta_fcc = new_fcc - fcc;
@@ -1296,21 +1334,22 @@
delta_fcc, DELTA_FCC_PERCENT, fcc);
}
- spin_lock_irqsave(&the_chip->bms_output_lock, flags);
- pm_bms_lock_output_data(the_chip);
- pm_bms_read_output_data(the_chip, LAST_GOOD_OCV_VALUE,
- &the_chip->ocv_reading_at_100);
- read_cc(the_chip, &cc_reading);
- pm_bms_unlock_output_data(the_chip);
- spin_unlock_irqrestore(&the_chip->bms_output_lock, flags);
- the_chip->cc_reading_at_100 = cc_reading;
- pr_debug("EOC ocv_reading = 0x%x cc_reading = %d\n",
+ spin_lock_irqsave(&the_chip->bms_100_lock, flags);
+ the_chip->ocv_reading_at_100 = raw.last_good_ocv_raw;
+ the_chip->cc_reading_at_100 = raw.cc;
+ spin_unlock_irqrestore(&the_chip->bms_100_lock, flags);
+ pr_debug("EOC ocv_reading = 0x%x cc = %d\n",
the_chip->ocv_reading_at_100,
the_chip->cc_reading_at_100);
}
-charge_cycle_calculation:
- the_chip->end_percent = pm8921_bms_get_percent_charge();
+ the_chip->end_percent = calculate_state_of_charge(the_chip, &raw,
+ batt_temp, last_chargecycles);
+
+ bms_end_percent = the_chip->end_percent;
+ bms_end_ocv_uv = raw.last_good_ocv_uv;
+ calculate_cc_mah(the_chip, raw.cc, &bms_end_cc_mah, &coulumb_counter);
+
if (the_chip->end_percent > the_chip->start_percent) {
last_charge_increase =
the_chip->end_percent - the_chip->start_percent;
@@ -1472,24 +1511,26 @@
static void check_initial_ocv(struct pm8921_bms_chip *chip)
{
- int ocv, rc;
+ int ocv_uv, rc;
+ int16_t ocv_raw;
/*
* Check if a ocv is available in bms hw,
* if not compute it here at boot time and save it
* in the last_ocv_uv.
*/
- ocv = 0;
- rc = read_last_good_ocv(chip, &ocv);
- if (rc || ocv == 0) {
- rc = adc_based_ocv(chip, &ocv);
+ ocv_uv = 0;
+ pm_bms_read_output_data(chip, LAST_GOOD_OCV_VALUE, &ocv_raw);
+ rc = convert_vbatt_raw_to_uv(chip, ocv_raw, &ocv_uv);
+ if (rc || ocv_uv == 0) {
+ rc = adc_based_ocv(chip, &ocv_uv);
if (rc) {
- pr_err("failed to read adc based ocv rc = %d\n", rc);
- ocv = DEFAULT_OCV_MICROVOLTS;
+ pr_err("failed to read adc based ocv_uv rc = %d\n", rc);
+ ocv_uv = DEFAULT_OCV_MICROVOLTS;
}
- last_ocv_uv = ocv;
+ last_ocv_uv = ocv_uv;
}
- pr_debug("ocv = %d last_ocv_uv = %d\n", ocv, last_ocv_uv);
+ pr_debug("ocv_uv = %d last_ocv_uv = %d\n", ocv_uv, last_ocv_uv);
}
static int64_t read_battery_id(struct pm8921_bms_chip *chip)
@@ -1596,13 +1637,16 @@
{
int param = (int)data;
int ret = 0;
+ struct pm8921_soc_params raw;
+
+ read_soc_params_raw(the_chip, &raw);
*val = 0;
/* global irq number passed in via data */
switch (param) {
case CALC_RBATT:
- *val = calculate_rbatt(the_chip);
+ *val = calculate_rbatt(the_chip, &raw);
break;
case CALC_FCC:
*val = calculate_fcc(the_chip, test_batt_temp,
@@ -1613,7 +1657,7 @@
test_chargecycle);
break;
case CALC_SOC:
- *val = calculate_state_of_charge(the_chip,
+ *val = calculate_state_of_charge(the_chip, &raw,
test_batt_temp, test_chargecycle);
break;
case CALIB_HKADC:
@@ -1637,6 +1681,9 @@
{
int param = (int)data;
int ret = 0;
+ struct pm8921_soc_params raw;
+
+ read_soc_params_raw(the_chip, &raw);
*val = 0;
@@ -1644,19 +1691,19 @@
switch (param) {
case CC_MSB:
case CC_LSB:
- read_cc(the_chip, (int *)val);
+ *val = raw.cc;
break;
case LAST_GOOD_OCV_VALUE:
- read_last_good_ocv(the_chip, (uint *)val);
+ *val = raw.last_good_ocv_uv;
break;
case VBATT_FOR_RBATT:
- read_vbatt_for_rbatt(the_chip, (uint *)val);
+ *val = raw.vbatt_for_rbatt_uv;
break;
case VSENSE_FOR_RBATT:
- read_vsense_for_rbatt(the_chip, (uint *)val);
+ *val = raw.vsense_for_rbatt_uv;
break;
case OCV_FOR_RBATT:
- read_ocv_for_rbatt(the_chip, (uint *)val);
+ *val = raw.ocv_for_rbatt_uv;
break;
case VSENSE_AVG:
read_vsense_avg(the_chip, (uint *)val);
@@ -1794,11 +1841,13 @@
return -ENOMEM;
}
spin_lock_init(&chip->bms_output_lock);
+ spin_lock_init(&chip->bms_100_lock);
chip->dev = &pdev->dev;
chip->r_sense = pdata->r_sense;
chip->i_test = pdata->i_test;
chip->v_failure = pdata->v_failure;
chip->calib_delay_ms = pdata->calib_delay_ms;
+ chip->max_voltage_uv = pdata->max_voltage_uv;
rc = set_battery_data(chip);
if (rc) {
pr_err("%s bad battery data %d\n", __func__, rc);
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index e908799..5bf9c76 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -192,16 +192,16 @@
/**
* struct pm8921_chg_chip -device information
* @dev: device pointer to access the parent
- * @is_usb_path_used: indicates whether USB charging is used at all
- * @is_usb_path_used: indicates whether DC charging is used at all
* @usb_present: present status of usb
* @dc_present: present status of dc
* @usb_charger_current: usb current to charge the battery with used when
* the usb path is enabled or charging is resumed
* @safety_time: max time for which charging will happen
* @update_time: how frequently the userland needs to be updated
- * @max_voltage: the max volts the batt should be charged up to
- * @min_voltage: the min battery voltage before turning the FETon
+ * @max_voltage_mv: the max volts the batt should be charged up to
+ * @min_voltage_mv: the min battery voltage before turning the FETon
+ * @cool_temp_dc: the cool temp threshold in deciCelcius
+ * @warm_temp_dc: the warm temp threshold in deciCelcius
* @resume_voltage_delta: the voltage delta from vdd max at which the
* battery should resume charging
* @term_current: The charging based term current
@@ -217,10 +217,10 @@
unsigned int safety_time;
unsigned int ttrkl_time;
unsigned int update_time;
- unsigned int max_voltage;
- unsigned int min_voltage;
- unsigned int cool_temp;
- unsigned int warm_temp;
+ unsigned int max_voltage_mv;
+ unsigned int min_voltage_mv;
+ unsigned int cool_temp_dc;
+ unsigned int warm_temp_dc;
unsigned int temp_check_period;
unsigned int cool_bat_chg_current;
unsigned int warm_bat_chg_current;
@@ -239,6 +239,7 @@
struct dentry *dent;
struct bms_notify bms_notify;
struct ext_chg_pm8921 *ext;
+ bool keep_btm_on_suspend;
bool ext_charging;
bool ext_charge_done;
DECLARE_BITMAP(enabled_irqs, PM_CHG_MAX_INTS);
@@ -922,7 +923,7 @@
POWER_SUPPLY_PROP_ENERGY_FULL,
};
-static int get_prop_battery_mvolts(struct pm8921_chg_chip *chip)
+static int get_prop_battery_uvolts(struct pm8921_chg_chip *chip)
{
int rc;
struct pm8xxx_adc_chan_result result;
@@ -940,16 +941,17 @@
static unsigned int voltage_based_capacity(struct pm8921_chg_chip *chip)
{
- unsigned int current_voltage = get_prop_battery_mvolts(chip);
- unsigned int low_voltage = chip->min_voltage;
- unsigned int high_voltage = chip->max_voltage;
+ unsigned int current_voltage_uv = get_prop_battery_uvolts(chip);
+ unsigned int current_voltage_mv = current_voltage_uv / 1000;
+ unsigned int low_voltage = chip->min_voltage_mv;
+ unsigned int high_voltage = chip->max_voltage_mv;
- if (current_voltage <= low_voltage)
+ if (current_voltage_mv <= low_voltage)
return 0;
- else if (current_voltage >= high_voltage)
+ else if (current_voltage_mv >= high_voltage)
return 100;
else
- return (current_voltage - low_voltage) * 100
+ return (current_voltage_mv - low_voltage) * 100
/ (high_voltage - low_voltage);
}
@@ -968,18 +970,18 @@
static int get_prop_batt_current(struct pm8921_chg_chip *chip)
{
- int result_ma, rc;
+ int result_ua, rc;
- rc = pm8921_bms_get_battery_current(&result_ma);
+ rc = pm8921_bms_get_battery_current(&result_ua);
if (rc == -ENXIO) {
- rc = pm8xxx_ccadc_get_battery_current(&result_ma);
+ rc = pm8xxx_ccadc_get_battery_current(&result_ua);
}
if (rc) {
pr_err("unable to get batt current rc = %d\n", rc);
return rc;
} else {
- return result_ma;
+ return result_ua;
}
}
@@ -1104,13 +1106,13 @@
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
- val->intval = chip->max_voltage;
+ val->intval = chip->max_voltage_mv * 1000;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
- val->intval = chip->min_voltage;
+ val->intval = chip->min_voltage_mv * 1000;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = get_prop_battery_mvolts(chip);
+ val->intval = get_prop_battery_uvolts(chip);
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = get_prop_batt_capacity(chip);
@@ -1122,7 +1124,7 @@
val->intval = get_prop_batt_temp(chip);
break;
case POWER_SUPPLY_PROP_ENERGY_FULL:
- val->intval = get_prop_batt_fcc(chip);
+ val->intval = get_prop_batt_fcc(chip) * 1000;
break;
default:
return -EINVAL;
@@ -1843,8 +1845,8 @@
struct delayed_work *dwork = to_delayed_work(work);
struct pm8921_chg_chip *chip = container_of(dwork,
struct pm8921_chg_chip, eoc_work);
- int vbat_meas, vbat_programmed;
- int ichg_meas, iterm_programmed;
+ int vbat_meas_uv, vbat_meas_mv, vbat_programmed;
+ int ichg_meas_ma, iterm_programmed;
int regulation_loop, fast_chg, vcp;
int rc;
static int count;
@@ -1872,18 +1874,19 @@
goto reset_and_reschedule;
/* reset count if battery voltage is less than vddmax */
- vbat_meas = get_prop_battery_mvolts(chip);
- if (vbat_meas < 0)
+ vbat_meas_uv = get_prop_battery_uvolts(chip);
+ if (vbat_meas_uv < 0)
goto reset_and_reschedule;
+ vbat_meas_mv = vbat_meas_uv / 1000;
rc = pm_chg_vddmax_get(chip, &vbat_programmed);
if (rc) {
pr_err("couldnt read vddmax rc = %d\n", rc);
goto reset_and_reschedule;
}
- pr_debug("vddmax = %d vbat_meas=%d\n",
- vbat_programmed, vbat_meas);
- if (vbat_meas < vbat_programmed - VBAT_TOLERANCE_MV)
+ pr_debug("vddmax = %d vbat_meas_mv=%d\n",
+ vbat_programmed, vbat_meas_mv);
+ if (vbat_meas_mv < vbat_programmed - VBAT_TOLERANCE_MV)
goto reset_and_reschedule;
} /* !is_ext_charging */
@@ -1894,17 +1897,17 @@
goto reset_and_reschedule;
}
- ichg_meas = get_prop_batt_current(chip);
- pr_debug("iterm_programmed = %d ichg_meas=%d\n",
- iterm_programmed, ichg_meas);
+ ichg_meas_ma = (get_prop_batt_current(chip)) / 1000;
+ pr_debug("iterm_programmed = %d ichg_meas_ma=%d\n",
+ iterm_programmed, ichg_meas_ma);
/*
- * ichg_meas < 0 means battery is drawing current
- * ichg_meas > 0 means battery is providing current
+ * ichg_meas_ma < 0 means battery is drawing current
+ * ichg_meas_ma > 0 means battery is providing current
*/
- if (ichg_meas > 0)
+ if (ichg_meas_ma > 0)
goto reset_and_reschedule;
- if (ichg_meas * -1 > iterm_programmed)
+ if (ichg_meas_ma * -1 > iterm_programmed)
goto reset_and_reschedule;
if (!is_ext_charging(chip)) {
@@ -1972,7 +1975,7 @@
if (chip->is_bat_warm)
chg_current = min(chg_current, chip->warm_bat_chg_current);
- if (thermal_mitigation != 0 && !chip->thermal_mitigation)
+ if (thermal_mitigation != 0 && chip->thermal_mitigation)
chg_current = min(chg_current,
chip->thermal_mitigation[thermal_mitigation]);
@@ -1988,18 +1991,19 @@
the_chip->is_bat_cool = enter;
if (enter) {
btm_config.low_thr_temp =
- the_chip->cool_temp + TEMP_HYSTERISIS_DEGC;
+ the_chip->cool_temp_dc + TEMP_HYSTERISIS_DEGC;
set_appropriate_battery_current(the_chip);
pm_chg_vddmax_set(the_chip, the_chip->cool_bat_voltage);
pm_chg_vbatdet_set(the_chip,
the_chip->cool_bat_voltage
- the_chip->resume_voltage_delta);
} else {
- btm_config.low_thr_temp = the_chip->cool_temp;
+ btm_config.low_thr_temp = the_chip->cool_temp_dc;
set_appropriate_battery_current(the_chip);
- pm_chg_vddmax_set(the_chip, the_chip->max_voltage);
+ pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv);
pm_chg_vbatdet_set(the_chip,
- the_chip->max_voltage - the_chip->resume_voltage_delta);
+ the_chip->max_voltage_mv
+ - the_chip->resume_voltage_delta);
}
schedule_work(&btm_config_work);
}
@@ -2012,18 +2016,19 @@
the_chip->is_bat_warm = enter;
if (enter) {
btm_config.high_thr_temp =
- the_chip->warm_temp - TEMP_HYSTERISIS_DEGC;
+ the_chip->warm_temp_dc - TEMP_HYSTERISIS_DEGC;
set_appropriate_battery_current(the_chip);
pm_chg_vddmax_set(the_chip, the_chip->warm_bat_voltage);
pm_chg_vbatdet_set(the_chip,
the_chip->warm_bat_voltage
- the_chip->resume_voltage_delta);
} else {
- btm_config.high_thr_temp = the_chip->warm_temp;
+ btm_config.high_thr_temp = the_chip->warm_temp_dc;
set_appropriate_battery_current(the_chip);
- pm_chg_vddmax_set(the_chip, the_chip->max_voltage);
+ pm_chg_vddmax_set(the_chip, the_chip->max_voltage_mv);
pm_chg_vbatdet_set(the_chip,
- the_chip->max_voltage - the_chip->resume_voltage_delta);
+ the_chip->max_voltage_mv
+ - the_chip->resume_voltage_delta);
}
schedule_work(&btm_config_work);
}
@@ -2034,8 +2039,8 @@
btm_config.btm_warm_fn = battery_warm;
btm_config.btm_cool_fn = battery_cool;
- btm_config.low_thr_temp = chip->cool_temp;
- btm_config.high_thr_temp = chip->warm_temp;
+ btm_config.low_thr_temp = chip->cool_temp_dc;
+ btm_config.high_thr_temp = chip->warm_temp_dc;
btm_config.interval = chip->temp_check_period;
rc = pm8xxx_adc_btm_configure(&btm_config);
if (rc)
@@ -2314,24 +2319,25 @@
return rc;
}
- rc = pm_chg_vddsafe_set(chip, chip->max_voltage);
+ rc = pm_chg_vddsafe_set(chip, chip->max_voltage_mv);
if (rc) {
pr_err("Failed to set safe voltage to %d rc=%d\n",
- chip->max_voltage, rc);
+ chip->max_voltage_mv, rc);
return rc;
}
rc = pm_chg_vbatdet_set(chip,
- chip->max_voltage - chip->resume_voltage_delta);
+ chip->max_voltage_mv
+ - chip->resume_voltage_delta);
if (rc) {
pr_err("Failed to set vbatdet comprator voltage to %d rc=%d\n",
- chip->max_voltage - chip->resume_voltage_delta, rc);
+ chip->max_voltage_mv - chip->resume_voltage_delta, rc);
return rc;
}
- rc = pm_chg_vddmax_set(chip, chip->max_voltage);
+ rc = pm_chg_vddmax_set(chip, chip->max_voltage_mv);
if (rc) {
pr_err("Failed to set max voltage to %d rc=%d\n",
- chip->max_voltage, rc);
+ chip->max_voltage_mv, rc);
return rc;
}
rc = pm_chg_ibatsafe_set(chip, SAFE_CURRENT_MA);
@@ -2671,6 +2677,37 @@
}
}
+static int pm8921_charger_resume(struct device *dev)
+{
+ int rc;
+ struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
+
+ if (!(chip->cool_temp_dc == 0 && chip->warm_temp_dc == 0)
+ && !(chip->keep_btm_on_suspend)) {
+ rc = pm8xxx_adc_btm_configure(&btm_config);
+ if (rc)
+ pr_err("couldn't reconfigure btm rc=%d\n", rc);
+
+ rc = pm8xxx_adc_btm_start();
+ if (rc)
+ pr_err("couldn't restart btm rc=%d\n", rc);
+ }
+ return 0;
+}
+
+static int pm8921_charger_suspend(struct device *dev)
+{
+ int rc;
+ struct pm8921_chg_chip *chip = dev_get_drvdata(dev);
+
+ if (!(chip->cool_temp_dc == 0 && chip->warm_temp_dc == 0)
+ && !(chip->keep_btm_on_suspend)) {
+ rc = pm8xxx_adc_btm_end();
+ if (rc)
+ pr_err("Failed to disable BTM on suspend rc=%d\n", rc);
+ }
+ return 0;
+}
static int __devinit pm8921_charger_probe(struct platform_device *pdev)
{
int rc = 0;
@@ -2694,8 +2731,8 @@
chip->safety_time = pdata->safety_time;
chip->ttrkl_time = pdata->ttrkl_time;
chip->update_time = pdata->update_time;
- chip->max_voltage = pdata->max_voltage;
- chip->min_voltage = pdata->min_voltage;
+ chip->max_voltage_mv = pdata->max_voltage;
+ chip->min_voltage_mv = pdata->min_voltage;
chip->resume_voltage_delta = pdata->resume_voltage_delta;
chip->term_current = pdata->term_current;
chip->vbat_channel = pdata->charger_cdata.vbat_channel;
@@ -2703,14 +2740,15 @@
chip->batt_id_channel = pdata->charger_cdata.batt_id_channel;
chip->batt_id_min = pdata->batt_id_min;
chip->batt_id_max = pdata->batt_id_max;
- chip->cool_temp = pdata->cool_temp;
- chip->warm_temp = pdata->warm_temp;
+ chip->cool_temp_dc = pdata->cool_temp * 10;
+ chip->warm_temp_dc = pdata->warm_temp * 10;
chip->temp_check_period = pdata->temp_check_period;
chip->max_bat_chg_current = pdata->max_bat_chg_current;
chip->cool_bat_chg_current = pdata->cool_bat_chg_current;
chip->warm_bat_chg_current = pdata->warm_bat_chg_current;
chip->cool_bat_voltage = pdata->cool_bat_voltage;
chip->warm_bat_voltage = pdata->warm_bat_voltage;
+ chip->keep_btm_on_suspend = pdata->keep_btm_on_suspend;
chip->trkl_voltage = pdata->trkl_voltage;
chip->weak_voltage = pdata->weak_voltage;
chip->trkl_current = pdata->trkl_current;
@@ -2786,10 +2824,10 @@
enable_irq_wake(chip->pmic_chg_irq[BAT_TEMP_OK_IRQ]);
enable_irq_wake(chip->pmic_chg_irq[VBATDET_LOW_IRQ]);
/*
- * if both the cool_temp and warm_temp are zero the device doesnt
+ * if both the cool_temp_dc and warm_temp_dc are zero the device doesnt
* care for jeita compliance
*/
- if (!(chip->cool_temp == 0 && chip->warm_temp == 0)) {
+ if (!(chip->cool_temp_dc == 0 && chip->warm_temp_dc == 0)) {
rc = configure_btm(chip);
if (rc) {
pr_err("couldn't register with btm rc=%d\n", rc);
@@ -2837,13 +2875,17 @@
kfree(chip);
return 0;
}
-
+static const struct dev_pm_ops pm8921_pm_ops = {
+ .suspend = pm8921_charger_suspend,
+ .resume = pm8921_charger_resume,
+};
static struct platform_driver pm8921_charger_driver = {
- .probe = pm8921_charger_probe,
- .remove = __devexit_p(pm8921_charger_remove),
- .driver = {
- .name = PM8921_CHARGER_DEV_NAME,
- .owner = THIS_MODULE,
+ .probe = pm8921_charger_probe,
+ .remove = __devexit_p(pm8921_charger_remove),
+ .driver = {
+ .name = PM8921_CHARGER_DEV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &pm8921_pm_ops,
},
};
diff --git a/drivers/power/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c
index 5e0f8ec..17cf53a 100644
--- a/drivers/power/pm8xxx-ccadc.c
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -76,6 +76,7 @@
static struct pm8xxx_ccadc_chip *the_chip;
+#ifdef DEBUG
static s64 microvolt_to_ccadc_reading_v1(s64 uv)
{
return div_s64(uv * CCADC_READING_RESOLUTION_D_V1,
@@ -98,6 +99,7 @@
microvolt_to_ccadc_reading_v1((s64)cc) :
microvolt_to_ccadc_reading_v2((s64)cc);
}
+#endif
static int cc_adjust_for_offset(u16 raw)
{
@@ -256,7 +258,7 @@
static void calib_ccadc_read_offset_and_gain(struct pm8xxx_ccadc_chip *chip,
int *gain, u16 *offset)
{
- s8 data_msb;
+ u8 data_msb;
u8 data_lsb;
int rc;
@@ -328,7 +330,7 @@
void pm8xxx_calib_ccadc(void)
{
u8 data_msb, data_lsb, sec_cntrl;
- int result_offset, voltage_offset, result_gain;
+ int result_offset, result_gain;
u16 result;
int i, rc;
@@ -385,26 +387,11 @@
result_offset = result_offset / SAMPLE_COUNT;
- voltage_offset = pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
- ((s64)result_offset - CCADC_INTRINSIC_OFFSET));
- pr_debug("offset result_offset = 0x%x, voltage = %d microVolts\n",
- result_offset, voltage_offset);
-
- /* Sanity Check */
- if (voltage_offset > CCADC_MAX_0UV) {
- pr_err("offset voltage = %d is huge limiting to %d\n",
- voltage_offset, CCADC_MAX_0UV);
- result_offset = CCADC_INTRINSIC_OFFSET
- + microvolt_to_ccadc_reading(the_chip,
- (s64)CCADC_MAX_0UV);
- } else if (voltage_offset < CCADC_MIN_0UV) {
- pr_err("offset voltage = %d is too low limiting to %d\n",
- voltage_offset, CCADC_MIN_0UV);
- result_offset = CCADC_INTRINSIC_OFFSET
- + microvolt_to_ccadc_reading(the_chip,
- (s64)CCADC_MIN_0UV);
- }
+ pr_debug("offset result_offset = 0x%x, voltage = %llduV\n",
+ result_offset,
+ pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
+ ((s64)result_offset - CCADC_INTRINSIC_OFFSET)));
the_chip->ccadc_offset = result_offset;
data_msb = the_chip->ccadc_offset >> 8;
@@ -474,22 +461,6 @@
pr_debug("gain result_gain = 0x%x, voltage = %d microVolts\n",
result_gain, the_chip->ccadc_gain_uv);
- /* Sanity Check */
- if (the_chip->ccadc_gain_uv > CCADC_MAX_25MV) {
- pr_err("gain voltage = %d is huge limiting to %d\n",
- the_chip->ccadc_gain_uv,
- CCADC_MAX_25MV);
- the_chip->ccadc_gain_uv = CCADC_MAX_25MV;
- result_gain = result_offset +
- microvolt_to_ccadc_reading(the_chip, CCADC_MAX_25MV);
- } else if (the_chip->ccadc_gain_uv < CCADC_MIN_25MV) {
- pr_err("gain voltage = %d is too low limiting to %d\n",
- the_chip->ccadc_gain_uv,
- CCADC_MIN_25MV);
- the_chip->ccadc_gain_uv = CCADC_MIN_25MV;
- result_gain = result_offset +
- microvolt_to_ccadc_reading(the_chip, CCADC_MIN_25MV);
- }
data_msb = result_gain >> 8;
data_lsb = result_gain;
@@ -568,14 +539,15 @@
the_chip->ccadc_offset);
*voltage_uv = pm8xxx_ccadc_reading_to_microvolt(the_chip->revision,
((s64)result));
- pr_debug("Vsense before gain = %d uV\n", *voltage_uv);
+ pr_debug("Vsense before gain of %d = %d uV\n", the_chip->ccadc_gain_uv,
+ *voltage_uv);
*voltage_uv = pm8xxx_cc_adjust_for_gain(*voltage_uv);
pr_debug("Vsense = %d uV\n", *voltage_uv);
return 0;
}
-int pm8xxx_ccadc_get_battery_current(int *bat_current)
+int pm8xxx_ccadc_get_battery_current(int *bat_current_ua)
{
int voltage_uv, rc;
@@ -585,13 +557,13 @@
return rc;
}
- *bat_current = voltage_uv/the_chip->r_sense;
+ *bat_current_ua = voltage_uv * 1000/the_chip->r_sense;
/*
* ccadc reads +ve current when the battery is charging
* We need to return -ve if the battery is charging
*/
- *bat_current = -1 * (*bat_current);
- pr_debug("bat current = %d ma\n", *bat_current);
+ *bat_current_ua = -1 * (*bat_current_ua);
+ pr_debug("bat current = %d ma\n", *bat_current_ua);
return 0;
}
EXPORT_SYMBOL(pm8xxx_ccadc_get_battery_current);
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 8eb443f..a8e2282 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -327,6 +327,15 @@
switching regulators. It also provides a negative charge pump and
voltage switches.
+config REGULATOR_PM8XXX
+ tristate "Qualcomm PM8XXX PMIC Voltage regulators"
+ depends on MFD_PM8XXX
+ help
+ This driver supports voltage regulators in Qualcomm PM8XXX PMIC chips.
+ PM8XXX chips provide several different varieties of LDO and switching
+ regulators. They also provide negative charge pumps and voltage
+ switches.
+
config REGULATOR_GPIO
tristate "GPIO regulator"
depends on GPIOLIB
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 4e5fd66..3051dc0 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -49,5 +49,6 @@
obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
obj-$(CONFIG_REGULATOR_PM8058_XO) += pm8058-xo.o
obj-$(CONFIG_REGULATOR_PM8018) += pm8018-regulator.o
+obj-$(CONFIG_REGULATOR_PM8XXX) += pm8xxx-regulator.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/pm8xxx-regulator.c b/drivers/regulator/pm8xxx-regulator.c
new file mode 100644
index 0000000..15a9cb1
--- /dev/null
+++ b/drivers/regulator/pm8xxx-regulator.c
@@ -0,0 +1,3065 @@
+/*
+ * Copyright (c) 2011, 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.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/mfd/pm8xxx/core.h>
+#include <linux/mfd/pm8xxx/regulator.h>
+
+/* Debug Flag Definitions */
+enum {
+ PM8XXX_VREG_DEBUG_REQUEST = BIT(0),
+ PM8XXX_VREG_DEBUG_DUPLICATE = BIT(1),
+ PM8XXX_VREG_DEBUG_INIT = BIT(2),
+ PM8XXX_VREG_DEBUG_WRITES = BIT(3), /* SSBI writes */
+};
+
+static int pm8xxx_vreg_debug_mask;
+module_param_named(
+ debug_mask, pm8xxx_vreg_debug_mask, int, S_IRUSR | S_IWUSR
+);
+
+/* Common Masks */
+#define REGULATOR_ENABLE_MASK 0x80
+#define REGULATOR_ENABLE 0x80
+#define REGULATOR_DISABLE 0x00
+
+#define REGULATOR_BANK_MASK 0xF0
+#define REGULATOR_BANK_SEL(n) ((n) << 4)
+#define REGULATOR_BANK_WRITE 0x80
+
+#define LDO_TEST_BANKS 7
+#define NLDO1200_TEST_BANKS 5
+#define SMPS_TEST_BANKS 8
+
+/*
+ * This voltage in uV is returned by get_voltage functions when there is no way
+ * to determine the current voltage level. It is needed because the regulator
+ * framework treats a 0 uV voltage as an error.
+ */
+#define VOLTAGE_UNKNOWN 1
+
+/* LDO masks and values */
+
+/* CTRL register */
+#define LDO_ENABLE_MASK 0x80
+#define LDO_DISABLE 0x00
+#define LDO_ENABLE 0x80
+#define LDO_PULL_DOWN_ENABLE_MASK 0x40
+#define LDO_PULL_DOWN_ENABLE 0x40
+
+#define LDO_CTRL_PM_MASK 0x20
+#define LDO_CTRL_PM_HPM 0x00
+#define LDO_CTRL_PM_LPM 0x20
+
+#define LDO_CTRL_VPROG_MASK 0x1F
+
+/* TEST register bank 0 */
+#define LDO_TEST_LPM_MASK 0x04
+#define LDO_TEST_LPM_SEL_CTRL 0x00
+#define LDO_TEST_LPM_SEL_TCXO 0x04
+
+/* TEST register bank 2 */
+#define LDO_TEST_VPROG_UPDATE_MASK 0x08
+#define LDO_TEST_RANGE_SEL_MASK 0x04
+#define LDO_TEST_FINE_STEP_MASK 0x02
+#define LDO_TEST_FINE_STEP_SHIFT 1
+
+/* TEST register bank 4 */
+#define LDO_TEST_RANGE_EXT_MASK 0x01
+
+/* TEST register bank 5 */
+#define LDO_TEST_PIN_CTRL_MASK 0x0F
+#define LDO_TEST_PIN_CTRL_EN3 0x08
+#define LDO_TEST_PIN_CTRL_EN2 0x04
+#define LDO_TEST_PIN_CTRL_EN1 0x02
+#define LDO_TEST_PIN_CTRL_EN0 0x01
+
+/* TEST register bank 6 */
+#define LDO_TEST_PIN_CTRL_LPM_MASK 0x0F
+
+/*
+ * If a given voltage could be output by two ranges, then the preferred one must
+ * be determined by the range limits. Specified voltage ranges should must
+ * not overlap.
+ *
+ * Allowable voltage ranges:
+ */
+#define PLDO_LOW_UV_MIN 750000
+#define PLDO_LOW_UV_MAX 1487500
+#define PLDO_LOW_UV_FINE_STEP 12500
+
+#define PLDO_NORM_UV_MIN 1500000
+#define PLDO_NORM_UV_MAX 3075000
+#define PLDO_NORM_UV_FINE_STEP 25000
+
+#define PLDO_HIGH_UV_MIN 1750000
+#define PLDO_HIGH_UV_SET_POINT_MIN 3100000
+#define PLDO_HIGH_UV_MAX 4900000
+#define PLDO_HIGH_UV_FINE_STEP 50000
+
+#define PLDO_LOW_SET_POINTS ((PLDO_LOW_UV_MAX - PLDO_LOW_UV_MIN) \
+ / PLDO_LOW_UV_FINE_STEP + 1)
+#define PLDO_NORM_SET_POINTS ((PLDO_NORM_UV_MAX - PLDO_NORM_UV_MIN) \
+ / PLDO_NORM_UV_FINE_STEP + 1)
+#define PLDO_HIGH_SET_POINTS ((PLDO_HIGH_UV_MAX \
+ - PLDO_HIGH_UV_SET_POINT_MIN) \
+ / PLDO_HIGH_UV_FINE_STEP + 1)
+#define PLDO_SET_POINTS (PLDO_LOW_SET_POINTS \
+ + PLDO_NORM_SET_POINTS \
+ + PLDO_HIGH_SET_POINTS)
+
+#define NLDO_UV_MIN 750000
+#define NLDO_UV_MAX 1537500
+#define NLDO_UV_FINE_STEP 12500
+
+#define NLDO_SET_POINTS ((NLDO_UV_MAX - NLDO_UV_MIN) \
+ / NLDO_UV_FINE_STEP + 1)
+
+/* NLDO1200 masks and values */
+
+/* CTRL register */
+#define NLDO1200_ENABLE_MASK 0x80
+#define NLDO1200_DISABLE 0x00
+#define NLDO1200_ENABLE 0x80
+
+/* Legacy mode */
+#define NLDO1200_LEGACY_PM_MASK 0x20
+#define NLDO1200_LEGACY_PM_HPM 0x00
+#define NLDO1200_LEGACY_PM_LPM 0x20
+
+/* Advanced mode */
+#define NLDO1200_CTRL_RANGE_MASK 0x40
+#define NLDO1200_CTRL_RANGE_HIGH 0x00
+#define NLDO1200_CTRL_RANGE_LOW 0x40
+#define NLDO1200_CTRL_VPROG_MASK 0x3F
+
+#define NLDO1200_LOW_UV_MIN 375000
+#define NLDO1200_LOW_UV_MAX 743750
+#define NLDO1200_LOW_UV_STEP 6250
+
+#define NLDO1200_HIGH_UV_MIN 750000
+#define NLDO1200_HIGH_UV_MAX 1537500
+#define NLDO1200_HIGH_UV_STEP 12500
+
+#define NLDO1200_LOW_SET_POINTS ((NLDO1200_LOW_UV_MAX \
+ - NLDO1200_LOW_UV_MIN) \
+ / NLDO1200_LOW_UV_STEP + 1)
+#define NLDO1200_HIGH_SET_POINTS ((NLDO1200_HIGH_UV_MAX \
+ - NLDO1200_HIGH_UV_MIN) \
+ / NLDO1200_HIGH_UV_STEP + 1)
+#define NLDO1200_SET_POINTS (NLDO1200_LOW_SET_POINTS \
+ + NLDO1200_HIGH_SET_POINTS)
+
+/* TEST register bank 0 */
+#define NLDO1200_TEST_LPM_MASK 0x04
+#define NLDO1200_TEST_LPM_SEL_CTRL 0x00
+#define NLDO1200_TEST_LPM_SEL_TCXO 0x04
+
+/* TEST register bank 1 */
+#define NLDO1200_PULL_DOWN_ENABLE_MASK 0x02
+#define NLDO1200_PULL_DOWN_ENABLE 0x02
+
+/* TEST register bank 2 */
+#define NLDO1200_ADVANCED_MODE_MASK 0x08
+#define NLDO1200_ADVANCED_MODE 0x00
+#define NLDO1200_LEGACY_MODE 0x08
+
+/* Advanced mode power mode control */
+#define NLDO1200_ADVANCED_PM_MASK 0x02
+#define NLDO1200_ADVANCED_PM_HPM 0x00
+#define NLDO1200_ADVANCED_PM_LPM 0x02
+
+#define NLDO1200_IN_ADVANCED_MODE(vreg) \
+ ((vreg->test_reg[2] & NLDO1200_ADVANCED_MODE_MASK) \
+ == NLDO1200_ADVANCED_MODE)
+
+/* SMPS masks and values */
+
+/* CTRL register */
+
+/* Legacy mode */
+#define SMPS_LEGACY_ENABLE_MASK 0x80
+#define SMPS_LEGACY_DISABLE 0x00
+#define SMPS_LEGACY_ENABLE 0x80
+#define SMPS_LEGACY_PULL_DOWN_ENABLE 0x40
+#define SMPS_LEGACY_VREF_SEL_MASK 0x20
+#define SMPS_LEGACY_VPROG_MASK 0x1F
+
+/* Advanced mode */
+#define SMPS_ADVANCED_BAND_MASK 0xC0
+#define SMPS_ADVANCED_BAND_OFF 0x00
+#define SMPS_ADVANCED_BAND_1 0x40
+#define SMPS_ADVANCED_BAND_2 0x80
+#define SMPS_ADVANCED_BAND_3 0xC0
+#define SMPS_ADVANCED_VPROG_MASK 0x3F
+
+/* Legacy mode voltage ranges */
+#define SMPS_MODE3_UV_MIN 375000
+#define SMPS_MODE3_UV_MAX 725000
+#define SMPS_MODE3_UV_STEP 25000
+
+#define SMPS_MODE2_UV_MIN 750000
+#define SMPS_MODE2_UV_MAX 1475000
+#define SMPS_MODE2_UV_STEP 25000
+
+#define SMPS_MODE1_UV_MIN 1500000
+#define SMPS_MODE1_UV_MAX 3050000
+#define SMPS_MODE1_UV_STEP 50000
+
+#define SMPS_MODE3_SET_POINTS ((SMPS_MODE3_UV_MAX \
+ - SMPS_MODE3_UV_MIN) \
+ / SMPS_MODE3_UV_STEP + 1)
+#define SMPS_MODE2_SET_POINTS ((SMPS_MODE2_UV_MAX \
+ - SMPS_MODE2_UV_MIN) \
+ / SMPS_MODE2_UV_STEP + 1)
+#define SMPS_MODE1_SET_POINTS ((SMPS_MODE1_UV_MAX \
+ - SMPS_MODE1_UV_MIN) \
+ / SMPS_MODE1_UV_STEP + 1)
+#define SMPS_LEGACY_SET_POINTS (SMPS_MODE3_SET_POINTS \
+ + SMPS_MODE2_SET_POINTS \
+ + SMPS_MODE1_SET_POINTS)
+
+/* Advanced mode voltage ranges */
+#define SMPS_BAND1_UV_MIN 375000
+#define SMPS_BAND1_UV_MAX 737500
+#define SMPS_BAND1_UV_STEP 12500
+
+#define SMPS_BAND2_UV_MIN 750000
+#define SMPS_BAND2_UV_MAX 1487500
+#define SMPS_BAND2_UV_STEP 12500
+
+#define SMPS_BAND3_UV_MIN 1500000
+#define SMPS_BAND3_UV_MAX 3075000
+#define SMPS_BAND3_UV_STEP 25000
+
+#define SMPS_BAND1_SET_POINTS ((SMPS_BAND1_UV_MAX \
+ - SMPS_BAND1_UV_MIN) \
+ / SMPS_BAND1_UV_STEP + 1)
+#define SMPS_BAND2_SET_POINTS ((SMPS_BAND2_UV_MAX \
+ - SMPS_BAND2_UV_MIN) \
+ / SMPS_BAND2_UV_STEP + 1)
+#define SMPS_BAND3_SET_POINTS ((SMPS_BAND3_UV_MAX \
+ - SMPS_BAND3_UV_MIN) \
+ / SMPS_BAND3_UV_STEP + 1)
+#define SMPS_ADVANCED_SET_POINTS (SMPS_BAND1_SET_POINTS \
+ + SMPS_BAND2_SET_POINTS \
+ + SMPS_BAND3_SET_POINTS)
+
+/* Test2 register bank 1 */
+#define SMPS_LEGACY_VLOW_SEL_MASK 0x01
+
+/* Test2 register bank 6 */
+#define SMPS_ADVANCED_PULL_DOWN_ENABLE 0x08
+
+/* Test2 register bank 7 */
+#define SMPS_ADVANCED_MODE_MASK 0x02
+#define SMPS_ADVANCED_MODE 0x02
+#define SMPS_LEGACY_MODE 0x00
+
+#define SMPS_IN_ADVANCED_MODE(vreg) \
+ ((vreg->test_reg[7] & SMPS_ADVANCED_MODE_MASK) == SMPS_ADVANCED_MODE)
+
+/* BUCK_SLEEP_CNTRL register */
+#define SMPS_PIN_CTRL_MASK 0xF0
+#define SMPS_PIN_CTRL_EN3 0x80
+#define SMPS_PIN_CTRL_EN2 0x40
+#define SMPS_PIN_CTRL_EN1 0x20
+#define SMPS_PIN_CTRL_EN0 0x10
+
+#define SMPS_PIN_CTRL_LPM_MASK 0x0F
+#define SMPS_PIN_CTRL_LPM_EN3 0x08
+#define SMPS_PIN_CTRL_LPM_EN2 0x04
+#define SMPS_PIN_CTRL_LPM_EN1 0x02
+#define SMPS_PIN_CTRL_LPM_EN0 0x01
+
+/* BUCK_CLOCK_CNTRL register */
+#define SMPS_CLK_DIVIDE2 0x40
+
+#define SMPS_CLK_CTRL_MASK 0x30
+#define SMPS_CLK_CTRL_FOLLOW_TCXO 0x00
+#define SMPS_CLK_CTRL_PWM 0x10
+#define SMPS_CLK_CTRL_PFM 0x20
+
+/* FTSMPS masks and values */
+
+/* CTRL register */
+#define FTSMPS_VCTRL_BAND_MASK 0xC0
+#define FTSMPS_VCTRL_BAND_OFF 0x00
+#define FTSMPS_VCTRL_BAND_1 0x40
+#define FTSMPS_VCTRL_BAND_2 0x80
+#define FTSMPS_VCTRL_BAND_3 0xC0
+#define FTSMPS_VCTRL_VPROG_MASK 0x3F
+
+#define FTSMPS_BAND1_UV_MIN 350000
+#define FTSMPS_BAND1_UV_MAX 650000
+/* 3 LSB's of program voltage must be 0 in band 1. */
+/* Logical step size */
+#define FTSMPS_BAND1_UV_LOG_STEP 50000
+/* Physical step size */
+#define FTSMPS_BAND1_UV_PHYS_STEP 6250
+
+#define FTSMPS_BAND2_UV_MIN 700000
+#define FTSMPS_BAND2_UV_MAX 1400000
+#define FTSMPS_BAND2_UV_STEP 12500
+
+#define FTSMPS_BAND3_UV_MIN 1400000
+#define FTSMPS_BAND3_UV_SET_POINT_MIN 1500000
+#define FTSMPS_BAND3_UV_MAX 3300000
+#define FTSMPS_BAND3_UV_STEP 50000
+
+#define FTSMPS_BAND1_SET_POINTS ((FTSMPS_BAND1_UV_MAX \
+ - FTSMPS_BAND1_UV_MIN) \
+ / FTSMPS_BAND1_UV_LOG_STEP + 1)
+#define FTSMPS_BAND2_SET_POINTS ((FTSMPS_BAND2_UV_MAX \
+ - FTSMPS_BAND2_UV_MIN) \
+ / FTSMPS_BAND2_UV_STEP + 1)
+#define FTSMPS_BAND3_SET_POINTS ((FTSMPS_BAND3_UV_MAX \
+ - FTSMPS_BAND3_UV_SET_POINT_MIN) \
+ / FTSMPS_BAND3_UV_STEP + 1)
+#define FTSMPS_SET_POINTS (FTSMPS_BAND1_SET_POINTS \
+ + FTSMPS_BAND2_SET_POINTS \
+ + FTSMPS_BAND3_SET_POINTS)
+
+/* FTS_CNFG1 register bank 0 */
+#define FTSMPS_CNFG1_PM_MASK 0x0C
+#define FTSMPS_CNFG1_PM_PWM 0x00
+#define FTSMPS_CNFG1_PM_PFM 0x08
+
+/* PWR_CNFG register */
+#define FTSMPS_PULL_DOWN_ENABLE_MASK 0x40
+#define FTSMPS_PULL_DOWN_ENABLE 0x40
+
+/* VS masks and values */
+
+/* CTRL register */
+#define VS_ENABLE_MASK 0x80
+#define VS_DISABLE 0x00
+#define VS_ENABLE 0x80
+#define VS_PULL_DOWN_ENABLE_MASK 0x40
+#define VS_PULL_DOWN_DISABLE 0x40
+#define VS_PULL_DOWN_ENABLE 0x00
+
+#define VS_PIN_CTRL_MASK 0x0F
+#define VS_PIN_CTRL_EN0 0x08
+#define VS_PIN_CTRL_EN1 0x04
+#define VS_PIN_CTRL_EN2 0x02
+#define VS_PIN_CTRL_EN3 0x01
+
+/* VS300 masks and values */
+
+/* CTRL register */
+#define VS300_CTRL_ENABLE_MASK 0xC0
+#define VS300_CTRL_DISABLE 0x00
+#define VS300_CTRL_ENABLE 0x40
+
+#define VS300_PULL_DOWN_ENABLE_MASK 0x20
+#define VS300_PULL_DOWN_ENABLE 0x20
+
+/* NCP masks and values */
+
+/* CTRL register */
+#define NCP_ENABLE_MASK 0x80
+#define NCP_DISABLE 0x00
+#define NCP_ENABLE 0x80
+#define NCP_VPROG_MASK 0x1F
+
+#define NCP_UV_MIN 1500000
+#define NCP_UV_MAX 3050000
+#define NCP_UV_STEP 50000
+
+#define NCP_SET_POINTS ((NCP_UV_MAX - NCP_UV_MIN) \
+ / NCP_UV_STEP + 1)
+
+#define vreg_err(vreg, fmt, ...) \
+ pr_err("%s: " fmt, vreg->rdesc.name, ##__VA_ARGS__)
+
+/* Determines which label to add to the print. */
+enum pm8xxx_regulator_action {
+ PM8XXX_REGULATOR_ACTION_INIT,
+ PM8XXX_REGULATOR_ACTION_ENABLE,
+ PM8XXX_REGULATOR_ACTION_DISABLE,
+ PM8XXX_REGULATOR_ACTION_VOLTAGE,
+ PM8XXX_REGULATOR_ACTION_MODE,
+ PM8XXX_REGULATOR_ACTION_PIN_CTRL,
+};
+
+/* Debug state printing */
+static void pm8xxx_vreg_show_state(struct regulator_dev *rdev,
+ enum pm8xxx_regulator_action action);
+
+/*
+ * Perform a masked write to a PMIC register only if the new value differs
+ * from the last value written to the register. This removes redundant
+ * register writing.
+ *
+ * No locking is required because registers are not shared between regulators.
+ */
+static int pm8xxx_vreg_masked_write(struct pm8xxx_vreg *vreg, u16 addr, u8 val,
+ u8 mask, u8 *reg_save)
+{
+ int rc = 0;
+ u8 reg;
+
+ reg = (*reg_save & ~mask) | (val & mask);
+ if (reg != *reg_save) {
+ rc = pm8xxx_writeb(vreg->dev->parent, addr, reg);
+
+ if (rc) {
+ pr_err("%s: pm8xxx_writeb failed; addr=0x%03X, rc=%d\n",
+ vreg->rdesc.name, addr, rc);
+ } else {
+ *reg_save = reg;
+ vreg->write_count++;
+ if (pm8xxx_vreg_debug_mask & PM8XXX_VREG_DEBUG_WRITES)
+ pr_info("%s: write(0x%03X)=0x%02X\n",
+ vreg->rdesc.name, addr, reg);
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * Perform a masked write to a PMIC register without checking the previously
+ * written value. This is needed for registers that must be rewritten even if
+ * the value hasn't changed in order for changes in other registers to take
+ * effect.
+ */
+static int pm8xxx_vreg_masked_write_forced(struct pm8xxx_vreg *vreg, u16 addr,
+ u8 val, u8 mask, u8 *reg_save)
+{
+ int rc = 0;
+ u8 reg;
+
+ reg = (*reg_save & ~mask) | (val & mask);
+ rc = pm8xxx_writeb(vreg->dev->parent, addr, reg);
+
+ if (rc) {
+ pr_err("%s: pm8xxx_writeb failed; addr=0x%03X, rc=%d\n",
+ vreg->rdesc.name, addr, rc);
+ } else {
+ *reg_save = reg;
+ vreg->write_count++;
+ if (pm8xxx_vreg_debug_mask & PM8XXX_VREG_DEBUG_WRITES)
+ pr_info("%s: write(0x%03X)=0x%02X\n", vreg->rdesc.name,
+ addr, reg);
+ }
+
+ return rc;
+}
+
+static int pm8xxx_vreg_is_pin_controlled(struct pm8xxx_vreg *vreg)
+{
+ int ret = 0;
+
+ switch (vreg->type) {
+ case PM8XXX_REGULATOR_TYPE_PLDO:
+ case PM8XXX_REGULATOR_TYPE_NLDO:
+ ret = ((vreg->test_reg[5] & LDO_TEST_PIN_CTRL_MASK) << 4)
+ | (vreg->test_reg[6] & LDO_TEST_PIN_CTRL_LPM_MASK);
+ break;
+ case PM8XXX_REGULATOR_TYPE_SMPS:
+ ret = vreg->sleep_ctrl_reg
+ & (SMPS_PIN_CTRL_MASK | SMPS_PIN_CTRL_LPM_MASK);
+ break;
+ case PM8XXX_REGULATOR_TYPE_VS:
+ ret = vreg->ctrl_reg & VS_PIN_CTRL_MASK;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/*
+ * Returns the logical pin control enable state because the pin control options
+ * present in the hardware out of restart could be different from those desired
+ * by the consumer.
+ */
+static int pm8xxx_vreg_pin_control_is_enabled(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int enabled;
+
+ mutex_lock(&vreg->pc_lock);
+ enabled = vreg->is_enabled_pc;
+ mutex_unlock(&vreg->pc_lock);
+
+ return enabled;
+}
+
+/* Returns the physical enable state of the regulator. */
+static int _pm8xxx_vreg_is_enabled(struct pm8xxx_vreg *vreg)
+{
+ int rc = 0;
+
+ /*
+ * All regulator types except advanced mode SMPS, FTSMPS, and VS300 have
+ * enable bit in bit 7 of the control register.
+ */
+ switch (vreg->type) {
+ case PM8XXX_REGULATOR_TYPE_FTSMPS:
+ if ((vreg->ctrl_reg & FTSMPS_VCTRL_BAND_MASK)
+ != FTSMPS_VCTRL_BAND_OFF)
+ rc = 1;
+ break;
+ case PM8XXX_REGULATOR_TYPE_VS300:
+ if ((vreg->ctrl_reg & VS300_CTRL_ENABLE_MASK)
+ != VS300_CTRL_DISABLE)
+ rc = 1;
+ break;
+ case PM8XXX_REGULATOR_TYPE_SMPS:
+ if (SMPS_IN_ADVANCED_MODE(vreg)) {
+ if ((vreg->ctrl_reg & SMPS_ADVANCED_BAND_MASK)
+ != SMPS_ADVANCED_BAND_OFF)
+ rc = 1;
+ break;
+ }
+ /* Fall through for legacy mode SMPS. */
+ default:
+ if ((vreg->ctrl_reg & REGULATOR_ENABLE_MASK)
+ == REGULATOR_ENABLE)
+ rc = 1;
+ }
+
+ return rc;
+}
+
+/*
+ * Returns the logical enable state of the regulator which may be different from
+ * the physical enable state thanks to HPM/LPM pin control.
+ */
+static int pm8xxx_vreg_is_enabled(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int enabled;
+
+ if (vreg->type == PM8XXX_REGULATOR_TYPE_PLDO
+ || vreg->type == PM8XXX_REGULATOR_TYPE_NLDO
+ || vreg->type == PM8XXX_REGULATOR_TYPE_SMPS
+ || vreg->type == PM8XXX_REGULATOR_TYPE_VS) {
+ /* Pin controllable */
+ mutex_lock(&vreg->pc_lock);
+ enabled = vreg->is_enabled;
+ mutex_unlock(&vreg->pc_lock);
+ } else {
+ /* Not pin controlable */
+ enabled = _pm8xxx_vreg_is_enabled(vreg);
+ }
+
+ return enabled;
+}
+
+static int pm8xxx_pldo_get_voltage(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int vmin, fine_step;
+ u8 range_ext, range_sel, vprog, fine_step_reg;
+
+ mutex_lock(&vreg->pc_lock);
+
+ fine_step_reg = vreg->test_reg[2] & LDO_TEST_FINE_STEP_MASK;
+ range_sel = vreg->test_reg[2] & LDO_TEST_RANGE_SEL_MASK;
+ range_ext = vreg->test_reg[4] & LDO_TEST_RANGE_EXT_MASK;
+ vprog = vreg->ctrl_reg & LDO_CTRL_VPROG_MASK;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ vprog = (vprog << 1) | (fine_step_reg >> LDO_TEST_FINE_STEP_SHIFT);
+
+ if (range_sel) {
+ /* low range mode */
+ fine_step = PLDO_LOW_UV_FINE_STEP;
+ vmin = PLDO_LOW_UV_MIN;
+ } else if (!range_ext) {
+ /* normal mode */
+ fine_step = PLDO_NORM_UV_FINE_STEP;
+ vmin = PLDO_NORM_UV_MIN;
+ } else {
+ /* high range mode */
+ fine_step = PLDO_HIGH_UV_FINE_STEP;
+ vmin = PLDO_HIGH_UV_MIN;
+ }
+
+ return fine_step * vprog + vmin;
+}
+
+static int pm8xxx_pldo_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ int uV;
+
+ if (selector >= PLDO_SET_POINTS)
+ return 0;
+
+ if (selector < PLDO_LOW_SET_POINTS)
+ uV = selector * PLDO_LOW_UV_FINE_STEP + PLDO_LOW_UV_MIN;
+ else if (selector < (PLDO_LOW_SET_POINTS + PLDO_NORM_SET_POINTS))
+ uV = (selector - PLDO_LOW_SET_POINTS) * PLDO_NORM_UV_FINE_STEP
+ + PLDO_NORM_UV_MIN;
+ else
+ uV = (selector - PLDO_LOW_SET_POINTS - PLDO_NORM_SET_POINTS)
+ * PLDO_HIGH_UV_FINE_STEP
+ + PLDO_HIGH_UV_SET_POINT_MIN;
+
+ return uV;
+}
+
+static int pm8xxx_pldo_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc = 0, uV = min_uV;
+ int vmin;
+ unsigned vprog, fine_step;
+ u8 range_ext, range_sel, fine_step_reg, prev_reg;
+ bool reg_changed = false;
+
+ if (uV < PLDO_LOW_UV_MIN && max_uV >= PLDO_LOW_UV_MIN)
+ uV = PLDO_LOW_UV_MIN;
+
+ if (uV < PLDO_LOW_UV_MIN || uV > PLDO_HIGH_UV_MAX) {
+ vreg_err(vreg,
+ "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+ min_uV, max_uV, PLDO_LOW_UV_MIN, PLDO_HIGH_UV_MAX);
+ return -EINVAL;
+ }
+
+ if (uV > PLDO_NORM_UV_MAX) {
+ vmin = PLDO_HIGH_UV_MIN;
+ fine_step = PLDO_HIGH_UV_FINE_STEP;
+ range_ext = LDO_TEST_RANGE_EXT_MASK;
+ range_sel = 0;
+ } else if (uV > PLDO_LOW_UV_MAX) {
+ vmin = PLDO_NORM_UV_MIN;
+ fine_step = PLDO_NORM_UV_FINE_STEP;
+ range_ext = 0;
+ range_sel = 0;
+ } else {
+ vmin = PLDO_LOW_UV_MIN;
+ fine_step = PLDO_LOW_UV_FINE_STEP;
+ range_ext = 0;
+ range_sel = LDO_TEST_RANGE_SEL_MASK;
+ }
+
+ vprog = (uV - vmin + fine_step - 1) / fine_step;
+ uV = vprog * fine_step + vmin;
+ fine_step_reg = (vprog & 1) << LDO_TEST_FINE_STEP_SHIFT;
+ vprog >>= 1;
+
+ if (uV > max_uV) {
+ vreg_err(vreg,
+ "request v=[%d, %d] cannot be met by any set point\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+
+ mutex_lock(&vreg->pc_lock);
+
+ /* Write fine step, range select and program voltage update. */
+ prev_reg = vreg->test_reg[2];
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ fine_step_reg | range_sel | REGULATOR_BANK_SEL(2)
+ | REGULATOR_BANK_WRITE | LDO_TEST_VPROG_UPDATE_MASK,
+ LDO_TEST_FINE_STEP_MASK | LDO_TEST_RANGE_SEL_MASK
+ | REGULATOR_BANK_MASK | LDO_TEST_VPROG_UPDATE_MASK,
+ &vreg->test_reg[2]);
+ if (rc)
+ goto bail;
+ if (prev_reg != vreg->test_reg[2])
+ reg_changed = true;
+
+ /* Write range extension. */
+ prev_reg = vreg->test_reg[4];
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ range_ext | REGULATOR_BANK_SEL(4)
+ | REGULATOR_BANK_WRITE,
+ LDO_TEST_RANGE_EXT_MASK | REGULATOR_BANK_MASK,
+ &vreg->test_reg[4]);
+ if (rc)
+ goto bail;
+ if (prev_reg != vreg->test_reg[4])
+ reg_changed = true;
+
+ /* Write new voltage. */
+ if (reg_changed) {
+ /*
+ * Force a CTRL register write even if the value hasn't changed.
+ * This is neccessary because range select, range extension, and
+ * fine step will not update until a value is written into the
+ * control register.
+ */
+ rc = pm8xxx_vreg_masked_write_forced(vreg, vreg->ctrl_addr,
+ vprog, LDO_CTRL_VPROG_MASK, &vreg->ctrl_reg);
+ } else {
+ /* Only write to control register if new value is different. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, vprog,
+ LDO_CTRL_VPROG_MASK, &vreg->ctrl_reg);
+ }
+bail:
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+ return rc;
+}
+
+static int pm8xxx_nldo_get_voltage(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ u8 vprog, fine_step_reg;
+
+ mutex_lock(&vreg->pc_lock);
+
+ fine_step_reg = vreg->test_reg[2] & LDO_TEST_FINE_STEP_MASK;
+ vprog = vreg->ctrl_reg & LDO_CTRL_VPROG_MASK;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ vprog = (vprog << 1) | (fine_step_reg >> LDO_TEST_FINE_STEP_SHIFT);
+
+ return NLDO_UV_FINE_STEP * vprog + NLDO_UV_MIN;
+}
+
+static int pm8xxx_nldo_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ if (selector >= NLDO_SET_POINTS)
+ return 0;
+
+ return selector * NLDO_UV_FINE_STEP + NLDO_UV_MIN;
+}
+
+static int pm8xxx_nldo_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ unsigned vprog, fine_step_reg, prev_reg;
+ int rc;
+ int uV = min_uV;
+
+ if (uV < NLDO_UV_MIN && max_uV >= NLDO_UV_MIN)
+ uV = NLDO_UV_MIN;
+
+ if (uV < NLDO_UV_MIN || uV > NLDO_UV_MAX) {
+ vreg_err(vreg,
+ "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+ min_uV, max_uV, NLDO_UV_MIN, NLDO_UV_MAX);
+ return -EINVAL;
+ }
+
+ vprog = (uV - NLDO_UV_MIN + NLDO_UV_FINE_STEP - 1) / NLDO_UV_FINE_STEP;
+ uV = vprog * NLDO_UV_FINE_STEP + NLDO_UV_MIN;
+ fine_step_reg = (vprog & 1) << LDO_TEST_FINE_STEP_SHIFT;
+ vprog >>= 1;
+
+ if (uV > max_uV) {
+ vreg_err(vreg,
+ "request v=[%d, %d] cannot be met by any set point\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+
+ mutex_lock(&vreg->pc_lock);
+
+ /* Write fine step. */
+ prev_reg = vreg->test_reg[2];
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ fine_step_reg | REGULATOR_BANK_SEL(2)
+ | REGULATOR_BANK_WRITE | LDO_TEST_VPROG_UPDATE_MASK,
+ LDO_TEST_FINE_STEP_MASK | REGULATOR_BANK_MASK
+ | LDO_TEST_VPROG_UPDATE_MASK,
+ &vreg->test_reg[2]);
+ if (rc)
+ goto bail;
+
+ /* Write new voltage. */
+ if (prev_reg != vreg->test_reg[2]) {
+ /*
+ * Force a CTRL register write even if the value hasn't changed.
+ * This is neccessary because fine step will not update until a
+ * value is written into the control register.
+ */
+ rc = pm8xxx_vreg_masked_write_forced(vreg, vreg->ctrl_addr,
+ vprog, LDO_CTRL_VPROG_MASK, &vreg->ctrl_reg);
+ } else {
+ /* Only write to control register if new value is different. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, vprog,
+ LDO_CTRL_VPROG_MASK, &vreg->ctrl_reg);
+ }
+bail:
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+ return rc;
+}
+
+static int _pm8xxx_nldo1200_get_voltage(struct pm8xxx_vreg *vreg)
+{
+ int uV = 0;
+ int vprog;
+
+ if (!NLDO1200_IN_ADVANCED_MODE(vreg)) {
+ pr_warn("%s: currently in legacy mode; voltage unknown.\n",
+ vreg->rdesc.name);
+ return vreg->save_uV;
+ }
+
+ vprog = vreg->ctrl_reg & NLDO1200_CTRL_VPROG_MASK;
+
+ if ((vreg->ctrl_reg & NLDO1200_CTRL_RANGE_MASK)
+ == NLDO1200_CTRL_RANGE_LOW)
+ uV = vprog * NLDO1200_LOW_UV_STEP + NLDO1200_LOW_UV_MIN;
+ else
+ uV = vprog * NLDO1200_HIGH_UV_STEP + NLDO1200_HIGH_UV_MIN;
+
+ return uV;
+}
+
+static int pm8xxx_nldo1200_get_voltage(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+
+ return _pm8xxx_nldo1200_get_voltage(vreg);
+}
+
+static int pm8xxx_nldo1200_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ int uV;
+
+ if (selector >= NLDO1200_SET_POINTS)
+ return 0;
+
+ if (selector < NLDO1200_LOW_SET_POINTS)
+ uV = selector * NLDO1200_LOW_UV_STEP + NLDO1200_LOW_UV_MIN;
+ else
+ uV = (selector - NLDO1200_LOW_SET_POINTS)
+ * NLDO1200_HIGH_UV_STEP
+ + NLDO1200_HIGH_UV_MIN;
+
+ return uV;
+}
+
+static int _pm8xxx_nldo1200_set_voltage(struct pm8xxx_vreg *vreg, int min_uV,
+ int max_uV)
+{
+ u8 vprog, range;
+ int rc;
+ int uV = min_uV;
+
+ if (uV < NLDO1200_LOW_UV_MIN && max_uV >= NLDO1200_LOW_UV_MIN)
+ uV = NLDO1200_LOW_UV_MIN;
+
+ if (uV < NLDO1200_LOW_UV_MIN || uV > NLDO1200_HIGH_UV_MAX) {
+ vreg_err(vreg,
+ "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+ min_uV, max_uV, NLDO_UV_MIN, NLDO_UV_MAX);
+ return -EINVAL;
+ }
+
+ if (uV > NLDO1200_LOW_UV_MAX) {
+ vprog = (uV - NLDO1200_HIGH_UV_MIN + NLDO1200_HIGH_UV_STEP - 1)
+ / NLDO1200_HIGH_UV_STEP;
+ uV = vprog * NLDO1200_HIGH_UV_STEP + NLDO1200_HIGH_UV_MIN;
+ vprog &= NLDO1200_CTRL_VPROG_MASK;
+ range = NLDO1200_CTRL_RANGE_HIGH;
+ } else {
+ vprog = (uV - NLDO1200_LOW_UV_MIN + NLDO1200_LOW_UV_STEP - 1)
+ / NLDO1200_LOW_UV_STEP;
+ uV = vprog * NLDO1200_LOW_UV_STEP + NLDO1200_LOW_UV_MIN;
+ vprog &= NLDO1200_CTRL_VPROG_MASK;
+ range = NLDO1200_CTRL_RANGE_LOW;
+ }
+
+ if (uV > max_uV) {
+ vreg_err(vreg,
+ "request v=[%d, %d] cannot be met by any set point\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+
+ /* Set to advanced mode */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ NLDO1200_ADVANCED_MODE | REGULATOR_BANK_SEL(2)
+ | REGULATOR_BANK_WRITE, NLDO1200_ADVANCED_MODE_MASK
+ | REGULATOR_BANK_MASK, &vreg->test_reg[2]);
+ if (rc)
+ goto bail;
+
+ /* Set voltage and range selection. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, vprog | range,
+ NLDO1200_CTRL_VPROG_MASK | NLDO1200_CTRL_RANGE_MASK,
+ &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+
+ vreg->save_uV = uV;
+
+bail:
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int pm8xxx_nldo1200_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ rc = _pm8xxx_nldo1200_set_voltage(vreg, min_uV, max_uV);
+
+ if (!rc)
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+ return rc;
+}
+
+static int pm8xxx_smps_get_voltage_advanced(struct pm8xxx_vreg *vreg)
+{
+ u8 vprog, band;
+ int uV = 0;
+
+ vprog = vreg->ctrl_reg & SMPS_ADVANCED_VPROG_MASK;
+ band = vreg->ctrl_reg & SMPS_ADVANCED_BAND_MASK;
+
+ if (band == SMPS_ADVANCED_BAND_1)
+ uV = vprog * SMPS_BAND1_UV_STEP + SMPS_BAND1_UV_MIN;
+ else if (band == SMPS_ADVANCED_BAND_2)
+ uV = vprog * SMPS_BAND2_UV_STEP + SMPS_BAND2_UV_MIN;
+ else if (band == SMPS_ADVANCED_BAND_3)
+ uV = vprog * SMPS_BAND3_UV_STEP + SMPS_BAND3_UV_MIN;
+ else if (vreg->save_uV > 0)
+ uV = vreg->save_uV;
+ else
+ uV = VOLTAGE_UNKNOWN;
+
+ return uV;
+}
+
+static int pm8xxx_smps_get_voltage_legacy(struct pm8xxx_vreg *vreg)
+{
+ u8 vlow, vref, vprog;
+ int uV;
+
+ vlow = vreg->test_reg[1] & SMPS_LEGACY_VLOW_SEL_MASK;
+ vref = vreg->ctrl_reg & SMPS_LEGACY_VREF_SEL_MASK;
+ vprog = vreg->ctrl_reg & SMPS_LEGACY_VPROG_MASK;
+
+ if (vlow && vref) {
+ /* mode 3 */
+ uV = vprog * SMPS_MODE3_UV_STEP + SMPS_MODE3_UV_MIN;
+ } else if (vref) {
+ /* mode 2 */
+ uV = vprog * SMPS_MODE2_UV_STEP + SMPS_MODE2_UV_MIN;
+ } else {
+ /* mode 1 */
+ uV = vprog * SMPS_MODE1_UV_STEP + SMPS_MODE1_UV_MIN;
+ }
+
+ return uV;
+}
+
+static int _pm8xxx_smps_get_voltage(struct pm8xxx_vreg *vreg)
+{
+ if (SMPS_IN_ADVANCED_MODE(vreg))
+ return pm8xxx_smps_get_voltage_advanced(vreg);
+
+ return pm8xxx_smps_get_voltage_legacy(vreg);
+}
+
+static int pm8xxx_smps_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ int uV;
+
+ if (selector >= SMPS_ADVANCED_SET_POINTS)
+ return 0;
+
+ if (selector < SMPS_BAND1_SET_POINTS)
+ uV = selector * SMPS_BAND1_UV_STEP + SMPS_BAND1_UV_MIN;
+ else if (selector < (SMPS_BAND1_SET_POINTS + SMPS_BAND2_SET_POINTS))
+ uV = (selector - SMPS_BAND1_SET_POINTS) * SMPS_BAND2_UV_STEP
+ + SMPS_BAND2_UV_MIN;
+ else
+ uV = (selector - SMPS_BAND1_SET_POINTS - SMPS_BAND2_SET_POINTS)
+ * SMPS_BAND3_UV_STEP
+ + SMPS_BAND3_UV_MIN;
+
+ return uV;
+}
+
+static int pm8xxx_smps_get_voltage(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int uV;
+
+ mutex_lock(&vreg->pc_lock);
+ uV = _pm8xxx_smps_get_voltage(vreg);
+ mutex_unlock(&vreg->pc_lock);
+
+ return uV;
+}
+
+static int pm8xxx_smps_set_voltage_advanced(struct pm8xxx_vreg *vreg,
+ int min_uV, int max_uV, int force_on)
+{
+ u8 vprog, band;
+ int rc;
+ int uV = min_uV;
+
+ if (uV < SMPS_BAND1_UV_MIN && max_uV >= SMPS_BAND1_UV_MIN)
+ uV = SMPS_BAND1_UV_MIN;
+
+ if (uV < SMPS_BAND1_UV_MIN || uV > SMPS_BAND3_UV_MAX) {
+ vreg_err(vreg,
+ "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+ min_uV, max_uV, SMPS_BAND1_UV_MIN, SMPS_BAND3_UV_MAX);
+ return -EINVAL;
+ }
+
+ if (uV > SMPS_BAND2_UV_MAX) {
+ vprog = (uV - SMPS_BAND3_UV_MIN + SMPS_BAND3_UV_STEP - 1)
+ / SMPS_BAND3_UV_STEP;
+ band = SMPS_ADVANCED_BAND_3;
+ uV = SMPS_BAND3_UV_MIN + vprog * SMPS_BAND3_UV_STEP;
+ } else if (uV > SMPS_BAND1_UV_MAX) {
+ vprog = (uV - SMPS_BAND2_UV_MIN + SMPS_BAND2_UV_STEP - 1)
+ / SMPS_BAND2_UV_STEP;
+ band = SMPS_ADVANCED_BAND_2;
+ uV = SMPS_BAND2_UV_MIN + vprog * SMPS_BAND2_UV_STEP;
+ } else {
+ vprog = (uV - SMPS_BAND1_UV_MIN + SMPS_BAND1_UV_STEP - 1)
+ / SMPS_BAND1_UV_STEP;
+ band = SMPS_ADVANCED_BAND_1;
+ uV = SMPS_BAND1_UV_MIN + vprog * SMPS_BAND1_UV_STEP;
+ }
+
+ if (uV > max_uV) {
+ vreg_err(vreg,
+ "request v=[%d, %d] cannot be met by any set point\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+
+ /* Do not set band if regulator currently disabled. */
+ if (!_pm8xxx_vreg_is_enabled(vreg) && !force_on)
+ band = SMPS_ADVANCED_BAND_OFF;
+
+ /* Set advanced mode bit to 1. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr, SMPS_ADVANCED_MODE
+ | REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(7),
+ SMPS_ADVANCED_MODE_MASK | REGULATOR_BANK_MASK,
+ &vreg->test_reg[7]);
+ if (rc)
+ goto bail;
+
+ /* Set voltage and voltage band. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, band | vprog,
+ SMPS_ADVANCED_BAND_MASK | SMPS_ADVANCED_VPROG_MASK,
+ &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+
+ vreg->save_uV = uV;
+
+bail:
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int pm8xxx_smps_set_voltage_legacy(struct pm8xxx_vreg *vreg, int min_uV,
+ int max_uV)
+{
+ u8 vlow, vref, vprog, pd, en;
+ int rc;
+ int uV = min_uV;
+
+ if (uV < SMPS_MODE3_UV_MIN && max_uV >= SMPS_MODE3_UV_MIN)
+ uV = SMPS_MODE3_UV_MIN;
+
+ if (uV < SMPS_MODE3_UV_MIN || uV > SMPS_MODE1_UV_MAX) {
+ vreg_err(vreg,
+ "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+ min_uV, max_uV, SMPS_MODE3_UV_MIN, SMPS_MODE1_UV_MAX);
+ return -EINVAL;
+ }
+
+ if (uV > SMPS_MODE2_UV_MAX) {
+ vprog = (uV - SMPS_MODE1_UV_MIN + SMPS_MODE1_UV_STEP - 1)
+ / SMPS_MODE1_UV_STEP;
+ vref = 0;
+ vlow = 0;
+ uV = SMPS_MODE1_UV_MIN + vprog * SMPS_MODE1_UV_STEP;
+ } else if (uV > SMPS_MODE3_UV_MAX) {
+ vprog = (uV - SMPS_MODE2_UV_MIN + SMPS_MODE2_UV_STEP - 1)
+ / SMPS_MODE2_UV_STEP;
+ vref = SMPS_LEGACY_VREF_SEL_MASK;
+ vlow = 0;
+ uV = SMPS_MODE2_UV_MIN + vprog * SMPS_MODE2_UV_STEP;
+ } else {
+ vprog = (uV - SMPS_MODE3_UV_MIN + SMPS_MODE3_UV_STEP - 1)
+ / SMPS_MODE3_UV_STEP;
+ vref = SMPS_LEGACY_VREF_SEL_MASK;
+ vlow = SMPS_LEGACY_VLOW_SEL_MASK;
+ uV = SMPS_MODE3_UV_MIN + vprog * SMPS_MODE3_UV_STEP;
+ }
+
+ if (uV > max_uV) {
+ vreg_err(vreg,
+ "request v=[%d, %d] cannot be met by any set point\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+
+ /* set vlow bit for ultra low voltage mode */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ vlow | REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(1),
+ REGULATOR_BANK_MASK | SMPS_LEGACY_VLOW_SEL_MASK,
+ &vreg->test_reg[1]);
+ if (rc)
+ goto bail;
+
+ /* Set advanced mode bit to 0. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr, SMPS_LEGACY_MODE
+ | REGULATOR_BANK_WRITE | REGULATOR_BANK_SEL(7),
+ SMPS_ADVANCED_MODE_MASK | REGULATOR_BANK_MASK,
+ &vreg->test_reg[7]);
+ if (rc)
+ goto bail;
+
+ en = (_pm8xxx_vreg_is_enabled(vreg) ? SMPS_LEGACY_ENABLE : 0);
+ pd = (vreg->pdata.pull_down_enable ? SMPS_LEGACY_PULL_DOWN_ENABLE : 0);
+
+ /* Set voltage (and the rest of the control register). */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ en | pd | vref | vprog,
+ SMPS_LEGACY_ENABLE_MASK | SMPS_LEGACY_PULL_DOWN_ENABLE
+ | SMPS_LEGACY_VREF_SEL_MASK | SMPS_LEGACY_VPROG_MASK,
+ &vreg->ctrl_reg);
+
+ vreg->save_uV = uV;
+
+bail:
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int pm8xxx_smps_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ mutex_lock(&vreg->pc_lock);
+
+ if (SMPS_IN_ADVANCED_MODE(vreg) || !pm8xxx_vreg_is_pin_controlled(vreg))
+ rc = pm8xxx_smps_set_voltage_advanced(vreg, min_uV, max_uV, 0);
+ else
+ rc = pm8xxx_smps_set_voltage_legacy(vreg, min_uV, max_uV);
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (!rc)
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+ return rc;
+}
+
+static int _pm8xxx_ftsmps_get_voltage(struct pm8xxx_vreg *vreg)
+{
+ u8 vprog, band;
+ int uV = 0;
+
+ if ((vreg->test_reg[0] & FTSMPS_CNFG1_PM_MASK) == FTSMPS_CNFG1_PM_PFM) {
+ vprog = vreg->pfm_ctrl_reg & FTSMPS_VCTRL_VPROG_MASK;
+ band = vreg->pfm_ctrl_reg & FTSMPS_VCTRL_BAND_MASK;
+ if (band == FTSMPS_VCTRL_BAND_OFF && vprog == 0) {
+ /* PWM_VCTRL overrides PFM_VCTRL */
+ vprog = vreg->ctrl_reg & FTSMPS_VCTRL_VPROG_MASK;
+ band = vreg->ctrl_reg & FTSMPS_VCTRL_BAND_MASK;
+ }
+ } else {
+ vprog = vreg->ctrl_reg & FTSMPS_VCTRL_VPROG_MASK;
+ band = vreg->ctrl_reg & FTSMPS_VCTRL_BAND_MASK;
+ }
+
+ if (band == FTSMPS_VCTRL_BAND_1)
+ uV = vprog * FTSMPS_BAND1_UV_PHYS_STEP + FTSMPS_BAND1_UV_MIN;
+ else if (band == FTSMPS_VCTRL_BAND_2)
+ uV = vprog * FTSMPS_BAND2_UV_STEP + FTSMPS_BAND2_UV_MIN;
+ else if (band == FTSMPS_VCTRL_BAND_3)
+ uV = vprog * FTSMPS_BAND3_UV_STEP + FTSMPS_BAND3_UV_MIN;
+ else if (vreg->save_uV > 0)
+ uV = vreg->save_uV;
+ else
+ uV = VOLTAGE_UNKNOWN;
+
+ return uV;
+}
+
+static int pm8xxx_ftsmps_get_voltage(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+
+ return _pm8xxx_ftsmps_get_voltage(vreg);
+}
+
+static int pm8xxx_ftsmps_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ int uV;
+
+ if (selector >= FTSMPS_SET_POINTS)
+ return 0;
+
+ if (selector < FTSMPS_BAND1_SET_POINTS)
+ uV = selector * FTSMPS_BAND1_UV_LOG_STEP + FTSMPS_BAND1_UV_MIN;
+ else if (selector < (FTSMPS_BAND1_SET_POINTS + FTSMPS_BAND2_SET_POINTS))
+ uV = (selector - FTSMPS_BAND1_SET_POINTS) * FTSMPS_BAND2_UV_STEP
+ + FTSMPS_BAND2_UV_MIN;
+ else
+ uV = (selector - FTSMPS_BAND1_SET_POINTS
+ - FTSMPS_BAND2_SET_POINTS)
+ * FTSMPS_BAND3_UV_STEP
+ + FTSMPS_BAND3_UV_SET_POINT_MIN;
+
+ return uV;
+}
+
+static int _pm8xxx_ftsmps_set_voltage(struct pm8xxx_vreg *vreg, int min_uV,
+ int max_uV, int force_on)
+{
+ int rc = 0;
+ u8 vprog, band;
+ int uV = min_uV;
+
+ if (uV < FTSMPS_BAND1_UV_MIN && max_uV >= FTSMPS_BAND1_UV_MIN)
+ uV = FTSMPS_BAND1_UV_MIN;
+
+ if (uV < FTSMPS_BAND1_UV_MIN || uV > FTSMPS_BAND3_UV_MAX) {
+ vreg_err(vreg,
+ "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+ min_uV, max_uV, FTSMPS_BAND1_UV_MIN,
+ FTSMPS_BAND3_UV_MAX);
+ return -EINVAL;
+ }
+
+ /* Round up for set points in the gaps between bands. */
+ if (uV > FTSMPS_BAND1_UV_MAX && uV < FTSMPS_BAND2_UV_MIN)
+ uV = FTSMPS_BAND2_UV_MIN;
+ else if (uV > FTSMPS_BAND2_UV_MAX
+ && uV < FTSMPS_BAND3_UV_SET_POINT_MIN)
+ uV = FTSMPS_BAND3_UV_SET_POINT_MIN;
+
+ if (uV > FTSMPS_BAND2_UV_MAX) {
+ vprog = (uV - FTSMPS_BAND3_UV_MIN + FTSMPS_BAND3_UV_STEP - 1)
+ / FTSMPS_BAND3_UV_STEP;
+ band = FTSMPS_VCTRL_BAND_3;
+ uV = FTSMPS_BAND3_UV_MIN + vprog * FTSMPS_BAND3_UV_STEP;
+ } else if (uV > FTSMPS_BAND1_UV_MAX) {
+ vprog = (uV - FTSMPS_BAND2_UV_MIN + FTSMPS_BAND2_UV_STEP - 1)
+ / FTSMPS_BAND2_UV_STEP;
+ band = FTSMPS_VCTRL_BAND_2;
+ uV = FTSMPS_BAND2_UV_MIN + vprog * FTSMPS_BAND2_UV_STEP;
+ } else {
+ vprog = (uV - FTSMPS_BAND1_UV_MIN
+ + FTSMPS_BAND1_UV_LOG_STEP - 1)
+ / FTSMPS_BAND1_UV_LOG_STEP;
+ uV = FTSMPS_BAND1_UV_MIN + vprog * FTSMPS_BAND1_UV_LOG_STEP;
+ vprog *= FTSMPS_BAND1_UV_LOG_STEP / FTSMPS_BAND1_UV_PHYS_STEP;
+ band = FTSMPS_VCTRL_BAND_1;
+ }
+
+ if (uV > max_uV) {
+ vreg_err(vreg,
+ "request v=[%d, %d] cannot be met by any set point\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+
+ /*
+ * Do not set voltage if regulator is currently disabled because doing
+ * so will enable it.
+ */
+ if (_pm8xxx_vreg_is_enabled(vreg) || force_on) {
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ band | vprog,
+ FTSMPS_VCTRL_BAND_MASK | FTSMPS_VCTRL_VPROG_MASK,
+ &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+
+ /* Program PFM_VCTRL as 0x00 so that PWM_VCTRL overrides it. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->pfm_ctrl_addr, 0x00,
+ FTSMPS_VCTRL_BAND_MASK | FTSMPS_VCTRL_VPROG_MASK,
+ &vreg->pfm_ctrl_reg);
+ if (rc)
+ goto bail;
+ }
+
+ vreg->save_uV = uV;
+
+bail:
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int pm8xxx_ftsmps_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ rc = _pm8xxx_ftsmps_set_voltage(vreg, min_uV, max_uV, 0);
+
+ if (!rc)
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+ return rc;
+}
+
+static int pm8xxx_ncp_get_voltage(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ u8 vprog;
+
+ vprog = vreg->ctrl_reg & NCP_VPROG_MASK;
+
+ return NCP_UV_MIN + vprog * NCP_UV_STEP;
+}
+
+static int pm8xxx_ncp_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ if (selector >= NCP_SET_POINTS)
+ return 0;
+
+ return selector * NCP_UV_STEP + NCP_UV_MIN;
+}
+
+static int pm8xxx_ncp_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+ int uV = min_uV;
+ u8 val;
+
+ if (uV < NCP_UV_MIN && max_uV >= NCP_UV_MIN)
+ uV = NCP_UV_MIN;
+
+ if (uV < NCP_UV_MIN || uV > NCP_UV_MAX) {
+ vreg_err(vreg,
+ "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+ min_uV, max_uV, NCP_UV_MIN, NCP_UV_MAX);
+ return -EINVAL;
+ }
+
+ val = (uV - NCP_UV_MIN + NCP_UV_STEP - 1) / NCP_UV_STEP;
+ uV = val * NCP_UV_STEP + NCP_UV_MIN;
+
+ if (uV > max_uV) {
+ vreg_err(vreg,
+ "request v=[%d, %d] cannot be met by any set point\n",
+ min_uV, max_uV);
+ return -EINVAL;
+ }
+
+ /* voltage setting */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
+ NCP_VPROG_MASK, &vreg->ctrl_reg);
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_VOLTAGE);
+
+ return rc;
+}
+
+static unsigned int pm8xxx_ldo_get_mode(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ unsigned int mode = 0;
+
+ mutex_lock(&vreg->pc_lock);
+ mode = vreg->mode;
+ mutex_unlock(&vreg->pc_lock);
+
+ return mode;
+}
+
+static int pm8xxx_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_IDLE) {
+ vreg_err(vreg, "invalid mode: %u\n", mode);
+ return -EINVAL;
+ }
+
+ mutex_lock(&vreg->pc_lock);
+
+ if (mode == REGULATOR_MODE_NORMAL
+ || (vreg->is_enabled_pc
+ && vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE)) {
+ /* HPM */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ LDO_CTRL_PM_HPM, LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+ } else {
+ /* LPM */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ LDO_CTRL_PM_LPM, LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ LDO_TEST_LPM_SEL_CTRL | REGULATOR_BANK_WRITE
+ | REGULATOR_BANK_SEL(0),
+ LDO_TEST_LPM_MASK | REGULATOR_BANK_MASK,
+ &vreg->test_reg[0]);
+ }
+
+bail:
+ if (!rc)
+ vreg->mode = mode;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_MODE);
+
+ return rc;
+}
+
+static unsigned int pm8xxx_nldo1200_get_mode(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ unsigned int mode = 0;
+
+ if (NLDO1200_IN_ADVANCED_MODE(vreg)) {
+ /* Advanced mode */
+ if ((vreg->test_reg[2] & NLDO1200_ADVANCED_PM_MASK)
+ == NLDO1200_ADVANCED_PM_LPM)
+ mode = REGULATOR_MODE_IDLE;
+ else
+ mode = REGULATOR_MODE_NORMAL;
+ } else {
+ /* Legacy mode */
+ if ((vreg->ctrl_reg & NLDO1200_LEGACY_PM_MASK)
+ == NLDO1200_LEGACY_PM_LPM)
+ mode = REGULATOR_MODE_IDLE;
+ else
+ mode = REGULATOR_MODE_NORMAL;
+ }
+
+ return mode;
+}
+
+static int pm8xxx_nldo1200_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_IDLE) {
+ vreg_err(vreg, "invalid mode: %u\n", mode);
+ return -EINVAL;
+ }
+
+ /*
+ * Make sure that advanced mode is in use. If it isn't, then set it
+ * and update the voltage accordingly.
+ */
+ if (!NLDO1200_IN_ADVANCED_MODE(vreg)) {
+ rc = _pm8xxx_nldo1200_set_voltage(vreg, vreg->save_uV,
+ vreg->save_uV);
+ if (rc)
+ goto bail;
+ }
+
+ if (mode == REGULATOR_MODE_NORMAL) {
+ /* HPM */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ NLDO1200_ADVANCED_PM_HPM | REGULATOR_BANK_WRITE
+ | REGULATOR_BANK_SEL(2), NLDO1200_ADVANCED_PM_MASK
+ | REGULATOR_BANK_MASK, &vreg->test_reg[2]);
+ } else {
+ /* LPM */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ NLDO1200_ADVANCED_PM_LPM | REGULATOR_BANK_WRITE
+ | REGULATOR_BANK_SEL(2), NLDO1200_ADVANCED_PM_MASK
+ | REGULATOR_BANK_MASK, &vreg->test_reg[2]);
+ }
+
+bail:
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_MODE);
+
+ return rc;
+}
+
+static unsigned int pm8xxx_smps_get_mode(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ unsigned int mode = 0;
+
+ mutex_lock(&vreg->pc_lock);
+ mode = vreg->mode;
+ mutex_unlock(&vreg->pc_lock);
+
+ return mode;
+}
+
+static int pm8xxx_smps_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_IDLE) {
+ vreg_err(vreg, "invalid mode: %u\n", mode);
+ return -EINVAL;
+ }
+
+ mutex_lock(&vreg->pc_lock);
+
+ if (mode == REGULATOR_MODE_NORMAL
+ || (vreg->is_enabled_pc
+ && vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE)) {
+ /* HPM */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->clk_ctrl_addr,
+ SMPS_CLK_CTRL_PWM, SMPS_CLK_CTRL_MASK,
+ &vreg->clk_ctrl_reg);
+ } else {
+ /* LPM */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->clk_ctrl_addr,
+ SMPS_CLK_CTRL_PFM, SMPS_CLK_CTRL_MASK,
+ &vreg->clk_ctrl_reg);
+ }
+
+ if (!rc)
+ vreg->mode = mode;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_MODE);
+
+ return rc;
+}
+
+static unsigned int pm8xxx_ftsmps_get_mode(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ unsigned int mode = 0;
+
+ if ((vreg->test_reg[0] & FTSMPS_CNFG1_PM_MASK) == FTSMPS_CNFG1_PM_PFM)
+ mode = REGULATOR_MODE_IDLE;
+ else
+ mode = REGULATOR_MODE_NORMAL;
+
+ return mode;
+}
+
+static int pm8xxx_ftsmps_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc = 0;
+
+ if (mode == REGULATOR_MODE_NORMAL) {
+ /* HPM */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ FTSMPS_CNFG1_PM_PWM | REGULATOR_BANK_WRITE
+ | REGULATOR_BANK_SEL(0), FTSMPS_CNFG1_PM_MASK
+ | REGULATOR_BANK_MASK, &vreg->test_reg[0]);
+ } else if (mode == REGULATOR_MODE_IDLE) {
+ /* LPM */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ FTSMPS_CNFG1_PM_PFM | REGULATOR_BANK_WRITE
+ | REGULATOR_BANK_SEL(0), FTSMPS_CNFG1_PM_MASK
+ | REGULATOR_BANK_MASK, &vreg->test_reg[0]);
+ } else {
+ vreg_err(vreg, "invalid mode: %u\n", mode);
+ return -EINVAL;
+ }
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_MODE);
+
+ return rc;
+}
+
+static unsigned int pm8xxx_vreg_get_optimum_mode(struct regulator_dev *rdev,
+ int input_uV, int output_uV, int load_uA)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ unsigned int mode;
+
+ if (load_uA + vreg->pdata.system_uA >= vreg->hpm_min_load)
+ mode = REGULATOR_MODE_NORMAL;
+ else
+ mode = REGULATOR_MODE_IDLE;
+
+ return mode;
+}
+
+static int pm8xxx_ldo_enable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc, val;
+
+ mutex_lock(&vreg->pc_lock);
+
+ /*
+ * Choose HPM if previously set to HPM or if pin control is enabled in
+ * on/off mode.
+ */
+ val = LDO_CTRL_PM_LPM;
+ if (vreg->mode == REGULATOR_MODE_NORMAL
+ || (vreg->is_enabled_pc
+ && vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE))
+ val = LDO_CTRL_PM_HPM;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val | LDO_ENABLE,
+ LDO_ENABLE_MASK | LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+
+ if (!rc)
+ vreg->is_enabled = true;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+ return rc;
+}
+
+static int pm8xxx_ldo_disable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ mutex_lock(&vreg->pc_lock);
+
+ /*
+ * Only disable the regulator if it isn't still required for HPM/LPM
+ * pin control.
+ */
+ if (!vreg->is_enabled_pc
+ || vreg->pdata.pin_fn != PM8XXX_VREG_PIN_FN_MODE) {
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ LDO_DISABLE, LDO_ENABLE_MASK, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+ }
+
+ /* Change to LPM if HPM/LPM pin control is enabled. */
+ if (vreg->is_enabled_pc
+ && vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_MODE) {
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ LDO_CTRL_PM_LPM, LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ LDO_TEST_LPM_SEL_CTRL | REGULATOR_BANK_WRITE
+ | REGULATOR_BANK_SEL(0),
+ LDO_TEST_LPM_MASK | REGULATOR_BANK_MASK,
+ &vreg->test_reg[0]);
+ }
+
+ if (!rc)
+ vreg->is_enabled = false;
+bail:
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+ return rc;
+}
+
+static int pm8xxx_nldo1200_enable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, NLDO1200_ENABLE,
+ NLDO1200_ENABLE_MASK, &vreg->ctrl_reg);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+ return rc;
+}
+
+static int pm8xxx_nldo1200_disable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, NLDO1200_DISABLE,
+ NLDO1200_ENABLE_MASK, &vreg->ctrl_reg);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+ return rc;
+}
+
+static int pm8xxx_smps_enable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc = 0;
+ int val;
+
+ mutex_lock(&vreg->pc_lock);
+
+ if (SMPS_IN_ADVANCED_MODE(vreg)
+ || !pm8xxx_vreg_is_pin_controlled(vreg)) {
+ /* Enable in advanced mode if not using pin control. */
+ rc = pm8xxx_smps_set_voltage_advanced(vreg, vreg->save_uV,
+ vreg->save_uV, 1);
+ } else {
+ rc = pm8xxx_smps_set_voltage_legacy(vreg, vreg->save_uV,
+ vreg->save_uV);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ SMPS_LEGACY_ENABLE, SMPS_LEGACY_ENABLE_MASK,
+ &vreg->ctrl_reg);
+ }
+
+ /*
+ * Choose HPM if previously set to HPM or if pin control is enabled in
+ * on/off mode.
+ */
+ val = SMPS_CLK_CTRL_PFM;
+ if (vreg->mode == REGULATOR_MODE_NORMAL
+ || (vreg->is_enabled_pc
+ && vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE))
+ val = SMPS_CLK_CTRL_PWM;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->clk_ctrl_addr, val,
+ SMPS_CLK_CTRL_MASK, &vreg->clk_ctrl_reg);
+
+ if (!rc)
+ vreg->is_enabled = true;
+bail:
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+ return rc;
+}
+
+static int pm8xxx_smps_disable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ mutex_lock(&vreg->pc_lock);
+
+ if (SMPS_IN_ADVANCED_MODE(vreg)) {
+ /* Change SMPS to legacy mode before disabling. */
+ rc = pm8xxx_smps_set_voltage_legacy(vreg, vreg->save_uV,
+ vreg->save_uV);
+ if (rc)
+ goto bail;
+ }
+
+ /*
+ * Only disable the regulator if it isn't still required for HPM/LPM
+ * pin control.
+ */
+ if (!vreg->is_enabled_pc
+ || vreg->pdata.pin_fn != PM8XXX_VREG_PIN_FN_MODE) {
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ SMPS_LEGACY_DISABLE, SMPS_LEGACY_ENABLE_MASK,
+ &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+ }
+
+ /* Change to LPM if HPM/LPM pin control is enabled. */
+ if (vreg->is_enabled_pc
+ && vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_MODE)
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->clk_ctrl_addr,
+ SMPS_CLK_CTRL_PFM, SMPS_CLK_CTRL_MASK,
+ &vreg->clk_ctrl_reg);
+
+ if (!rc)
+ vreg->is_enabled = false;
+
+bail:
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+ return rc;
+}
+
+static int pm8xxx_ftsmps_enable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ rc = _pm8xxx_ftsmps_set_voltage(vreg, vreg->save_uV, vreg->save_uV, 1);
+
+ if (rc)
+ vreg_err(vreg, "set voltage failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+ return rc;
+}
+
+static int pm8xxx_ftsmps_disable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ FTSMPS_VCTRL_BAND_OFF, FTSMPS_VCTRL_BAND_MASK, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->pfm_ctrl_addr,
+ FTSMPS_VCTRL_BAND_OFF, FTSMPS_VCTRL_BAND_MASK,
+ &vreg->pfm_ctrl_reg);
+bail:
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+ return rc;
+}
+
+static int pm8xxx_vs_enable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ mutex_lock(&vreg->pc_lock);
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, VS_ENABLE,
+ VS_ENABLE_MASK, &vreg->ctrl_reg);
+
+ if (!rc)
+ vreg->is_enabled = true;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+ return rc;
+}
+
+static int pm8xxx_vs_disable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ mutex_lock(&vreg->pc_lock);
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, VS_DISABLE,
+ VS_ENABLE_MASK, &vreg->ctrl_reg);
+
+ if (!rc)
+ vreg->is_enabled = false;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+ return rc;
+}
+
+static int pm8xxx_vs300_enable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, VS300_CTRL_ENABLE,
+ VS300_CTRL_ENABLE_MASK, &vreg->ctrl_reg);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+ return rc;
+}
+
+static int pm8xxx_vs300_disable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, VS300_CTRL_DISABLE,
+ VS300_CTRL_ENABLE_MASK, &vreg->ctrl_reg);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+ return rc;
+}
+
+static int pm8xxx_ncp_enable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, NCP_ENABLE,
+ NCP_ENABLE_MASK, &vreg->ctrl_reg);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_ENABLE);
+
+ return rc;
+}
+
+static int pm8xxx_ncp_disable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, NCP_DISABLE,
+ NCP_ENABLE_MASK, &vreg->ctrl_reg);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_DISABLE);
+
+ return rc;
+}
+
+static int pm8xxx_ldo_pin_control_enable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc = 0;
+ int bank;
+ u8 val = 0;
+ u8 mask;
+
+ mutex_lock(&vreg->pc_lock);
+
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN0)
+ val |= LDO_TEST_PIN_CTRL_EN0;
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN1)
+ val |= LDO_TEST_PIN_CTRL_EN1;
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN2)
+ val |= LDO_TEST_PIN_CTRL_EN2;
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN3)
+ val |= LDO_TEST_PIN_CTRL_EN3;
+
+ bank = (vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE ? 5 : 6);
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ val | REGULATOR_BANK_SEL(bank) | REGULATOR_BANK_WRITE,
+ LDO_TEST_PIN_CTRL_MASK | REGULATOR_BANK_MASK,
+ &vreg->test_reg[bank]);
+ if (rc)
+ goto bail;
+
+ /* Unset pin control bits in unused bank. */
+ bank = (bank == 5 ? 6 : 5);
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ REGULATOR_BANK_SEL(bank) | REGULATOR_BANK_WRITE,
+ LDO_TEST_PIN_CTRL_MASK | REGULATOR_BANK_MASK,
+ &vreg->test_reg[bank]);
+ if (rc)
+ goto bail;
+
+ val = LDO_TEST_LPM_SEL_CTRL | REGULATOR_BANK_WRITE
+ | REGULATOR_BANK_SEL(0);
+ mask = LDO_TEST_LPM_MASK | REGULATOR_BANK_MASK;
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr, val, mask,
+ &vreg->test_reg[0]);
+ if (rc)
+ goto bail;
+
+ if (vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE) {
+ /* Pin control ON/OFF */
+ val = LDO_CTRL_PM_HPM;
+ /* Leave physically enabled if already enabled. */
+ val |= (vreg->is_enabled ? LDO_ENABLE : LDO_DISABLE);
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
+ LDO_ENABLE_MASK | LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+ } else {
+ /* Pin control LPM/HPM */
+ val = LDO_ENABLE;
+ /* Leave in HPM if already enabled in HPM. */
+ val |= (vreg->is_enabled && vreg->mode == REGULATOR_MODE_NORMAL
+ ? LDO_CTRL_PM_HPM : LDO_CTRL_PM_LPM);
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
+ LDO_ENABLE_MASK | LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+ }
+
+bail:
+ if (!rc)
+ vreg->is_enabled_pc = true;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_PIN_CTRL);
+
+ return rc;
+}
+
+static int pm8xxx_ldo_pin_control_disable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ mutex_lock(&vreg->pc_lock);
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ REGULATOR_BANK_SEL(5) | REGULATOR_BANK_WRITE,
+ LDO_TEST_PIN_CTRL_MASK | REGULATOR_BANK_MASK,
+ &vreg->test_reg[5]);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ REGULATOR_BANK_SEL(6) | REGULATOR_BANK_WRITE,
+ LDO_TEST_PIN_CTRL_MASK | REGULATOR_BANK_MASK,
+ &vreg->test_reg[6]);
+
+ /*
+ * Physically disable the regulator if it was enabled in HPM/LPM pin
+ * control mode previously and it logically should not be enabled.
+ */
+ if ((vreg->ctrl_reg & LDO_ENABLE_MASK) == LDO_ENABLE
+ && !vreg->is_enabled) {
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ LDO_DISABLE, LDO_ENABLE_MASK, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+ }
+
+ /* Change to LPM if LPM was enabled. */
+ if (vreg->is_enabled && vreg->mode == REGULATOR_MODE_IDLE) {
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ LDO_CTRL_PM_LPM, LDO_CTRL_PM_MASK, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ LDO_TEST_LPM_SEL_CTRL | REGULATOR_BANK_WRITE
+ | REGULATOR_BANK_SEL(0),
+ LDO_TEST_LPM_MASK | REGULATOR_BANK_MASK,
+ &vreg->test_reg[0]);
+ if (rc)
+ goto bail;
+ }
+
+bail:
+ if (!rc)
+ vreg->is_enabled_pc = false;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_PIN_CTRL);
+
+ return rc;
+}
+
+static int pm8xxx_smps_pin_control_enable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc = 0;
+ u8 val = 0;
+
+ mutex_lock(&vreg->pc_lock);
+
+ if (vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE) {
+ /* Pin control ON/OFF */
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN0)
+ val |= SMPS_PIN_CTRL_EN0;
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN1)
+ val |= SMPS_PIN_CTRL_EN1;
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN2)
+ val |= SMPS_PIN_CTRL_EN2;
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN3)
+ val |= SMPS_PIN_CTRL_EN3;
+ } else {
+ /* Pin control LPM/HPM */
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN0)
+ val |= SMPS_PIN_CTRL_LPM_EN0;
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN1)
+ val |= SMPS_PIN_CTRL_LPM_EN1;
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN2)
+ val |= SMPS_PIN_CTRL_LPM_EN2;
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN3)
+ val |= SMPS_PIN_CTRL_LPM_EN3;
+ }
+
+ rc = pm8xxx_smps_set_voltage_legacy(vreg, vreg->save_uV, vreg->save_uV);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->sleep_ctrl_addr, val,
+ SMPS_PIN_CTRL_MASK | SMPS_PIN_CTRL_LPM_MASK,
+ &vreg->sleep_ctrl_reg);
+ if (rc)
+ goto bail;
+
+ /*
+ * Physically enable the regulator if using HPM/LPM pin control mode or
+ * if the regulator should be logically left on.
+ */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ ((vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_MODE
+ || vreg->is_enabled) ?
+ SMPS_LEGACY_ENABLE : SMPS_LEGACY_DISABLE),
+ SMPS_LEGACY_ENABLE_MASK, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+
+ /*
+ * Set regulator to HPM if using on/off pin control or if the regulator
+ * is already enabled in HPM. Otherwise, set it to LPM.
+ */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->clk_ctrl_addr,
+ (vreg->pdata.pin_fn == PM8XXX_VREG_PIN_FN_ENABLE
+ || (vreg->is_enabled
+ && vreg->mode == REGULATOR_MODE_NORMAL)
+ ? SMPS_CLK_CTRL_PWM : SMPS_CLK_CTRL_PFM),
+ SMPS_CLK_CTRL_MASK, &vreg->clk_ctrl_reg);
+
+bail:
+ if (!rc)
+ vreg->is_enabled_pc = true;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_PIN_CTRL);
+
+ return rc;
+}
+
+static int pm8xxx_smps_pin_control_disable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ mutex_lock(&vreg->pc_lock);
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->sleep_ctrl_addr, 0,
+ SMPS_PIN_CTRL_MASK | SMPS_PIN_CTRL_LPM_MASK,
+ &vreg->sleep_ctrl_reg);
+ if (rc)
+ goto bail;
+
+ /*
+ * Physically disable the regulator if it was enabled in HPM/LPM pin
+ * control mode previously and it logically should not be enabled.
+ */
+ if ((vreg->ctrl_reg & SMPS_LEGACY_ENABLE_MASK) == SMPS_LEGACY_ENABLE
+ && vreg->is_enabled == false) {
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ SMPS_LEGACY_DISABLE, SMPS_LEGACY_ENABLE_MASK,
+ &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+ }
+
+ /* Change to LPM if LPM was enabled. */
+ if (vreg->is_enabled && vreg->mode == REGULATOR_MODE_IDLE) {
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->clk_ctrl_addr,
+ SMPS_CLK_CTRL_PFM, SMPS_CLK_CTRL_MASK,
+ &vreg->clk_ctrl_reg);
+ if (rc)
+ goto bail;
+ }
+
+ rc = pm8xxx_smps_set_voltage_advanced(vreg, vreg->save_uV,
+ vreg->save_uV, 0);
+
+bail:
+ if (!rc)
+ vreg->is_enabled_pc = false;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_PIN_CTRL);
+
+ return rc;
+}
+
+static int pm8xxx_vs_pin_control_enable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+ u8 val = 0;
+
+ mutex_lock(&vreg->pc_lock);
+
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN0)
+ val |= VS_PIN_CTRL_EN0;
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN1)
+ val |= VS_PIN_CTRL_EN1;
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN2)
+ val |= VS_PIN_CTRL_EN2;
+ if (vreg->pdata.pin_ctrl & PM8XXX_VREG_PIN_CTRL_EN3)
+ val |= VS_PIN_CTRL_EN3;
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, val,
+ VS_PIN_CTRL_MASK | VS_ENABLE_MASK, &vreg->ctrl_reg);
+
+ if (!rc)
+ vreg->is_enabled_pc = true;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_PIN_CTRL);
+
+ return rc;
+}
+
+static int pm8xxx_vs_pin_control_disable(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int rc;
+
+ mutex_lock(&vreg->pc_lock);
+
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr, 0,
+ VS_PIN_CTRL_MASK, &vreg->ctrl_reg);
+
+ if (!rc)
+ vreg->is_enabled_pc = false;
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+ else
+ pm8xxx_vreg_show_state(rdev, PM8XXX_REGULATOR_ACTION_PIN_CTRL);
+
+ return rc;
+}
+
+static int pm8xxx_enable_time(struct regulator_dev *rdev)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+
+ return vreg->pdata.enable_time;
+}
+
+static const char const *pm8xxx_print_actions[] = {
+ [PM8XXX_REGULATOR_ACTION_INIT] = "initial ",
+ [PM8XXX_REGULATOR_ACTION_ENABLE] = "enable ",
+ [PM8XXX_REGULATOR_ACTION_DISABLE] = "disable ",
+ [PM8XXX_REGULATOR_ACTION_VOLTAGE] = "set voltage",
+ [PM8XXX_REGULATOR_ACTION_MODE] = "set mode ",
+ [PM8XXX_REGULATOR_ACTION_PIN_CTRL] = "pin control",
+};
+
+static void pm8xxx_vreg_show_state(struct regulator_dev *rdev,
+ enum pm8xxx_regulator_action action)
+{
+ struct pm8xxx_vreg *vreg = rdev_get_drvdata(rdev);
+ int uV, pc;
+ unsigned int mode;
+ const char *pc_en0 = "", *pc_en1 = "", *pc_en2 = "", *pc_en3 = "";
+ const char *pc_total = "";
+ const char *action_label = pm8xxx_print_actions[action];
+ const char *enable_label;
+
+ mutex_lock(&vreg->pc_lock);
+
+ /*
+ * Do not print unless REQUEST is specified and SSBI writes have taken
+ * place, or DUPLICATE is specified.
+ */
+ if (!((pm8xxx_vreg_debug_mask & PM8XXX_VREG_DEBUG_DUPLICATE)
+ || ((pm8xxx_vreg_debug_mask & PM8XXX_VREG_DEBUG_REQUEST)
+ && (vreg->write_count != vreg->prev_write_count)))) {
+ mutex_unlock(&vreg->pc_lock);
+ return;
+ }
+
+ vreg->prev_write_count = vreg->write_count;
+
+ pc = vreg->pdata.pin_ctrl;
+ if (vreg->is_enabled_pc) {
+ if (pc & PM8XXX_VREG_PIN_CTRL_EN0)
+ pc_en0 = " EN0";
+ if (pc & PM8XXX_VREG_PIN_CTRL_EN1)
+ pc_en1 = " EN1";
+ if (pc & PM8XXX_VREG_PIN_CTRL_EN2)
+ pc_en2 = " EN2";
+ if (pc & PM8XXX_VREG_PIN_CTRL_EN3)
+ pc_en3 = " EN3";
+ if (pc == PM8XXX_VREG_PIN_CTRL_NONE)
+ pc_total = " none";
+ } else {
+ pc_total = " none";
+ }
+
+ mutex_unlock(&vreg->pc_lock);
+
+ enable_label = pm8xxx_vreg_is_enabled(rdev) ? "on " : "off";
+
+ switch (vreg->type) {
+ case PM8XXX_REGULATOR_TYPE_PLDO:
+ uV = pm8xxx_pldo_get_voltage(rdev);
+ mode = pm8xxx_ldo_get_mode(rdev);
+ pr_info("%s %-9s: %s, v=%7d uV, mode=%s, pc=%s%s%s%s%s\n",
+ action_label, vreg->rdesc.name, enable_label, uV,
+ (mode == REGULATOR_MODE_NORMAL ? "HPM" : "LPM"),
+ pc_en0, pc_en1, pc_en2, pc_en3, pc_total);
+ break;
+ case PM8XXX_REGULATOR_TYPE_NLDO:
+ uV = pm8xxx_nldo_get_voltage(rdev);
+ mode = pm8xxx_ldo_get_mode(rdev);
+ pr_info("%s %-9s: %s, v=%7d uV, mode=%s, pc=%s%s%s%s%s\n",
+ action_label, vreg->rdesc.name, enable_label, uV,
+ (mode == REGULATOR_MODE_NORMAL ? "HPM" : "LPM"),
+ pc_en0, pc_en1, pc_en2, pc_en3, pc_total);
+ break;
+ case PM8XXX_REGULATOR_TYPE_NLDO1200:
+ uV = pm8xxx_nldo1200_get_voltage(rdev);
+ mode = pm8xxx_nldo1200_get_mode(rdev);
+ pr_info("%s %-9s: %s, v=%7d uV, mode=%s\n",
+ action_label, vreg->rdesc.name, enable_label, uV,
+ (mode == REGULATOR_MODE_NORMAL ? "HPM" : "LPM"));
+ break;
+ case PM8XXX_REGULATOR_TYPE_SMPS:
+ uV = pm8xxx_smps_get_voltage(rdev);
+ mode = pm8xxx_smps_get_mode(rdev);
+ pr_info("%s %-9s: %s, v=%7d uV, mode=%s, pc=%s%s%s%s%s\n",
+ action_label, vreg->rdesc.name, enable_label, uV,
+ (mode == REGULATOR_MODE_NORMAL ? "HPM" : "LPM"),
+ pc_en0, pc_en1, pc_en2, pc_en3, pc_total);
+ break;
+ case PM8XXX_REGULATOR_TYPE_FTSMPS:
+ uV = pm8xxx_ftsmps_get_voltage(rdev);
+ mode = pm8xxx_ftsmps_get_mode(rdev);
+ pr_info("%s %-9s: %s, v=%7d uV, mode=%s\n",
+ action_label, vreg->rdesc.name, enable_label, uV,
+ (mode == REGULATOR_MODE_NORMAL ? "HPM" : "LPM"));
+ break;
+ case PM8XXX_REGULATOR_TYPE_VS:
+ pr_info("%s %-9s: %s, pc=%s%s%s%s%s\n",
+ action_label, vreg->rdesc.name, enable_label,
+ pc_en0, pc_en1, pc_en2, pc_en3, pc_total);
+ break;
+ case PM8XXX_REGULATOR_TYPE_VS300:
+ pr_info("%s %-9s: %s\n",
+ action_label, vreg->rdesc.name, enable_label);
+ break;
+ case PM8XXX_REGULATOR_TYPE_NCP:
+ uV = pm8xxx_ncp_get_voltage(rdev);
+ pr_info("%s %-9s: %s, v=%7d uV\n",
+ action_label, vreg->rdesc.name, enable_label, uV);
+ break;
+ default:
+ break;
+ }
+}
+
+/* Real regulator operations. */
+static struct regulator_ops pm8xxx_pldo_ops = {
+ .enable = pm8xxx_ldo_enable,
+ .disable = pm8xxx_ldo_disable,
+ .is_enabled = pm8xxx_vreg_is_enabled,
+ .set_voltage = pm8xxx_pldo_set_voltage,
+ .get_voltage = pm8xxx_pldo_get_voltage,
+ .list_voltage = pm8xxx_pldo_list_voltage,
+ .set_mode = pm8xxx_ldo_set_mode,
+ .get_mode = pm8xxx_ldo_get_mode,
+ .get_optimum_mode = pm8xxx_vreg_get_optimum_mode,
+ .enable_time = pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_nldo_ops = {
+ .enable = pm8xxx_ldo_enable,
+ .disable = pm8xxx_ldo_disable,
+ .is_enabled = pm8xxx_vreg_is_enabled,
+ .set_voltage = pm8xxx_nldo_set_voltage,
+ .get_voltage = pm8xxx_nldo_get_voltage,
+ .list_voltage = pm8xxx_nldo_list_voltage,
+ .set_mode = pm8xxx_ldo_set_mode,
+ .get_mode = pm8xxx_ldo_get_mode,
+ .get_optimum_mode = pm8xxx_vreg_get_optimum_mode,
+ .enable_time = pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_nldo1200_ops = {
+ .enable = pm8xxx_nldo1200_enable,
+ .disable = pm8xxx_nldo1200_disable,
+ .is_enabled = pm8xxx_vreg_is_enabled,
+ .set_voltage = pm8xxx_nldo1200_set_voltage,
+ .get_voltage = pm8xxx_nldo1200_get_voltage,
+ .list_voltage = pm8xxx_nldo1200_list_voltage,
+ .set_mode = pm8xxx_nldo1200_set_mode,
+ .get_mode = pm8xxx_nldo1200_get_mode,
+ .get_optimum_mode = pm8xxx_vreg_get_optimum_mode,
+ .enable_time = pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_smps_ops = {
+ .enable = pm8xxx_smps_enable,
+ .disable = pm8xxx_smps_disable,
+ .is_enabled = pm8xxx_vreg_is_enabled,
+ .set_voltage = pm8xxx_smps_set_voltage,
+ .get_voltage = pm8xxx_smps_get_voltage,
+ .list_voltage = pm8xxx_smps_list_voltage,
+ .set_mode = pm8xxx_smps_set_mode,
+ .get_mode = pm8xxx_smps_get_mode,
+ .get_optimum_mode = pm8xxx_vreg_get_optimum_mode,
+ .enable_time = pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_ftsmps_ops = {
+ .enable = pm8xxx_ftsmps_enable,
+ .disable = pm8xxx_ftsmps_disable,
+ .is_enabled = pm8xxx_vreg_is_enabled,
+ .set_voltage = pm8xxx_ftsmps_set_voltage,
+ .get_voltage = pm8xxx_ftsmps_get_voltage,
+ .list_voltage = pm8xxx_ftsmps_list_voltage,
+ .set_mode = pm8xxx_ftsmps_set_mode,
+ .get_mode = pm8xxx_ftsmps_get_mode,
+ .get_optimum_mode = pm8xxx_vreg_get_optimum_mode,
+ .enable_time = pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_vs_ops = {
+ .enable = pm8xxx_vs_enable,
+ .disable = pm8xxx_vs_disable,
+ .is_enabled = pm8xxx_vreg_is_enabled,
+ .enable_time = pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_vs300_ops = {
+ .enable = pm8xxx_vs300_enable,
+ .disable = pm8xxx_vs300_disable,
+ .is_enabled = pm8xxx_vreg_is_enabled,
+ .enable_time = pm8xxx_enable_time,
+};
+
+static struct regulator_ops pm8xxx_ncp_ops = {
+ .enable = pm8xxx_ncp_enable,
+ .disable = pm8xxx_ncp_disable,
+ .is_enabled = pm8xxx_vreg_is_enabled,
+ .set_voltage = pm8xxx_ncp_set_voltage,
+ .get_voltage = pm8xxx_ncp_get_voltage,
+ .list_voltage = pm8xxx_ncp_list_voltage,
+ .enable_time = pm8xxx_enable_time,
+};
+
+/* Pin control regulator operations. */
+static struct regulator_ops pm8xxx_ldo_pc_ops = {
+ .enable = pm8xxx_ldo_pin_control_enable,
+ .disable = pm8xxx_ldo_pin_control_disable,
+ .is_enabled = pm8xxx_vreg_pin_control_is_enabled,
+};
+
+static struct regulator_ops pm8xxx_smps_pc_ops = {
+ .enable = pm8xxx_smps_pin_control_enable,
+ .disable = pm8xxx_smps_pin_control_disable,
+ .is_enabled = pm8xxx_vreg_pin_control_is_enabled,
+};
+
+static struct regulator_ops pm8xxx_vs_pc_ops = {
+ .enable = pm8xxx_vs_pin_control_enable,
+ .disable = pm8xxx_vs_pin_control_disable,
+ .is_enabled = pm8xxx_vreg_pin_control_is_enabled,
+};
+
+static struct regulator_ops *pm8xxx_reg_ops[PM8XXX_REGULATOR_TYPE_MAX] = {
+ [PM8XXX_REGULATOR_TYPE_PLDO] = &pm8xxx_pldo_ops,
+ [PM8XXX_REGULATOR_TYPE_NLDO] = &pm8xxx_nldo_ops,
+ [PM8XXX_REGULATOR_TYPE_NLDO1200] = &pm8xxx_nldo1200_ops,
+ [PM8XXX_REGULATOR_TYPE_SMPS] = &pm8xxx_smps_ops,
+ [PM8XXX_REGULATOR_TYPE_FTSMPS] = &pm8xxx_ftsmps_ops,
+ [PM8XXX_REGULATOR_TYPE_VS] = &pm8xxx_vs_ops,
+ [PM8XXX_REGULATOR_TYPE_VS300] = &pm8xxx_vs300_ops,
+ [PM8XXX_REGULATOR_TYPE_NCP] = &pm8xxx_ncp_ops,
+};
+
+static struct regulator_ops *pm8xxx_reg_pc_ops[PM8XXX_REGULATOR_TYPE_MAX] = {
+ [PM8XXX_REGULATOR_TYPE_PLDO] = &pm8xxx_ldo_pc_ops,
+ [PM8XXX_REGULATOR_TYPE_NLDO] = &pm8xxx_ldo_pc_ops,
+ [PM8XXX_REGULATOR_TYPE_SMPS] = &pm8xxx_smps_pc_ops,
+ [PM8XXX_REGULATOR_TYPE_VS] = &pm8xxx_vs_pc_ops,
+};
+
+static unsigned pm8xxx_n_voltages[PM8XXX_REGULATOR_TYPE_MAX] = {
+ [PM8XXX_REGULATOR_TYPE_PLDO] = PLDO_SET_POINTS,
+ [PM8XXX_REGULATOR_TYPE_NLDO] = NLDO_SET_POINTS,
+ [PM8XXX_REGULATOR_TYPE_NLDO1200] = NLDO1200_SET_POINTS,
+ [PM8XXX_REGULATOR_TYPE_SMPS] = SMPS_ADVANCED_SET_POINTS,
+ [PM8XXX_REGULATOR_TYPE_FTSMPS] = FTSMPS_SET_POINTS,
+ [PM8XXX_REGULATOR_TYPE_VS] = 0,
+ [PM8XXX_REGULATOR_TYPE_VS300] = 0,
+ [PM8XXX_REGULATOR_TYPE_NCP] = NCP_SET_POINTS,
+};
+
+static int pm8xxx_init_ldo(struct pm8xxx_vreg *vreg, bool is_real)
+{
+ int rc = 0;
+ int i;
+ u8 bank;
+
+ /* Save the current control register state. */
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+
+ /* Save the current test register state. */
+ for (i = 0; i < LDO_TEST_BANKS; i++) {
+ bank = REGULATOR_BANK_SEL(i);
+ rc = pm8xxx_writeb(vreg->dev->parent, vreg->test_addr, bank);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+ &vreg->test_reg[i]);
+ if (rc)
+ goto bail;
+ vreg->test_reg[i] |= REGULATOR_BANK_WRITE;
+ }
+
+ if (is_real) {
+ /* Set pull down enable based on platform data. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ (vreg->pdata.pull_down_enable ? LDO_PULL_DOWN_ENABLE : 0),
+ LDO_PULL_DOWN_ENABLE_MASK, &vreg->ctrl_reg);
+
+ vreg->is_enabled = !!_pm8xxx_vreg_is_enabled(vreg);
+
+ vreg->mode = ((vreg->ctrl_reg & LDO_CTRL_PM_MASK)
+ == LDO_CTRL_PM_LPM ?
+ REGULATOR_MODE_IDLE : REGULATOR_MODE_NORMAL);
+ }
+bail:
+ if (rc)
+ vreg_err(vreg, "pm8xxx_readb/writeb failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int pm8xxx_init_nldo1200(struct pm8xxx_vreg *vreg)
+{
+ int rc = 0;
+ int i;
+ u8 bank;
+
+ /* Save the current control register state. */
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+
+ /* Save the current test register state. */
+ for (i = 0; i < LDO_TEST_BANKS; i++) {
+ bank = REGULATOR_BANK_SEL(i);
+ rc = pm8xxx_writeb(vreg->dev->parent, vreg->test_addr, bank);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+ &vreg->test_reg[i]);
+ if (rc)
+ goto bail;
+ vreg->test_reg[i] |= REGULATOR_BANK_WRITE;
+ }
+
+ vreg->save_uV = _pm8xxx_nldo1200_get_voltage(vreg);
+
+ /* Set pull down enable based on platform data. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ (vreg->pdata.pull_down_enable ? NLDO1200_PULL_DOWN_ENABLE : 0)
+ | REGULATOR_BANK_SEL(1) | REGULATOR_BANK_WRITE,
+ NLDO1200_PULL_DOWN_ENABLE_MASK | REGULATOR_BANK_MASK,
+ &vreg->test_reg[1]);
+
+bail:
+ if (rc)
+ vreg_err(vreg, "pm8xxx_readb/writeb failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int pm8xxx_init_smps(struct pm8xxx_vreg *vreg, bool is_real)
+{
+ int rc = 0;
+ int i;
+ u8 bank;
+
+ /* Save the current control register state. */
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+
+ /* Save the current test2 register state. */
+ for (i = 0; i < SMPS_TEST_BANKS; i++) {
+ bank = REGULATOR_BANK_SEL(i);
+ rc = pm8xxx_writeb(vreg->dev->parent, vreg->test_addr, bank);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+ &vreg->test_reg[i]);
+ if (rc)
+ goto bail;
+ vreg->test_reg[i] |= REGULATOR_BANK_WRITE;
+ }
+
+ /* Save the current clock control register state. */
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->clk_ctrl_addr,
+ &vreg->clk_ctrl_reg);
+ if (rc)
+ goto bail;
+
+ /* Save the current sleep control register state. */
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->sleep_ctrl_addr,
+ &vreg->sleep_ctrl_reg);
+ if (rc)
+ goto bail;
+
+ vreg->save_uV = _pm8xxx_smps_get_voltage(vreg);
+
+ if (is_real) {
+ /* Set advanced mode pull down enable based on platform data. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->test_addr,
+ (vreg->pdata.pull_down_enable
+ ? SMPS_ADVANCED_PULL_DOWN_ENABLE : 0)
+ | REGULATOR_BANK_SEL(6) | REGULATOR_BANK_WRITE,
+ REGULATOR_BANK_MASK | SMPS_ADVANCED_PULL_DOWN_ENABLE,
+ &vreg->test_reg[6]);
+ if (rc)
+ goto bail;
+
+ vreg->is_enabled = !!_pm8xxx_vreg_is_enabled(vreg);
+
+ vreg->mode = ((vreg->clk_ctrl_reg & SMPS_CLK_CTRL_MASK)
+ == SMPS_CLK_CTRL_PFM ?
+ REGULATOR_MODE_IDLE : REGULATOR_MODE_NORMAL);
+ }
+
+ if (!SMPS_IN_ADVANCED_MODE(vreg) && is_real) {
+ /* Set legacy mode pull down enable based on platform data. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ (vreg->pdata.pull_down_enable
+ ? SMPS_LEGACY_PULL_DOWN_ENABLE : 0),
+ SMPS_LEGACY_PULL_DOWN_ENABLE, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+ }
+
+bail:
+ if (rc)
+ vreg_err(vreg, "pm8xxx_readb/writeb failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int pm8xxx_init_ftsmps(struct pm8xxx_vreg *vreg)
+{
+ int rc, i;
+ u8 bank;
+
+ /* Save the current control register state. */
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+ if (rc)
+ goto bail;
+
+ /* Store current regulator register values. */
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->pfm_ctrl_addr,
+ &vreg->pfm_ctrl_reg);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->pwr_cnfg_addr,
+ &vreg->pwr_cnfg_reg);
+ if (rc)
+ goto bail;
+
+ /* Save the current fts_cnfg1 register state (uses 'test' member). */
+ for (i = 0; i < SMPS_TEST_BANKS; i++) {
+ bank = REGULATOR_BANK_SEL(i);
+ rc = pm8xxx_writeb(vreg->dev->parent, vreg->test_addr, bank);
+ if (rc)
+ goto bail;
+
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->test_addr,
+ &vreg->test_reg[i]);
+ if (rc)
+ goto bail;
+ vreg->test_reg[i] |= REGULATOR_BANK_WRITE;
+ }
+
+ vreg->save_uV = _pm8xxx_ftsmps_get_voltage(vreg);
+
+ /* Set pull down enable based on platform data. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->pwr_cnfg_addr,
+ (vreg->pdata.pull_down_enable ? FTSMPS_PULL_DOWN_ENABLE : 0),
+ FTSMPS_PULL_DOWN_ENABLE_MASK, &vreg->pwr_cnfg_reg);
+
+bail:
+ if (rc)
+ vreg_err(vreg, "pm8xxx_readb/writeb failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int pm8xxx_init_vs(struct pm8xxx_vreg *vreg, bool is_real)
+{
+ int rc = 0;
+
+ /* Save the current control register state. */
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+ if (rc) {
+ vreg_err(vreg, "pm8xxx_readb failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ if (is_real) {
+ /* Set pull down enable based on platform data. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ (vreg->pdata.pull_down_enable ? VS_PULL_DOWN_ENABLE
+ : VS_PULL_DOWN_DISABLE),
+ VS_PULL_DOWN_ENABLE_MASK, &vreg->ctrl_reg);
+
+ if (rc)
+ vreg_err(vreg,
+ "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+
+ vreg->is_enabled = !!_pm8xxx_vreg_is_enabled(vreg);
+ }
+
+ return rc;
+}
+
+static int pm8xxx_init_vs300(struct pm8xxx_vreg *vreg)
+{
+ int rc;
+
+ /* Save the current control register state. */
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+ if (rc) {
+ vreg_err(vreg, "pm8xxx_readb failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ /* Set pull down enable based on platform data. */
+ rc = pm8xxx_vreg_masked_write(vreg, vreg->ctrl_addr,
+ (vreg->pdata.pull_down_enable ? VS300_PULL_DOWN_ENABLE : 0),
+ VS300_PULL_DOWN_ENABLE_MASK, &vreg->ctrl_reg);
+
+ if (rc)
+ vreg_err(vreg, "pm8xxx_vreg_masked_write failed, rc=%d\n", rc);
+
+ return rc;
+}
+
+static int pm8xxx_init_ncp(struct pm8xxx_vreg *vreg)
+{
+ int rc;
+
+ /* Save the current control register state. */
+ rc = pm8xxx_readb(vreg->dev->parent, vreg->ctrl_addr, &vreg->ctrl_reg);
+ if (rc) {
+ vreg_err(vreg, "pm8xxx_readb failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ return rc;
+}
+
+static int __devinit pm8xxx_vreg_probe(struct platform_device *pdev)
+{
+ struct pm8xxx_regulator_core_platform_data *core_data;
+ const struct pm8xxx_regulator_platform_data *pdata;
+ enum pm8xxx_vreg_pin_function pin_fn;
+ struct regulator_desc *rdesc;
+ struct pm8xxx_vreg *vreg;
+ unsigned pin_ctrl;
+ int rc = 0;
+
+ if (pdev == NULL) {
+ pr_err("no platform device specified\n");
+ return -EINVAL;
+ }
+
+ core_data = pdev->dev.platform_data;
+ if (core_data == NULL) {
+ pr_err("no core data specified\n");
+ return -EINVAL;
+ }
+
+ pdata = core_data->pdata;
+ vreg = core_data->vreg;
+ if (pdata == NULL) {
+ pr_err("no pdata specified\n");
+ return -EINVAL;
+ } else if (vreg == NULL) {
+ pr_err("no vreg specified\n");
+ return -EINVAL;
+ }
+
+ if (vreg->rdesc.name == NULL) {
+ pr_err("regulator name missing\n");
+ return -EINVAL;
+ } else if (vreg->type < 0 || vreg->type > PM8XXX_REGULATOR_TYPE_MAX) {
+ pr_err("%s: regulator type=%d is invalid\n", vreg->rdesc.name,
+ vreg->type);
+ return -EINVAL;
+ } else if (core_data->is_pin_controlled
+ && pm8xxx_reg_pc_ops[vreg->type] == NULL) {
+ pr_err("%s: regulator type=%d does not support pin control\n",
+ vreg->rdesc.name, vreg->type);
+ return -EINVAL;
+ } else if (core_data->is_pin_controlled
+ && vreg->rdesc_pc.name == NULL) {
+ pr_err("%s: regulator pin control name missing\n",
+ vreg->rdesc.name);
+ return -EINVAL;
+ }
+
+ if (core_data->is_pin_controlled)
+ rdesc = &vreg->rdesc_pc;
+ else
+ rdesc = &vreg->rdesc;
+ if (!pdata) {
+ pr_err("%s requires platform data\n", vreg->rdesc.name);
+ return -EINVAL;
+ }
+
+ rdesc->id = pdev->id;
+ rdesc->owner = THIS_MODULE;
+ rdesc->type = REGULATOR_VOLTAGE;
+ if (core_data->is_pin_controlled) {
+ rdesc->ops = pm8xxx_reg_pc_ops[vreg->type];
+ rdesc->n_voltages = 0;
+ } else {
+ rdesc->ops = pm8xxx_reg_ops[vreg->type];
+ rdesc->n_voltages = pm8xxx_n_voltages[vreg->type];
+ }
+
+ mutex_lock(&vreg->pc_lock);
+
+ if (!core_data->is_pin_controlled) {
+ /* Do not modify pin control and pin function values. */
+ pin_ctrl = vreg->pdata.pin_ctrl;
+ pin_fn = vreg->pdata.pin_fn;
+ memcpy(&(vreg->pdata), pdata,
+ sizeof(struct pm8xxx_regulator_platform_data));
+ vreg->pdata.pin_ctrl = pin_ctrl;
+ vreg->pdata.pin_fn = pin_fn;
+ vreg->dev = &pdev->dev;
+ } else {
+ /* Pin control regulator */
+ if ((pdata->pin_ctrl & PM8XXX_VREG_PIN_CTRL_ALL)
+ == PM8XXX_VREG_PIN_CTRL_NONE) {
+ pr_err("%s: no pin control input specified\n",
+ vreg->rdesc.name);
+ mutex_unlock(&vreg->pc_lock);
+ return -EINVAL;
+ }
+ vreg->pdata.pin_ctrl = pdata->pin_ctrl;
+ vreg->pdata.pin_fn = pdata->pin_fn;
+ vreg->dev_pc = &pdev->dev;
+ if (!vreg->dev)
+ vreg->dev = &pdev->dev;
+ }
+
+ /* Initialize register values. */
+ switch (vreg->type) {
+ case PM8XXX_REGULATOR_TYPE_PLDO:
+ case PM8XXX_REGULATOR_TYPE_NLDO:
+ rc = pm8xxx_init_ldo(vreg, !core_data->is_pin_controlled);
+ break;
+ case PM8XXX_REGULATOR_TYPE_NLDO1200:
+ rc = pm8xxx_init_nldo1200(vreg);
+ break;
+ case PM8XXX_REGULATOR_TYPE_SMPS:
+ rc = pm8xxx_init_smps(vreg, !core_data->is_pin_controlled);
+ break;
+ case PM8XXX_REGULATOR_TYPE_FTSMPS:
+ rc = pm8xxx_init_ftsmps(vreg);
+ break;
+ case PM8XXX_REGULATOR_TYPE_VS:
+ rc = pm8xxx_init_vs(vreg, !core_data->is_pin_controlled);
+ break;
+ case PM8XXX_REGULATOR_TYPE_VS300:
+ rc = pm8xxx_init_vs300(vreg);
+ break;
+ case PM8XXX_REGULATOR_TYPE_NCP:
+ rc = pm8xxx_init_ncp(vreg);
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&vreg->pc_lock);
+
+ if (rc)
+ goto bail;
+
+ if (!core_data->is_pin_controlled) {
+ vreg->rdev = regulator_register(rdesc, &pdev->dev,
+ &(pdata->init_data), vreg);
+ if (IS_ERR(vreg->rdev)) {
+ rc = PTR_ERR(vreg->rdev);
+ vreg->rdev = NULL;
+ pr_err("regulator_register failed: %s, rc=%d\n",
+ vreg->rdesc.name, rc);
+ }
+ } else {
+ vreg->rdev_pc = regulator_register(rdesc, &pdev->dev,
+ &(pdata->init_data), vreg);
+ if (IS_ERR(vreg->rdev_pc)) {
+ rc = PTR_ERR(vreg->rdev_pc);
+ vreg->rdev_pc = NULL;
+ pr_err("regulator_register failed: %s, rc=%d\n",
+ vreg->rdesc.name, rc);
+ }
+ }
+ if ((pm8xxx_vreg_debug_mask & PM8XXX_VREG_DEBUG_INIT) && !rc
+ && vreg->rdev)
+ pm8xxx_vreg_show_state(vreg->rdev,
+ PM8XXX_REGULATOR_ACTION_INIT);
+
+ platform_set_drvdata(pdev, core_data);
+
+bail:
+ if (rc)
+ pr_err("error for %s, rc=%d\n", vreg->rdesc.name, rc);
+
+ return rc;
+}
+
+static int __devexit pm8xxx_vreg_remove(struct platform_device *pdev)
+{
+ struct pm8xxx_regulator_core_platform_data *core_data;
+
+ core_data = platform_get_drvdata(pdev);
+ platform_set_drvdata(pdev, NULL);
+
+ if (core_data) {
+ if (core_data->is_pin_controlled)
+ regulator_unregister(core_data->vreg->rdev_pc);
+ else
+ regulator_unregister(core_data->vreg->rdev);
+ }
+
+ return 0;
+}
+
+static struct platform_driver pm8xxx_vreg_driver = {
+ .probe = pm8xxx_vreg_probe,
+ .remove = __devexit_p(pm8xxx_vreg_remove),
+ .driver = {
+ .name = PM8XXX_REGULATOR_DEV_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pm8xxx_vreg_init(void)
+{
+ return platform_driver_register(&pm8xxx_vreg_driver);
+}
+postcore_initcall(pm8xxx_vreg_init);
+
+static void __exit pm8xxx_vreg_exit(void)
+{
+ platform_driver_unregister(&pm8xxx_vreg_driver);
+}
+module_exit(pm8xxx_vreg_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("PMIC PM8XXX regulator driver");
+MODULE_VERSION("1.0");
+MODULE_ALIAS("platform:" PM8XXX_REGULATOR_DEV_NAME);
diff --git a/drivers/slimbus/slim-msm-ctrl.c b/drivers/slimbus/slim-msm-ctrl.c
index 90e2687..984ab71 100644
--- a/drivers/slimbus/slim-msm-ctrl.c
+++ b/drivers/slimbus/slim-msm-ctrl.c
@@ -1764,6 +1764,13 @@
*/
wmb();
+ /* Register with framework before enabling frame, clock */
+ ret = slim_add_numbered_controller(&dev->ctrl);
+ if (ret) {
+ dev_err(dev->dev, "error adding controller\n");
+ goto err_ctrl_failed;
+ }
+
/* Framer register initialization */
writel_relaxed((0xA << REF_CLK_GEAR) | (0xA << CLK_GEAR) |
(1 << ROOT_FREQ) | (1 << FRM_ACTIVE) | 1,
@@ -1811,11 +1818,6 @@
pm_runtime_set_autosuspend_delay(&pdev->dev, MSM_SLIM_AUTOSUSPEND);
pm_runtime_set_active(&pdev->dev);
- ret = slim_add_numbered_controller(&dev->ctrl);
- if (ret) {
- dev_err(dev->dev, "error adding controller\n");
- goto err_ctrl_failed;
- }
dev_dbg(dev->dev, "MSM SB controller is up!\n");
return 0;
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
index d8003bf..a541d8b 100644
--- a/drivers/slimbus/slimbus.c
+++ b/drivers/slimbus/slimbus.c
@@ -1876,7 +1876,7 @@
*ctrlw = maxctrlw1;
}
} else {
- struct slim_ich *slc1;
+ struct slim_ich *slc1 = NULL;
struct slim_ich *slc3 = ctrl->sched.chc3[coeff3];
u32 expshft = SLIM_MAX_CLK_GEAR - clkgear;
int curexp, finalexp, exp1;
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index d055412..187d310 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -108,6 +108,8 @@
source "drivers/staging/zcache/Kconfig"
+source "drivers/staging/qcache/Kconfig"
+
source "drivers/staging/wlags49_h2/Kconfig"
source "drivers/staging/wlags49_h25/Kconfig"
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index ae62e92..a247583 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -46,6 +46,7 @@
obj-$(CONFIG_ZRAM) += zram/
obj-$(CONFIG_XVMALLOC) += zram/
obj-$(CONFIG_ZCACHE) += zcache/
+obj-$(CONFIG_QCACHE) += qcache/
obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/
obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/
obj-$(CONFIG_FB_SM7XX) += sm7xx/
diff --git a/drivers/staging/qcache/Kconfig b/drivers/staging/qcache/Kconfig
new file mode 100644
index 0000000..389341c
--- /dev/null
+++ b/drivers/staging/qcache/Kconfig
@@ -0,0 +1,8 @@
+config QCACHE
+ tristate "Dynamic compression of clean pagecache pages"
+ depends on CLEANCACHE
+ select LZO_COMPRESS
+ select LZO_DECOMPRESS
+ default n
+ help
+ Qcache is the backend for fmem
diff --git a/drivers/staging/qcache/Makefile b/drivers/staging/qcache/Makefile
new file mode 100644
index 0000000..4fdf05c
--- /dev/null
+++ b/drivers/staging/qcache/Makefile
@@ -0,0 +1,3 @@
+qcache-y := qcache-main.o tmem.o fmem.o
+
+obj-$(CONFIG_QCACHE) += qcache.o
diff --git a/drivers/staging/qcache/fmem.c b/drivers/staging/qcache/fmem.c
new file mode 100644
index 0000000..489d27a
--- /dev/null
+++ b/drivers/staging/qcache/fmem.c
@@ -0,0 +1,216 @@
+/*
+ *
+ * Copyright (c) 2011, 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/fmem.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include "tmem.h"
+#include <asm/mach/map.h>
+
+struct fmem_data fmem_data;
+enum fmem_state fmem_state;
+static spinlock_t fmem_state_lock;
+
+void *fmem_map_virtual_area(int cacheability)
+{
+ unsigned long addr;
+ const struct mem_type *type;
+ int ret;
+
+ addr = (unsigned long) fmem_data.area->addr;
+ type = get_mem_type(cacheability);
+ ret = ioremap_page_range(addr, addr + fmem_data.size,
+ fmem_data.phys, __pgprot(type->prot_pte));
+ if (ret)
+ return ERR_PTR(ret);
+
+ fmem_data.virt = fmem_data.area->addr;
+
+ return fmem_data.virt;
+}
+
+void fmem_unmap_virtual_area(void)
+{
+ unmap_kernel_range((unsigned long)fmem_data.virt, fmem_data.size);
+ fmem_data.virt = NULL;
+}
+
+static int fmem_probe(struct platform_device *pdev)
+{
+ struct fmem_platform_data *pdata = pdev->dev.platform_data;
+
+ if (!pdata->size)
+ return -ENODEV;
+
+ fmem_data.phys = pdata->phys;
+ fmem_data.size = pdata->size;
+ fmem_data.area = get_vm_area(pdata->size, VM_IOREMAP);
+ if (!fmem_data.area)
+ return -ENOMEM;
+
+ if (!fmem_map_virtual_area(MT_DEVICE_CACHED)) {
+ remove_vm_area(fmem_data.area->addr);
+ return -ENOMEM;
+ }
+ pr_info("fmem phys %lx virt %p size %lx\n",
+ fmem_data.phys, fmem_data.virt, fmem_data.size);
+
+ spin_lock_init(&fmem_state_lock);
+
+ return 0;
+}
+
+static int fmem_remove(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static struct platform_driver fmem_driver = {
+ .probe = fmem_probe,
+ .remove = fmem_remove,
+ .driver = { .name = "fmem" }
+};
+
+#ifdef CONFIG_SYSFS
+static ssize_t fmem_state_show(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ char *buf)
+{
+ if (fmem_state == FMEM_T_STATE)
+ return snprintf(buf, 3, "t\n");
+ else if (fmem_state == FMEM_C_STATE)
+ return snprintf(buf, 3, "c\n");
+ else if (fmem_state == FMEM_UNINITIALIZED)
+ return snprintf(buf, 15, "uninitialized\n");
+ return snprintf(buf, 3, "?\n");
+}
+
+static ssize_t fmem_state_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret = -EINVAL;
+
+ if (!strncmp(buf, "t", 1))
+ ret = fmem_set_state(FMEM_T_STATE);
+ else if (!strncmp(buf, "c", 1))
+ ret = fmem_set_state(FMEM_C_STATE);
+ if (ret)
+ return ret;
+ return 1;
+}
+
+static struct kobj_attribute fmem_state_attr = {
+ .attr = { .name = "state", .mode = 0644 },
+ .show = fmem_state_show,
+ .store = fmem_state_store,
+};
+
+static struct attribute *fmem_attrs[] = {
+ &fmem_state_attr.attr,
+ NULL,
+};
+
+static struct attribute_group fmem_attr_group = {
+ .attrs = fmem_attrs,
+ .name = "fmem",
+};
+
+static int fmem_create_sysfs(void)
+{
+ int ret = 0;
+
+ ret = sysfs_create_group(mm_kobj, &fmem_attr_group);
+ if (ret)
+ pr_err("fmem: can't create sysfs\n");
+ return ret;
+}
+
+#endif
+
+static int __init fmem_init(void)
+{
+ return platform_driver_register(&fmem_driver);
+}
+
+static void __exit fmem_exit(void)
+{
+ platform_driver_unregister(&fmem_driver);
+}
+
+struct fmem_data *fmem_get_info(void)
+{
+ return &fmem_data;
+}
+EXPORT_SYMBOL(fmem_get_info);
+
+void lock_fmem_state(void)
+{
+ spin_lock(&fmem_state_lock);
+}
+
+void unlock_fmem_state(void)
+{
+ spin_unlock(&fmem_state_lock);
+}
+
+int fmem_set_state(enum fmem_state new_state)
+{
+ int ret = 0;
+ int create_sysfs = 0;
+
+ lock_fmem_state();
+ if (fmem_state == new_state)
+ goto out;
+
+ if (fmem_state == FMEM_UNINITIALIZED) {
+ if (new_state == FMEM_T_STATE) {
+ tmem_enable(false);
+ create_sysfs = 1;
+ goto out_set;
+ }
+ if (new_state == FMEM_C_STATE) {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ if (new_state == FMEM_T_STATE) {
+ void *v;
+ v = fmem_map_virtual_area(MT_DEVICE_CACHED);
+ if (IS_ERR_OR_NULL(v)) {
+ ret = PTR_ERR(v);
+ goto out;
+ }
+ tmem_enable(true);
+ } else {
+ tmem_disable();
+ fmem_unmap_virtual_area();
+ }
+
+out_set:
+ fmem_state = new_state;
+out:
+ unlock_fmem_state();
+#ifdef CONFIG_SYSFS
+ if (create_sysfs)
+ fmem_create_sysfs();
+#endif
+ return ret;
+}
+EXPORT_SYMBOL(fmem_set_state);
+
+arch_initcall(fmem_init);
+module_exit(fmem_exit);
diff --git a/drivers/staging/qcache/qcache-main.c b/drivers/staging/qcache/qcache-main.c
new file mode 100644
index 0000000..b6de268
--- /dev/null
+++ b/drivers/staging/qcache/qcache-main.c
@@ -0,0 +1,1357 @@
+/*
+ * Copyright (c) 2010,2011, Dan Magenheimer, Oracle Corp.
+ * Copyright (c) 2010,2011, Nitin Gupta
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * Qcache provides an in-kernel "host implementation" for transcendent memory
+ * and, thus indirectly, for cleancache and frontswap. Qcache includes a
+ * page-accessible memory [1] interface, utilizing lzo1x compression:
+ * 1) "compression buddies" ("zbud") is used for ephemeral pages
+ * Zbud allows pairs (and potentially,
+ * in the future, more than a pair of) compressed pages to be closely linked
+ * so that reclaiming can be done via the kernel's physical-page-oriented
+ * "shrinker" interface.
+ *
+ * [1] For a definition of page-accessible memory (aka PAM), see:
+ * http://marc.info/?l=linux-mm&m=127811271605009
+ */
+
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/highmem.h>
+#include <linux/list.h>
+#include <linux/lzo.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/math64.h>
+#include <linux/bitmap.h>
+#include <linux/fmem.h>
+#include "tmem.h"
+
+#if !defined(CONFIG_CLEANCACHE)
+#error "qcache is useless without CONFIG_CLEANCACHE"
+#endif
+#include <linux/cleancache.h>
+
+#define ZCACHE_GFP_MASK \
+ (__GFP_FS | __GFP_NORETRY | __GFP_NOWARN | __GFP_NOMEMALLOC)
+
+#define MAX_POOLS_PER_CLIENT 16
+
+#define MAX_CLIENTS 16
+#define LOCAL_CLIENT ((uint16_t)-1)
+
+MODULE_LICENSE("GPL");
+
+struct zcache_client {
+ struct tmem_pool *tmem_pools[MAX_POOLS_PER_CLIENT];
+ struct xv_pool *xvpool;
+ bool allocated;
+ atomic_t refcount;
+};
+
+struct qcache_info {
+ void *addr;
+ unsigned long *bitmap;
+ spinlock_t lock;
+ unsigned pages;
+};
+static struct qcache_info qcache_info;
+static unsigned long zcache_qc_allocated;
+static unsigned long zcache_qc_freed;
+static unsigned long zcache_qc_used;
+static unsigned long zcache_qc_max_used;
+
+static struct zcache_client zcache_host;
+static struct zcache_client zcache_clients[MAX_CLIENTS];
+
+static inline uint16_t get_client_id_from_client(struct zcache_client *cli)
+{
+ BUG_ON(cli == NULL);
+ if (cli == &zcache_host)
+ return LOCAL_CLIENT;
+ return cli - &zcache_clients[0];
+}
+
+static inline bool is_local_client(struct zcache_client *cli)
+{
+ return cli == &zcache_host;
+}
+
+/**********
+ * Compression buddies ("zbud") provides for packing two (or, possibly
+ * in the future, more) compressed ephemeral pages into a single "raw"
+ * (physical) page and tracking them with data structures so that
+ * the raw pages can be easily reclaimed.
+ *
+ * A zbud page ("zbpg") is an aligned page containing a list_head,
+ * a lock, and two "zbud headers". The remainder of the physical
+ * page is divided up into aligned 64-byte "chunks" which contain
+ * the compressed data for zero, one, or two zbuds. Each zbpg
+ * resides on: (1) an "unused list" if it has no zbuds; (2) a
+ * "buddied" list if it is fully populated with two zbuds; or
+ * (3) one of PAGE_SIZE/64 "unbuddied" lists indexed by how many chunks
+ * the one unbuddied zbud uses. The data inside a zbpg cannot be
+ * read or written unless the zbpg's lock is held.
+ */
+
+#define ZBH_SENTINEL 0x43214321
+#define ZBPG_SENTINEL 0xdeadbeef
+
+#define ZBUD_MAX_BUDS 2
+
+struct zbud_hdr {
+ uint16_t client_id;
+ uint16_t pool_id;
+ struct tmem_oid oid;
+ uint32_t index;
+ uint16_t size; /* compressed size in bytes, zero means unused */
+ DECL_SENTINEL
+};
+
+struct zbud_page {
+ struct list_head bud_list;
+ spinlock_t lock;
+ struct zbud_hdr buddy[ZBUD_MAX_BUDS];
+ DECL_SENTINEL
+ /* followed by NUM_CHUNK aligned CHUNK_SIZE-byte chunks */
+};
+
+#define CHUNK_SHIFT 6
+#define CHUNK_SIZE (1 << CHUNK_SHIFT)
+#define CHUNK_MASK (~(CHUNK_SIZE-1))
+#define NCHUNKS (((PAGE_SIZE - sizeof(struct zbud_page)) & \
+ CHUNK_MASK) >> CHUNK_SHIFT)
+#define MAX_CHUNK (NCHUNKS-1)
+
+static struct {
+ struct list_head list;
+ unsigned count;
+} zbud_unbuddied[NCHUNKS];
+/* list N contains pages with N chunks USED and NCHUNKS-N unused */
+/* element 0 is never used but optimizing that isn't worth it */
+static unsigned long zbud_cumul_chunk_counts[NCHUNKS];
+
+struct list_head zbud_buddied_list;
+static unsigned long zcache_zbud_buddied_count;
+
+/* protects the buddied list and all unbuddied lists */
+static DEFINE_SPINLOCK(zbud_budlists_spinlock);
+
+static atomic_t zcache_zbud_curr_raw_pages;
+static atomic_t zcache_zbud_curr_zpages;
+static unsigned long zcache_zbud_curr_zbytes;
+static unsigned long zcache_zbud_cumul_zpages;
+static unsigned long zcache_zbud_cumul_zbytes;
+static unsigned long zcache_compress_poor;
+static unsigned long zcache_mean_compress_poor;
+
+/* forward references */
+static void *zcache_get_free_page(void);
+
+static void *qcache_alloc(void)
+{
+ void *addr;
+ unsigned long flags;
+ int offset;
+ struct qcache_info *qc = &qcache_info;
+
+ spin_lock_irqsave(&qc->lock, flags);
+ offset = bitmap_find_free_region(qc->bitmap, qc->pages, 0);
+ spin_unlock_irqrestore(&qc->lock, flags);
+
+ if (offset < 0)
+ return NULL;
+
+ addr = qc->addr + offset * PAGE_SIZE;
+ zcache_qc_allocated++;
+ zcache_qc_used++;
+ zcache_qc_max_used = max(zcache_qc_max_used, zcache_qc_used);
+
+ return addr;
+}
+
+static void qcache_free(void *addr)
+{
+ unsigned long flags;
+ int offset;
+ struct qcache_info *qc = &qcache_info;
+
+ offset = (addr - qc->addr) / PAGE_SIZE;
+
+ spin_lock_irqsave(&qc->lock, flags);
+ bitmap_release_region(qc->bitmap, offset, 0);
+ spin_unlock_irqrestore(&qc->lock, flags);
+
+ zcache_qc_freed++;
+ zcache_qc_used--;
+}
+
+/*
+ * zbud helper functions
+ */
+
+static inline unsigned zbud_max_buddy_size(void)
+{
+ return MAX_CHUNK << CHUNK_SHIFT;
+}
+
+static inline unsigned zbud_size_to_chunks(unsigned size)
+{
+ BUG_ON(size == 0 || size > zbud_max_buddy_size());
+ return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
+}
+
+static inline int zbud_budnum(struct zbud_hdr *zh)
+{
+ unsigned offset = (unsigned long)zh & (PAGE_SIZE - 1);
+ struct zbud_page *zbpg = NULL;
+ unsigned budnum = -1U;
+ int i;
+
+ for (i = 0; i < ZBUD_MAX_BUDS; i++)
+ if (offset == offsetof(typeof(*zbpg), buddy[i])) {
+ budnum = i;
+ break;
+ }
+ BUG_ON(budnum == -1U);
+ return budnum;
+}
+
+static char *zbud_data(struct zbud_hdr *zh, unsigned size)
+{
+ struct zbud_page *zbpg;
+ char *p;
+ unsigned budnum;
+
+ ASSERT_SENTINEL(zh, ZBH);
+ budnum = zbud_budnum(zh);
+ BUG_ON(size == 0 || size > zbud_max_buddy_size());
+ zbpg = container_of(zh, struct zbud_page, buddy[budnum]);
+ ASSERT_SPINLOCK(&zbpg->lock);
+ p = (char *)zbpg;
+ if (budnum == 0)
+ p += ((sizeof(struct zbud_page) + CHUNK_SIZE - 1) &
+ CHUNK_MASK);
+ else if (budnum == 1)
+ p += PAGE_SIZE - ((size + CHUNK_SIZE - 1) & CHUNK_MASK);
+ return p;
+}
+
+/*
+ * zbud raw page management
+ */
+
+static struct zbud_page *zbud_alloc_raw_page(void)
+{
+ struct zbud_page *zbpg = NULL;
+ struct zbud_hdr *zh0, *zh1;
+
+ zbpg = zcache_get_free_page();
+ if (likely(zbpg != NULL)) {
+ INIT_LIST_HEAD(&zbpg->bud_list);
+ zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1];
+ spin_lock_init(&zbpg->lock);
+ atomic_inc(&zcache_zbud_curr_raw_pages);
+ INIT_LIST_HEAD(&zbpg->bud_list);
+ SET_SENTINEL(zbpg, ZBPG);
+ zh0->size = 0; zh1->size = 0;
+ tmem_oid_set_invalid(&zh0->oid);
+ tmem_oid_set_invalid(&zh1->oid);
+ }
+ return zbpg;
+}
+
+static void zbud_free_raw_page(struct zbud_page *zbpg)
+{
+ struct zbud_hdr *zh0 = &zbpg->buddy[0], *zh1 = &zbpg->buddy[1];
+
+ ASSERT_SENTINEL(zbpg, ZBPG);
+ BUG_ON(!list_empty(&zbpg->bud_list));
+ ASSERT_SPINLOCK(&zbpg->lock);
+ BUG_ON(zh0->size != 0 || tmem_oid_valid(&zh0->oid));
+ BUG_ON(zh1->size != 0 || tmem_oid_valid(&zh1->oid));
+ INVERT_SENTINEL(zbpg, ZBPG);
+ spin_unlock(&zbpg->lock);
+ qcache_free(zbpg);
+}
+
+/*
+ * core zbud handling routines
+ */
+
+static unsigned zbud_free(struct zbud_hdr *zh)
+{
+ unsigned size;
+
+ ASSERT_SENTINEL(zh, ZBH);
+ BUG_ON(!tmem_oid_valid(&zh->oid));
+ size = zh->size;
+ BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size());
+ zh->size = 0;
+ tmem_oid_set_invalid(&zh->oid);
+ INVERT_SENTINEL(zh, ZBH);
+ zcache_zbud_curr_zbytes -= size;
+ atomic_dec(&zcache_zbud_curr_zpages);
+ return size;
+}
+
+static void zbud_free_and_delist(struct zbud_hdr *zh)
+{
+ unsigned chunks;
+ struct zbud_hdr *zh_other;
+ unsigned budnum = zbud_budnum(zh), size;
+ struct zbud_page *zbpg =
+ container_of(zh, struct zbud_page, buddy[budnum]);
+
+ spin_lock(&zbpg->lock);
+ if (list_empty(&zbpg->bud_list)) {
+ spin_unlock(&zbpg->lock);
+ return;
+ }
+ size = zbud_free(zh);
+ ASSERT_SPINLOCK(&zbpg->lock);
+ zh_other = &zbpg->buddy[(budnum == 0) ? 1 : 0];
+ if (zh_other->size == 0) { /* was unbuddied: unlist and free */
+ chunks = zbud_size_to_chunks(size) ;
+ spin_lock(&zbud_budlists_spinlock);
+ BUG_ON(list_empty(&zbud_unbuddied[chunks].list));
+ list_del_init(&zbpg->bud_list);
+ zbud_unbuddied[chunks].count--;
+ spin_unlock(&zbud_budlists_spinlock);
+ zbud_free_raw_page(zbpg);
+ } else { /* was buddied: move remaining buddy to unbuddied list */
+ chunks = zbud_size_to_chunks(zh_other->size) ;
+ spin_lock(&zbud_budlists_spinlock);
+ list_del_init(&zbpg->bud_list);
+ zcache_zbud_buddied_count--;
+ list_add_tail(&zbpg->bud_list, &zbud_unbuddied[chunks].list);
+ zbud_unbuddied[chunks].count++;
+ spin_unlock(&zbud_budlists_spinlock);
+ spin_unlock(&zbpg->lock);
+ }
+}
+
+static struct zbud_hdr *zbud_create(uint16_t client_id, uint16_t pool_id,
+ struct tmem_oid *oid,
+ uint32_t index, struct page *page,
+ void *cdata, unsigned size)
+{
+ struct zbud_hdr *zh0, *zh1, *zh = NULL;
+ struct zbud_page *zbpg = NULL, *ztmp;
+ unsigned nchunks;
+ char *to;
+ int i, found_good_buddy = 0;
+
+ nchunks = zbud_size_to_chunks(size) ;
+ for (i = MAX_CHUNK - nchunks + 1; i > 0; i--) {
+ spin_lock(&zbud_budlists_spinlock);
+ if (!list_empty(&zbud_unbuddied[i].list)) {
+ list_for_each_entry_safe(zbpg, ztmp,
+ &zbud_unbuddied[i].list, bud_list) {
+ if (spin_trylock(&zbpg->lock)) {
+ found_good_buddy = i;
+ goto found_unbuddied;
+ }
+ }
+ }
+ spin_unlock(&zbud_budlists_spinlock);
+ }
+ /* didn't find a good buddy, try allocating a new page */
+ zbpg = zbud_alloc_raw_page();
+ if (unlikely(zbpg == NULL))
+ goto out;
+ /* ok, have a page, now compress the data before taking locks */
+ spin_lock(&zbpg->lock);
+ spin_lock(&zbud_budlists_spinlock);
+ list_add_tail(&zbpg->bud_list, &zbud_unbuddied[nchunks].list);
+ zbud_unbuddied[nchunks].count++;
+ zh = &zbpg->buddy[0];
+ goto init_zh;
+
+found_unbuddied:
+ ASSERT_SPINLOCK(&zbpg->lock);
+ zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1];
+ BUG_ON(!((zh0->size == 0) ^ (zh1->size == 0)));
+ if (zh0->size != 0) { /* buddy0 in use, buddy1 is vacant */
+ ASSERT_SENTINEL(zh0, ZBH);
+ zh = zh1;
+ } else if (zh1->size != 0) { /* buddy1 in use, buddy0 is vacant */
+ ASSERT_SENTINEL(zh1, ZBH);
+ zh = zh0;
+ } else
+ BUG();
+ list_del_init(&zbpg->bud_list);
+ zbud_unbuddied[found_good_buddy].count--;
+ list_add_tail(&zbpg->bud_list, &zbud_buddied_list);
+ zcache_zbud_buddied_count++;
+
+init_zh:
+ SET_SENTINEL(zh, ZBH);
+ zh->size = size;
+ zh->index = index;
+ zh->oid = *oid;
+ zh->pool_id = pool_id;
+ zh->client_id = client_id;
+ /* can wait to copy the data until the list locks are dropped */
+ spin_unlock(&zbud_budlists_spinlock);
+
+ to = zbud_data(zh, size);
+ memcpy(to, cdata, size);
+ spin_unlock(&zbpg->lock);
+ zbud_cumul_chunk_counts[nchunks]++;
+ atomic_inc(&zcache_zbud_curr_zpages);
+ zcache_zbud_cumul_zpages++;
+ zcache_zbud_curr_zbytes += size;
+ zcache_zbud_cumul_zbytes += size;
+out:
+ return zh;
+}
+
+static int zbud_decompress(struct page *page, struct zbud_hdr *zh)
+{
+ struct zbud_page *zbpg;
+ unsigned budnum = zbud_budnum(zh);
+ size_t out_len = PAGE_SIZE;
+ char *to_va, *from_va;
+ unsigned size;
+ int ret = 0;
+
+ zbpg = container_of(zh, struct zbud_page, buddy[budnum]);
+ spin_lock(&zbpg->lock);
+ if (list_empty(&zbpg->bud_list)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ ASSERT_SENTINEL(zh, ZBH);
+ BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size());
+ to_va = kmap_atomic(page, KM_USER0);
+ size = zh->size;
+ from_va = zbud_data(zh, size);
+ ret = lzo1x_decompress_safe(from_va, size, to_va, &out_len);
+ BUG_ON(ret != LZO_E_OK);
+ BUG_ON(out_len != PAGE_SIZE);
+ kunmap_atomic(to_va, KM_USER0);
+out:
+ spin_unlock(&zbpg->lock);
+ return ret;
+}
+
+static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id,
+ uint16_t poolid);
+static void zcache_put_pool(struct tmem_pool *pool);
+
+static void zbud_init(void)
+{
+ int i;
+
+ INIT_LIST_HEAD(&zbud_buddied_list);
+ zcache_zbud_buddied_count = 0;
+ for (i = 0; i < NCHUNKS; i++) {
+ INIT_LIST_HEAD(&zbud_unbuddied[i].list);
+ zbud_unbuddied[i].count = 0;
+ }
+}
+
+#ifdef CONFIG_SYSFS
+/*
+ * These sysfs routines show a nice distribution of how many zbpg's are
+ * currently (and have ever been placed) in each unbuddied list. It's fun
+ * to watch but can probably go away before final merge.
+ */
+static int zbud_show_unbuddied_list_counts(char *buf)
+{
+ int i;
+ char *p = buf;
+
+ for (i = 0; i < NCHUNKS; i++)
+ p += sprintf(p, "%u ", zbud_unbuddied[i].count);
+ return p - buf;
+}
+
+static int zbud_show_cumul_chunk_counts(char *buf)
+{
+ unsigned long i, chunks = 0, total_chunks = 0, sum_total_chunks = 0;
+ unsigned long total_chunks_lte_21 = 0, total_chunks_lte_32 = 0;
+ unsigned long total_chunks_lte_42 = 0;
+ char *p = buf;
+
+ for (i = 0; i < NCHUNKS; i++) {
+ p += sprintf(p, "%lu ", zbud_cumul_chunk_counts[i]);
+ chunks += zbud_cumul_chunk_counts[i];
+ total_chunks += zbud_cumul_chunk_counts[i];
+ sum_total_chunks += i * zbud_cumul_chunk_counts[i];
+ if (i == 21)
+ total_chunks_lte_21 = total_chunks;
+ if (i == 32)
+ total_chunks_lte_32 = total_chunks;
+ if (i == 42)
+ total_chunks_lte_42 = total_chunks;
+ }
+ p += sprintf(p, "<=21:%lu <=32:%lu <=42:%lu, mean:%lu\n",
+ total_chunks_lte_21, total_chunks_lte_32, total_chunks_lte_42,
+ chunks == 0 ? 0 : sum_total_chunks / chunks);
+ return p - buf;
+}
+#endif
+
+/*
+ * zcache core code starts here
+ */
+
+/* useful stats not collected by cleancache or frontswap */
+static unsigned long zcache_flush_total;
+static unsigned long zcache_flush_found;
+static unsigned long zcache_flobj_total;
+static unsigned long zcache_flobj_found;
+static unsigned long zcache_failed_eph_puts;
+
+/*
+ * Tmem operations assume the poolid implies the invoking client.
+ * Zcache only has one client (the kernel itself): LOCAL_CLIENT.
+ * RAMster has each client numbered by cluster node, and a KVM version
+ * of zcache would have one client per guest and each client might
+ * have a poolid==N.
+ */
+static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id, uint16_t poolid)
+{
+ struct tmem_pool *pool = NULL;
+ struct zcache_client *cli = NULL;
+
+ if (cli_id == LOCAL_CLIENT)
+ cli = &zcache_host;
+ else {
+ if (cli_id >= MAX_CLIENTS)
+ goto out;
+ cli = &zcache_clients[cli_id];
+ if (cli == NULL)
+ goto out;
+ atomic_inc(&cli->refcount);
+ }
+ if (poolid < MAX_POOLS_PER_CLIENT) {
+ pool = cli->tmem_pools[poolid];
+ if (pool != NULL)
+ atomic_inc(&pool->refcount);
+ }
+out:
+ return pool;
+}
+
+static void zcache_put_pool(struct tmem_pool *pool)
+{
+ struct zcache_client *cli = NULL;
+
+ if (pool == NULL)
+ BUG();
+ cli = pool->client;
+ atomic_dec(&pool->refcount);
+ atomic_dec(&cli->refcount);
+}
+
+int zcache_new_client(uint16_t cli_id)
+{
+ struct zcache_client *cli = NULL;
+ int ret = -1;
+
+ if (cli_id == LOCAL_CLIENT)
+ cli = &zcache_host;
+ else if ((unsigned int)cli_id < MAX_CLIENTS)
+ cli = &zcache_clients[cli_id];
+ if (cli == NULL)
+ goto out;
+ if (cli->allocated)
+ goto out;
+ cli->allocated = 1;
+ ret = 0;
+out:
+ return ret;
+}
+
+/* counters for debugging */
+static unsigned long zcache_failed_get_free_pages;
+static unsigned long zcache_failed_alloc;
+static unsigned long zcache_put_to_flush;
+static unsigned long zcache_aborted_preload;
+static unsigned long zcache_aborted_shrink;
+
+/*
+ * Ensure that memory allocation requests in zcache don't result
+ * in direct reclaim requests via the shrinker, which would cause
+ * an infinite loop. Maybe a GFP flag would be better?
+ */
+static DEFINE_SPINLOCK(zcache_direct_reclaim_lock);
+
+/*
+ * for now, used named slabs so can easily track usage; later can
+ * either just use kmalloc, or perhaps add a slab-like allocator
+ * to more carefully manage total memory utilization
+ */
+static struct kmem_cache *zcache_objnode_cache;
+static struct kmem_cache *zcache_obj_cache;
+static atomic_t zcache_curr_obj_count = ATOMIC_INIT(0);
+static unsigned long zcache_curr_obj_count_max;
+static atomic_t zcache_curr_objnode_count = ATOMIC_INIT(0);
+static unsigned long zcache_curr_objnode_count_max;
+
+/*
+ * to avoid memory allocation recursion (e.g. due to direct reclaim), we
+ * preload all necessary data structures so the hostops callbacks never
+ * actually do a malloc
+ */
+struct zcache_preload {
+ void *page;
+ struct tmem_obj *obj;
+ int nr;
+ struct tmem_objnode *objnodes[OBJNODE_TREE_MAX_PATH];
+};
+static DEFINE_PER_CPU(struct zcache_preload, zcache_preloads) = { 0, };
+
+static int zcache_do_preload(struct tmem_pool *pool)
+{
+ struct zcache_preload *kp;
+ struct tmem_objnode *objnode;
+ struct tmem_obj *obj;
+ void *page;
+ int ret = -ENOMEM;
+
+ if (unlikely(zcache_objnode_cache == NULL))
+ goto out;
+ if (unlikely(zcache_obj_cache == NULL))
+ goto out;
+ if (!spin_trylock(&zcache_direct_reclaim_lock)) {
+ zcache_aborted_preload++;
+ goto out;
+ }
+ preempt_disable();
+ kp = &__get_cpu_var(zcache_preloads);
+ while (kp->nr < ARRAY_SIZE(kp->objnodes)) {
+ preempt_enable_no_resched();
+ objnode = kmem_cache_alloc(zcache_objnode_cache,
+ ZCACHE_GFP_MASK);
+ if (unlikely(objnode == NULL)) {
+ zcache_failed_alloc++;
+ goto unlock_out;
+ }
+ preempt_disable();
+ kp = &__get_cpu_var(zcache_preloads);
+ if (kp->nr < ARRAY_SIZE(kp->objnodes))
+ kp->objnodes[kp->nr++] = objnode;
+ else
+ kmem_cache_free(zcache_objnode_cache, objnode);
+ }
+ preempt_enable_no_resched();
+ obj = kmem_cache_alloc(zcache_obj_cache, ZCACHE_GFP_MASK);
+ if (unlikely(obj == NULL)) {
+ zcache_failed_alloc++;
+ goto unlock_out;
+ }
+ page = qcache_alloc();
+ if (unlikely(page == NULL)) {
+ zcache_failed_get_free_pages++;
+ kmem_cache_free(zcache_obj_cache, obj);
+ goto unlock_out;
+ }
+ preempt_disable();
+ kp = &__get_cpu_var(zcache_preloads);
+ if (kp->obj == NULL)
+ kp->obj = obj;
+ else
+ kmem_cache_free(zcache_obj_cache, obj);
+ if (kp->page == NULL)
+ kp->page = page;
+ else
+ qcache_free(page);
+ ret = 0;
+unlock_out:
+ spin_unlock(&zcache_direct_reclaim_lock);
+out:
+ return ret;
+}
+
+static void *zcache_get_free_page(void)
+{
+ struct zcache_preload *kp;
+ void *page;
+
+ kp = &__get_cpu_var(zcache_preloads);
+ page = kp->page;
+ BUG_ON(page == NULL);
+ kp->page = NULL;
+ return page;
+}
+
+/*
+ * zcache implementation for tmem host ops
+ */
+
+static struct tmem_objnode *zcache_objnode_alloc(struct tmem_pool *pool)
+{
+ struct tmem_objnode *objnode = NULL;
+ unsigned long count;
+ struct zcache_preload *kp;
+
+ kp = &__get_cpu_var(zcache_preloads);
+ if (kp->nr <= 0)
+ goto out;
+ objnode = kp->objnodes[kp->nr - 1];
+ BUG_ON(objnode == NULL);
+ kp->objnodes[kp->nr - 1] = NULL;
+ kp->nr--;
+ count = atomic_inc_return(&zcache_curr_objnode_count);
+ if (count > zcache_curr_objnode_count_max)
+ zcache_curr_objnode_count_max = count;
+out:
+ return objnode;
+}
+
+static void zcache_objnode_free(struct tmem_objnode *objnode,
+ struct tmem_pool *pool)
+{
+ atomic_dec(&zcache_curr_objnode_count);
+ BUG_ON(atomic_read(&zcache_curr_objnode_count) < 0);
+ kmem_cache_free(zcache_objnode_cache, objnode);
+}
+
+static struct tmem_obj *zcache_obj_alloc(struct tmem_pool *pool)
+{
+ struct tmem_obj *obj = NULL;
+ unsigned long count;
+ struct zcache_preload *kp;
+
+ kp = &__get_cpu_var(zcache_preloads);
+ obj = kp->obj;
+ BUG_ON(obj == NULL);
+ kp->obj = NULL;
+ count = atomic_inc_return(&zcache_curr_obj_count);
+ if (count > zcache_curr_obj_count_max)
+ zcache_curr_obj_count_max = count;
+ return obj;
+}
+
+static void zcache_obj_free(struct tmem_obj *obj, struct tmem_pool *pool)
+{
+ atomic_dec(&zcache_curr_obj_count);
+ BUG_ON(atomic_read(&zcache_curr_obj_count) < 0);
+ kmem_cache_free(zcache_obj_cache, obj);
+}
+
+static void zcache_flush_all_obj(void)
+{
+ struct tmem_pool *pool;
+ int pool_id;
+ struct zcache_preload *kp;
+
+ kp = &__get_cpu_var(zcache_preloads);
+
+ for (pool_id = 0; pool_id < MAX_POOLS_PER_CLIENT; pool_id++) {
+ pool = zcache_get_pool_by_id(LOCAL_CLIENT, pool_id);
+ tmem_flush_pool(pool);
+ }
+ if (kp->page) {
+ qcache_free(kp->page);
+ kp->page = NULL;
+ }
+ if (zcache_qc_used)
+ pr_warn("pages used not 0 after qcache flush all, is %ld\n",
+ zcache_qc_used);
+}
+
+/*
+ * When zcache is disabled ("frozen"), pools can be created and destroyed,
+ * but all puts (and thus all other operations that require memory allocation)
+ * must fail. If zcache is unfrozen, accepts puts, then frozen again,
+ * data consistency requires all puts while frozen to be converted into
+ * flushes.
+ */
+static bool zcache_freeze;
+
+static void zcache_control(bool freeze)
+{
+ zcache_freeze = freeze;
+}
+
+static struct tmem_hostops zcache_hostops = {
+ .obj_alloc = zcache_obj_alloc,
+ .obj_free = zcache_obj_free,
+ .objnode_alloc = zcache_objnode_alloc,
+ .objnode_free = zcache_objnode_free,
+ .flush_all_obj = zcache_flush_all_obj,
+ .control = zcache_control,
+};
+
+/*
+ * zcache implementations for PAM page descriptor ops
+ */
+
+static atomic_t zcache_curr_eph_pampd_count = ATOMIC_INIT(0);
+static unsigned long zcache_curr_eph_pampd_count_max;
+
+/* forward reference */
+static int zcache_compress(struct page *from, void **out_va, size_t *out_len);
+
+static void *zcache_pampd_create(char *data, size_t size, bool raw, int eph,
+ struct tmem_pool *pool, struct tmem_oid *oid,
+ uint32_t index)
+{
+ void *pampd = NULL, *cdata;
+ size_t clen;
+ int ret;
+ unsigned long count;
+ struct page *page = (struct page *)(data);
+ struct zcache_client *cli = pool->client;
+ uint16_t client_id = get_client_id_from_client(cli);
+
+ ret = zcache_compress(page, &cdata, &clen);
+ if (ret == 0)
+ goto out;
+ if (clen == 0 || clen > zbud_max_buddy_size()) {
+ zcache_compress_poor++;
+ goto out;
+ }
+ pampd = (void *)zbud_create(client_id, pool->pool_id, oid,
+ index, page, cdata, clen);
+ if (pampd != NULL) {
+ count = atomic_inc_return(&zcache_curr_eph_pampd_count);
+ if (count > zcache_curr_eph_pampd_count_max)
+ zcache_curr_eph_pampd_count_max = count;
+ }
+out:
+ return pampd;
+}
+
+/*
+ * fill the pageframe corresponding to the struct page with the data
+ * from the passed pampd
+ */
+static int zcache_pampd_get_data(char *data, size_t *bufsize, bool raw,
+ void *pampd, struct tmem_pool *pool,
+ struct tmem_oid *oid, uint32_t index)
+{
+ BUG();
+ return 0;
+}
+
+/*
+ * fill the pageframe corresponding to the struct page with the data
+ * from the passed pampd
+ */
+static int zcache_pampd_get_data_and_free(char *data, size_t *bufsize, bool raw,
+ void *pampd, struct tmem_pool *pool,
+ struct tmem_oid *oid, uint32_t index)
+{
+ int ret = 0;
+
+ zbud_decompress((struct page *)(data), pampd);
+ zbud_free_and_delist((struct zbud_hdr *)pampd);
+ atomic_dec(&zcache_curr_eph_pampd_count);
+ return ret;
+}
+
+/*
+ * free the pampd and remove it from any zcache lists
+ * pampd must no longer be pointed to from any tmem data structures!
+ */
+static void zcache_pampd_free(void *pampd, struct tmem_pool *pool,
+ struct tmem_oid *oid, uint32_t index)
+{
+ zbud_free_and_delist((struct zbud_hdr *)pampd);
+ atomic_dec(&zcache_curr_eph_pampd_count);
+ BUG_ON(atomic_read(&zcache_curr_eph_pampd_count) < 0);
+}
+
+static void zcache_pampd_free_obj(struct tmem_pool *pool, struct tmem_obj *obj)
+{
+}
+
+static void zcache_pampd_new_obj(struct tmem_obj *obj)
+{
+}
+
+static int zcache_pampd_replace_in_obj(void *pampd, struct tmem_obj *obj)
+{
+ return -1;
+}
+
+static bool zcache_pampd_is_remote(void *pampd)
+{
+ return 0;
+}
+
+static struct tmem_pamops zcache_pamops = {
+ .create = zcache_pampd_create,
+ .get_data = zcache_pampd_get_data,
+ .get_data_and_free = zcache_pampd_get_data_and_free,
+ .free = zcache_pampd_free,
+ .free_obj = zcache_pampd_free_obj,
+ .new_obj = zcache_pampd_new_obj,
+ .replace_in_obj = zcache_pampd_replace_in_obj,
+ .is_remote = zcache_pampd_is_remote,
+};
+
+/*
+ * zcache compression/decompression and related per-cpu stuff
+ */
+
+#define LZO_WORKMEM_BYTES LZO1X_1_MEM_COMPRESS
+#define LZO_DSTMEM_PAGE_ORDER 1
+static DEFINE_PER_CPU(unsigned char *, zcache_workmem);
+static DEFINE_PER_CPU(unsigned char *, zcache_dstmem);
+
+static int zcache_compress(struct page *from, void **out_va, size_t *out_len)
+{
+ int ret = 0;
+ unsigned char *dmem = __get_cpu_var(zcache_dstmem);
+ unsigned char *wmem = __get_cpu_var(zcache_workmem);
+ char *from_va;
+
+ BUG_ON(!irqs_disabled());
+ if (unlikely(dmem == NULL || wmem == NULL))
+ goto out; /* no buffer, so can't compress */
+ from_va = kmap_atomic(from, KM_USER0);
+ mb();
+ ret = lzo1x_1_compress(from_va, PAGE_SIZE, dmem, out_len, wmem);
+ BUG_ON(ret != LZO_E_OK);
+ *out_va = dmem;
+ kunmap_atomic(from_va, KM_USER0);
+ ret = 1;
+out:
+ return ret;
+}
+
+#ifdef CONFIG_SYSFS
+#define ZCACHE_SYSFS_RO(_name) \
+ static ssize_t zcache_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+ { \
+ return sprintf(buf, "%lu\n", zcache_##_name); \
+ } \
+ static struct kobj_attribute zcache_##_name##_attr = { \
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
+ .show = zcache_##_name##_show, \
+ }
+
+#define ZCACHE_SYSFS_RO_ATOMIC(_name) \
+ static ssize_t zcache_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+ { \
+ return sprintf(buf, "%d\n", atomic_read(&zcache_##_name)); \
+ } \
+ static struct kobj_attribute zcache_##_name##_attr = { \
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
+ .show = zcache_##_name##_show, \
+ }
+
+#define ZCACHE_SYSFS_RO_CUSTOM(_name, _func) \
+ static ssize_t zcache_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+ { \
+ return _func(buf); \
+ } \
+ static struct kobj_attribute zcache_##_name##_attr = { \
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
+ .show = zcache_##_name##_show, \
+ }
+
+ZCACHE_SYSFS_RO(curr_obj_count_max);
+ZCACHE_SYSFS_RO(curr_objnode_count_max);
+ZCACHE_SYSFS_RO(flush_total);
+ZCACHE_SYSFS_RO(flush_found);
+ZCACHE_SYSFS_RO(flobj_total);
+ZCACHE_SYSFS_RO(flobj_found);
+ZCACHE_SYSFS_RO(failed_eph_puts);
+ZCACHE_SYSFS_RO(zbud_curr_zbytes);
+ZCACHE_SYSFS_RO(zbud_cumul_zpages);
+ZCACHE_SYSFS_RO(zbud_cumul_zbytes);
+ZCACHE_SYSFS_RO(zbud_buddied_count);
+ZCACHE_SYSFS_RO(failed_get_free_pages);
+ZCACHE_SYSFS_RO(failed_alloc);
+ZCACHE_SYSFS_RO(put_to_flush);
+ZCACHE_SYSFS_RO(aborted_preload);
+ZCACHE_SYSFS_RO(aborted_shrink);
+ZCACHE_SYSFS_RO(compress_poor);
+ZCACHE_SYSFS_RO(mean_compress_poor);
+ZCACHE_SYSFS_RO(qc_allocated);
+ZCACHE_SYSFS_RO(qc_freed);
+ZCACHE_SYSFS_RO(qc_used);
+ZCACHE_SYSFS_RO(qc_max_used);
+ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_raw_pages);
+ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_zpages);
+ZCACHE_SYSFS_RO_ATOMIC(curr_obj_count);
+ZCACHE_SYSFS_RO_ATOMIC(curr_objnode_count);
+ZCACHE_SYSFS_RO_CUSTOM(zbud_unbuddied_list_counts,
+ zbud_show_unbuddied_list_counts);
+ZCACHE_SYSFS_RO_CUSTOM(zbud_cumul_chunk_counts,
+ zbud_show_cumul_chunk_counts);
+
+static struct attribute *qcache_attrs[] = {
+ &zcache_curr_obj_count_attr.attr,
+ &zcache_curr_obj_count_max_attr.attr,
+ &zcache_curr_objnode_count_attr.attr,
+ &zcache_curr_objnode_count_max_attr.attr,
+ &zcache_flush_total_attr.attr,
+ &zcache_flobj_total_attr.attr,
+ &zcache_flush_found_attr.attr,
+ &zcache_flobj_found_attr.attr,
+ &zcache_failed_eph_puts_attr.attr,
+ &zcache_compress_poor_attr.attr,
+ &zcache_mean_compress_poor_attr.attr,
+ &zcache_zbud_curr_raw_pages_attr.attr,
+ &zcache_zbud_curr_zpages_attr.attr,
+ &zcache_zbud_curr_zbytes_attr.attr,
+ &zcache_zbud_cumul_zpages_attr.attr,
+ &zcache_zbud_cumul_zbytes_attr.attr,
+ &zcache_zbud_buddied_count_attr.attr,
+ &zcache_failed_get_free_pages_attr.attr,
+ &zcache_failed_alloc_attr.attr,
+ &zcache_put_to_flush_attr.attr,
+ &zcache_aborted_preload_attr.attr,
+ &zcache_aborted_shrink_attr.attr,
+ &zcache_zbud_unbuddied_list_counts_attr.attr,
+ &zcache_zbud_cumul_chunk_counts_attr.attr,
+ &zcache_qc_allocated_attr.attr,
+ &zcache_qc_freed_attr.attr,
+ &zcache_qc_used_attr.attr,
+ &zcache_qc_max_used_attr.attr,
+ NULL,
+};
+
+static struct attribute_group qcache_attr_group = {
+ .attrs = qcache_attrs,
+ .name = "qcache",
+};
+
+#endif /* CONFIG_SYSFS */
+
+/*
+ * zcache shims between cleancache ops and tmem
+ */
+
+static int zcache_put_page(int cli_id, int pool_id, struct tmem_oid *oidp,
+ uint32_t index, struct page *page)
+{
+ struct tmem_pool *pool;
+ int ret = -1;
+
+ BUG_ON(!irqs_disabled());
+ pool = zcache_get_pool_by_id(cli_id, pool_id);
+ if (unlikely(pool == NULL))
+ goto out;
+ if (!zcache_freeze && zcache_do_preload(pool) == 0) {
+ /* preload does preempt_disable on success */
+ ret = tmem_put(pool, oidp, index, (char *)(page),
+ PAGE_SIZE, 0, is_ephemeral(pool));
+ if (ret < 0) {
+ zcache_failed_eph_puts++;
+ }
+ zcache_put_pool(pool);
+ preempt_enable_no_resched();
+ } else {
+ zcache_put_to_flush++;
+ if (atomic_read(&pool->obj_count) > 0)
+ /* the put fails whether the flush succeeds or not */
+ (void)tmem_flush_page(pool, oidp, index);
+ zcache_put_pool(pool);
+ }
+out:
+ return ret;
+}
+
+static int zcache_get_page(int cli_id, int pool_id, struct tmem_oid *oidp,
+ uint32_t index, struct page *page)
+{
+ struct tmem_pool *pool;
+ int ret = -1;
+ unsigned long flags;
+ size_t size = PAGE_SIZE;
+
+ local_irq_save(flags);
+ pool = zcache_get_pool_by_id(cli_id, pool_id);
+ if (likely(pool != NULL)) {
+ if (atomic_read(&pool->obj_count) > 0)
+ ret = tmem_get(pool, oidp, index, (char *)(page),
+ &size, 0, is_ephemeral(pool));
+ zcache_put_pool(pool);
+ }
+ local_irq_restore(flags);
+ return ret;
+}
+
+static int zcache_flush_page(int cli_id, int pool_id,
+ struct tmem_oid *oidp, uint32_t index)
+{
+ struct tmem_pool *pool;
+ int ret = -1;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ zcache_flush_total++;
+ pool = zcache_get_pool_by_id(cli_id, pool_id);
+ if (likely(pool != NULL)) {
+ if (atomic_read(&pool->obj_count) > 0)
+ ret = tmem_flush_page(pool, oidp, index);
+ zcache_put_pool(pool);
+ }
+ if (ret >= 0)
+ zcache_flush_found++;
+ local_irq_restore(flags);
+ return ret;
+}
+
+static int zcache_flush_object(int cli_id, int pool_id,
+ struct tmem_oid *oidp)
+{
+ struct tmem_pool *pool;
+ int ret = -1;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ zcache_flobj_total++;
+ pool = zcache_get_pool_by_id(cli_id, pool_id);
+ if (likely(pool != NULL)) {
+ if (atomic_read(&pool->obj_count) > 0)
+ ret = tmem_flush_object(pool, oidp);
+ zcache_put_pool(pool);
+ }
+ if (ret >= 0)
+ zcache_flobj_found++;
+ local_irq_restore(flags);
+ return ret;
+}
+
+static int zcache_destroy_pool(int cli_id, int pool_id)
+{
+ struct tmem_pool *pool = NULL;
+ struct zcache_client *cli = NULL;
+ int ret = -1;
+
+ if (pool_id < 0)
+ goto out;
+ if (cli_id == LOCAL_CLIENT)
+ cli = &zcache_host;
+ else if ((unsigned int)cli_id < MAX_CLIENTS)
+ cli = &zcache_clients[cli_id];
+ if (cli == NULL)
+ goto out;
+ atomic_inc(&cli->refcount);
+ pool = cli->tmem_pools[pool_id];
+ if (pool == NULL)
+ goto out;
+ cli->tmem_pools[pool_id] = NULL;
+ /* wait for pool activity on other cpus to quiesce */
+ while (atomic_read(&pool->refcount) != 0)
+ ;
+ atomic_dec(&cli->refcount);
+ local_bh_disable();
+ ret = tmem_destroy_pool(pool);
+ local_bh_enable();
+ kfree(pool);
+ pr_info("qcache: destroyed pool id=%d, cli_id=%d\n",
+ pool_id, cli_id);
+out:
+ return ret;
+}
+
+static int zcache_new_pool(uint16_t cli_id, uint32_t flags)
+{
+ int poolid = -1;
+ struct tmem_pool *pool;
+ struct zcache_client *cli = NULL;
+
+ if (cli_id == LOCAL_CLIENT)
+ cli = &zcache_host;
+ else if ((unsigned int)cli_id < MAX_CLIENTS)
+ cli = &zcache_clients[cli_id];
+ if (cli == NULL)
+ goto out;
+ atomic_inc(&cli->refcount);
+ pool = kmalloc(sizeof(struct tmem_pool), GFP_KERNEL);
+ if (pool == NULL) {
+ pr_info("qcache: pool creation failed: out of memory\n");
+ goto out;
+ }
+
+ for (poolid = 0; poolid < MAX_POOLS_PER_CLIENT; poolid++)
+ if (cli->tmem_pools[poolid] == NULL)
+ break;
+ if (poolid >= MAX_POOLS_PER_CLIENT) {
+ pr_info("qcache: pool creation failed: max exceeded\n");
+ kfree(pool);
+ poolid = -1;
+ goto out;
+ }
+ atomic_set(&pool->refcount, 0);
+ pool->client = cli;
+ pool->pool_id = poolid;
+ tmem_new_pool(pool, flags);
+ cli->tmem_pools[poolid] = pool;
+ pr_info("qcache: created %s tmem pool, id=%d, client=%d\n",
+ flags & TMEM_POOL_PERSIST ? "persistent" : "ephemeral",
+ poolid, cli_id);
+out:
+ if (cli != NULL)
+ atomic_dec(&cli->refcount);
+ return poolid;
+}
+
+/**********
+ * Two kernel functionalities currently can be layered on top of tmem.
+ * These are "cleancache" which is used as a second-chance cache for clean
+ * page cache pages; and "frontswap" which is used for swap pages
+ * to avoid writes to disk. A generic "shim" is provided here for each
+ * to translate in-kernel semantics to zcache semantics.
+ */
+
+static void zcache_cleancache_put_page(int pool_id,
+ struct cleancache_filekey key,
+ pgoff_t index, struct page *page)
+{
+ u32 ind = (u32) index;
+ struct tmem_oid oid = *(struct tmem_oid *)&key;
+
+ if (likely(ind == index))
+ (void)zcache_put_page(LOCAL_CLIENT, pool_id, &oid, index, page);
+}
+
+static int zcache_cleancache_get_page(int pool_id,
+ struct cleancache_filekey key,
+ pgoff_t index, struct page *page)
+{
+ u32 ind = (u32) index;
+ struct tmem_oid oid = *(struct tmem_oid *)&key;
+ int ret = -1;
+
+ if (likely(ind == index))
+ ret = zcache_get_page(LOCAL_CLIENT, pool_id, &oid, index, page);
+ return ret;
+}
+
+static void zcache_cleancache_flush_page(int pool_id,
+ struct cleancache_filekey key,
+ pgoff_t index)
+{
+ u32 ind = (u32) index;
+ struct tmem_oid oid = *(struct tmem_oid *)&key;
+
+ if (likely(ind == index))
+ (void)zcache_flush_page(LOCAL_CLIENT, pool_id, &oid, ind);
+}
+
+static void zcache_cleancache_flush_inode(int pool_id,
+ struct cleancache_filekey key)
+{
+ struct tmem_oid oid = *(struct tmem_oid *)&key;
+
+ (void)zcache_flush_object(LOCAL_CLIENT, pool_id, &oid);
+}
+
+static void zcache_cleancache_flush_fs(int pool_id)
+{
+ if (pool_id >= 0)
+ (void)zcache_destroy_pool(LOCAL_CLIENT, pool_id);
+}
+
+static int zcache_cleancache_init_fs(size_t pagesize)
+{
+ BUG_ON(sizeof(struct cleancache_filekey) !=
+ sizeof(struct tmem_oid));
+ BUG_ON(pagesize != PAGE_SIZE);
+ return zcache_new_pool(LOCAL_CLIENT, 0);
+}
+
+static int zcache_cleancache_init_shared_fs(char *uuid, size_t pagesize)
+{
+ /* shared pools are unsupported and map to private */
+ BUG_ON(sizeof(struct cleancache_filekey) !=
+ sizeof(struct tmem_oid));
+ BUG_ON(pagesize != PAGE_SIZE);
+ return zcache_new_pool(LOCAL_CLIENT, 0);
+}
+
+static struct cleancache_ops zcache_cleancache_ops = {
+ .put_page = zcache_cleancache_put_page,
+ .get_page = zcache_cleancache_get_page,
+ .flush_page = zcache_cleancache_flush_page,
+ .flush_inode = zcache_cleancache_flush_inode,
+ .flush_fs = zcache_cleancache_flush_fs,
+ .init_shared_fs = zcache_cleancache_init_shared_fs,
+ .init_fs = zcache_cleancache_init_fs
+};
+
+struct cleancache_ops zcache_cleancache_register_ops(void)
+{
+ struct cleancache_ops old_ops =
+ cleancache_register_ops(&zcache_cleancache_ops);
+
+ return old_ops;
+}
+
+static int __init qcache_init(void)
+{
+ int ret = 0;
+ struct qcache_info *qc = &qcache_info;
+ struct fmem_data *fdp;
+ int bitmap_size;
+ unsigned int cpu;
+ struct cleancache_ops old_ops;
+
+#ifdef CONFIG_SYSFS
+ ret = sysfs_create_group(mm_kobj, &qcache_attr_group);
+ if (ret) {
+ pr_err("qcache: can't create sysfs\n");
+ goto out;
+ }
+#endif /* CONFIG_SYSFS */
+
+ fdp = fmem_get_info();
+ qc->addr = fdp->virt;
+ qc->pages = fdp->size >> PAGE_SHIFT;
+ if (!qc->pages)
+ goto out;
+
+ tmem_register_hostops(&zcache_hostops);
+ tmem_register_pamops(&zcache_pamops);
+ for_each_online_cpu(cpu) {
+ per_cpu(zcache_dstmem, cpu) = (void *)__get_free_pages(
+ GFP_KERNEL | __GFP_REPEAT,
+ LZO_DSTMEM_PAGE_ORDER),
+ per_cpu(zcache_workmem, cpu) =
+ kzalloc(LZO1X_MEM_COMPRESS,
+ GFP_KERNEL | __GFP_REPEAT);
+ }
+ zcache_objnode_cache = kmem_cache_create("zcache_objnode",
+ sizeof(struct tmem_objnode), 0, 0, NULL);
+ zcache_obj_cache = kmem_cache_create("zcache_obj",
+ sizeof(struct tmem_obj), 0, 0, NULL);
+ ret = zcache_new_client(LOCAL_CLIENT);
+ if (ret) {
+ pr_err("qcache: can't create client\n");
+ goto out;
+ }
+
+ zbud_init();
+ old_ops = zcache_cleancache_register_ops();
+ pr_info("qcache: cleancache enabled using kernel "
+ "transcendent memory and compression buddies\n");
+ if (old_ops.init_fs != NULL)
+ pr_warning("qcache: cleancache_ops overridden");
+
+
+ bitmap_size = BITS_TO_LONGS(qc->pages) * sizeof(long);
+
+ qc->bitmap = kzalloc(bitmap_size, GFP_KERNEL);
+ if (!qc->bitmap) {
+ pr_info("can't allocate qcache bitmap!\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ spin_lock_init(&qc->lock);
+
+ fmem_set_state(FMEM_T_STATE);
+
+out:
+ return ret;
+}
+
+module_init(qcache_init)
diff --git a/drivers/staging/qcache/tmem.c b/drivers/staging/qcache/tmem.c
new file mode 100644
index 0000000..8c9049c
--- /dev/null
+++ b/drivers/staging/qcache/tmem.c
@@ -0,0 +1,835 @@
+/*
+ * In-kernel transcendent memory (generic implementation)
+ *
+ * Copyright (c) 2009-2011, Dan Magenheimer, Oracle Corp.
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * The primary purpose of Transcedent Memory ("tmem") is to map object-oriented
+ * "handles" (triples containing a pool id, and object id, and an index), to
+ * pages in a page-accessible memory (PAM). Tmem references the PAM pages via
+ * an abstract "pampd" (PAM page-descriptor), which can be operated on by a
+ * set of functions (pamops). Each pampd contains some representation of
+ * PAGE_SIZE bytes worth of data. Tmem must support potentially millions of
+ * pages and must be able to insert, find, and delete these pages at a
+ * potential frequency of thousands per second concurrently across many CPUs,
+ * (and, if used with KVM, across many vcpus across many guests).
+ * Tmem is tracked with a hierarchy of data structures, organized by
+ * the elements in a handle-tuple: pool_id, object_id, and page index.
+ * One or more "clients" (e.g. guests) each provide one or more tmem_pools.
+ * Each pool, contains a hash table of rb_trees of tmem_objs. Each
+ * tmem_obj contains a radix-tree-like tree of pointers, with intermediate
+ * nodes called tmem_objnodes. Each leaf pointer in this tree points to
+ * a pampd, which is accessible only through a small set of callbacks
+ * registered by the PAM implementation (see tmem_register_pamops). Tmem
+ * does all memory allocation via a set of callbacks registered by the tmem
+ * host implementation (e.g. see tmem_register_hostops).
+ */
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/atomic.h>
+
+#include "tmem.h"
+
+/* data structure sentinels used for debugging... see tmem.h */
+#define POOL_SENTINEL 0x87658765
+#define OBJ_SENTINEL 0x12345678
+#define OBJNODE_SENTINEL 0xfedcba09
+
+static bool tmem_enabled;
+
+static void lock_tmem_state(void)
+{
+ lock_fmem_state();
+}
+
+static void unlock_tmem_state(void)
+{
+ unlock_fmem_state();
+}
+
+/*
+ * A tmem host implementation must use this function to register callbacks
+ * for memory allocation.
+ */
+static struct tmem_hostops tmem_hostops;
+
+static void tmem_objnode_tree_init(void);
+
+void tmem_register_hostops(struct tmem_hostops *m)
+{
+ tmem_objnode_tree_init();
+ tmem_hostops = *m;
+}
+
+/*
+ * A tmem host implementation must use this function to register
+ * callbacks for a page-accessible memory (PAM) implementation
+ */
+static struct tmem_pamops tmem_pamops;
+
+void tmem_register_pamops(struct tmem_pamops *m)
+{
+ tmem_pamops = *m;
+}
+
+/*
+ * Oid's are potentially very sparse and tmem_objs may have an indeterminately
+ * short life, being added and deleted at a relatively high frequency.
+ * So an rb_tree is an ideal data structure to manage tmem_objs. But because
+ * of the potentially huge number of tmem_objs, each pool manages a hashtable
+ * of rb_trees to reduce search, insert, delete, and rebalancing time.
+ * Each hashbucket also has a lock to manage concurrent access.
+ *
+ * The following routines manage tmem_objs. When any tmem_obj is accessed,
+ * the hashbucket lock must be held.
+ */
+
+/* searches for object==oid in pool, returns locked object if found */
+static struct tmem_obj *tmem_obj_find(struct tmem_hashbucket *hb,
+ struct tmem_oid *oidp)
+{
+ struct rb_node *rbnode;
+ struct tmem_obj *obj;
+
+ rbnode = hb->obj_rb_root.rb_node;
+ while (rbnode) {
+ BUG_ON(RB_EMPTY_NODE(rbnode));
+ obj = rb_entry(rbnode, struct tmem_obj, rb_tree_node);
+ switch (tmem_oid_compare(oidp, &obj->oid)) {
+ case 0: /* equal */
+ goto out;
+ case -1:
+ rbnode = rbnode->rb_left;
+ break;
+ case 1:
+ rbnode = rbnode->rb_right;
+ break;
+ }
+ }
+ obj = NULL;
+out:
+ return obj;
+}
+
+static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *);
+
+/* free an object that has no more pampds in it */
+static void tmem_obj_free(struct tmem_obj *obj, struct tmem_hashbucket *hb)
+{
+ struct tmem_pool *pool;
+
+ BUG_ON(obj == NULL);
+ ASSERT_SENTINEL(obj, OBJ);
+ BUG_ON(obj->pampd_count > 0);
+ pool = obj->pool;
+ BUG_ON(pool == NULL);
+ if (obj->objnode_tree_root != NULL) /* may be "stump" with no leaves */
+ tmem_pampd_destroy_all_in_obj(obj);
+ BUG_ON(obj->objnode_tree_root != NULL);
+ BUG_ON((long)obj->objnode_count != 0);
+ atomic_dec(&pool->obj_count);
+ BUG_ON(atomic_read(&pool->obj_count) < 0);
+ INVERT_SENTINEL(obj, OBJ);
+ obj->pool = NULL;
+ tmem_oid_set_invalid(&obj->oid);
+ rb_erase(&obj->rb_tree_node, &hb->obj_rb_root);
+}
+
+/*
+ * initialize, and insert an tmem_object_root (called only if find failed)
+ */
+static void tmem_obj_init(struct tmem_obj *obj, struct tmem_hashbucket *hb,
+ struct tmem_pool *pool,
+ struct tmem_oid *oidp)
+{
+ struct rb_root *root = &hb->obj_rb_root;
+ struct rb_node **new = &(root->rb_node), *parent = NULL;
+ struct tmem_obj *this;
+
+ BUG_ON(pool == NULL);
+ atomic_inc(&pool->obj_count);
+ obj->objnode_tree_height = 0;
+ obj->objnode_tree_root = NULL;
+ obj->pool = pool;
+ obj->oid = *oidp;
+ obj->objnode_count = 0;
+ obj->pampd_count = 0;
+ (*tmem_pamops.new_obj)(obj);
+ SET_SENTINEL(obj, OBJ);
+ while (*new) {
+ BUG_ON(RB_EMPTY_NODE(*new));
+ this = rb_entry(*new, struct tmem_obj, rb_tree_node);
+ parent = *new;
+ switch (tmem_oid_compare(oidp, &this->oid)) {
+ case 0:
+ BUG(); /* already present; should never happen! */
+ break;
+ case -1:
+ new = &(*new)->rb_left;
+ break;
+ case 1:
+ new = &(*new)->rb_right;
+ break;
+ }
+ }
+ rb_link_node(&obj->rb_tree_node, parent, new);
+ rb_insert_color(&obj->rb_tree_node, root);
+}
+
+/*
+ * Tmem is managed as a set of tmem_pools with certain attributes, such as
+ * "ephemeral" vs "persistent". These attributes apply to all tmem_objs
+ * and all pampds that belong to a tmem_pool. A tmem_pool is created
+ * or deleted relatively rarely (for example, when a filesystem is
+ * mounted or unmounted.
+ */
+
+/* flush all data from a pool and, optionally, free it */
+static void tmem_pool_flush(struct tmem_pool *pool, bool destroy)
+{
+ struct rb_node *rbnode;
+ struct tmem_obj *obj;
+ struct tmem_hashbucket *hb = &pool->hashbucket[0];
+ int i;
+
+ BUG_ON(pool == NULL);
+ for (i = 0; i < TMEM_HASH_BUCKETS; i++, hb++) {
+ spin_lock(&hb->lock);
+ rbnode = rb_first(&hb->obj_rb_root);
+ while (rbnode != NULL) {
+ obj = rb_entry(rbnode, struct tmem_obj, rb_tree_node);
+ rbnode = rb_next(rbnode);
+ tmem_pampd_destroy_all_in_obj(obj);
+ tmem_obj_free(obj, hb);
+ (*tmem_hostops.obj_free)(obj, pool);
+ }
+ spin_unlock(&hb->lock);
+ }
+ if (destroy)
+ list_del(&pool->pool_list);
+}
+
+/*
+ * A tmem_obj contains a radix-tree-like tree in which the intermediate
+ * nodes are called tmem_objnodes. (The kernel lib/radix-tree.c implementation
+ * is very specialized and tuned for specific uses and is not particularly
+ * suited for use from this code, though some code from the core algorithms has
+ * been reused, thus the copyright notices below). Each tmem_objnode contains
+ * a set of pointers which point to either a set of intermediate tmem_objnodes
+ * or a set of of pampds.
+ *
+ * Portions Copyright (C) 2001 Momchil Velikov
+ * Portions Copyright (C) 2001 Christoph Hellwig
+ * Portions Copyright (C) 2005 SGI, Christoph Lameter <clameter@sgi.com>
+ */
+
+struct tmem_objnode_tree_path {
+ struct tmem_objnode *objnode;
+ int offset;
+};
+
+/* objnode height_to_maxindex translation */
+static unsigned long tmem_objnode_tree_h2max[OBJNODE_TREE_MAX_PATH + 1];
+
+static void tmem_objnode_tree_init(void)
+{
+ unsigned int ht, tmp;
+
+ for (ht = 0; ht < ARRAY_SIZE(tmem_objnode_tree_h2max); ht++) {
+ tmp = ht * OBJNODE_TREE_MAP_SHIFT;
+ if (tmp >= OBJNODE_TREE_INDEX_BITS)
+ tmem_objnode_tree_h2max[ht] = ~0UL;
+ else
+ tmem_objnode_tree_h2max[ht] =
+ (~0UL >> (OBJNODE_TREE_INDEX_BITS - tmp - 1)) >> 1;
+ }
+}
+
+static struct tmem_objnode *tmem_objnode_alloc(struct tmem_obj *obj)
+{
+ struct tmem_objnode *objnode;
+
+ ASSERT_SENTINEL(obj, OBJ);
+ BUG_ON(obj->pool == NULL);
+ ASSERT_SENTINEL(obj->pool, POOL);
+ objnode = (*tmem_hostops.objnode_alloc)(obj->pool);
+ if (unlikely(objnode == NULL))
+ goto out;
+ objnode->obj = obj;
+ SET_SENTINEL(objnode, OBJNODE);
+ memset(&objnode->slots, 0, sizeof(objnode->slots));
+ objnode->slots_in_use = 0;
+ obj->objnode_count++;
+out:
+ return objnode;
+}
+
+static void tmem_objnode_free(struct tmem_objnode *objnode)
+{
+ struct tmem_pool *pool;
+ int i;
+
+ BUG_ON(objnode == NULL);
+ for (i = 0; i < OBJNODE_TREE_MAP_SIZE; i++)
+ BUG_ON(objnode->slots[i] != NULL);
+ ASSERT_SENTINEL(objnode, OBJNODE);
+ INVERT_SENTINEL(objnode, OBJNODE);
+ BUG_ON(objnode->obj == NULL);
+ ASSERT_SENTINEL(objnode->obj, OBJ);
+ pool = objnode->obj->pool;
+ BUG_ON(pool == NULL);
+ ASSERT_SENTINEL(pool, POOL);
+ objnode->obj->objnode_count--;
+ objnode->obj = NULL;
+ (*tmem_hostops.objnode_free)(objnode, pool);
+}
+
+/*
+ * lookup index in object and return associated pampd (or NULL if not found)
+ */
+static void **__tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index)
+{
+ unsigned int height, shift;
+ struct tmem_objnode **slot = NULL;
+
+ BUG_ON(obj == NULL);
+ ASSERT_SENTINEL(obj, OBJ);
+ BUG_ON(obj->pool == NULL);
+ ASSERT_SENTINEL(obj->pool, POOL);
+
+ height = obj->objnode_tree_height;
+ if (index > tmem_objnode_tree_h2max[obj->objnode_tree_height])
+ goto out;
+ if (height == 0 && obj->objnode_tree_root) {
+ slot = &obj->objnode_tree_root;
+ goto out;
+ }
+ shift = (height-1) * OBJNODE_TREE_MAP_SHIFT;
+ slot = &obj->objnode_tree_root;
+ while (height > 0) {
+ if (*slot == NULL)
+ goto out;
+ slot = (struct tmem_objnode **)
+ ((*slot)->slots +
+ ((index >> shift) & OBJNODE_TREE_MAP_MASK));
+ shift -= OBJNODE_TREE_MAP_SHIFT;
+ height--;
+ }
+out:
+ return slot != NULL ? (void **)slot : NULL;
+}
+
+static void *tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index)
+{
+ struct tmem_objnode **slot;
+
+ slot = (struct tmem_objnode **)__tmem_pampd_lookup_in_obj(obj, index);
+ return slot != NULL ? *slot : NULL;
+}
+
+static void *tmem_pampd_replace_in_obj(struct tmem_obj *obj, uint32_t index,
+ void *new_pampd)
+{
+ struct tmem_objnode **slot;
+ void *ret = NULL;
+
+ slot = (struct tmem_objnode **)__tmem_pampd_lookup_in_obj(obj, index);
+ if ((slot != NULL) && (*slot != NULL)) {
+ void *old_pampd = *(void **)slot;
+ *(void **)slot = new_pampd;
+ (*tmem_pamops.free)(old_pampd, obj->pool, NULL, 0);
+ ret = new_pampd;
+ }
+ return ret;
+}
+
+static int tmem_pampd_add_to_obj(struct tmem_obj *obj, uint32_t index,
+ void *pampd)
+{
+ int ret = 0;
+ struct tmem_objnode *objnode = NULL, *newnode, *slot;
+ unsigned int height, shift;
+ int offset = 0;
+
+ /* if necessary, extend the tree to be higher */
+ if (index > tmem_objnode_tree_h2max[obj->objnode_tree_height]) {
+ height = obj->objnode_tree_height + 1;
+ if (index > tmem_objnode_tree_h2max[height])
+ while (index > tmem_objnode_tree_h2max[height])
+ height++;
+ if (obj->objnode_tree_root == NULL) {
+ obj->objnode_tree_height = height;
+ goto insert;
+ }
+ do {
+ newnode = tmem_objnode_alloc(obj);
+ if (!newnode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ newnode->slots[0] = obj->objnode_tree_root;
+ newnode->slots_in_use = 1;
+ obj->objnode_tree_root = newnode;
+ obj->objnode_tree_height++;
+ } while (height > obj->objnode_tree_height);
+ }
+insert:
+ slot = obj->objnode_tree_root;
+ height = obj->objnode_tree_height;
+ shift = (height-1) * OBJNODE_TREE_MAP_SHIFT;
+ while (height > 0) {
+ if (slot == NULL) {
+ /* add a child objnode. */
+ slot = tmem_objnode_alloc(obj);
+ if (!slot) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ if (objnode) {
+
+ objnode->slots[offset] = slot;
+ objnode->slots_in_use++;
+ } else
+ obj->objnode_tree_root = slot;
+ }
+ /* go down a level */
+ offset = (index >> shift) & OBJNODE_TREE_MAP_MASK;
+ objnode = slot;
+ slot = objnode->slots[offset];
+ shift -= OBJNODE_TREE_MAP_SHIFT;
+ height--;
+ }
+ BUG_ON(slot != NULL);
+ if (objnode) {
+ objnode->slots_in_use++;
+ objnode->slots[offset] = pampd;
+ } else
+ obj->objnode_tree_root = pampd;
+ obj->pampd_count++;
+out:
+ return ret;
+}
+
+static void *tmem_pampd_delete_from_obj(struct tmem_obj *obj, uint32_t index)
+{
+ struct tmem_objnode_tree_path path[OBJNODE_TREE_MAX_PATH + 1];
+ struct tmem_objnode_tree_path *pathp = path;
+ struct tmem_objnode *slot = NULL;
+ unsigned int height, shift;
+ int offset;
+
+ BUG_ON(obj == NULL);
+ ASSERT_SENTINEL(obj, OBJ);
+ BUG_ON(obj->pool == NULL);
+ ASSERT_SENTINEL(obj->pool, POOL);
+ height = obj->objnode_tree_height;
+ if (index > tmem_objnode_tree_h2max[height])
+ goto out;
+ slot = obj->objnode_tree_root;
+ if (height == 0 && obj->objnode_tree_root) {
+ obj->objnode_tree_root = NULL;
+ goto out;
+ }
+ shift = (height - 1) * OBJNODE_TREE_MAP_SHIFT;
+ pathp->objnode = NULL;
+ do {
+ if (slot == NULL)
+ goto out;
+ pathp++;
+ offset = (index >> shift) & OBJNODE_TREE_MAP_MASK;
+ pathp->offset = offset;
+ pathp->objnode = slot;
+ slot = slot->slots[offset];
+ shift -= OBJNODE_TREE_MAP_SHIFT;
+ height--;
+ } while (height > 0);
+ if (slot == NULL)
+ goto out;
+ while (pathp->objnode) {
+ pathp->objnode->slots[pathp->offset] = NULL;
+ pathp->objnode->slots_in_use--;
+ if (pathp->objnode->slots_in_use) {
+ if (pathp->objnode == obj->objnode_tree_root) {
+ while (obj->objnode_tree_height > 0 &&
+ obj->objnode_tree_root->slots_in_use == 1 &&
+ obj->objnode_tree_root->slots[0]) {
+ struct tmem_objnode *to_free =
+ obj->objnode_tree_root;
+
+ obj->objnode_tree_root =
+ to_free->slots[0];
+ obj->objnode_tree_height--;
+ to_free->slots[0] = NULL;
+ to_free->slots_in_use = 0;
+ tmem_objnode_free(to_free);
+ }
+ }
+ goto out;
+ }
+ tmem_objnode_free(pathp->objnode); /* 0 slots used, free it */
+ pathp--;
+ }
+ obj->objnode_tree_height = 0;
+ obj->objnode_tree_root = NULL;
+
+out:
+ if (slot != NULL)
+ obj->pampd_count--;
+ BUG_ON(obj->pampd_count < 0);
+ return slot;
+}
+
+/* recursively walk the objnode_tree destroying pampds and objnodes */
+static void tmem_objnode_node_destroy(struct tmem_obj *obj,
+ struct tmem_objnode *objnode,
+ unsigned int ht)
+{
+ int i;
+
+ if (ht == 0)
+ return;
+ for (i = 0; i < OBJNODE_TREE_MAP_SIZE; i++) {
+ if (objnode->slots[i]) {
+ if (ht == 1) {
+ obj->pampd_count--;
+ (*tmem_pamops.free)(objnode->slots[i],
+ obj->pool, NULL, 0);
+ objnode->slots[i] = NULL;
+ continue;
+ }
+ tmem_objnode_node_destroy(obj, objnode->slots[i], ht-1);
+ tmem_objnode_free(objnode->slots[i]);
+ objnode->slots[i] = NULL;
+ }
+ }
+}
+
+static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *obj)
+{
+ if (obj->objnode_tree_root == NULL)
+ return;
+ if (obj->objnode_tree_height == 0) {
+ obj->pampd_count--;
+ (*tmem_pamops.free)(obj->objnode_tree_root, obj->pool, NULL, 0);
+ } else {
+ tmem_objnode_node_destroy(obj, obj->objnode_tree_root,
+ obj->objnode_tree_height);
+ tmem_objnode_free(obj->objnode_tree_root);
+ obj->objnode_tree_height = 0;
+ }
+ obj->objnode_tree_root = NULL;
+ (*tmem_pamops.free_obj)(obj->pool, obj);
+}
+
+/*
+ * Tmem is operated on by a set of well-defined actions:
+ * "put", "get", "flush", "flush_object", "new pool" and "destroy pool".
+ * (The tmem ABI allows for subpages and exchanges but these operations
+ * are not included in this implementation.)
+ *
+ * These "tmem core" operations are implemented in the following functions.
+ */
+
+/*
+ * "Put" a page, e.g. copy a page from the kernel into newly allocated
+ * PAM space (if such space is available). Tmem_put is complicated by
+ * a corner case: What if a page with matching handle already exists in
+ * tmem? To guarantee coherency, one of two actions is necessary: Either
+ * the data for the page must be overwritten, or the page must be
+ * "flushed" so that the data is not accessible to a subsequent "get".
+ * Since these "duplicate puts" are relatively rare, this implementation
+ * always flushes for simplicity.
+ */
+int tmem_put(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index,
+ char *data, size_t size, bool raw, bool ephemeral)
+{
+ struct tmem_obj *obj = NULL, *objfound = NULL, *objnew = NULL;
+ void *pampd = NULL, *pampd_del = NULL;
+ int ret = -ENOMEM;
+ struct tmem_hashbucket *hb;
+
+ lock_tmem_state();
+ if (!tmem_enabled)
+ goto disabled;
+ hb = &pool->hashbucket[tmem_oid_hash(oidp)];
+ spin_lock(&hb->lock);
+ obj = objfound = tmem_obj_find(hb, oidp);
+ if (obj != NULL) {
+ pampd = tmem_pampd_lookup_in_obj(objfound, index);
+ if (pampd != NULL) {
+ /* if found, is a dup put, flush the old one */
+ pampd_del = tmem_pampd_delete_from_obj(obj, index);
+ BUG_ON(pampd_del != pampd);
+ (*tmem_pamops.free)(pampd, pool, oidp, index);
+ if (obj->pampd_count == 0) {
+ objnew = obj;
+ objfound = NULL;
+ }
+ pampd = NULL;
+ }
+ } else {
+ obj = objnew = (*tmem_hostops.obj_alloc)(pool);
+ if (unlikely(obj == NULL)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ tmem_obj_init(obj, hb, pool, oidp);
+ }
+ BUG_ON(obj == NULL);
+ BUG_ON(((objnew != obj) && (objfound != obj)) || (objnew == objfound));
+ pampd = (*tmem_pamops.create)(data, size, raw, ephemeral,
+ obj->pool, &obj->oid, index);
+ if (unlikely(pampd == NULL))
+ goto free;
+ ret = tmem_pampd_add_to_obj(obj, index, pampd);
+ if (unlikely(ret == -ENOMEM))
+ /* may have partially built objnode tree ("stump") */
+ goto delete_and_free;
+ goto out;
+
+delete_and_free:
+ (void)tmem_pampd_delete_from_obj(obj, index);
+free:
+ if (pampd)
+ (*tmem_pamops.free)(pampd, pool, NULL, 0);
+ if (objnew) {
+ tmem_obj_free(objnew, hb);
+ (*tmem_hostops.obj_free)(objnew, pool);
+ }
+out:
+ spin_unlock(&hb->lock);
+disabled:
+ unlock_tmem_state();
+ return ret;
+}
+
+/*
+ * "Get" a page, e.g. if one can be found, copy the tmem page with the
+ * matching handle from PAM space to the kernel. By tmem definition,
+ * when a "get" is successful on an ephemeral page, the page is "flushed",
+ * and when a "get" is successful on a persistent page, the page is retained
+ * in tmem. Note that to preserve
+ * coherency, "get" can never be skipped if tmem contains the data.
+ * That is, if a get is done with a certain handle and fails, any
+ * subsequent "get" must also fail (unless of course there is a
+ * "put" done with the same handle).
+
+ */
+int tmem_get(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index,
+ char *data, size_t *size, bool raw, int get_and_free)
+{
+ struct tmem_obj *obj;
+ void *pampd;
+ bool ephemeral = is_ephemeral(pool);
+ int ret = -1;
+ struct tmem_hashbucket *hb;
+ bool free = (get_and_free == 1) || ((get_and_free == 0) && ephemeral);
+ bool lock_held = false;
+
+ lock_tmem_state();
+ if (!tmem_enabled)
+ goto disabled;
+ hb = &pool->hashbucket[tmem_oid_hash(oidp)];
+ spin_lock(&hb->lock);
+ lock_held = true;
+ obj = tmem_obj_find(hb, oidp);
+ if (obj == NULL)
+ goto out;
+ if (free)
+ pampd = tmem_pampd_delete_from_obj(obj, index);
+ else
+ pampd = tmem_pampd_lookup_in_obj(obj, index);
+ if (pampd == NULL)
+ goto out;
+ if (free) {
+ if (obj->pampd_count == 0) {
+ tmem_obj_free(obj, hb);
+ (*tmem_hostops.obj_free)(obj, pool);
+ obj = NULL;
+ }
+ }
+ if (tmem_pamops.is_remote(pampd)) {
+ lock_held = false;
+ spin_unlock(&hb->lock);
+ }
+ if (free)
+ ret = (*tmem_pamops.get_data_and_free)(
+ data, size, raw, pampd, pool, oidp, index);
+ else
+ ret = (*tmem_pamops.get_data)(
+ data, size, raw, pampd, pool, oidp, index);
+ if (ret < 0)
+ goto out;
+ ret = 0;
+out:
+ if (lock_held)
+ spin_unlock(&hb->lock);
+disabled:
+ unlock_tmem_state();
+ return ret;
+}
+
+/*
+ * If a page in tmem matches the handle, "flush" this page from tmem such
+ * that any subsequent "get" does not succeed (unless, of course, there
+ * was another "put" with the same handle).
+ */
+int tmem_flush_page(struct tmem_pool *pool,
+ struct tmem_oid *oidp, uint32_t index)
+{
+ struct tmem_obj *obj;
+ void *pampd;
+ int ret = -1;
+ struct tmem_hashbucket *hb;
+
+ hb = &pool->hashbucket[tmem_oid_hash(oidp)];
+ spin_lock(&hb->lock);
+ obj = tmem_obj_find(hb, oidp);
+ if (obj == NULL)
+ goto out;
+ pampd = tmem_pampd_delete_from_obj(obj, index);
+ if (pampd == NULL)
+ goto out;
+ (*tmem_pamops.free)(pampd, pool, oidp, index);
+ if (obj->pampd_count == 0) {
+ tmem_obj_free(obj, hb);
+ (*tmem_hostops.obj_free)(obj, pool);
+ }
+ ret = 0;
+
+out:
+ spin_unlock(&hb->lock);
+ return ret;
+}
+
+/*
+ * If a page in tmem matches the handle, replace the page so that any
+ * subsequent "get" gets the new page. Returns 0 if
+ * there was a page to replace, else returns -1.
+ */
+int tmem_replace(struct tmem_pool *pool, struct tmem_oid *oidp,
+ uint32_t index, void *new_pampd)
+{
+ struct tmem_obj *obj;
+ int ret = -1;
+ struct tmem_hashbucket *hb;
+
+ lock_tmem_state();
+ if (!tmem_enabled)
+ goto disabled;
+ hb = &pool->hashbucket[tmem_oid_hash(oidp)];
+ spin_lock(&hb->lock);
+ obj = tmem_obj_find(hb, oidp);
+ if (obj == NULL)
+ goto out;
+ new_pampd = tmem_pampd_replace_in_obj(obj, index, new_pampd);
+ ret = (*tmem_pamops.replace_in_obj)(new_pampd, obj);
+out:
+ spin_unlock(&hb->lock);
+disabled:
+ unlock_tmem_state();
+ return ret;
+}
+
+/*
+ * "Flush" all pages in tmem matching this oid.
+ */
+int tmem_flush_object(struct tmem_pool *pool, struct tmem_oid *oidp)
+{
+ struct tmem_obj *obj;
+ struct tmem_hashbucket *hb;
+ int ret = -1;
+
+ hb = &pool->hashbucket[tmem_oid_hash(oidp)];
+ spin_lock(&hb->lock);
+ obj = tmem_obj_find(hb, oidp);
+ if (obj == NULL)
+ goto out;
+ tmem_pampd_destroy_all_in_obj(obj);
+ tmem_obj_free(obj, hb);
+ (*tmem_hostops.obj_free)(obj, pool);
+ ret = 0;
+
+out:
+ spin_unlock(&hb->lock);
+ return ret;
+}
+
+/*
+ * "Flush" all pages (and tmem_objs) from this tmem_pool and disable
+ * all subsequent access to this tmem_pool.
+ */
+int tmem_destroy_pool(struct tmem_pool *pool)
+{
+ int ret = -1;
+
+ if (pool == NULL)
+ goto out;
+ tmem_pool_flush(pool, 1);
+ ret = 0;
+out:
+ return ret;
+}
+
+int tmem_flush_pool(struct tmem_pool *pool)
+{
+ int ret = -1;
+
+ if (pool == NULL)
+ goto out;
+ tmem_pool_flush(pool, 0);
+ ret = 0;
+out:
+ return ret;
+}
+
+static LIST_HEAD(tmem_global_pool_list);
+
+/*
+ * Create a new tmem_pool with the provided flag and return
+ * a pool id provided by the tmem host implementation.
+ */
+void tmem_new_pool(struct tmem_pool *pool, uint32_t flags)
+{
+ int persistent = flags & TMEM_POOL_PERSIST;
+ int shared = flags & TMEM_POOL_SHARED;
+ struct tmem_hashbucket *hb = &pool->hashbucket[0];
+ int i;
+
+ for (i = 0; i < TMEM_HASH_BUCKETS; i++, hb++) {
+ hb->obj_rb_root = RB_ROOT;
+ spin_lock_init(&hb->lock);
+ }
+ INIT_LIST_HEAD(&pool->pool_list);
+ atomic_set(&pool->obj_count, 0);
+ SET_SENTINEL(pool, POOL);
+ list_add_tail(&pool->pool_list, &tmem_global_pool_list);
+ pool->persistent = persistent;
+ pool->shared = shared;
+}
+
+/* The following must be called with tmem state locked */
+static void tmem_reset(void)
+{
+ (*tmem_hostops.flush_all_obj)();
+}
+
+void tmem_enable(bool reset)
+{
+ pr_info("turning tmem on\n");
+ tmem_enabled = true;
+
+ if (!reset)
+ return;
+
+ tmem_reset();
+ (*tmem_hostops.control)(false);
+}
+
+void tmem_disable(void)
+{
+ pr_info("turning tmem off\n");
+ (*tmem_hostops.control)(true);
+ tmem_enabled = false;
+}
diff --git a/drivers/staging/qcache/tmem.h b/drivers/staging/qcache/tmem.h
new file mode 100644
index 0000000..344561f
--- /dev/null
+++ b/drivers/staging/qcache/tmem.h
@@ -0,0 +1,214 @@
+/*
+ * tmem.h
+ *
+ * Transcendent memory
+ *
+ * Copyright (c) 2009-2011, Dan Magenheimer, Oracle Corp.
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ */
+
+#ifndef _TMEM_H_
+#define _TMEM_H_
+
+#include <linux/types.h>
+#include <linux/highmem.h>
+#include <linux/hash.h>
+#include <linux/atomic.h>
+#include <linux/fmem.h>
+
+/*
+ * These are pre-defined by the Xen<->Linux ABI
+ */
+#define TMEM_PUT_PAGE 4
+#define TMEM_GET_PAGE 5
+#define TMEM_FLUSH_PAGE 6
+#define TMEM_FLUSH_OBJECT 7
+#define TMEM_POOL_PERSIST 1
+#define TMEM_POOL_SHARED 2
+#define TMEM_POOL_PRECOMPRESSED 4
+#define TMEM_POOL_PAGESIZE_SHIFT 4
+#define TMEM_POOL_PAGESIZE_MASK 0xf
+#define TMEM_POOL_RESERVED_BITS 0x00ffff00
+
+/*
+ * sentinels have proven very useful for debugging but can be removed
+ * or disabled before final merge.
+ */
+#define SENTINELS
+#ifdef SENTINELS
+#define DECL_SENTINEL uint32_t sentinel;
+#define SET_SENTINEL(_x, _y) (_x->sentinel = _y##_SENTINEL)
+#define INVERT_SENTINEL(_x, _y) (_x->sentinel = ~_y##_SENTINEL)
+#define ASSERT_SENTINEL(_x, _y) WARN_ON(_x->sentinel != _y##_SENTINEL)
+#define ASSERT_INVERTED_SENTINEL(_x, _y) WARN_ON(_x->sentinel != ~_y##_SENTINEL)
+#else
+#define DECL_SENTINEL
+#define SET_SENTINEL(_x, _y) do { } while (0)
+#define INVERT_SENTINEL(_x, _y) do { } while (0)
+#define ASSERT_SENTINEL(_x, _y) do { } while (0)
+#define ASSERT_INVERTED_SENTINEL(_x, _y) do { } while (0)
+#endif
+
+#define ASSERT_SPINLOCK(_l) WARN_ON(!spin_is_locked(_l))
+
+/*
+ * A pool is the highest-level data structure managed by tmem and
+ * usually corresponds to a large independent set of pages such as
+ * a filesystem. Each pool has an id, and certain attributes and counters.
+ * It also contains a set of hash buckets, each of which contains an rbtree
+ * of objects and a lock to manage concurrency within the pool.
+ */
+
+#define TMEM_HASH_BUCKET_BITS 8
+#define TMEM_HASH_BUCKETS (1<<TMEM_HASH_BUCKET_BITS)
+
+struct tmem_hashbucket {
+ struct rb_root obj_rb_root;
+ spinlock_t lock;
+};
+
+struct tmem_pool {
+ void *client; /* "up" for some clients, avoids table lookup */
+ struct list_head pool_list;
+ uint32_t pool_id;
+ bool persistent;
+ bool shared;
+ atomic_t obj_count;
+ atomic_t refcount;
+ struct tmem_hashbucket hashbucket[TMEM_HASH_BUCKETS];
+ DECL_SENTINEL
+};
+
+#define is_persistent(_p) (_p->persistent)
+#define is_ephemeral(_p) (!(_p->persistent))
+
+/*
+ * An object id ("oid") is large: 192-bits (to ensure, for example, files
+ * in a modern filesystem can be uniquely identified).
+ */
+
+struct tmem_oid {
+ uint64_t oid[3];
+};
+
+static inline void tmem_oid_set_invalid(struct tmem_oid *oidp)
+{
+ oidp->oid[0] = oidp->oid[1] = oidp->oid[2] = -1UL;
+}
+
+static inline bool tmem_oid_valid(struct tmem_oid *oidp)
+{
+ return oidp->oid[0] != -1UL || oidp->oid[1] != -1UL ||
+ oidp->oid[2] != -1UL;
+}
+
+static inline int tmem_oid_compare(struct tmem_oid *left,
+ struct tmem_oid *right)
+{
+ int ret;
+
+ if (left->oid[2] == right->oid[2]) {
+ if (left->oid[1] == right->oid[1]) {
+ if (left->oid[0] == right->oid[0])
+ ret = 0;
+ else if (left->oid[0] < right->oid[0])
+ ret = -1;
+ else
+ return 1;
+ } else if (left->oid[1] < right->oid[1])
+ ret = -1;
+ else
+ ret = 1;
+ } else if (left->oid[2] < right->oid[2])
+ ret = -1;
+ else
+ ret = 1;
+ return ret;
+}
+
+static inline unsigned tmem_oid_hash(struct tmem_oid *oidp)
+{
+ return hash_long(oidp->oid[0] ^ oidp->oid[1] ^ oidp->oid[2],
+ TMEM_HASH_BUCKET_BITS);
+}
+
+/*
+ * A tmem_obj contains an identifier (oid), pointers to the parent
+ * pool and the rb_tree to which it belongs, counters, and an ordered
+ * set of pampds, structured in a radix-tree-like tree. The intermediate
+ * nodes of the tree are called tmem_objnodes.
+ */
+
+struct tmem_objnode;
+
+struct tmem_obj {
+ struct tmem_oid oid;
+ struct tmem_pool *pool;
+ struct rb_node rb_tree_node;
+ struct tmem_objnode *objnode_tree_root;
+ unsigned int objnode_tree_height;
+ unsigned long objnode_count;
+ long pampd_count;
+ void *extra; /* for private use by pampd implementation */
+ DECL_SENTINEL
+};
+
+#define OBJNODE_TREE_MAP_SHIFT 6
+#define OBJNODE_TREE_MAP_SIZE (1UL << OBJNODE_TREE_MAP_SHIFT)
+#define OBJNODE_TREE_MAP_MASK (OBJNODE_TREE_MAP_SIZE-1)
+#define OBJNODE_TREE_INDEX_BITS (8 /* CHAR_BIT */ * sizeof(unsigned long))
+#define OBJNODE_TREE_MAX_PATH \
+ (OBJNODE_TREE_INDEX_BITS/OBJNODE_TREE_MAP_SHIFT + 2)
+
+struct tmem_objnode {
+ struct tmem_obj *obj;
+ DECL_SENTINEL
+ void *slots[OBJNODE_TREE_MAP_SIZE];
+ unsigned int slots_in_use;
+};
+
+/* pampd abstract datatype methods provided by the PAM implementation */
+struct tmem_pamops {
+ void *(*create)(char *, size_t, bool, int,
+ struct tmem_pool *, struct tmem_oid *, uint32_t);
+ int (*get_data)(char *, size_t *, bool, void *, struct tmem_pool *,
+ struct tmem_oid *, uint32_t);
+ int (*get_data_and_free)(char *, size_t *, bool, void *,
+ struct tmem_pool *, struct tmem_oid *,
+ uint32_t);
+ void (*free)(void *, struct tmem_pool *, struct tmem_oid *, uint32_t);
+ void (*free_obj)(struct tmem_pool *, struct tmem_obj *);
+ bool (*is_remote)(void *);
+ void (*new_obj)(struct tmem_obj *);
+ int (*replace_in_obj)(void *, struct tmem_obj *);
+};
+extern void tmem_register_pamops(struct tmem_pamops *m);
+
+/* memory allocation methods provided by the host implementation */
+struct tmem_hostops {
+ struct tmem_obj *(*obj_alloc)(struct tmem_pool *);
+ void (*obj_free)(struct tmem_obj *, struct tmem_pool *);
+ struct tmem_objnode *(*objnode_alloc)(struct tmem_pool *);
+ void (*objnode_free)(struct tmem_objnode *, struct tmem_pool *);
+ void (*flush_all_obj)(void);
+ void (*control)(bool);
+};
+extern void tmem_register_hostops(struct tmem_hostops *m);
+
+/* core tmem accessor functions */
+extern int tmem_put(struct tmem_pool *, struct tmem_oid *, uint32_t index,
+ char *, size_t, bool, bool);
+extern int tmem_get(struct tmem_pool *, struct tmem_oid *, uint32_t index,
+ char *, size_t *, bool, int);
+extern int tmem_replace(struct tmem_pool *, struct tmem_oid *, uint32_t index,
+ void *);
+extern int tmem_flush_page(struct tmem_pool *, struct tmem_oid *,
+ uint32_t index);
+extern int tmem_flush_object(struct tmem_pool *, struct tmem_oid *);
+extern int tmem_destroy_pool(struct tmem_pool *);
+extern int tmem_flush_pool(struct tmem_pool *);
+extern void tmem_new_pool(struct tmem_pool *, uint32_t);
+
+extern void tmem_enable(bool);
+extern void tmem_disable(void);
+#endif /* _TMEM_H */
diff --git a/drivers/thermal/msm_tsens.c b/drivers/thermal/msm_tsens.c
index d9a6efc..401ad88 100644
--- a/drivers/thermal/msm_tsens.c
+++ b/drivers/thermal/msm_tsens.c
@@ -511,7 +511,6 @@
reg = readl_relaxed(TSENS_CNTL_ADDR);
writel_relaxed(reg | TSENS_SW_RST, TSENS_CNTL_ADDR);
reg |= TSENS_SLP_CLK_ENA | TSENS_EN | (TSENS_MEASURE_PERIOD << 16) |
- TSENS_LOWER_STATUS_CLR | TSENS_UPPER_STATUS_CLR |
TSENS_MIN_STATUS_MASK | TSENS_MAX_STATUS_MASK |
(((1 << TSENS_NUM_SENSORS) - 1) << 3);
diff --git a/drivers/tty/serial/msm_serial_hs_lite.c b/drivers/tty/serial/msm_serial_hs_lite.c
index a094b4e..9e1e388 100644
--- a/drivers/tty/serial/msm_serial_hs_lite.c
+++ b/drivers/tty/serial/msm_serial_hs_lite.c
@@ -642,9 +642,6 @@
vid = msm_hsl_port->ver_id;
msm_hsl_write(port, baud_code, regmap[vid][UARTDM_CSR]);
- if (vid == UARTDM_VERSION_14)
- rxstale = 5000;
-
/* RX stale watermark */
watermark = UARTDM_IPR_STALE_LSB_BMSK & rxstale;
watermark |= UARTDM_IPR_STALE_TIMEOUT_MSB_BMSK & (rxstale << 2);
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index af6f351..9dc4044 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -2,6 +2,7 @@
* Gadget Driver for Android
*
* Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
* Author: Mike Lockwood <lockwood@android.com>
*
* This software is licensed under the terms of the GNU General Public
@@ -60,7 +61,7 @@
#include "u_ctrl_hsic.c"
#include "u_data_hsic.c"
#include "f_serial.c"
-//#include "f_acm.c"
+#include "f_acm.c"
#include "f_adb.c"
#include "f_ccid.c"
#include "f_mtp.c"
@@ -373,12 +374,13 @@
char *name;
char buf[32], *b;
int once = 0, err = -1;
- int (*notify)(uint32_t, const char *) = NULL;
+ int (*notify)(uint32_t, const char *);
strlcpy(buf, diag_clients, sizeof(buf));
b = strim(buf);
while (b) {
+ notify = NULL;
name = strsep(&b, ",");
/* Allow only first diag channel to update pid and serial no */
if (_android_dev->pdata && !once++)
@@ -475,6 +477,77 @@
.attributes = serial_function_attributes,
};
+/* ACM */
+static char acm_transports[32]; /*enabled ACM ports - "tty[,sdio]"*/
+static ssize_t acm_transports_store(
+ struct device *device, struct device_attribute *attr,
+ const char *buff, size_t size)
+{
+ strlcpy(acm_transports, buff, sizeof(acm_transports));
+
+ return size;
+}
+
+static DEVICE_ATTR(acm_transports, S_IWUSR, NULL, acm_transports_store);
+static struct device_attribute *acm_function_attributes[] = {
+ &dev_attr_acm_transports, NULL };
+
+static void acm_function_cleanup(struct android_usb_function *f)
+{
+ gserial_cleanup();
+}
+
+static int acm_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ char *name;
+ char buf[32], *b;
+ int err = -1, i;
+ static int acm_initialized, ports;
+
+ if (acm_initialized)
+ goto bind_config;
+
+ acm_initialized = 1;
+ strlcpy(buf, acm_transports, sizeof(buf));
+ b = strim(buf);
+
+ while (b) {
+ name = strsep(&b, ",");
+
+ if (name) {
+ err = acm_init_port(ports, name);
+ if (err) {
+ pr_err("acm: Cannot open port '%s'", name);
+ goto out;
+ }
+ ports++;
+ }
+ }
+ err = acm_port_setup(c);
+ if (err) {
+ pr_err("acm: Cannot setup transports");
+ goto out;
+ }
+
+bind_config:
+ for (i = 0; i < ports; i++) {
+ err = acm_bind_config(c, i);
+ if (err) {
+ pr_err("acm: bind_config failed for port %d", i);
+ goto out;
+ }
+ }
+
+out:
+ return err;
+}
+static struct android_usb_function acm_function = {
+ .name = "acm",
+ .cleanup = acm_function_cleanup,
+ .bind_config = acm_function_bind_config,
+ .attributes = acm_function_attributes,
+};
/* ADB */
static int adb_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev)
@@ -524,79 +597,6 @@
.bind_config = ccid_function_bind_config,
};
-#if 0
-#define MAX_ACM_INSTANCES 4
-struct acm_function_config {
- int instances;
-};
-
-static int acm_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev)
-{
- f->config = kzalloc(sizeof(struct acm_function_config), GFP_KERNEL);
- if (!f->config)
- return -ENOMEM;
-
- return gserial_setup(cdev->gadget, MAX_ACM_INSTANCES);
-}
-
-static void acm_function_cleanup(struct android_usb_function *f)
-{
- gserial_cleanup();
- kfree(f->config);
- f->config = NULL;
-}
-
-static int acm_function_bind_config(struct android_usb_function *f, struct usb_configuration *c)
-{
- int i;
- int ret = 0;
- struct acm_function_config *config = f->config;
-
- for (i = 0; i < config->instances; i++) {
- ret = acm_bind_config(c, i);
- if (ret) {
- pr_err("Could not bind acm%u config\n", i);
- break;
- }
- }
-
- return ret;
-}
-
-static ssize_t acm_instances_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct android_usb_function *f = dev_get_drvdata(dev);
- struct acm_function_config *config = f->config;
- return sprintf(buf, "%d\n", config->instances);
-}
-
-static ssize_t acm_instances_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
-{
- struct android_usb_function *f = dev_get_drvdata(dev);
- struct acm_function_config *config = f->config;
- int value;
-
- sscanf(buf, "%d", &value);
- if (value > MAX_ACM_INSTANCES)
- value = MAX_ACM_INSTANCES;
- config->instances = value;
- return size;
-}
-
-static DEVICE_ATTR(instances, S_IRUGO | S_IWUSR, acm_instances_show, acm_instances_store);
-static struct device_attribute *acm_function_attributes[] = { &dev_attr_instances, NULL };
-
-static struct android_usb_function acm_function = {
- .name = "acm",
- .init = acm_function_init,
- .cleanup = acm_function_cleanup,
- .bind_config = acm_function_bind_config,
- .attributes = acm_function_attributes,
-};
-#endif
-
static int mtp_function_init(struct android_usb_function *f, struct usb_composite_dev *cdev)
{
return mtp_setup();
@@ -968,7 +968,7 @@
&serial_function,
&adb_function,
&ccid_function,
-// &acm_function,
+ &acm_function,
&mtp_function,
&ptp_function,
&rndis_function,
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index e0842787..6282389 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -155,6 +155,7 @@
#define CAP_ENDPTLISTADDR (0x018UL)
#define CAP_PORTSC (0x044UL)
#define CAP_DEVLC (0x084UL)
+#define CAP_ENDPTPIPEID (0x0BCUL)
#define CAP_USBMODE (hw_bank.lpm ? 0x0C8UL : 0x068UL)
#define CAP_ENDPTSETUPSTAT (hw_bank.lpm ? 0x0D8UL : 0x06CUL)
#define CAP_ENDPTPRIME (hw_bank.lpm ? 0x0DCUL : 0x070UL)
@@ -1638,6 +1639,23 @@
if (!mReq->req.no_interrupt)
mReq->ptr->token |= TD_IOC;
}
+
+ /* MSM Specific: updating the request as required for
+ * SPS mode. Enable MSM proprietary DMA engine acording
+ * to the UDC private data in the request.
+ */
+ if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+ if (mReq->req.udc_priv & MSM_SPS_MODE) {
+ mReq->ptr->token = TD_STATUS_ACTIVE;
+ if (mReq->req.udc_priv & MSM_TBE)
+ mReq->ptr->next = TD_TERMINATE;
+ else
+ mReq->ptr->next = MSM_ETD_TYPE | mReq->dma;
+ if (!mReq->req.no_interrupt)
+ mReq->ptr->token |= MSM_ETD_IOC;
+ }
+ }
+
mReq->ptr->page[0] = mReq->req.dma;
for (i = 1; i < 5; i++)
mReq->ptr->page[i] =
@@ -1680,6 +1698,39 @@
}
mEp->qh.ptr->td.next = mReq->dma; /* TERMINATE = 0 */
+
+ if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+ if (mReq->req.udc_priv & MSM_SPS_MODE) {
+ mEp->qh.ptr->td.next |= MSM_ETD_TYPE;
+ i = hw_cread(CAP_ENDPTPIPEID +
+ mEp->num * sizeof(u32), ~0);
+ /* Read current value of this EPs pipe id */
+ i = (mEp->dir == TX) ?
+ ((i >> MSM_TX_PIPE_ID_OFS) & MSM_PIPE_ID_MASK) :
+ (i & MSM_PIPE_ID_MASK);
+ /* If requested pipe id is different from current,
+ then write it */
+ if (i != (mReq->req.udc_priv & MSM_PIPE_ID_MASK)) {
+ if (mEp->dir == TX)
+ hw_cwrite(
+ CAP_ENDPTPIPEID +
+ mEp->num * sizeof(u32),
+ MSM_PIPE_ID_MASK <<
+ MSM_TX_PIPE_ID_OFS,
+ (mReq->req.udc_priv &
+ MSM_PIPE_ID_MASK)
+ << MSM_TX_PIPE_ID_OFS);
+ else
+ hw_cwrite(
+ CAP_ENDPTPIPEID +
+ mEp->num * sizeof(u32),
+ MSM_PIPE_ID_MASK,
+ mReq->req.udc_priv &
+ MSM_PIPE_ID_MASK);
+ }
+ }
+ }
+
mEp->qh.ptr->td.token &= ~TD_STATUS; /* clear status */
mEp->qh.ptr->cap |= QH_ZLT;
@@ -1712,6 +1763,10 @@
if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0)
return -EBUSY;
+ if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID)
+ if ((mReq->req.udc_priv & MSM_SPS_MODE) &&
+ (mReq->req.udc_priv & MSM_TBE))
+ return -EBUSY;
if (mReq->zptr) {
if ((TD_STATUS_ACTIVE & mReq->zptr->token) != 0)
return -EBUSY;
@@ -1756,6 +1811,7 @@
__acquires(mEp->lock)
{
struct ci13xxx_ep *mEpTemp = mEp;
+ unsigned val;
trace("%p", mEp);
@@ -1771,6 +1827,21 @@
list_entry(mEp->qh.queue.next,
struct ci13xxx_req, queue);
list_del_init(&mReq->queue);
+
+ /* MSM Specific: Clear end point proprietary register */
+ if (CI13XX_REQ_VENDOR_ID(mReq->req.udc_priv) == MSM_VENDOR_ID) {
+ if (mReq->req.udc_priv & MSM_SPS_MODE) {
+ val = hw_cread(CAP_ENDPTPIPEID +
+ mEp->num * sizeof(u32),
+ ~0);
+
+ if (val != MSM_EP_PIPE_ID_RESET_VAL)
+ hw_cwrite(
+ CAP_ENDPTPIPEID +
+ mEp->num * sizeof(u32),
+ ~0, MSM_EP_PIPE_ID_RESET_VAL);
+ }
+ }
mReq->req.status = -ESHUTDOWN;
if (mReq->req.complete != NULL) {
diff --git a/drivers/usb/gadget/ci13xxx_udc.h b/drivers/usb/gadget/ci13xxx_udc.h
index b917fe9..35b0712 100644
--- a/drivers/usb/gadget/ci13xxx_udc.h
+++ b/drivers/usb/gadget/ci13xxx_udc.h
@@ -25,6 +25,21 @@
#define RX (0) /* similar to USB_DIR_OUT but can be used as an index */
#define TX (1) /* similar to USB_DIR_IN but can be used as an index */
+/* UDC private data:
+ * 16MSb - Vendor ID | 16 LSb Vendor private data
+ */
+#define CI13XX_REQ_VENDOR_ID(id) (id & 0xFFFF0000UL)
+
+/* MSM specific */
+#define MSM_PIPE_ID_MASK (0x1F)
+#define MSM_TX_PIPE_ID_OFS (16)
+#define MSM_SPS_MODE BIT(5)
+#define MSM_TBE BIT(6)
+#define MSM_ETD_TYPE BIT(1)
+#define MSM_ETD_IOC BIT(9)
+#define MSM_VENDOR_ID BIT(16)
+#define MSM_EP_PIPE_ID_RESET_VAL 0x1F001F
+
/******************************************************************************
* STRUCTURES
*****************************************************************************/
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index 7299dff..69a36af 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/usb/android_composite.h>
+#include <mach/usb_gadget_xport.h>
#include "u_serial.h"
#include "gadget_chips.h"
@@ -86,12 +87,12 @@
#define ACM_CTRL_DCD (1 << 0)
};
-static unsigned int no_tty_ports;
-static unsigned int no_sdio_ports;
-static unsigned int no_smd_ports;
-static unsigned int nr_ports;
+static unsigned int no_acm_tty_ports;
+static unsigned int no_acm_sdio_ports;
+static unsigned int no_acm_smd_ports;
+static unsigned int nr_acm_ports;
-static struct port_info {
+static struct acm_port_info {
enum transport_type transport;
unsigned port_num;
unsigned client_port_num;
@@ -107,24 +108,25 @@
return container_of(p, struct f_acm, port);
}
-static int gport_setup(struct usb_configuration *c)
+static int acm_port_setup(struct usb_configuration *c)
{
int ret = 0;
- pr_debug("%s: no_tty_ports:%u no_sdio_ports: %u nr_ports:%u\n",
- __func__, no_tty_ports, no_sdio_ports, nr_ports);
+ pr_debug("%s: no_acm_tty_ports:%u no_acm_sdio_ports: %u nr_acm_ports:%u\n",
+ __func__, no_acm_tty_ports, no_acm_sdio_ports,
+ nr_acm_ports);
- if (no_tty_ports)
- ret = gserial_setup(c->cdev->gadget, no_tty_ports);
- if (no_sdio_ports)
- ret = gsdio_setup(c->cdev->gadget, no_sdio_ports);
- if (no_smd_ports)
- ret = gsmd_setup(c->cdev->gadget, no_smd_ports);
+ if (no_acm_tty_ports)
+ ret = gserial_setup(c->cdev->gadget, no_acm_tty_ports);
+ if (no_acm_sdio_ports)
+ ret = gsdio_setup(c->cdev->gadget, no_acm_sdio_ports);
+ if (no_acm_smd_ports)
+ ret = gsmd_setup(c->cdev->gadget, no_acm_smd_ports);
return ret;
}
-static int gport_connect(struct f_acm *acm)
+static int acm_port_connect(struct f_acm *acm)
{
unsigned port_num;
@@ -136,13 +138,13 @@
acm, &acm->port, acm->port_num, port_num);
switch (acm->transport) {
- case USB_GADGET_FSERIAL_TRANSPORT_TTY:
+ case USB_GADGET_XPORT_TTY:
gserial_connect(&acm->port, port_num);
break;
- case USB_GADGET_FSERIAL_TRANSPORT_SDIO:
+ case USB_GADGET_XPORT_SDIO:
gsdio_connect(&acm->port, port_num);
break;
- case USB_GADGET_FSERIAL_TRANSPORT_SMD:
+ case USB_GADGET_XPORT_SMD:
gsmd_connect(&acm->port, port_num);
break;
default:
@@ -154,7 +156,7 @@
return 0;
}
-static int gport_disconnect(struct f_acm *acm)
+static int acm_port_disconnect(struct f_acm *acm)
{
unsigned port_num;
@@ -165,13 +167,13 @@
acm, &acm->port, acm->port_num, port_num);
switch (acm->transport) {
- case USB_GADGET_FSERIAL_TRANSPORT_TTY:
+ case USB_GADGET_XPORT_TTY:
gserial_disconnect(&acm->port);
break;
- case USB_GADGET_FSERIAL_TRANSPORT_SDIO:
+ case USB_GADGET_XPORT_SDIO:
gsdio_disconnect(&acm->port, port_num);
break;
- case USB_GADGET_FSERIAL_TRANSPORT_SMD:
+ case USB_GADGET_XPORT_SMD:
gsmd_disconnect(&acm->port, port_num);
break;
default:
@@ -504,7 +506,7 @@
} else if (intf == acm->data_id) {
if (acm->port.in->driver_data) {
DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
- gport_disconnect(acm);
+ acm_port_disconnect(acm);
} else {
DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
}
@@ -512,7 +514,7 @@
acm->hs.in, acm->fs.in);
acm->port.out_desc = ep_choose(cdev->gadget,
acm->hs.out, acm->fs.out);
- gport_connect(acm);
+ acm_port_connect(acm);
} else
return -EINVAL;
@@ -526,7 +528,7 @@
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
- gport_disconnect(acm);
+ acm_port_disconnect(acm);
usb_ep_disable(acm->notify);
acm->notify->driver_data = NULL;
}
@@ -888,116 +890,43 @@
return status;
}
-#ifdef CONFIG_USB_ANDROID_ACM
-#include <linux/platform_device.h>
-
-static struct acm_platform_data *acm_pdata;
-
-static int acm_probe(struct platform_device *pdev)
+/**
+ * acm_init_port - bind a acm_port to its transport
+ */
+static int acm_init_port(int port_num, const char *name)
{
- acm_pdata = pdev->dev.platform_data;
- return 0;
-}
+ enum transport_type transport;
-static struct platform_driver acm_platform_driver = {
- .driver = { .name = "acm", },
- .probe = acm_probe,
-};
+ if (port_num >= GSERIAL_NO_PORTS)
+ return -ENODEV;
-int acm1_function_bind_config(struct usb_configuration *c)
-{
- int ret = acm_bind_config(c, 0);
- if (ret == 0)
- gport_setup(c);
- return ret;
-}
+ transport = str_to_xport(name);
+ pr_debug("%s, port:%d, transport:%s\n", __func__,
+ port_num, xport_to_str(transport));
-int acm2_function_bind_config(struct usb_configuration *c)
-{
- int ret = acm_bind_config(c, 1);
+ gacm_ports[port_num].transport = transport;
+ gacm_ports[port_num].port_num = port_num;
- return ret;
-}
-
-static struct android_usb_function acm1_function = {
- .name = "acm1",
- .bind_config = acm1_function_bind_config,
-};
-
-static struct android_usb_function acm2_function = {
- .name = "acm2",
- .bind_config = acm2_function_bind_config,
-};
-
-static int facm_remove(struct platform_device *pdev)
-{
- gserial_cleanup();
-
- return 0;
-}
-
-static struct platform_driver usb_facm = {
- .remove = facm_remove,
- .driver = {
- .name = "usb_facm",
- .owner = THIS_MODULE,
- },
-};
-
-static int __init facm_probe(struct platform_device *pdev)
-{
- struct usb_gadget_facm_pdata *pdata = pdev->dev.platform_data;
- int i;
-
- dev_dbg(&pdev->dev, "%s: probe\n", __func__);
-
- if (!pdata)
- goto probe_android_register;
-
- for (i = 0; i < GSERIAL_NO_PORTS; i++) {
- gacm_ports[i].transport = pdata->transport[i];
- gacm_ports[i].port_num = i;
-
- switch (gacm_ports[i].transport) {
- case USB_GADGET_FSERIAL_TRANSPORT_TTY:
- gacm_ports[i].client_port_num = no_tty_ports;
- no_tty_ports++;
- break;
- case USB_GADGET_FSERIAL_TRANSPORT_SDIO:
- gacm_ports[i].client_port_num = no_sdio_ports;
- no_sdio_ports++;
- break;
- case USB_GADGET_FSERIAL_TRANSPORT_SMD:
- gacm_ports[i].client_port_num = no_smd_ports;
- no_smd_ports++;
- break;
- default:
- pr_err("%s: Un-supported transport transport: %u\n",
- __func__, gacm_ports[i].transport);
- return -ENODEV;
- }
-
- nr_ports++;
+ switch (transport) {
+ case USB_GADGET_XPORT_TTY:
+ gacm_ports[port_num].client_port_num = no_acm_tty_ports;
+ no_acm_tty_ports++;
+ break;
+ case USB_GADGET_XPORT_SDIO:
+ gacm_ports[port_num].client_port_num = no_acm_sdio_ports;
+ no_acm_sdio_ports++;
+ break;
+ case USB_GADGET_XPORT_SMD:
+ gacm_ports[port_num].client_port_num = no_acm_smd_ports;
+ no_acm_smd_ports++;
+ break;
+ default:
+ pr_err("%s: Un-supported transport transport: %u\n",
+ __func__, gacm_ports[port_num].transport);
+ return -ENODEV;
}
- pr_info("%s:gport:tty_ports:%u sdio_ports:%u "
- "smd_ports:%u nr_ports:%u\n",
- __func__, no_tty_ports, no_sdio_ports,
- no_smd_ports, nr_ports);
-
-probe_android_register:
- android_register_function(&acm1_function);
- android_register_function(&acm2_function);
+ nr_acm_ports++;
return 0;
}
-
-static int __init init(void)
-{
- printk(KERN_INFO "f_acm init\n");
-
- return platform_driver_probe(&usb_facm, facm_probe);
-}
-module_init(init);
-
-#endif /* CONFIG_USB_ANDROID_ACM */
diff --git a/drivers/usb/gadget/f_diag.c b/drivers/usb/gadget/f_diag.c
index ccfd2e3..f492143 100644
--- a/drivers/usb/gadget/f_diag.c
+++ b/drivers/usb/gadget/f_diag.c
@@ -515,9 +515,11 @@
int rc = 0;
dev->in_desc = ep_choose(cdev->gadget,
- &hs_bulk_in_desc, &fs_bulk_in_desc);
+ (struct usb_endpoint_descriptor *)f->hs_descriptors[1],
+ (struct usb_endpoint_descriptor *)f->descriptors[1]);
dev->out_desc = ep_choose(cdev->gadget,
- &hs_bulk_out_desc, &fs_bulk_out_desc);
+ (struct usb_endpoint_descriptor *)f->hs_descriptors[2],
+ (struct usb_endpoint_descriptor *)f->descriptors[2]);
dev->in->driver_data = dev;
rc = usb_ep_enable(dev->in, dev->in_desc);
if (rc) {
@@ -669,10 +671,13 @@
temp += scnprintf(buf + temp, PAGE_SIZE - temp,
"---Name: %s---\n"
+ "endpoints: %s, %s\n"
"dpkts_tolaptop: %lu\n"
"dpkts_tomodem: %lu\n"
"pkts_tolaptop_pending: %u\n",
- ch->name, ctxt->dpkts_tolaptop,
+ ch->name,
+ ctxt->in->name, ctxt->out->name,
+ ctxt->dpkts_tolaptop,
ctxt->dpkts_tomodem,
ctxt->dpkts_tolaptop_pending);
}
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 32791d9..cbcf5ac 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -18,10 +18,10 @@
#include <linux/spinlock.h>
#include <mach/usb_gadget_xport.h>
+
#include "u_rmnet.h"
#include "gadget_chips.h"
-
#define RMNET_NOTIFY_INTERVAL 5
#define RMNET_MAX_NOTIFY_SIZE sizeof(struct usb_cdc_notification)
@@ -66,6 +66,7 @@
static unsigned int no_ctrl_smd_ports;
static unsigned int no_ctrl_hsic_ports;
static unsigned int no_data_bam_ports;
+static unsigned int no_data_bam2bam_ports;
static unsigned int no_data_hsic_ports;
static struct rmnet_ports {
enum transport_type data_xport;
@@ -241,13 +242,16 @@
int port_idx;
int i;
- pr_debug("%s: bam ports: %u data hsic ports: %u smd ports: %u"
- " ctrl hsic ports: %u nr_rmnet_ports: %u\n",
- __func__, no_data_bam_ports, no_data_hsic_ports,
- no_ctrl_smd_ports, no_ctrl_hsic_ports, nr_rmnet_ports);
+ pr_debug("%s: bam ports: %u bam2bam ports: %u data hsic ports: %u"
+ " smd ports: %u ctrl hsic ports: %u"
+ " nr_rmnet_ports: %u\n",
+ __func__, no_data_bam_ports, no_data_bam2bam_ports,
+ no_data_hsic_ports, no_ctrl_smd_ports,
+ no_ctrl_hsic_ports, nr_rmnet_ports);
- if (no_data_bam_ports) {
- ret = gbam_setup(no_data_bam_ports);
+ if (no_data_bam_ports || no_data_bam2bam_ports) {
+ ret = gbam_setup(no_data_bam_ports,
+ no_data_bam2bam_ports);
if (ret)
return ret;
}
@@ -329,7 +333,11 @@
port_num = rmnet_ports[dev->port_num].data_xport_num;
switch (dxport) {
case USB_GADGET_XPORT_BAM:
- ret = gbam_connect(&dev->port, port_num);
+ case USB_GADGET_XPORT_BAM2BAM:
+ /* currently only one connection (idx 0)
+ is supported */
+ ret = gbam_connect(&dev->port, port_num,
+ dxport, 0);
if (ret) {
pr_err("%s: gbam_connect failed: err:%d\n",
__func__, ret);
@@ -386,7 +394,8 @@
port_num = rmnet_ports[dev->port_num].data_xport_num;
switch (dxport) {
case USB_GADGET_XPORT_BAM:
- gbam_disconnect(&dev->port, port_num);
+ case USB_GADGET_XPORT_BAM2BAM:
+ gbam_disconnect(&dev->port, port_num, dxport);
break;
case USB_GADGET_XPORT_HSIC:
ghsic_data_disconnect(&dev->port, port_num);
@@ -954,6 +963,7 @@
nr_rmnet_ports = 0;
no_ctrl_smd_ports = 0;
no_data_bam_ports = 0;
+ no_data_bam2bam_ports = 0;
no_ctrl_hsic_ports = 0;
no_data_hsic_ports = 0;
}
@@ -1013,6 +1023,10 @@
rmnet_port->data_xport_num = no_data_bam_ports;
no_data_bam_ports++;
break;
+ case USB_GADGET_XPORT_BAM2BAM:
+ rmnet_port->data_xport_num = no_data_bam2bam_ports;
+ no_data_bam2bam_ports++;
+ break;
case USB_GADGET_XPORT_HSIC:
rmnet_port->data_xport_num = no_data_hsic_ports;
no_data_hsic_ports++;
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
index 0c42292..ea62262 100644
--- a/drivers/usb/gadget/msm72k_udc.c
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -116,6 +116,9 @@
*/
unsigned char bit;
unsigned char num;
+ unsigned long dTD_update_fail_count;
+ unsigned long false_prime_fail_count;
+ unsigned actual_prime_fail_count;
unsigned wedged:1;
/* pointers to DMA transfer list area */
@@ -195,6 +198,7 @@
unsigned phy_status;
unsigned phy_fail_count;
unsigned prime_fail_count;
+ unsigned long dTD_update_fail_count;
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
@@ -585,6 +589,7 @@
spin_lock_irqsave(&ui->lock, flags);
+ ept->false_prime_fail_count++;
if ((readl_relaxed(USB_ENDPTPRIME) & n)) {
/*
* ---- UNLIKELY ---
@@ -602,6 +607,7 @@
rmb();
if (ept->req && (ept->req->item->info & INFO_ACTIVE)) {
ui->prime_fail_count++;
+ ept->actual_prime_fail_count++;
pr_err("%s(): ept%d%s prime failed. ept: config: %x"
"active: %x next: %x info: %x\n",
__func__, ept->num,
@@ -1131,6 +1137,8 @@
if (info & INFO_ACTIVE) {
if (req_dequeue) {
req_dequeue = 0;
+ ui->dTD_update_fail_count++;
+ ept->dTD_update_fail_count++;
udelay(10);
goto dequeue;
} else {
@@ -1886,21 +1894,91 @@
.write = debug_write_release_wlocks,
};
+static ssize_t debug_reprime_ep(struct file *file, const char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct usb_info *ui = file->private_data;
+ struct msm_endpoint *ept;
+ char kbuf[10];
+ unsigned int ep_num, dir;
+ unsigned long flags;
+ unsigned n, i;
+
+ memset(kbuf, 0, 10);
+
+ if (copy_from_user(kbuf, ubuf, count > 10 ? 10 : count))
+ return -EFAULT;
+
+ if (sscanf(kbuf, "%u %u", &ep_num, &dir) != 2)
+ return -EINVAL;
+
+ if (dir)
+ i = ep_num + 16;
+ else
+ i = ep_num;
+
+ spin_lock_irqsave(&ui->lock, flags);
+ ept = ui->ept + i;
+ n = 1 << ept->bit;
+
+ if ((readl_relaxed(USB_ENDPTPRIME) & n))
+ goto out;
+
+ if (readl_relaxed(USB_ENDPTSTAT) & n)
+ goto out;
+
+ /* clear speculative loads on item->info */
+ rmb();
+ if (ept->req && (ept->req->item->info & INFO_ACTIVE)) {
+ pr_err("%s(): ept%d%s prime failed. ept: config: %x"
+ "active: %x next: %x info: %x\n",
+ __func__, ept->num,
+ ept->flags & EPT_FLAG_IN ? "in" : "out",
+ ept->head->config, ept->head->active,
+ ept->head->next, ept->head->info);
+ writel_relaxed(n, USB_ENDPTPRIME);
+ }
+out:
+ spin_unlock_irqrestore(&ui->lock, flags);
+
+ return count;
+}
+
+static char buffer[512];
static ssize_t debug_prime_fail_read(struct file *file, char __user *ubuf,
size_t count, loff_t *ppos)
{
struct usb_info *ui = file->private_data;
- char kbuf[10];
- size_t c = 0;
+ char *buf = buffer;
+ unsigned long flags;
+ struct msm_endpoint *ept;
+ int n;
+ int i = 0;
- memset(kbuf, 0, 10);
+ spin_lock_irqsave(&ui->lock, flags);
+ for (n = 0; n < 32; n++) {
+ ept = ui->ept + n;
+ if (ept->ep.maxpacket == 0)
+ continue;
- c = scnprintf(kbuf, 10, "%d", ui->prime_fail_count);
+ i += scnprintf(buf + i, PAGE_SIZE - i,
+ "ept%d %s false_prime_count=%lu prime_fail_count=%d dtd_fail_count=%lu\n",
+ ept->num, (ept->flags & EPT_FLAG_IN) ? "in " : "out",
+ ept->false_prime_fail_count,
+ ept->actual_prime_fail_count,
+ ept->dTD_update_fail_count);
+ }
- if (copy_to_user(ubuf, kbuf, c))
- return -EFAULT;
+ i += scnprintf(buf + i, PAGE_SIZE - i,
+ "dTD_update_fail count: %lu\n",
+ ui->dTD_update_fail_count);
- return c;
+ i += scnprintf(buf + i, PAGE_SIZE - i,
+ "prime_fail count: %d\n", ui->prime_fail_count);
+
+ spin_unlock_irqrestore(&ui->lock, flags);
+
+ return simple_read_from_buffer(ubuf, count, ppos, buf, i);
}
static int debug_prime_fail_open(struct inode *inode, struct file *file)
@@ -1912,6 +1990,7 @@
const struct file_operations prime_fail_ops = {
.open = debug_prime_fail_open,
.read = debug_prime_fail_read,
+ .write = debug_reprime_ep,
};
static void usb_debugfs_init(struct usb_info *ui)
@@ -1926,7 +2005,7 @@
debugfs_create_file("cycle", 0222, dent, ui, &debug_cycle_ops);
debugfs_create_file("release_wlocks", 0666, dent, ui,
&debug_wlocks_ops);
- debugfs_create_file("prime_fail_countt", 0222, dent, ui,
+ debugfs_create_file("prime_fail_countt", 0666, dent, ui,
&prime_fail_ops);
}
#else
diff --git a/drivers/usb/gadget/u_bam.c b/drivers/usb/gadget/u_bam.c
index 70cbd2f..9605927 100644
--- a/drivers/usb/gadget/u_bam.c
+++ b/drivers/usb/gadget/u_bam.c
@@ -23,12 +23,17 @@
#include <linux/bitops.h>
#include <linux/termios.h>
+#include <mach/usb_gadget_xport.h>
+#include <mach/usb_bam.h>
+
#include "u_rmnet.h"
#define BAM_N_PORTS 1
+#define BAM2BAM_N_PORTS 1
static struct workqueue_struct *gbam_wq;
static int n_bam_ports;
+static int n_bam2bam_ports;
static unsigned bam_ch_ids[] = { 8 };
static const char *bam_ch_names[] = { "bam_dmux_ch_8" };
@@ -68,6 +73,11 @@
#define BAM_CH_OPENED BIT(0)
#define BAM_CH_READY BIT(1)
+#define SPS_PARAMS_PIPE_ID_MASK (0x1F)
+#define SPS_PARAMS_SPS_MODE BIT(5)
+#define SPS_PARAMS_TBE BIT(6)
+#define MSM_VENDOR_ID BIT(16)
+
struct bam_ch_info {
unsigned long flags;
unsigned id;
@@ -81,6 +91,13 @@
struct gbam_port *port;
struct work_struct write_tobam_w;
+ struct usb_request *rx_req;
+ struct usb_request *tx_req;
+
+ u8 src_pipe_idx;
+ u8 dst_pipe_idx;
+ u8 connection_idx;
+
/* stats */
unsigned int pending_with_bam;
unsigned int tohost_drp_cnt;
@@ -96,6 +113,7 @@
spinlock_t port_lock;
struct grmnet *port_usb;
+ struct grmnet *gr;
struct bam_ch_info data_ch;
@@ -108,7 +126,10 @@
struct platform_driver pdrv;
} bam_ports[BAM_N_PORTS];
+struct gbam_port *bam2bam_ports[BAM2BAM_N_PORTS];
static void gbam_start_rx(struct gbam_port *port);
+static void gbam_start_endless_rx(struct gbam_port *port);
+static void gbam_start_endless_tx(struct gbam_port *port);
/*---------------misc functions---------------- */
static void gbam_free_requests(struct usb_ep *ep, struct list_head *head)
@@ -273,10 +294,9 @@
while (d->pending_with_bam < BAM_PENDING_LIMIT) {
skb = __skb_dequeue(&d->rx_skb_q);
- if (!skb) {
- spin_unlock_irqrestore(&port->port_lock, flags);
- return;
- }
+ if (!skb)
+ break;
+
d->pending_with_bam++;
d->to_modem++;
@@ -414,6 +434,20 @@
}
}
+static void gbam_endless_rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ int status = req->status;
+
+ pr_debug("%s status: %d\n", __func__, status);
+}
+
+static void gbam_endless_tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ int status = req->status;
+
+ pr_debug("%s status: %d\n", __func__, status);
+}
+
static void gbam_start_rx(struct gbam_port *port)
{
struct usb_request *req;
@@ -469,6 +503,26 @@
spin_unlock_irqrestore(&port->port_lock, flags);
}
+static void gbam_start_endless_rx(struct gbam_port *port)
+{
+ struct bam_ch_info *d = &port->data_ch;
+ int status;
+
+ status = usb_ep_queue(port->port_usb->out, d->rx_req, GFP_ATOMIC);
+ if (status)
+ pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
+}
+
+static void gbam_start_endless_tx(struct gbam_port *port)
+{
+ struct bam_ch_info *d = &port->data_ch;
+ int status;
+
+ status = usb_ep_queue(port->port_usb->in, d->tx_req, GFP_ATOMIC);
+ if (status)
+ pr_err("%s: error enqueuing transfer, %d\n", __func__, status);
+}
+
static void gbam_start_io(struct gbam_port *port)
{
unsigned long flags;
@@ -520,6 +574,32 @@
}
}
+static void gbam_free_buffers(struct gbam_port *port)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+ struct bam_ch_info *d;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ if (!port || !port->port_usb)
+ goto free_buf_out;
+
+ d = &port->data_ch;
+
+ gbam_free_requests(port->port_usb->in, &d->tx_idle);
+ gbam_free_requests(port->port_usb->out, &d->rx_idle);
+
+ while ((skb = __skb_dequeue(&d->tx_skb_q)))
+ dev_kfree_skb_any(skb);
+
+ while ((skb = __skb_dequeue(&d->rx_skb_q)))
+ dev_kfree_skb_any(skb);
+
+free_buf_out:
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
static void gbam_disconnect_work(struct work_struct *w)
{
struct gbam_port *port =
@@ -533,6 +613,22 @@
clear_bit(BAM_CH_OPENED, &d->flags);
}
+static void gbam2bam_disconnect_work(struct work_struct *w)
+{
+ struct gbam_port *port =
+ container_of(w, struct gbam_port, disconnect_w);
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ port->port_usb = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ /* disable endpoints */
+ usb_ep_disable(port->gr->out);
+ usb_ep_disable(port->gr->in);
+
+}
+
static void gbam_connect_work(struct work_struct *w)
{
struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
@@ -563,30 +659,47 @@
pr_debug("%s: done\n", __func__);
}
-static void gbam_free_buffers(struct gbam_port *port)
+static void gbam2bam_connect_work(struct work_struct *w)
{
- struct sk_buff *skb;
- unsigned long flags;
- struct bam_ch_info *d;
+ struct gbam_port *port = container_of(w, struct gbam_port, connect_w);
+ struct bam_ch_info *d = &port->data_ch;
+ u32 sps_params;
+ int ret;
- spin_lock_irqsave(&port->port_lock, flags);
+ ret = usb_bam_connect(d->connection_idx, &d->src_pipe_idx,
+ &d->dst_pipe_idx);
+ if (ret) {
+ pr_err("%s: usb_bam_connect failed: err:%d\n",
+ __func__, ret);
+ return;
+ }
- if (!port || !port->port_usb)
- goto free_buf_out;
+ d->rx_req = usb_ep_alloc_request(port->port_usb->out, GFP_KERNEL);
+ if (!d->rx_req)
+ return;
- d = &port->data_ch;
+ d->rx_req->context = port;
+ d->rx_req->complete = gbam_endless_rx_complete;
+ d->rx_req->length = 0;
+ sps_params = (SPS_PARAMS_SPS_MODE | d->src_pipe_idx |
+ MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
+ d->rx_req->udc_priv = sps_params;
+ d->tx_req = usb_ep_alloc_request(port->port_usb->in, GFP_KERNEL);
+ if (!d->tx_req)
+ return;
- gbam_free_requests(port->port_usb->in, &d->tx_idle);
- gbam_free_requests(port->port_usb->out, &d->rx_idle);
+ d->tx_req->context = port;
+ d->tx_req->complete = gbam_endless_tx_complete;
+ d->tx_req->length = 0;
+ sps_params = (SPS_PARAMS_SPS_MODE | d->dst_pipe_idx |
+ MSM_VENDOR_ID) & ~SPS_PARAMS_TBE;
+ d->tx_req->udc_priv = sps_params;
- while ((skb = __skb_dequeue(&d->tx_skb_q)))
- dev_kfree_skb_any(skb);
+ /* queue in & out requests */
+ gbam_start_endless_rx(port);
+ gbam_start_endless_tx(port);
- while ((skb = __skb_dequeue(&d->rx_skb_q)))
- dev_kfree_skb_any(skb);
-
-free_buf_out:
- spin_unlock_irqrestore(&port->port_lock, flags);
+ pr_debug("%s: done\n", __func__);
}
/* BAM data channel ready, allow attempt to open */
@@ -673,6 +786,13 @@
}
}
+static void gbam2bam_port_free(int portno)
+{
+ struct gbam_port *port = bam2bam_ports[portno];
+
+ kfree(port);
+}
+
static int gbam_port_alloc(int portno)
{
struct gbam_port *port;
@@ -709,6 +829,32 @@
pdrv->driver.owner = THIS_MODULE;
platform_driver_register(pdrv);
+ pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+ return 0;
+}
+
+static int gbam2bam_port_alloc(int portno)
+{
+ struct gbam_port *port;
+ struct bam_ch_info *d;
+
+ port = kzalloc(sizeof(struct gbam_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->port_num = portno;
+
+ /* port initialization */
+ spin_lock_init(&port->port_lock);
+
+ INIT_WORK(&port->connect_w, gbam2bam_connect_work);
+ INIT_WORK(&port->disconnect_w, gbam2bam_disconnect_work);
+
+ /* data ch */
+ d = &port->data_ch;
+ d->port = port;
+ bam2bam_ports[portno] = port;
pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
@@ -820,7 +966,7 @@
static void gam_debugfs_init(void) { }
#endif
-void gbam_disconnect(struct grmnet *gr, u8 port_num)
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans)
{
struct gbam_port *port;
unsigned long flags;
@@ -828,8 +974,17 @@
pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
- if (port_num >= n_bam_ports) {
- pr_err("%s: invalid portno#%d\n", __func__, port_num);
+ if (trans == USB_GADGET_XPORT_BAM &&
+ port_num >= n_bam_ports) {
+ pr_err("%s: invalid bam portno#%d\n",
+ __func__, port_num);
+ return;
+ }
+
+ if (trans == USB_GADGET_XPORT_BAM2BAM &&
+ port_num >= n_bam2bam_ports) {
+ pr_err("%s: invalid bam2bam portno#%d\n",
+ __func__, port_num);
return;
}
@@ -837,24 +992,31 @@
pr_err("%s: grmnet port is null\n", __func__);
return;
}
+ if (trans == USB_GADGET_XPORT_BAM)
+ port = bam_ports[port_num].port;
+ else
+ port = bam2bam_ports[port_num];
- port = bam_ports[port_num].port;
d = &port->data_ch;
+ port->gr = gr;
- gbam_free_buffers(port);
+ if (trans == USB_GADGET_XPORT_BAM) {
+ gbam_free_buffers(port);
- spin_lock_irqsave(&port->port_lock, flags);
- port->port_usb = 0;
- spin_unlock_irqrestore(&port->port_lock, flags);
+ spin_lock_irqsave(&port->port_lock, flags);
+ port->port_usb = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
- /* disable endpoints */
- usb_ep_disable(gr->out);
- usb_ep_disable(gr->in);
+ /* disable endpoints */
+ usb_ep_disable(gr->out);
+ usb_ep_disable(gr->in);
+ }
queue_work(gbam_wq, &port->disconnect_w);
}
-int gbam_connect(struct grmnet *gr, u8 port_num)
+int gbam_connect(struct grmnet *gr, u8 port_num,
+ enum transport_type trans, u8 connection_idx)
{
struct gbam_port *port;
struct bam_ch_info *d;
@@ -863,7 +1025,12 @@
pr_debug("%s: grmnet:%p port#%d\n", __func__, gr, port_num);
- if (port_num >= n_bam_ports) {
+ if (trans == USB_GADGET_XPORT_BAM && port_num >= n_bam_ports) {
+ pr_err("%s: invalid portno#%d\n", __func__, port_num);
+ return -ENODEV;
+ }
+
+ if (trans == USB_GADGET_XPORT_BAM2BAM && port_num >= n_bam2bam_ports) {
pr_err("%s: invalid portno#%d\n", __func__, port_num);
return -ENODEV;
}
@@ -873,7 +1040,11 @@
return -ENODEV;
}
- port = bam_ports[port_num].port;
+ if (trans == USB_GADGET_XPORT_BAM)
+ port = bam_ports[port_num].port;
+ else
+ port = bam2bam_ports[port_num];
+
d = &port->data_ch;
ret = usb_ep_enable(gr->in, gr->in_desc);
@@ -896,29 +1067,35 @@
spin_lock_irqsave(&port->port_lock, flags);
port->port_usb = gr;
- d->to_host = 0;
- d->to_modem = 0;
- d->pending_with_bam = 0;
- d->tohost_drp_cnt = 0;
- d->tomodem_drp_cnt = 0;
+ if (trans == USB_GADGET_XPORT_BAM) {
+ d->to_host = 0;
+ d->to_modem = 0;
+ d->pending_with_bam = 0;
+ d->tohost_drp_cnt = 0;
+ d->tomodem_drp_cnt = 0;
+ }
spin_unlock_irqrestore(&port->port_lock, flags);
+ if (trans == USB_GADGET_XPORT_BAM2BAM)
+ d->connection_idx = connection_idx;
queue_work(gbam_wq, &port->connect_w);
return 0;
}
-int gbam_setup(unsigned int count)
+int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port)
{
int i;
int ret;
- pr_debug("%s: requested ports:%d\n", __func__, count);
+ pr_debug("%s: requested BAM ports:%d and BAM2BAM ports:%d\n",
+ __func__, no_bam_port, no_bam2bam_port);
- if (!count || count > BAM_N_PORTS) {
- pr_err("%s: Invalid num of ports count:%d\n",
- __func__, count);
+ if ((!no_bam_port && !no_bam2bam_port) || no_bam_port > BAM_N_PORTS
+ || no_bam2bam_port > BAM2BAM_N_PORTS) {
+ pr_err("%s: Invalid num of ports count:%d,%d\n",
+ __func__, no_bam_port, no_bam2bam_port);
return -EINVAL;
}
@@ -929,7 +1106,7 @@
return -ENOMEM;
}
- for (i = 0; i < count; i++) {
+ for (i = 0; i < no_bam_port; i++) {
n_bam_ports++;
ret = gbam_port_alloc(i);
if (ret) {
@@ -939,13 +1116,23 @@
}
}
+ for (i = 0; i < no_bam2bam_port; i++) {
+ n_bam2bam_ports++;
+ ret = gbam2bam_port_alloc(i);
+ if (ret) {
+ n_bam2bam_ports--;
+ pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+ goto free_bam_ports;
+ }
+ }
gbam_debugfs_init();
-
return 0;
+
free_bam_ports:
for (i = 0; i < n_bam_ports; i++)
gbam_port_free(i);
-
+ for (i = 0; i < n_bam2bam_ports; i++)
+ gbam2bam_port_free(i);
destroy_workqueue(gbam_wq);
return ret;
diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h
index d8de31e..fd1e124f 100644
--- a/drivers/usb/gadget/u_rmnet.h
+++ b/drivers/usb/gadget/u_rmnet.h
@@ -48,10 +48,10 @@
void (*connect)(struct grmnet *g);
};
-int gbam_setup(unsigned int count);
-int gbam_connect(struct grmnet *, u8 port_num);
-void gbam_disconnect(struct grmnet *, u8 port_num);
-
+int gbam_setup(unsigned int no_bam_port, unsigned int no_bam2bam_port);
+int gbam_connect(struct grmnet *gr, u8 port_num,
+ enum transport_type trans, u8 connection_idx);
+void gbam_disconnect(struct grmnet *gr, u8 port_num, enum transport_type trans);
int gsmd_ctrl_connect(struct grmnet *gr, int port_num);
void gsmd_ctrl_disconnect(struct grmnet *gr, u8 port_num);
int gsmd_ctrl_setup(unsigned int count);
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_smd.c b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
index 8b08b7a..90cbe54 100644
--- a/drivers/usb/gadget/u_rmnet_ctrl_smd.c
+++ b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
@@ -544,12 +544,13 @@
}
for (i = 0; i < count; i++) {
+ n_rmnet_ctrl_ports++;
ret = grmnet_ctrl_smd_port_alloc(i);
if (ret) {
pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+ n_rmnet_ctrl_ports--;
goto free_ctrl_smd_ports;
}
- n_rmnet_ctrl_ports++;
}
return 0;
diff --git a/drivers/usb/misc/diag_bridge.c b/drivers/usb/misc/diag_bridge.c
index 00b2ec1..9b60f70 100644
--- a/drivers/usb/misc/diag_bridge.c
+++ b/drivers/usb/misc/diag_bridge.c
@@ -31,6 +31,7 @@
struct usb_anchor submitted;
__u8 in_epAddr;
__u8 out_epAddr;
+ int err;
struct kref kref;
struct diag_bridge_ops *ops;
struct platform_device *pdev;
@@ -47,6 +48,7 @@
}
dev->ops = ops;
+ dev->err = 0;
return 0;
}
@@ -72,6 +74,12 @@
dev_dbg(&dev->udev->dev, "%s: status:%d actual:%d\n", __func__,
urb->status, urb->actual_length);
+ if (urb->status == -EPROTO) {
+ /* save error so that subsequent read/write returns ESHUTDOWN */
+ dev->err = urb->status;
+ return;
+ }
+
cbs->read_complete_cb(cbs->ctxt,
urb->transfer_buffer,
urb->transfer_buffer_length,
@@ -97,6 +105,10 @@
return -ENODEV;
}
+ /* if there was a previous unrecoverable error, just quit */
+ if (dev->err)
+ return -ESHUTDOWN;
+
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
dev_err(&dev->udev->dev, "unable to allocate urb\n");
@@ -129,6 +141,12 @@
dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+ if (urb->status == -EPROTO) {
+ /* save error so that subsequent read/write returns ESHUTDOWN */
+ dev->err = urb->status;
+ return;
+ }
+
cbs->write_complete_cb(cbs->ctxt,
urb->transfer_buffer,
urb->transfer_buffer_length,
@@ -154,6 +172,10 @@
return -ENODEV;
}
+ /* if there was a previous unrecoverable error, just quit */
+ if (dev->err)
+ return -ESHUTDOWN;
+
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
err("unable to allocate urb");
diff --git a/drivers/usb/misc/mdm_ctrl_bridge.c b/drivers/usb/misc/mdm_ctrl_bridge.c
index 0397428..11d388c 100644
--- a/drivers/usb/misc/mdm_ctrl_bridge.c
+++ b/drivers/usb/misc/mdm_ctrl_bridge.c
@@ -697,6 +697,8 @@
dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+ platform_device_del(dev->pdev);
+
kfree(dev->in_ctlreq);
kfree(dev->readbuf);
kfree(dev->intbuf);
@@ -704,7 +706,6 @@
usb_free_urb(dev->readurb);
usb_free_urb(dev->inturb);
- platform_device_del(dev->pdev);
__dev[id] = NULL;
ch_id--;
diff --git a/drivers/usb/misc/mdm_data_bridge.c b/drivers/usb/misc/mdm_data_bridge.c
index 7c44a9a..6fe5b1d 100644
--- a/drivers/usb/misc/mdm_data_bridge.c
+++ b/drivers/usb/misc/mdm_data_bridge.c
@@ -779,7 +779,7 @@
usb_get_dev(udev);
if (iface_num != DUN_IFACE_NUM && iface_num != TETHERED_RMNET_IFACE_NUM)
- return 0;
+ return -ENODEV;
numends = iface->cur_altsetting->desc.bNumEndpoints;
for (i = 0; i < numends; i++) {
diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c
index c55ba6e..79a2858 100644
--- a/drivers/usb/otg/msm_otg.c
+++ b/drivers/usb/otg/msm_otg.c
@@ -591,6 +591,7 @@
bool session_active;
u32 phy_ctrl_val = 0;
unsigned ret;
+ u32 portsc;
if (atomic_read(&motg->in_lpm))
return 0;
@@ -632,17 +633,21 @@
motg->lpm_flags |= PHY_OTG_COMP_DISABLED;
}
- /*
+ /* Set the PHCD bit, only if it is not set by the controller.
* PHY may take some time or even fail to enter into low power
* mode (LPM). Hence poll for 500 msec and reset the PHY and link
* in failure case.
*/
- writel(readl(USB_PORTSC) | PORTSC_PHCD, USB_PORTSC);
- while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
- if (readl(USB_PORTSC) & PORTSC_PHCD)
- break;
- udelay(1);
- cnt++;
+ portsc = readl_relaxed(USB_PORTSC);
+ if (!(portsc & PORTSC_PHCD)) {
+ writel_relaxed(portsc | PORTSC_PHCD,
+ USB_PORTSC);
+ while (cnt < PHY_SUSPEND_TIMEOUT_USEC) {
+ if (readl_relaxed(USB_PORTSC) & PORTSC_PHCD)
+ break;
+ udelay(1);
+ cnt++;
+ }
}
if (cnt >= PHY_SUSPEND_TIMEOUT_USEC) {
@@ -1653,6 +1658,12 @@
msm_otg_reset(otg);
msm_otg_init_sm(motg);
otg->state = OTG_STATE_B_IDLE;
+ if (!test_bit(B_SESS_VLD, &motg->inputs) &&
+ test_bit(ID, &motg->inputs)) {
+ pm_runtime_put_noidle(otg->dev);
+ pm_runtime_suspend(otg->dev);
+ break;
+ }
/* FALL THROUGH */
case OTG_STATE_B_IDLE:
dev_dbg(otg->dev, "OTG_STATE_B_IDLE state\n");
@@ -1850,7 +1861,7 @@
struct msm_otg *motg = the_msm_otg;
/* We depend on PMIC for only VBUS ON interrupt */
- if (!atomic_read(&motg->in_lpm) || !online)
+ if (!atomic_read(&motg->in_lpm) || !online || motg->async_int)
return;
/*
@@ -1978,7 +1989,7 @@
{
struct msm_otg *motg = s->private;
- seq_printf(s, chg_to_string(motg->chg_type));
+ seq_printf(s, "%s\n", chg_to_string(motg->chg_type));
return 0;
}
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index e4fad5e..64fc6ea 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -406,6 +406,10 @@
portdata = usb_get_serial_port_data(port);
intfdata = serial->private;
+ /* explicitly set the driver mode to raw */
+ tty->raw = 0;
+ tty->real_raw = 0;
+
dbg("%s", __func__);
/* Start reading from the IN endpoint */
diff --git a/drivers/video/msm/external_common.c b/drivers/video/msm/external_common.c
index 5d9795a..cd7d5c4 100644
--- a/drivers/video/msm/external_common.c
+++ b/drivers/video/msm/external_common.c
@@ -569,11 +569,27 @@
return ret;
}
+static ssize_t external_common_rda_hdmi_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ ssize_t ret;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d\n",
+ external_common_state->hdmi_sink);
+
+ DEV_DBG("%s: '%d'\n", __func__,
+ external_common_state->hdmi_sink);
+
+ return ret;
+}
+
+
static DEVICE_ATTR(video_mode, S_IRUGO | S_IWUGO,
external_common_rda_video_mode, external_common_wta_video_mode);
static DEVICE_ATTR(video_mode_str, S_IRUGO, external_common_rda_video_mode_str,
NULL);
static DEVICE_ATTR(connected, S_IRUGO, external_common_rda_connected, NULL);
+static DEVICE_ATTR(hdmi_mode, S_IRUGO, external_common_rda_hdmi_mode, NULL);
#ifdef CONFIG_FB_MSM_HDMI_COMMON
static DEVICE_ATTR(edid_modes, S_IRUGO, hdmi_common_rda_edid_modes, NULL);
static DEVICE_ATTR(hpd, S_IRUGO | S_IWUGO, hdmi_common_rda_hpd,
@@ -591,6 +607,7 @@
&dev_attr_video_mode.attr,
&dev_attr_video_mode_str.attr,
&dev_attr_connected.attr,
+ &dev_attr_hdmi_mode.attr,
#ifdef CONFIG_FB_MSM_HDMI_COMMON
&dev_attr_edid_modes.attr,
&dev_attr_hdcp.attr,
@@ -1002,7 +1019,11 @@
img_size_v = (((uint32)data_buf[0xE] & 0xF) << 8)
| data_buf[0xD];
- aspect_ratio_4_3 = (img_size_h * 3 == img_size_v * 4);
+ /*
+ * aspect ratio as 4:3 if within specificed range , rathaer than being
+ * absolute value
+ */
+ aspect_ratio_4_3 = (abs(img_size_h * 3 - img_size_v * 4) < 5) ? 1 : 0;
max_num_of_elements = sizeof(hdmi_edid_disp_mode_lut)
/ sizeof(*hdmi_edid_disp_mode_lut);
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
index 0f1d19b..e246339 100644
--- a/drivers/video/msm/hdmi_msm.c
+++ b/drivers/video/msm/hdmi_msm.c
@@ -2301,8 +2301,8 @@
aksv[4] = qfprom_aksv_1 & 0xFF;
/* check there are 20 ones in AKSV */
if (hdmi_msm_count_one(aksv, 5) != 20) {
- DEV_ERR("HDCP: AKSV read from QFPROM doesn't have\
- 20 1's and 20 0's, FAIL (AKSV=%02x%08x)\n",
+ DEV_ERR("HDCP: AKSV read from QFPROM doesn't have "
+ "20 1's and 20 0's, FAIL (AKSV=%02x%08x)\n",
qfprom_aksv_1, qfprom_aksv_0);
ret = -EINVAL;
goto error;
@@ -2332,9 +2332,9 @@
}
/* check there are 20 ones in BKSV */
if (hdmi_msm_count_one(bksv, 5) != 20) {
- DEV_ERR("HDCP: BKSV read from Sink doesn't have\
- 20 1's and 20 0's, FAIL (BKSV=\
- %02x%02x%02x%02x%02x)\n",
+ DEV_ERR("HDCP: BKSV read from Sink doesn't have "
+ "20 1's and 20 0's, FAIL (BKSV="
+ "%02x%02x%02x%02x%02x)\n",
bksv[4], bksv[3], bksv[2], bksv[1], bksv[0]);
ret = -EINVAL;
goto error;
@@ -2507,8 +2507,8 @@
hpd_int_status = HDMI_INP_ND(0x0250);
/* HDMI_HPD_INT_CTRL[0x0254] */
hpd_int_ctrl = HDMI_INP_ND(0x0254);
- DEV_DBG("[SR-DEUG]: HPD_INTR_CTRL=[%u] HPD_INTR_STATUS=[%u]\
- before reading R0'\n", hpd_int_ctrl, hpd_int_status);
+ DEV_DBG("[SR-DEUG]: HPD_INTR_CTRL=[%u] HPD_INTR_STATUS=[%u] "
+ "before reading R0'\n", hpd_int_ctrl, hpd_int_status);
/*
* HDCP Compliace Test case 1B-01:
@@ -2525,12 +2525,12 @@
goto error;
}
+ DEV_DBG("HDCP: R0'=%02x%02x\n", buf[1], buf[0]);
+ INIT_COMPLETION(hdmi_msm_state->hdcp_success_done);
/* 0x013C HDCP_RCVPORT_DATA2_0
[15:0] LINK0_RI */
HDMI_OUTP(0x013C, (((uint32)buf[1]) << 8) | buf[0]);
- DEV_DBG("HDCP: R0'=%02x%02x\n", buf[1], buf[0]);
- INIT_COMPLETION(hdmi_msm_state->hdcp_success_done);
timeout_count = wait_for_completion_interruptible_timeout(
&hdmi_msm_state->hdcp_success_done, HZ*2);
@@ -2968,8 +2968,8 @@
hdmi_msm_state->hdcp_activating = FALSE;
mutex_unlock(&hdmi_msm_state_mutex);
if (hdmi_msm_state->hpd_during_auth) {
- DEV_WARN("Calling Deauthentication: HPD occured during\
- authentication from [%s]\n", __func__);
+ DEV_WARN("Calling Deauthentication: HPD occured during "
+ "authentication from [%s]\n", __func__);
hdcp_deauthenticate();
mutex_lock(&hdcp_auth_state_mutex);
hdmi_msm_state->hpd_during_auth = FALSE;
@@ -3398,18 +3398,6 @@
HDMI_OUTP(0x002C, audio_info_ctrl_reg);
}
-static void hdmi_msm_audio_ctrl_setup(boolean enabled, int delay)
-{
- uint32 audio_pkt_ctrl_reg = 0;
-
- /* Enable Packet Transmission */
- audio_pkt_ctrl_reg |= enabled ? 0x00000001 : 0;
- audio_pkt_ctrl_reg |= (delay << 4);
-
- /* HDMI_AUDIO_PKT_CTRL1[0x0020] */
- HDMI_OUTP(0x0020, audio_pkt_ctrl_reg);
-}
-
static void hdmi_msm_en_gc_packet(boolean av_mute_is_requested)
{
/* HDMI_GC[0x0040] */
@@ -3491,9 +3479,11 @@
{
msm_hdmi_sample_rate = rate;
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
if (hdmi_msm_has_hdcp())
hdcp_deauthenticate();
else
+#endif
hdmi_msm_turn_on();
}
EXPORT_SYMBOL(hdmi_msm_audio_sample_rate_reset);
@@ -3544,7 +3534,6 @@
}
}
hdmi_msm_audio_info_setup(FALSE, 0, 0, FALSE);
- hdmi_msm_audio_ctrl_setup(FALSE, 0);
hdmi_msm_audio_acr_setup(FALSE, 0, 0, 0);
DEV_INFO("HDMI Audio: Disabled\n");
return 0;
@@ -4288,9 +4277,13 @@
if (rc)
goto error;
- if (hdmi_msm_has_hdcp())
+ if (hdmi_msm_has_hdcp()) {
+ /* Don't Set Encryption in case of non HDCP builds */
+ external_common_state->present_hdcp = FALSE;
+#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
external_common_state->present_hdcp = TRUE;
- else {
+#endif
+ } else {
external_common_state->present_hdcp = FALSE;
#ifdef CONFIG_FB_MSM_HDMI_MSM_PANEL_HDCP_SUPPORT
/*
@@ -4459,7 +4452,7 @@
{
int rc;
- if (cpu_is_msm8627())
+ if (cpu_is_msm8930())
return 0;
if (msm_fb_detect_client("hdmi_msm"))
diff --git a/drivers/video/msm/mdp.c b/drivers/video/msm/mdp.c
index 462ede1..e5c8987 100644
--- a/drivers/video/msm/mdp.c
+++ b/drivers/video/msm/mdp.c
@@ -31,7 +31,7 @@
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
-
+#include <linux/memory_alloc.h>
#include <asm/system.h>
#include <asm/mach-types.h>
#include <linux/semaphore.h>
@@ -52,6 +52,7 @@
int mdp_rev;
static struct regulator *footswitch;
+static unsigned int mdp_footswitch_on;
struct completion mdp_ppp_comp;
struct semaphore mdp_ppp_mutex;
@@ -1122,18 +1123,6 @@
__func__, mdp_hw_revision);
}
-int mdp4_writeback_offset(void)
-{
- int off = 0;
-
- if (mdp_pdata->writeback_offset)
- off = mdp_pdata->writeback_offset();
-
- pr_debug("%s: writeback_offset=%d %x\n", __func__, off, off);
-
- return off;
-}
-
#ifdef CONFIG_FB_MSM_MDP40
static void configure_mdp_core_clk_table(uint32 min_clk_rate)
{
@@ -1251,8 +1240,10 @@
footswitch = regulator_get(NULL, "fs_mdp");
if (IS_ERR(footswitch))
footswitch = NULL;
- else
+ else {
regulator_enable(footswitch);
+ mdp_footswitch_on = 1;
+ }
mdp_clk = clk_get(NULL, "mdp_clk");
if (IS_ERR(mdp_clk)) {
@@ -1312,6 +1303,7 @@
#endif
if ((pdev->id == 0) && (pdev->num_resources > 0)) {
+
mdp_pdata = pdev->dev.platform_data;
size = resource_size(&pdev->resource[0]);
@@ -1349,6 +1341,28 @@
#endif
mdp_resource_initialized = 1;
+
+ if (!mdp_pdata)
+ return 0;
+
+ size = mdp_pdata->mdp_writeback_size_ov0 +
+ mdp_pdata->mdp_writeback_size_ov1;
+ if (size) {
+ mdp_pdata->mdp_writeback_phys =
+ (void *)allocate_contiguous_memory_nomap
+ (size,
+ mdp_pdata->mdp_writeback_memtype,
+ 4); /* align to word size */
+ if (mdp_pdata->mdp_writeback_phys) {
+ pr_info("allocating %d bytes at %p for mdp writeback\n",
+ size, mdp_pdata->mdp_writeback_phys);
+ } else {
+ pr_err("%s cannot allocate memory for mdp writeback!\n",
+ __func__);
+ }
+ } else {
+ mdp_pdata->mdp_writeback_phys = 0;
+ }
return 0;
}
@@ -1619,6 +1633,20 @@
}
}
#endif
+
+ if (mdp_pdata && mdp_pdata->mdp_writeback_phys) {
+ mfd->writeback_overlay0_phys =
+ (mdp_pdata->mdp_writeback_size_ov0) ?
+ mdp_pdata->mdp_writeback_phys : 0;
+ mfd->writeback_overlay1_phys =
+ (mdp_pdata->mdp_writeback_size_ov1) ?
+ (mdp_pdata->mdp_writeback_phys +
+ mdp_pdata->mdp_writeback_size_ov0) : 0;
+ } else {
+ mfd->writeback_overlay0_phys = 0;
+ mfd->writeback_overlay1_phys = 0;
+ }
+
/* set driver data */
platform_set_drvdata(msm_fb_dev, mfd);
@@ -1644,6 +1672,28 @@
return rc;
}
+void mdp_footswitch_ctrl(boolean on)
+{
+ mutex_lock(&mdp_suspend_mutex);
+ if (!mdp_suspended || mdp4_extn_disp || !footswitch ||
+ mdp_rev <= MDP_REV_41) {
+ mutex_unlock(&mdp_suspend_mutex);
+ return;
+ }
+
+ if (on && !mdp_footswitch_on) {
+ pr_debug("Enable MDP FS\n");
+ regulator_enable(footswitch);
+ mdp_footswitch_on = 1;
+ } else if (!on && mdp_footswitch_on) {
+ pr_debug("Disable MDP FS\n");
+ regulator_disable(footswitch);
+ mdp_footswitch_on = 0;
+ }
+
+ mutex_unlock(&mdp_suspend_mutex);
+}
+
#ifdef CONFIG_PM
static void mdp_suspend_sub(void)
{
@@ -1688,15 +1738,12 @@
#ifdef CONFIG_FB_MSM_DTV
mdp4_dtv_set_black_screen();
#endif
- if (footswitch && mdp_rev > MDP_REV_42)
- regulator_disable(footswitch);
+ mdp_footswitch_ctrl(FALSE);
}
static void mdp_early_resume(struct early_suspend *h)
{
- if (footswitch && mdp_rev > MDP_REV_42)
- regulator_enable(footswitch);
-
+ mdp_footswitch_ctrl(TRUE);
mutex_lock(&mdp_suspend_mutex);
mdp_suspended = FALSE;
mutex_unlock(&mdp_suspend_mutex);
diff --git a/drivers/video/msm/mdp.h b/drivers/video/msm/mdp.h
index 8817213..0a0609e 100644
--- a/drivers/video/msm/mdp.h
+++ b/drivers/video/msm/mdp.h
@@ -722,6 +722,7 @@
int mdp_start_histogram(struct fb_info *info);
int mdp_stop_histogram(struct fb_info *info);
int mdp_histogram_ctrl(boolean en);
+void mdp_footswitch_ctrl(boolean on);
#ifdef CONFIG_FB_MSM_MDP303
static inline void mdp4_dsi_cmd_dma_busy_wait(struct msm_fb_data_type *mfd)
diff --git a/drivers/video/msm/mdp4.h b/drivers/video/msm/mdp4.h
index 2bcac9c..e170702 100644
--- a/drivers/video/msm/mdp4.h
+++ b/drivers/video/msm/mdp4.h
@@ -219,6 +219,7 @@
#define MDP4_OP_SCALEX_FIR (0 << 2)
#define MDP4_OP_SCALEX_MN_PHASE (1 << 2)
#define MDP4_OP_SCALEX_PIXEL_RPT (2 << 2)
+#define MDP4_OP_SCALE_RGB_ENHANCED (1 << 4)
#define MDP4_OP_SCALE_RGB_PIXEL_RPT (0 << 3)
#define MDP4_OP_SCALE_RGB_BILINEAR (1 << 3)
#define MDP4_OP_SCALE_ALPHA_PIXEL_RPT (0 << 2)
@@ -635,7 +636,6 @@
struct msmfb_overlay_3d *r3d);
int mdp4_mixer_info(int mixer_num, struct mdp_mixer_info *info);
-int mdp4_writeback_offset(void);
void mdp_dmap_vsync_set(int enable);
int mdp_dmap_vsync_get(void);
diff --git a/drivers/video/msm/mdp4_dtv.c b/drivers/video/msm/mdp4_dtv.c
index a44f7c0..ba774b9 100644
--- a/drivers/video/msm/mdp4_dtv.c
+++ b/drivers/video/msm/mdp4_dtv.c
@@ -109,6 +109,7 @@
clk_disable(ebi1_clk);
#endif
mdp4_extn_disp = 0;
+ mdp_footswitch_ctrl(FALSE);
return ret;
}
@@ -126,6 +127,7 @@
pm_qos_rate = panel_pixclock_freq / 1000 ;
else
pm_qos_rate = 58000;
+ mdp_footswitch_ctrl(TRUE);
mdp4_extn_disp = 1;
#ifdef CONFIG_MSM_BUS_SCALING
if (dtv_bus_scale_handle > 0)
diff --git a/drivers/video/msm/mdp4_overlay.c b/drivers/video/msm/mdp4_overlay.c
index 72a11a2..c56828d 100644
--- a/drivers/video/msm/mdp4_overlay.c
+++ b/drivers/video/msm/mdp4_overlay.c
@@ -365,15 +365,16 @@
pipe->op_mode |= MDP4_OP_SCALEY_EN;
if (pipe->pipe_num >= OVERLAY_PIPE_VG1) {
- if (pipe->alpha_enable)
+ if (pipe->alpha_enable && pipe->dst_h > pipe->src_h)
pipe->op_mode |= MDP4_OP_SCALEY_PIXEL_RPT;
else if (pipe->dst_h <= (pipe->src_h / 4))
pipe->op_mode |= MDP4_OP_SCALEY_MN_PHASE;
else
pipe->op_mode |= MDP4_OP_SCALEY_FIR;
} else { /* RGB pipe */
- pipe->op_mode |= MDP4_OP_SCALE_RGB_BILINEAR;
- pipe->op_mode |= MDP4_OP_SCALE_ALPHA_BILINEAR;
+ pipe->op_mode |= MDP4_OP_SCALE_RGB_ENHANCED |
+ MDP4_OP_SCALE_RGB_BILINEAR |
+ MDP4_OP_SCALE_ALPHA_BILINEAR;
}
pipe->phasey_step = mdp4_scale_phase_step(29,
@@ -386,15 +387,16 @@
pipe->op_mode |= MDP4_OP_SCALEX_EN;
if (pipe->pipe_num >= OVERLAY_PIPE_VG1) {
- if (pipe->alpha_enable)
+ if (pipe->alpha_enable && pipe->dst_w > pipe->src_w)
pipe->op_mode |= MDP4_OP_SCALEX_PIXEL_RPT;
else if (pipe->dst_w <= (pipe->src_w / 4))
pipe->op_mode |= MDP4_OP_SCALEX_MN_PHASE;
else
pipe->op_mode |= MDP4_OP_SCALEX_FIR;
} else { /* RGB pipe */
- pipe->op_mode |= MDP4_OP_SCALE_RGB_BILINEAR;
- pipe->op_mode |= MDP4_OP_SCALE_ALPHA_BILINEAR;
+ pipe->op_mode |= MDP4_OP_SCALE_RGB_ENHANCED |
+ MDP4_OP_SCALE_RGB_BILINEAR |
+ MDP4_OP_SCALE_ALPHA_BILINEAR;
}
pipe->phasex_step = mdp4_scale_phase_step(29,
@@ -1960,16 +1962,16 @@
if (req->flags & MDP_DEINTERLACE)
return OVERLAY_PERF_LEVEL1;
+ if (ctrl->plist[OVERLAY_PIPE_VG1].pipe_used &&
+ ctrl->plist[OVERLAY_PIPE_VG2].pipe_used)
+ return OVERLAY_PERF_LEVEL1;
+
if (mdp4_overlay_is_rgb_type(req->src.format) && is_fg &&
((req->src.width * req->src.height) <= OVERLAY_WSVGA_SIZE))
return OVERLAY_PERF_LEVEL4;
else if (mdp4_overlay_is_rgb_type(req->src.format))
return OVERLAY_PERF_LEVEL1;
- if (ctrl->plist[OVERLAY_PIPE_VG1].pipe_used &&
- ctrl->plist[OVERLAY_PIPE_VG2].pipe_used)
- return OVERLAY_PERF_LEVEL1;
-
if (req->src.width*req->src.height <= OVERLAY_VGA_SIZE)
return OVERLAY_PERF_LEVEL3;
else if (req->src.width*req->src.height <= OVERLAY_720P_TILE_SIZE)
@@ -2001,6 +2003,9 @@
static void mdp4_overlay_update_blt_mode(struct msm_fb_data_type *mfd)
{
+ if (mfd->use_ov0_blt == mfd->ov0_blt_state)
+ return;
+
if (mfd->use_ov0_blt) {
if (mfd->panel_info.type == LCDC_PANEL)
mdp4_lcdc_overlay_blt_start(mfd);
@@ -2016,6 +2021,7 @@
else if (ctrl->panel_mode & MDP4_PANEL_DSI_CMD)
mdp4_dsi_overlay_blt_stop(mfd);
}
+ mfd->ov0_blt_state = mfd->use_ov0_blt;
}
static u32 mdp4_overlay_blt_enable(struct mdp_overlay *req,
@@ -2237,8 +2243,6 @@
mfd->use_ov0_blt &= ~(1 << (pipe->pipe_ndx-1));
mdp4_overlay_update_blt_mode(mfd);
- mfd->ov0_blt_state = mfd->use_ov0_blt;
-
}
else { /* mixer1, DTV, ATV */
if (ctrl->panel_mode & MDP4_PANEL_DTV)
@@ -2463,10 +2467,8 @@
}
}
- if (mfd->use_ov0_blt != mfd->ov0_blt_state) {
+ if (mfd->use_ov0_blt)
mdp4_overlay_update_blt_mode(mfd);
- mfd->ov0_blt_state = mfd->use_ov0_blt;
- }
if (pipe->pipe_num >= OVERLAY_PIPE_VG1)
mdp4_overlay_vg_setup(pipe); /* video/graphic pipe */
@@ -2501,15 +2503,20 @@
}
#endif
} else {
+
/* primary interface */
ctrl->mixer0_played++;
if (ctrl->panel_mode & MDP4_PANEL_LCDC) {
mdp4_overlay_reg_flush(pipe, 1);
+ if (!mfd->use_ov0_blt)
+ mdp4_overlay_update_blt_mode(mfd);
mdp4_overlay_lcdc_vsync_push(mfd, pipe);
}
#ifdef CONFIG_FB_MSM_MIPI_DSI
else if (ctrl->panel_mode & MDP4_PANEL_DSI_VIDEO) {
mdp4_overlay_reg_flush(pipe, 1);
+ if (!mfd->use_ov0_blt)
+ mdp4_overlay_update_blt_mode(mfd);
mdp4_overlay_dsi_video_vsync_push(mfd, pipe);
}
#endif
diff --git a/drivers/video/msm/mdp4_overlay_dsi_cmd.c b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
index 4479ece..484ed7d 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_cmd.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_cmd.c
@@ -45,8 +45,6 @@
struct timer_list dsi_clock_timer;
-static int writeback_offset;
-
void mdp4_overlay_dsi_state_set(int state)
{
unsigned long flag;
@@ -119,7 +117,6 @@
void mdp4_overlay_update_dsi_cmd(struct msm_fb_data_type *mfd)
{
MDPIBUF *iBuf = &mfd->ibuf;
- struct fb_info *fbi = mfd->fbi;
uint8 *src;
int ptype;
struct mdp4_overlay_pipe *pipe;
@@ -159,14 +156,9 @@
dsi_pipe = pipe; /* keep it */
- writeback_offset = mdp4_writeback_offset();
+ pipe->blt_base = (ulong) mfd->writeback_overlay0_phys;
+ pipe->blt_addr = 0;
- if (writeback_offset > 0) {
- pipe->blt_base = (ulong)fbi->fix.smem_start;
- pipe->blt_base += writeback_offset;
- } else {
- pipe->blt_base = 0;
- }
} else {
pipe = dsi_pipe;
}
@@ -351,7 +343,7 @@
int mdp4_dsi_overlay_blt_offset(struct msm_fb_data_type *mfd,
struct msmfb_overlay_blt *req)
{
- req->offset = writeback_offset;
+ req->offset = 0;
req->width = dsi_pipe->src_width;
req->height = dsi_pipe->src_height;
req->bpp = dsi_pipe->bpp;
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 938d7c6..d68396a 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -37,8 +37,6 @@
static int first_pixel_start_x;
static int first_pixel_start_y;
-static int writeback_offset;
-
static struct mdp4_overlay_pipe *dsi_pipe;
static struct completion dsi_video_comp;
@@ -141,14 +139,9 @@
dsi_pipe = pipe; /* keep it */
init_completion(&dsi_video_comp);
- writeback_offset = mdp4_writeback_offset();
+ pipe->blt_base = (ulong) mfd->writeback_overlay0_phys;
+ pipe->blt_addr = 0;
- if (writeback_offset > 0) {
- pipe->blt_base = (ulong)fbi->fix.smem_start;
- pipe->blt_base += writeback_offset;
- } else {
- pipe->blt_base = 0;
- }
} else {
pipe = dsi_pipe;
}
@@ -634,7 +627,7 @@
int mdp4_dsi_video_overlay_blt_offset(struct msm_fb_data_type *mfd,
struct msmfb_overlay_blt *req)
{
- req->offset = writeback_offset;
+ req->offset = 0;
req->width = dsi_pipe->src_width;
req->height = dsi_pipe->src_height;
req->bpp = dsi_pipe->bpp;
diff --git a/drivers/video/msm/mdp4_overlay_lcdc.c b/drivers/video/msm/mdp4_overlay_lcdc.c
index f318691..1dbbf94 100644
--- a/drivers/video/msm/mdp4_overlay_lcdc.c
+++ b/drivers/video/msm/mdp4_overlay_lcdc.c
@@ -42,8 +42,6 @@
int first_pixel_start_x;
int first_pixel_start_y;
-static int writeback_offset;
-
static struct mdp4_overlay_pipe *lcdc_pipe;
static struct completion lcdc_comp;
@@ -128,14 +126,9 @@
lcdc_pipe = pipe; /* keep it */
init_completion(&lcdc_comp);
- writeback_offset = mdp4_writeback_offset();
+ pipe->blt_base = (ulong) mfd->writeback_overlay0_phys;
+ pipe->blt_addr = 0;
- if (writeback_offset > 0) {
- pipe->blt_base = (ulong)fbi->fix.smem_start;
- pipe->blt_base += writeback_offset;
- } else {
- pipe->blt_base = 0;
- }
} else {
pipe = lcdc_pipe;
}
@@ -507,7 +500,7 @@
int mdp4_lcdc_overlay_blt_offset(struct msm_fb_data_type *mfd,
struct msmfb_overlay_blt *req)
{
- req->offset = writeback_offset;
+ req->offset = 0;
req->width = lcdc_pipe->src_width;
req->height = lcdc_pipe->src_height;
req->bpp = lcdc_pipe->bpp;
diff --git a/drivers/video/msm/mdp4_overlay_writeback.c b/drivers/video/msm/mdp4_overlay_writeback.c
index 4fcff20..cd027e9 100644
--- a/drivers/video/msm/mdp4_overlay_writeback.c
+++ b/drivers/video/msm/mdp4_overlay_writeback.c
@@ -85,7 +85,7 @@
ptype = mdp4_overlay_format2type(format);
if (ptype < 0)
pr_err("%s: format2type failed\n", __func__);
- pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER1, 0);
+ pipe = mdp4_overlay_pipe_alloc(ptype, MDP4_MIXER1);
if (pipe == NULL)
pr_info("%s: pipe_alloc failed\n", __func__);
pipe->pipe_used++;
@@ -331,8 +331,6 @@
(unsigned int)writeback_pipe->blt_addr);
mdp4_writeback_kickoff_ui(mfd, writeback_pipe);
- mdp4_stat.kickoff_writeback++;
-
/* signal if pan function is waiting for the
* update completion */
if (mfd->pan_waiting) {
diff --git a/drivers/video/msm/mhl_api.h b/drivers/video/msm/mhl_api.h
new file mode 100644
index 0000000..3f70dac
--- /dev/null
+++ b/drivers/video/msm/mhl_api.h
@@ -0,0 +1,26 @@
+
+/* Copyright (c) 2011, 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 __MHL_API_H__
+#define __MHL_API_H__
+
+#ifdef CONFIG_MHL
+bool mhl_is_connected(void);
+#else
+static bool mhl_is_connected(void)
+{
+ return false;
+}
+#endif
+
+#endif /* __MHL_API_H__ */
diff --git a/drivers/video/msm/mipi_dsi_host.c b/drivers/video/msm/mipi_dsi_host.c
index 35af84d..0a80363 100644
--- a/drivers/video/msm/mipi_dsi_host.c
+++ b/drivers/video/msm/mipi_dsi_host.c
@@ -792,6 +792,11 @@
uint32 dsi_ctrl, intr_ctrl;
uint32 data;
+ if (mdp_rev > MDP_REV_41 || mdp_rev == MDP_REV_303)
+ pinfo->rgb_swap = DSI_RGB_SWAP_RGB;
+ else
+ pinfo->rgb_swap = DSI_RGB_SWAP_BGR;
+
if (pinfo->mode == DSI_VIDEO_MODE) {
data = 0;
if (pinfo->pulse_mode_hsa_he)
@@ -811,11 +816,6 @@
data |= (pinfo->vc & 0x03);
MIPI_OUTP(MIPI_DSI_BASE + 0x000c, data);
- if (mdp_rev > MDP_REV_41 || mdp_rev == MDP_REV_303)
- pinfo->rgb_swap = DSI_RGB_SWAP_RGB;
- else
- pinfo->rgb_swap = DSI_RGB_SWAP_BGR;
-
data = 0;
data |= ((pinfo->rgb_swap & 0x07) << 12);
if (pinfo->b_sel)
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
index de2734d..9ee8972 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -166,6 +166,8 @@
struct completion msmfb_update_notify;
struct completion msmfb_no_update_notify;
u32 ov_start, ov_end;
+ void *writeback_overlay0_phys;
+ void *writeback_overlay1_phys;
struct mutex writeback_mutex;
struct mutex unregister_mutex;
struct list_head writeback_busy_queue;
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.h b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.h
index cc0acc3..1144166 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.h
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl.h
@@ -293,6 +293,7 @@
u32 prev_ip_frm_tag;
u32 cont_mode;
u32 reconfig_detected;
+ u32 dmx_disable;
};
union ddl_codec_data{
struct ddl_codec_data_hdr hdr;
@@ -434,6 +435,7 @@
void ddl_decoder_chroma_dpb_change(struct ddl_client_context *ddl);
u32 ddl_check_reconfig(struct ddl_client_context *ddl);
void ddl_handle_reconfig(u32 res_change, struct ddl_client_context *ddl);
+void ddl_fill_dec_desc_buffer(struct ddl_client_context *ddl);
#ifdef DDL_BUF_LOG
void ddl_list_buffers(struct ddl_client_context *ddl);
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
index 45b22ea..6078821 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_helper.c
@@ -237,7 +237,6 @@
struct ddl_frame_data_tag *frame;
u32 luma[DDL_MAX_BUFFER_COUNT], chroma[DDL_MAX_BUFFER_COUNT];
u32 mv[DDL_MAX_BUFFER_COUNT], luma_size, i, dpb;
-
frame = &decoder->dp_buf.dec_pic_buffers[0];
luma_size = ddl_get_yuv_buf_size(decoder->frame_size.width,
decoder->frame_size.height, DDL_YUV_BUF_TYPE_TILE);
@@ -248,9 +247,16 @@
dpb = DDL_MAX_BUFFER_COUNT;
for (i = 0; i < dpb; i++) {
if (frame[i].vcd_frm.virtual) {
- memset(frame[i].vcd_frm.virtual, 0x10101010, luma_size);
- memset(frame[i].vcd_frm.virtual + luma_size, 0x80808080,
+ if (luma_size <= frame[i].vcd_frm.alloc_len) {
+ memset(frame[i].vcd_frm.virtual,
+ 0x10101010, luma_size);
+ memset(frame[i].vcd_frm.virtual + luma_size,
+ 0x80808080,
frame[i].vcd_frm.alloc_len - luma_size);
+ } else {
+ DDL_MSG_ERROR("luma size error");
+ return VCD_ERR_FAIL;
+ }
}
luma[i] = DDL_OFFSET(ddl_context->dram_base_a.
@@ -697,6 +703,9 @@
DDL_KILO_BYTE(2));
if (!ptr)
status = VCD_ERR_ALLOC_FAIL;
+ else
+ memset(dec_bufs->desc.align_virtual_addr,
+ 0, buf_size.sz_desc);
}
if (status)
ddl_free_dec_hw_buffers(ddl);
@@ -973,3 +982,15 @@
}
}
+void ddl_fill_dec_desc_buffer(struct ddl_client_context *ddl)
+{
+ struct ddl_decoder_data *decoder = &ddl->codec_data.decoder;
+ struct vcd_frame_data *ip_bitstream = &(ddl->input_frame.vcd_frm);
+ struct ddl_buf_addr *dec_desc_buf = &(decoder->hw_bufs.desc);
+
+ if (ip_bitstream->desc_buf &&
+ ip_bitstream->desc_size < DDL_KILO_BYTE(128))
+ memcpy(dec_desc_buf->align_virtual_addr,
+ ip_bitstream->desc_buf,
+ ip_bitstream->desc_size);
+}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c
index a8aa44a..5485335 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_properties.c
@@ -13,6 +13,7 @@
#include "vcd_ddl.h"
#include "vcd_ddl_metadata.h"
+#include "vcd_res_tracker_api.h"
static u32 ddl_set_dec_property(struct ddl_client_context *pddl,
struct vcd_property_hdr *property_hdr, void *property_value);
@@ -411,6 +412,24 @@
}
}
break;
+ case VCD_I_DISABLE_DMX:
+ {
+ int disable_dmx_allowed = 0;
+ DDL_MSG_LOW("Set property VCD_I_DISABLE_DMX\n");
+ if (res_trk_get_disable_dmx() &&
+ ((decoder->codec.codec == VCD_CODEC_H264) ||
+ (decoder->codec.codec == VCD_CODEC_VC1) ||
+ (decoder->codec.codec == VCD_CODEC_VC1_RCV)))
+ disable_dmx_allowed = 1;
+
+ if (sizeof(u32) == property_hdr->sz &&
+ DDLCLIENT_STATE_IS(ddl, DDL_CLIENT_OPEN) &&
+ disable_dmx_allowed) {
+ decoder->dmx_disable = *(u32 *)property_value;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ }
+ break;
default:
vcd_status = VCD_ERR_ILLEGAL_OP;
break;
@@ -901,6 +920,9 @@
property_value);
vcd_status = VCD_S_SUCCESS;
break;
+ case VCD_I_META_BUFFER_MODE:
+ vcd_status = VCD_S_SUCCESS;
+ break;
default:
DDL_MSG_ERROR("INVALID ID %d\n", (int)property_hdr->prop_id);
vcd_status = VCD_ERR_ILLEGAL_OP;
@@ -1069,6 +1091,18 @@
vcd_status = VCD_S_SUCCESS;
}
break;
+ case VCD_I_DISABLE_DMX_SUPPORT:
+ if (sizeof(u32) == property_hdr->sz) {
+ *(u32 *)property_value = res_trk_get_disable_dmx();
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
+ case VCD_I_DISABLE_DMX:
+ if (sizeof(u32) == property_hdr->sz) {
+ *(u32 *)property_value = decoder->dmx_disable;
+ vcd_status = VCD_S_SUCCESS;
+ }
+ break;
default:
vcd_status = VCD_ERR_ILLEGAL_OP;
break;
@@ -1474,6 +1508,7 @@
decoder->field_needed_for_prev_ip = 0;
decoder->cont_mode = 0;
decoder->reconfig_detected = false;
+ decoder->dmx_disable = false;
ddl_set_default_metadata_flag(ddl);
ddl_set_default_decoder_buffer_req(decoder, true);
}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c
index df65d26..521e3b6 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_utils.c
@@ -65,7 +65,7 @@
addr->alloc_handle = ion_alloc(
ddl_context->video_ion_client, alloc_size, SZ_4K,
(1<<res_trk_get_mem_type()));
- if (!addr->alloc_handle) {
+ if (IS_ERR_OR_NULL(addr->alloc_handle)) {
DDL_MSG_ERROR("%s() :DDL ION alloc failed\n",
__func__);
goto bail_out;
@@ -145,7 +145,7 @@
return;
}
if (ddl_context->video_ion_client) {
- if (addr->alloc_handle) {
+ if (!IS_ERR_OR_NULL(addr->alloc_handle)) {
ion_free(ddl_context->video_ion_client,
addr->alloc_handle);
}
diff --git a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_vidc.c b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_vidc.c
index cf8f712..226bab5 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_vidc.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vcd_ddl_vidc.c
@@ -818,7 +818,8 @@
if (vidc_msg_timing)
ddl_set_core_start_time(__func__, DEC_OP_TIME);
ddl_decoder_dpb_transact(decoder, NULL, DDL_DPB_OP_INIT);
- ddl_decoder_dpb_init(ddl);
+ if (ddl_decoder_dpb_init(ddl) == VCD_ERR_FAIL)
+ return VCD_ERR_FAIL;
DDL_MSG_LOW("ddl_state_transition: %s ~~> DDL_CLIENT_WAIT_FOR_DPBDONE",
ddl_get_state_string(ddl->client_state));
ddl->client_state = DDL_CLIENT_WAIT_FOR_DPBDONE;
@@ -846,6 +847,7 @@
ddl_context->dram_base_a, ddl->shared_mem
[ddl->command_channel]);
init_buf_param.dpb_count = decoder->dp_buf.no_of_dec_pic_buf;
+ init_buf_param.dmx_disable = decoder->dmx_disable;
ddl_context->vidc_decode_init_buffers[ddl->command_channel] (
&init_buf_param);
return VCD_S_SUCCESS;
@@ -892,6 +894,9 @@
dec_param.release_dpb_bit_mask = dpb_mask->hw_mask;
dec_param.decode = VIDC_1080P_DEC_TYPE_FRAME_DATA;
dec_param.dpb_count = decoder->dp_buf.no_of_dec_pic_buf;
+ dec_param.dmx_disable = decoder->dmx_disable;
+ if (decoder->dmx_disable)
+ ddl_fill_dec_desc_buffer(ddl);
if (decoder->flush_pending) {
dec_param.dpb_flush = true;
decoder->flush_pending = false;
diff --git a/drivers/video/msm/vidc/1080p/ddl/vidc.c b/drivers/video/msm/vidc/1080p/ddl/vidc.c
index 5b4cdee..75014cc 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vidc.c
+++ b/drivers/video/msm/vidc/1080p/ddl/vidc.c
@@ -550,7 +550,6 @@
void vidc_1080p_decode_seq_start_ch0(
struct vidc_1080p_dec_seq_start_param *param)
{
-
VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
VIDC_HWIO_OUT(REG_666957, VIDC_1080P_INIT_CH_INST_ID);
VIDC_HWIO_OUT(REG_117192,
@@ -563,6 +562,7 @@
VIDC_HWIO_OUT(REG_190381, param->stream_buffersize);
VIDC_HWIO_OUT(REG_85655, param->descriptor_buffer_size);
VIDC_HWIO_OUT(REG_889944, param->shared_mem_addr_offset);
+ VIDC_HWIO_OUT(REG_404623, 0);
VIDC_HWIO_OUT(REG_397087, param->cmd_seq_num);
VIDC_HWIO_OUT(REG_666957, VIDC_1080P_DEC_TYPE_SEQ_HEADER |
param->inst_id);
@@ -571,7 +571,6 @@
void vidc_1080p_decode_seq_start_ch1(
struct vidc_1080p_dec_seq_start_param *param)
{
-
VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
VIDC_HWIO_OUT(REG_313350, VIDC_1080P_INIT_CH_INST_ID);
VIDC_HWIO_OUT(REG_980194,
@@ -584,6 +583,7 @@
VIDC_HWIO_OUT(REG_887095, param->stream_buffersize);
VIDC_HWIO_OUT(REG_576987, param->descriptor_buffer_size);
VIDC_HWIO_OUT(REG_652528, param->shared_mem_addr_offset);
+ VIDC_HWIO_OUT(REG_404623, 0);
VIDC_HWIO_OUT(REG_254093, param->cmd_seq_num);
VIDC_HWIO_OUT(REG_313350, VIDC_1080P_DEC_TYPE_SEQ_HEADER |
param->inst_id);
@@ -611,7 +611,10 @@
VIDC_HWIO_OUT(REG_190381,
param->stream_buffersize);
}
- dpb_config = VIDC_SETFIELD(param->dpb_flush,
+ dpb_config = VIDC_SETFIELD(param->dmx_disable,
+ VIDC_1080P_SI_RG10_DMX_DISABLE_SHFT,
+ VIDC_1080P_SI_RG10_DMX_DISABLE_BMSK) |
+ VIDC_SETFIELD(param->dpb_flush,
VIDC_1080P_SI_RG10_DPB_FLUSH_SHFT,
VIDC_1080P_SI_RG10_DPB_FLUSH_BMSK) |
VIDC_SETFIELD(param->dpb_count,
@@ -652,7 +655,10 @@
VIDC_HWIO_OUT(REG_887095,
param->stream_buffersize);
}
- dpb_config = VIDC_SETFIELD(param->dpb_flush,
+ dpb_config = VIDC_SETFIELD(param->dmx_disable,
+ VIDC_1080P_SI_RG10_DMX_DISABLE_SHFT,
+ VIDC_1080P_SI_RG10_DMX_DISABLE_BMSK) |
+ VIDC_SETFIELD(param->dpb_flush,
VIDC_1080P_SI_RG10_DPB_FLUSH_SHFT,
VIDC_1080P_SI_RG10_DPB_FLUSH_BMSK) |
VIDC_SETFIELD(param->dpb_count,
@@ -673,10 +679,17 @@
void vidc_1080p_decode_init_buffers_ch0(
struct vidc_1080p_dec_init_buffers_param *param)
{
+ u32 dpb_config;
VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
VIDC_HWIO_OUT(REG_666957, VIDC_1080P_INIT_CH_INST_ID);
+ dpb_config = VIDC_SETFIELD(param->dmx_disable,
+ VIDC_1080P_SI_RG10_DMX_DISABLE_SHFT,
+ VIDC_1080P_SI_RG10_DMX_DISABLE_BMSK) |
+ VIDC_SETFIELD(param->dpb_count,
+ VIDC_1080P_SI_RG10_NUM_DPB_SHFT,
+ VIDC_1080P_SI_RG10_NUM_DPB_BMSK);
VIDC_HWIO_OUT(REG_889944, param->shared_mem_addr_offset);
- VIDC_HWIO_OUT(REG_404623, param->dpb_count);
+ VIDC_HWIO_OUT(REG_404623, dpb_config);
VIDC_HWIO_OUT(REG_397087, param->cmd_seq_num);
VIDC_HWIO_OUT(REG_666957, VIDC_1080P_DEC_TYPE_INIT_BUFFERS |
param->inst_id);
@@ -685,10 +698,17 @@
void vidc_1080p_decode_init_buffers_ch1(
struct vidc_1080p_dec_init_buffers_param *param)
{
+ u32 dpb_config;
VIDC_HWIO_OUT(REG_695082, VIDC_1080P_RISC2HOST_CMD_EMPTY);
VIDC_HWIO_OUT(REG_313350, VIDC_1080P_INIT_CH_INST_ID);
+ dpb_config = VIDC_SETFIELD(param->dmx_disable,
+ VIDC_1080P_SI_RG10_DMX_DISABLE_SHFT,
+ VIDC_1080P_SI_RG10_DMX_DISABLE_BMSK) |
+ VIDC_SETFIELD(param->dpb_count,
+ VIDC_1080P_SI_RG10_NUM_DPB_SHFT,
+ VIDC_1080P_SI_RG10_NUM_DPB_BMSK);
VIDC_HWIO_OUT(REG_652528, param->shared_mem_addr_offset);
- VIDC_HWIO_OUT(REG_220637, param->dpb_count);
+ VIDC_HWIO_OUT(REG_220637, dpb_config);
VIDC_HWIO_OUT(REG_254093, param->cmd_seq_num);
VIDC_HWIO_OUT(REG_313350, VIDC_1080P_DEC_TYPE_INIT_BUFFERS |
param->inst_id);
diff --git a/drivers/video/msm/vidc/1080p/ddl/vidc.h b/drivers/video/msm/vidc/1080p/ddl/vidc.h
index 415030a..55db33c 100644
--- a/drivers/video/msm/vidc/1080p/ddl/vidc.h
+++ b/drivers/video/msm/vidc/1080p/ddl/vidc.h
@@ -340,6 +340,7 @@
u32 release_dpb_bit_mask;
u32 dpb_count;
u32 dpb_flush;
+ u32 dmx_disable;
enum vidc_1080p_decode decode;
};
struct vidc_1080p_dec_init_buffers_param{
@@ -347,6 +348,7 @@
u32 inst_id;
u32 shared_mem_addr_offset;
u32 dpb_count;
+ u32 dmx_disable;
};
struct vidc_1080p_seq_hdr_info{
u32 img_size_x;
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 03d4681..79f56bd 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
@@ -432,6 +432,8 @@
return;
}
}
+ resource_context.disable_dmx =
+ resource_context.vidc_platform_data->disable_dmx;
#ifdef CONFIG_MSM_BUS_SCALING
resource_context.vidc_bus_client_pdata =
resource_context.vidc_platform_data->
@@ -439,6 +441,7 @@
#endif
} else {
resource_context.memtype = -1;
+ resource_context.disable_dmx = 0;
}
resource_context.core_type = VCD_CORE_1080P;
if (!ddl_pmem_alloc(&resource_context.firmware_addr,
@@ -482,3 +485,7 @@
{
return resource_context.res_ion_client;
}
+
+u32 res_trk_get_disable_dmx(void){
+ return resource_context.disable_dmx;
+}
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h
index 4766019..a90ccec 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker.h
@@ -47,6 +47,7 @@
u32 core_type;
struct ddl_buf_addr firmware_addr;
struct ion_client *res_ion_client;
+ u32 disable_dmx;
};
#if DEBUG
diff --git a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h
index 30a784c..04f28c6 100644
--- a/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h
+++ b/drivers/video/msm/vidc/1080p/resource_tracker/vcd_res_tracker_api.h
@@ -32,4 +32,5 @@
u32 res_trk_get_mem_type(void);
u32 res_trk_get_enable_ion(void);
struct ion_client *res_trk_get_ion_client(void);
+u32 res_trk_get_disable_dmx(void);
#endif
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl.c
index d27b354..7c68d63 100644
--- a/drivers/video/msm/vidc/720p/ddl/vcd_ddl.c
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl.c
@@ -474,8 +474,7 @@
}
if (!input_frame ||
!input_frame->vcd_frm.physical ||
- ddl->codec_data.encoder.input_buf_req.sz !=
- input_frame->vcd_frm.data_len) {
+ !input_frame->vcd_frm.data_len) {
VIDC_LOGERR_STRING("ddl_enc_frame:Bad_input_params");
return VCD_ERR_ILLEGAL_PARM;
}
diff --git a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_properties.c b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_properties.c
index d1b1952..6d3c666 100644
--- a/drivers/video/msm/vidc/720p/ddl/vcd_ddl_properties.c
+++ b/drivers/video/msm/vidc/720p/ddl/vcd_ddl_properties.c
@@ -840,6 +840,11 @@
ddl, property_hdr, property_value);
break;
}
+ case VCD_I_META_BUFFER_MODE:
+ {
+ vcd_status = VCD_S_SUCCESS;
+ break;
+ }
default:
{
vcd_status = VCD_ERR_ILLEGAL_OP;
diff --git a/drivers/video/msm/vidc/common/dec/vdec.c b/drivers/video/msm/vidc/common/dec/vdec.c
index 65f9b54..4696d0b 100644
--- a/drivers/video/msm/vidc/common/dec/vdec.c
+++ b/drivers/video/msm/vidc/common/dec/vdec.c
@@ -184,6 +184,10 @@
return;
}
+ kfree(vcd_frame_data->desc_buf);
+ vcd_frame_data->desc_buf = NULL;
+ vcd_frame_data->desc_size = 0;
+
vdec_msg = kzalloc(sizeof(struct vid_dec_msg), GFP_KERNEL);
if (!vdec_msg) {
ERR("vid_dec_input_frame_done(): cannot allocate vid_dec_msg "
@@ -651,6 +655,57 @@
return true;
}
+static u32 vid_dec_get_disable_dmx_support(struct video_client_ctx *client_ctx,
+ u32 *disable_dmx)
+{
+
+ struct vcd_property_hdr vcd_property_hdr;
+ if (!client_ctx || !disable_dmx)
+ return false;
+ vcd_property_hdr.prop_id = VCD_I_DISABLE_DMX_SUPPORT;
+ vcd_property_hdr.sz = sizeof(u32);
+ if (vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+ disable_dmx))
+ return false;
+ else
+ return true;
+}
+static u32 vid_dec_get_disable_dmx(struct video_client_ctx *client_ctx,
+ u32 *disable_dmx)
+{
+
+ struct vcd_property_hdr vcd_property_hdr;
+ if (!client_ctx || !disable_dmx)
+ return false;
+ vcd_property_hdr.prop_id = VCD_I_DISABLE_DMX;
+ vcd_property_hdr.sz = sizeof(u32);
+ if (vcd_get_property(client_ctx->vcd_handle, &vcd_property_hdr,
+ disable_dmx))
+ return false;
+ else
+ return true;
+}
+
+static u32 vid_dec_set_disable_dmx(struct video_client_ctx *client_ctx)
+{
+
+ struct vcd_property_hdr vcd_property_hdr;
+ u32 vcd_disable_dmx;
+ if (!client_ctx)
+ return false;
+ vcd_property_hdr.prop_id = VCD_I_DISABLE_DMX;
+ vcd_property_hdr.sz = sizeof(u32);
+ vcd_disable_dmx = true;
+ DBG("%s() : Setting Disable DMX: %d\n",
+ __func__, vcd_disable_dmx);
+
+ if (vcd_set_property(client_ctx->vcd_handle, &vcd_property_hdr,
+ &vcd_disable_dmx))
+ return false;
+ else
+ return true;
+}
+
static u32 vid_dec_set_picture_order(struct video_client_ctx *client_ctx,
u32 *picture_order)
{
@@ -1104,7 +1159,8 @@
}
static u32 vid_dec_decode_frame(struct video_client_ctx *client_ctx,
- struct vdec_input_frameinfo *input_frame_info)
+ struct vdec_input_frameinfo *input_frame_info,
+ u8 *desc_buf, u32 desc_size)
{
struct vcd_frame_data vcd_input_buffer;
unsigned long kernel_vaddr, phy_addr, user_vaddr;
@@ -1137,6 +1193,8 @@
vcd_input_buffer.time_stamp = input_frame_info->timestamp;
/* Rely on VCD using the same flags as OMX */
vcd_input_buffer.flags = input_frame_info->flags;
+ vcd_input_buffer.desc_buf = desc_buf;
+ vcd_input_buffer.desc_size = desc_size;
vcd_status = vcd_decode_frame(client_ctx->vcd_handle,
&vcd_input_buffer);
@@ -1502,17 +1560,38 @@
case VDEC_IOCTL_DECODE_FRAME:
{
struct vdec_input_frameinfo input_frame_info;
+ u8 *desc_buf = NULL;
+ u32 desc_size = 0;
DBG("VDEC_IOCTL_DECODE_FRAME\n");
if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
return -EFAULT;
if (copy_from_user(&input_frame_info, vdec_msg.in,
sizeof(input_frame_info)))
return -EFAULT;
+ if (client_ctx->dmx_disable) {
+ if (input_frame_info.desc_addr) {
+ desc_size = input_frame_info.desc_size;
+ desc_buf = kzalloc(desc_size, GFP_KERNEL);
+ if (desc_buf) {
+ if (copy_from_user(desc_buf,
+ input_frame_info.desc_addr,
+ desc_size)) {
+ kfree(desc_buf);
+ desc_buf = NULL;
+ return -EFAULT;
+ }
+ }
+ } else
+ return -EINVAL;
+ }
+ result = vid_dec_decode_frame(client_ctx, &input_frame_info,
+ desc_buf, desc_size);
- result = vid_dec_decode_frame(client_ctx, &input_frame_info);
-
- if (!result)
+ if (!result) {
+ kfree(desc_buf);
+ desc_buf = NULL;
return -EIO;
+ }
break;
}
case VDEC_IOCTL_FILL_OUTPUT_BUFFER:
@@ -1683,6 +1762,48 @@
return -EIO;
break;
}
+
+ case VDEC_IOCTL_GET_DISABLE_DMX_SUPPORT:
+ {
+ u32 disable_dmx;
+ DBG("VDEC_IOCTL_GET_DISABLE_DMX_SUPPORT\n");
+ if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ result = vid_dec_get_disable_dmx_support(client_ctx,
+ &disable_dmx);
+ if (result) {
+ if (copy_to_user(vdec_msg.out, &disable_dmx,
+ sizeof(u32)))
+ return -EFAULT;
+ } else
+ return -EIO;
+ break;
+ }
+ case VDEC_IOCTL_GET_DISABLE_DMX:
+ {
+ u32 disable_dmx;
+ DBG("VDEC_IOCTL_GET_DISABLE_DMX\n");
+ if (copy_from_user(&vdec_msg, arg, sizeof(vdec_msg)))
+ return -EFAULT;
+ result = vid_dec_get_disable_dmx(client_ctx,
+ &disable_dmx);
+ if (result) {
+ if (copy_to_user(vdec_msg.out, &disable_dmx,
+ sizeof(u32)))
+ return -EFAULT;
+ } else
+ return -EIO;
+ break;
+ }
+ case VDEC_IOCTL_SET_DISABLE_DMX:
+ {
+ DBG("VDEC_IOCTL_SET_DISABLE_DMX\n");
+ result = vid_dec_set_disable_dmx(client_ctx);
+ if (!result)
+ return -EIO;
+ client_ctx->dmx_disable = 1;
+ break;
+ }
case VDEC_IOCTL_SET_PICTURE_ORDER:
{
u32 picture_order;
@@ -1875,6 +1996,7 @@
client_ctx->stop_msg = 0;
client_ctx->stop_called = false;
client_ctx->stop_sync_cb = false;
+ client_ctx->dmx_disable = 0;
if (vcd_get_ion_status()) {
client_ctx->user_ion_client = vcd_get_ion_client();
if (!client_ctx->user_ion_client) {
diff --git a/drivers/video/msm/vidc/common/enc/venc.c b/drivers/video/msm/vidc/common/enc/venc.c
index 1435b86..5ee315f 100644
--- a/drivers/video/msm/vidc/common/enc/venc.c
+++ b/drivers/video/msm/vidc/common/enc/venc.c
@@ -1540,6 +1540,29 @@
return -EFAULT;
break;
}
+ case VEN_IOCTL_SET_METABUFFER_MODE:
+ {
+ u32 metabuffer_mode, vcd_status;
+ struct vcd_property_hdr vcd_property_hdr;
+ struct vcd_property_live live_mode;
+
+ if (copy_from_user(&venc_msg, arg, sizeof(venc_msg)))
+ return -EFAULT;
+ if (copy_from_user(&metabuffer_mode, venc_msg.in,
+ sizeof(metabuffer_mode)))
+ return -EFAULT;
+ vcd_property_hdr.prop_id = VCD_I_META_BUFFER_MODE;
+ vcd_property_hdr.sz =
+ sizeof(struct vcd_property_live);
+ live_mode.live = metabuffer_mode;
+ vcd_status = vcd_set_property(client_ctx->vcd_handle,
+ &vcd_property_hdr, &live_mode);
+ if (vcd_status) {
+ pr_err(" Setting metabuffer mode failed");
+ return -EIO;
+ }
+ break;
+ }
case VEN_IOCTL_SET_AC_PREDICTION:
case VEN_IOCTL_GET_AC_PREDICTION:
case VEN_IOCTL_SET_RVLC:
diff --git a/drivers/video/msm/vidc/common/enc/venc_internal.c b/drivers/video/msm/vidc/common/enc/venc_internal.c
index ee38098..47f2db5 100644
--- a/drivers/video/msm/vidc/common/enc/venc_internal.c
+++ b/drivers/video/msm/vidc/common/enc/venc_internal.c
@@ -597,6 +597,9 @@
case VEN_LEVEL_H264_3p1:
level.level = VCD_LEVEL_H264_3p1;
break;
+ case VEN_LEVEL_H264_3p2:
+ level.level = VCD_LEVEL_H264_3p2;
+ break;
case VEN_LEVEL_H264_4:
level.level = VCD_LEVEL_H264_4;
break;
@@ -700,7 +703,7 @@
profile_level->level = VEN_LEVEL_H264_3p1;
break;
case VCD_LEVEL_H264_3p2:
- status = false;
+ profile_level->level = VEN_LEVEL_H264_3p2;
break;
case VCD_LEVEL_H264_4:
profile_level->level = VEN_LEVEL_H264_4;
diff --git a/drivers/video/msm/vidc/common/init/vidc_init.h b/drivers/video/msm/vidc/common/init/vidc_init.h
index 2bd8ecd..9dda277 100644
--- a/drivers/video/msm/vidc/common/init/vidc_init.h
+++ b/drivers/video/msm/vidc/common/init/vidc_init.h
@@ -56,6 +56,7 @@
struct ion_handle *seq_hdr_ion_handle;
struct ion_handle *h264_mv_ion_handle;
struct ion_handle *recon_buffer_ion_handle[4];
+ u32 dmx_disable;
};
void __iomem *vidc_get_ioaddr(void);
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_api.h b/drivers/video/msm/vidc/common/vcd/vcd_api.h
index 4fcd085..badab1e 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_api.h
+++ b/drivers/video/msm/vidc/common/vcd/vcd_api.h
@@ -67,6 +67,8 @@
enum vcd_frame frame;
u32 ip_frm_tag;
u32 intrlcd_ip_frm_tag;
+ u8 *desc_buf;
+ u32 desc_size;
};
struct vcd_sequence_hdr {
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c b/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c
index 973ed48..e00e85f 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_client_sm.c
@@ -90,13 +90,13 @@
return VCD_ERR_ILLEGAL_OP;
}
- if (!cctxt->in_buf_pool.entries ||
+ if ((!cctxt->meta_mode && !cctxt->in_buf_pool.entries) ||
!cctxt->out_buf_pool.entries ||
- cctxt->in_buf_pool.validated != cctxt->in_buf_pool.count ||
+ (!cctxt->meta_mode &&
+ cctxt->in_buf_pool.validated != cctxt->in_buf_pool.count) ||
cctxt->out_buf_pool.validated !=
cctxt->out_buf_pool.count) {
VCD_MSG_ERROR("Buffer pool is not completely setup yet");
-
return VCD_ERR_BAD_STATE;
}
@@ -495,6 +495,13 @@
rc = ddl_set_property(cctxt->ddl_handle, prop_hdr, prop_val);
VCD_FAILED_RETURN(rc, "Failed: ddl_set_property");
switch (prop_hdr->prop_id) {
+ case VCD_I_META_BUFFER_MODE:
+ {
+ struct vcd_property_live *live =
+ (struct vcd_property_live *)prop_val;
+ cctxt->meta_mode = live->live;
+ break;
+ }
case VCD_I_LIVE:
{
struct vcd_property_live *live =
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_core.h b/drivers/video/msm/vidc/common/vcd/vcd_core.h
index b424059..64dec2d 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_core.h
+++ b/drivers/video/msm/vidc/common/vcd/vcd_core.h
@@ -208,6 +208,7 @@
struct ion_client *vcd_ion_client;
u32 vcd_enable_ion;
struct vcd_clnt_ctxt *next;
+ u32 meta_mode;
};
#define VCD_BUFFERPOOL_INUSE_DECREMENT(val) \
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_property.h b/drivers/video/msm/vidc/common/vcd/vcd_property.h
index f51d226..a3069e3 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_property.h
+++ b/drivers/video/msm/vidc/common/vcd/vcd_property.h
@@ -47,6 +47,9 @@
#define VCD_I_GET_H264_MV_SIZE (VCD_START_BASE + 0x1F)
#define VCD_I_DEC_PICTYPE (VCD_START_BASE + 0x20)
#define VCD_I_CONT_ON_RECONFIG (VCD_START_BASE + 0x21)
+#define VCD_I_META_BUFFER_MODE (VCD_START_BASE + 0x22)
+#define VCD_I_DISABLE_DMX (VCD_START_BASE + 0x23)
+#define VCD_I_DISABLE_DMX_SUPPORT (VCD_START_BASE + 0x24)
#define VCD_START_REQ (VCD_START_BASE + 0x1000)
#define VCD_I_REQ_IFRAME (VCD_START_REQ + 0x1)
diff --git a/drivers/video/msm/vidc/common/vcd/vcd_sub.c b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
index 75e0acf..217030f 100644
--- a/drivers/video/msm/vidc/common/vcd/vcd_sub.c
+++ b/drivers/video/msm/vidc/common/vcd/vcd_sub.c
@@ -653,7 +653,7 @@
VCD_FAILED_RETURN(rc, "Invalid buffer type provided");
first_frm_recvd &= cctxt->status.mask;
- if (first_frm_recvd) {
+ if (first_frm_recvd && !cctxt->meta_mode) {
VCD_MSG_ERROR(
"VCD free buffer called when data path is active");
return VCD_ERR_BAD_STATE;
diff --git a/fs/anon_inodes.c b/fs/anon_inodes.c
index c5567cb..f11e43e 100644
--- a/fs/anon_inodes.c
+++ b/fs/anon_inodes.c
@@ -187,7 +187,7 @@
*/
static struct inode *anon_inode_mkinode(void)
{
- struct inode *inode = new_inode(anon_inode_mnt->mnt_sb);
+ struct inode *inode = new_inode_pseudo(anon_inode_mnt->mnt_sb);
if (!inode)
return ERR_PTR(-ENOMEM);
@@ -233,7 +233,7 @@
return 0;
err_mntput:
- mntput(anon_inode_mnt);
+ kern_unmount(anon_inode_mnt);
err_unregister_filesystem:
unregister_filesystem(&anon_inode_fs_type);
err_exit:
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index 7aafeb8..0b686ce 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -1030,6 +1030,7 @@
static void __exit exit_hugetlbfs_fs(void)
{
kmem_cache_destroy(hugetlbfs_inode_cachep);
+ kern_unmount(hugetlbfs_vfsmount);
unregister_filesystem(&hugetlbfs_fs_type);
bdi_destroy(&hugetlbfs_backing_dev_info);
}
diff --git a/fs/inode.c b/fs/inode.c
index 43566d1..e964818 100644
--- a/fs/inode.c
+++ b/fs/inode.c
@@ -369,9 +369,11 @@
static inline void inode_sb_list_del(struct inode *inode)
{
- spin_lock(&inode_sb_list_lock);
- list_del_init(&inode->i_sb_list);
- spin_unlock(&inode_sb_list_lock);
+ if (!list_empty(&inode->i_sb_list)) {
+ spin_lock(&inode_sb_list_lock);
+ list_del_init(&inode->i_sb_list);
+ spin_unlock(&inode_sb_list_lock);
+ }
}
static unsigned long hash(struct super_block *sb, unsigned long hashval)
@@ -843,6 +845,29 @@
EXPORT_SYMBOL(get_next_ino);
/**
+ * new_inode_pseudo - obtain an inode
+ * @sb: superblock
+ *
+ * Allocates a new inode for given superblock.
+ * Inode wont be chained in superblock s_inodes list
+ * This means :
+ * - fs can't be unmount
+ * - quotas, fsnotify, writeback can't work
+ */
+struct inode *new_inode_pseudo(struct super_block *sb)
+{
+ struct inode *inode = alloc_inode(sb);
+
+ if (inode) {
+ spin_lock(&inode->i_lock);
+ inode->i_state = 0;
+ spin_unlock(&inode->i_lock);
+ INIT_LIST_HEAD(&inode->i_sb_list);
+ }
+ return inode;
+}
+
+/**
* new_inode - obtain an inode
* @sb: superblock
*
@@ -860,13 +885,9 @@
spin_lock_prefetch(&inode_sb_list_lock);
- inode = alloc_inode(sb);
- if (inode) {
- spin_lock(&inode->i_lock);
- inode->i_state = 0;
- spin_unlock(&inode->i_lock);
+ inode = new_inode_pseudo(sb);
+ if (inode)
inode_sb_list_add(inode);
- }
return inode;
}
EXPORT_SYMBOL(new_inode);
diff --git a/fs/namespace.c b/fs/namespace.c
index fe59bd1..7b4fa8f 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -2721,6 +2721,25 @@
struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
{
- return vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
+ struct vfsmount *mnt;
+ mnt = vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
+ if (!IS_ERR(mnt)) {
+ /*
+ * it is a longterm mount, don't release mnt until
+ * we unmount before file sys is unregistered
+ */
+ mnt_make_longterm(mnt);
+ }
+ return mnt;
}
EXPORT_SYMBOL_GPL(kern_mount_data);
+
+void kern_unmount(struct vfsmount *mnt)
+{
+ /* release long term mount so mount point can be released */
+ if (!IS_ERR_OR_NULL(mnt)) {
+ mnt_make_shortterm(mnt);
+ mntput(mnt);
+ }
+}
+EXPORT_SYMBOL(kern_unmount);
diff --git a/fs/pipe.c b/fs/pipe.c
index da42f7d..0e0be1d 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -948,7 +948,7 @@
static struct inode * get_pipe_inode(void)
{
- struct inode *inode = new_inode(pipe_mnt->mnt_sb);
+ struct inode *inode = new_inode_pseudo(pipe_mnt->mnt_sb);
struct pipe_inode_info *pipe;
if (!inode)
@@ -1291,8 +1291,8 @@
static void __exit exit_pipe_fs(void)
{
+ kern_unmount(pipe_mnt);
unregister_filesystem(&pipe_fs_type);
- mntput(pipe_mnt);
}
fs_initcall(init_pipe_fs);
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 4900e9c..9c43f56 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -368,6 +368,7 @@
header-y += tipc.h
header-y += tipc_config.h
header-y += toshiba.h
+header-y += tspp.h
header-y += tty.h
header-y += types.h
header-y += udf_fs_i.h
diff --git a/include/linux/android_pmem.h b/include/linux/android_pmem.h
index c0291969..ab96379 100644
--- a/include/linux/android_pmem.h
+++ b/include/linux/android_pmem.h
@@ -157,12 +157,12 @@
* function to be called when the number of allocations goes from
* 0 -> 1
*/
- void (*request_region)(void *);
+ int (*request_region)(void *);
/*
* function to be called when the number of allocations goes from
* 1 -> 0
*/
- void (*release_region)(void *);
+ int (*release_region)(void *);
/*
* function to be called upon pmem registration
*/
@@ -171,6 +171,10 @@
* indicates that this region should be mapped/unmaped as needed
*/
int map_on_demand;
+ /*
+ * indicates this pmem may be reused via fmem
+ */
+ int reusable;
};
int pmem_setup(struct android_pmem_platform_data *pdata,
diff --git a/include/linux/fmem.h b/include/linux/fmem.h
new file mode 100644
index 0000000..c9e36b5
--- /dev/null
+++ b/include/linux/fmem.h
@@ -0,0 +1,57 @@
+/*
+ *
+ * Copyright (c) 2011, 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 _FMEM_H_
+#define _FMEM_H_
+
+#include <linux/vmalloc.h>
+
+struct fmem_platform_data {
+ unsigned long phys;
+ unsigned long size;
+};
+
+struct fmem_data {
+ unsigned long phys;
+ void *virt;
+ struct vm_struct *area;
+ unsigned long size;
+};
+
+enum fmem_state {
+ FMEM_UNINITIALIZED = 0,
+ FMEM_C_STATE,
+ FMEM_T_STATE,
+ FMEM_O_STATE,
+};
+
+#ifdef CONFIG_QCACHE
+struct fmem_data *fmem_get_info(void);
+int fmem_set_state(enum fmem_state);
+void lock_fmem_state(void);
+void unlock_fmem_state(void);
+void *fmem_map_virtual_area(int cacheability);
+void fmem_unmap_virtual_area(void);
+#else
+static inline struct fmem_data *fmem_get_info(void) { return NULL; }
+static inline int fmem_set_state(enum fmem_state f) { return -ENODEV; }
+static inline void lock_fmem_state(void) { return; }
+static inline void unlock_fmem_state(void) { return; }
+static inline void *fmem_map_virtual_area(int cacheability) { return NULL; }
+static inline void fmem_unmap_virtual_area(void) { return; }
+#endif
+
+int request_fmem_c_region(void *unused);
+int release_fmem_c_region(void *unused);
+#endif
diff --git a/include/linux/fs.h b/include/linux/fs.h
index b5b9792..d10b5b5 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1869,6 +1869,7 @@
extern int unregister_filesystem(struct file_system_type *);
extern struct vfsmount *kern_mount_data(struct file_system_type *, void *data);
#define kern_mount(type) kern_mount_data(type, NULL)
+extern void kern_unmount(struct vfsmount *mnt);
extern int may_umount_tree(struct vfsmount *);
extern int may_umount(struct vfsmount *);
extern long do_mount(char *, char *, char *, unsigned long, void *);
@@ -2276,7 +2277,8 @@
extern void iget_failed(struct inode *);
extern void end_writeback(struct inode *);
extern void __destroy_inode(struct inode *);
-extern struct inode *new_inode(struct super_block *);
+extern struct inode *new_inode_pseudo(struct super_block *sb);
+extern struct inode *new_inode(struct super_block *sb);
extern void free_inode_nonrcu(struct inode *inode);
extern int should_remove_suid(struct dentry *);
extern int file_remove_suid(struct file *);
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index 150107b..c22f9b0 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -95,6 +95,7 @@
* @flags: flags (see IRQF_* above)
* @name: name of the device
* @dev_id: cookie to identify the device
+ * @percpu_dev_id: cookie to identify the device
* @next: pointer to the next irqaction for shared interrupts
* @irq: interrupt number
* @dir: pointer to the proc/irq/NN/name entry
@@ -104,17 +105,18 @@
* @thread_mask: bitmask for keeping track of @thread activity
*/
struct irqaction {
- irq_handler_t handler;
- unsigned long flags;
- void *dev_id;
- struct irqaction *next;
- int irq;
- irq_handler_t thread_fn;
- struct task_struct *thread;
- unsigned long thread_flags;
- unsigned long thread_mask;
- const char *name;
- struct proc_dir_entry *dir;
+ irq_handler_t handler;
+ unsigned long flags;
+ void *dev_id;
+ void __percpu *percpu_dev_id;
+ struct irqaction *next;
+ int irq;
+ irq_handler_t thread_fn;
+ struct task_struct *thread;
+ unsigned long thread_flags;
+ unsigned long thread_mask;
+ const char *name;
+ struct proc_dir_entry *dir;
} ____cacheline_internodealigned_in_smp;
extern irqreturn_t no_action(int cpl, void *dev_id);
@@ -136,6 +138,10 @@
request_any_context_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *name, void *dev_id);
+extern int __must_check
+request_percpu_irq(unsigned int irq, irq_handler_t handler,
+ const char *devname, void __percpu *percpu_dev_id);
+
extern void exit_irq_thread(void);
#else
@@ -164,10 +170,18 @@
return request_irq(irq, handler, flags, name, dev_id);
}
+static inline int __must_check
+request_percpu_irq(unsigned int irq, irq_handler_t handler,
+ const char *devname, void __percpu *percpu_dev_id)
+{
+ return request_irq(irq, handler, 0, devname, percpu_dev_id);
+}
+
static inline void exit_irq_thread(void) { }
#endif
extern void free_irq(unsigned int, void *);
+extern void free_percpu_irq(unsigned int, void __percpu *);
struct device;
@@ -207,7 +221,9 @@
extern void disable_irq_nosync(unsigned int irq);
extern void disable_irq(unsigned int irq);
+extern void disable_percpu_irq(unsigned int irq);
extern void enable_irq(unsigned int irq);
+extern void enable_percpu_irq(unsigned int irq, unsigned int type);
/* The following three functions are for the core kernel use only. */
#ifdef CONFIG_GENERIC_HARDIRQS
diff --git a/include/linux/ion.h b/include/linux/ion.h
index b396369..2cced6d 100644
--- a/include/linux/ion.h
+++ b/include/linux/ion.h
@@ -27,8 +27,8 @@
* @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc
* @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc
* @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved
- * carveout heap, allocations are physically
- * contiguous
+ * carveout heap, allocations are physically
+ * contiguous
* @ION_HEAP_END: helper for iterating over heaps
*/
enum ion_heap_type {
@@ -97,8 +97,8 @@
/**
* struct ion_platform_heap - defines a heap in the given platform
* @type: type of the heap from ion_heap_type enum
- * @id: unique identifier for heap. When allocating (lower numbers
- * will be allocated from first)
+ * @id: unique identifier for heap. When allocating (lower numbers
+ * will be allocated from first)
* @name: used for debug purposes
* @base: base address of heap in physical memory if applicable
* @size: size of the heap in bytes if applicable
@@ -117,8 +117,8 @@
ion_phys_addr_t base;
size_t size;
enum ion_memory_types memory_type;
- void (*request_region)(void *);
- void (*release_region)(void *);
+ int (*request_region)(void *);
+ int (*release_region)(void *);
void *(*setup_region)(void);
};
@@ -136,8 +136,8 @@
*/
struct ion_platform_data {
int nr;
- void (*request_region)(void *);
- void (*release_region)(void *);
+ int (*request_region)(void *);
+ int (*release_region)(void *);
void *(*setup_region)(void);
struct ion_platform_heap heaps[];
};
diff --git a/include/linux/irq.h b/include/linux/irq.h
index d03bc09..93c601f 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -23,6 +23,7 @@
#include <linux/errno.h>
#include <linux/topology.h>
#include <linux/wait.h>
+#include <linux/module.h>
#include <asm/irq.h>
#include <asm/ptrace.h>
@@ -65,6 +66,7 @@
* IRQ_NO_BALANCING - Interrupt cannot be balanced (affinity set)
* IRQ_MOVE_PCNTXT - Interrupt can be migrated from process context
* IRQ_NESTED_TRHEAD - Interrupt nests into another thread
+ * IRQ_PER_CPU_DEVID - Dev_id is a per-cpu variable
*/
enum {
IRQ_TYPE_NONE = 0x00000000,
@@ -87,12 +89,13 @@
IRQ_MOVE_PCNTXT = (1 << 14),
IRQ_NESTED_THREAD = (1 << 15),
IRQ_NOTHREAD = (1 << 16),
+ IRQ_PER_CPU_DEVID = (1 << 17),
};
#define IRQF_MODIFY_MASK \
(IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \
IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \
- IRQ_PER_CPU | IRQ_NESTED_THREAD)
+ IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID)
#define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING)
@@ -371,6 +374,8 @@
struct irqaction;
extern int setup_irq(unsigned int irq, struct irqaction *new);
extern void remove_irq(unsigned int irq, struct irqaction *act);
+extern int setup_percpu_irq(unsigned int irq, struct irqaction *new);
+extern void remove_percpu_irq(unsigned int irq, struct irqaction *act);
extern void irq_cpu_online(void);
extern void irq_cpu_offline(void);
@@ -398,6 +403,7 @@
extern void handle_edge_eoi_irq(unsigned int irq, struct irq_desc *desc);
extern void handle_simple_irq(unsigned int irq, struct irq_desc *desc);
extern void handle_percpu_irq(unsigned int irq, struct irq_desc *desc);
+extern void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc);
extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc);
extern void handle_nested_irq(unsigned int irq);
@@ -428,6 +434,8 @@
irq_set_chip_and_handler_name(irq, chip, handle, NULL);
}
+extern int irq_set_percpu_devid(unsigned int irq);
+
extern void
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
const char *name);
@@ -489,6 +497,13 @@
irq_clear_status_flags(irq, IRQ_NESTED_THREAD);
}
+static inline void irq_set_percpu_devid_flags(unsigned int irq)
+{
+ irq_set_status_flags(irq,
+ IRQ_NOAUTOEN | IRQ_PER_CPU | IRQ_NOTHREAD |
+ IRQ_NOPROBE | IRQ_PER_CPU_DEVID);
+}
+
/* Handle dynamic irq creation and destruction */
extern unsigned int create_irq_nr(unsigned int irq_want, int node);
extern int create_irq(void);
@@ -556,7 +571,15 @@
return d->msi_desc;
}
-int irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node);
+int __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
+ struct module *owner);
+
+static inline int irq_alloc_descs(int irq, unsigned int from, unsigned int cnt,
+ int node)
+{
+ return __irq_alloc_descs(irq, from, cnt, node, THIS_MODULE);
+}
+
void irq_free_descs(unsigned int irq, unsigned int cnt);
int irq_reserve_irqs(unsigned int from, unsigned int cnt);
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index 2d921b3..6b69c2c 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -53,6 +53,7 @@
unsigned long last_unhandled; /* Aging timer for unhandled count */
unsigned int irqs_unhandled;
raw_spinlock_t lock;
+ struct cpumask *percpu_enabled;
#ifdef CONFIG_SMP
const struct cpumask *affinity_hint;
struct irq_affinity_notify *affinity_notify;
@@ -66,6 +67,7 @@
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *dir;
#endif
+ struct module *owner;
const char *name;
} ____cacheline_internodealigned_in_smp;
diff --git a/include/linux/mfd/pm8xxx/ccadc.h b/include/linux/mfd/pm8xxx/ccadc.h
index 6f6cb39..23d0fb0 100644
--- a/include/linux/mfd/pm8xxx/ccadc.h
+++ b/include/linux/mfd/pm8xxx/ccadc.h
@@ -73,7 +73,7 @@
/**
* pm8xxx_ccadc_get_battery_current - return the battery current based on vsense
- * resitor in milliamperes
+ * resitor in microamperes
* @result: The pointer where the voltage will be updated. A -ve
* result means that the current is flowing in
* the battery - during battery charging
diff --git a/include/linux/mfd/pm8xxx/pm8921-bms.h b/include/linux/mfd/pm8xxx/pm8921-bms.h
index 5d186df..82db9a4 100644
--- a/include/linux/mfd/pm8xxx/pm8921-bms.h
+++ b/include/linux/mfd/pm8xxx/pm8921-bms.h
@@ -109,6 +109,7 @@
unsigned int i_test;
unsigned int v_failure;
unsigned int calib_delay_ms;
+ unsigned int max_voltage_uv;
};
#if defined(CONFIG_PM8921_BMS) || defined(CONFIG_PM8921_BMS_MODULE)
@@ -129,7 +130,7 @@
/**
* pm8921_bms_get_battery_current - return the battery current based on vsense
- * resitor in milliamperes
+ * resitor in microamperes
* @result: The pointer where the voltage will be updated. A -ve
* result means that the current is flowing in
* the battery - during battery charging
diff --git a/include/linux/mfd/pm8xxx/pm8921-charger.h b/include/linux/mfd/pm8xxx/pm8921-charger.h
index ef08c47..31fa537 100644
--- a/include/linux/mfd/pm8xxx/pm8921-charger.h
+++ b/include/linux/mfd/pm8xxx/pm8921-charger.h
@@ -104,6 +104,7 @@
unsigned int (*get_batt_capacity_percent) (void);
int64_t batt_id_min;
int64_t batt_id_max;
+ bool keep_btm_on_suspend;
int trkl_voltage;
int weak_voltage;
int trkl_current;
diff --git a/include/linux/mfd/pm8xxx/pm8xxx-adc.h b/include/linux/mfd/pm8xxx/pm8xxx-adc.h
index ba4b29c..a572ae3 100644
--- a/include/linux/mfd/pm8xxx/pm8xxx-adc.h
+++ b/include/linux/mfd/pm8xxx/pm8xxx-adc.h
@@ -97,7 +97,7 @@
#define PM8XXX_ADC_PMIC_0 0x0
-#define PM8XXX_CHANNEL_ADC_625_MV 625
+#define PM8XXX_CHANNEL_ADC_625_UV 625000
#define PM8XXX_CHANNEL_MPP_SCALE1_IDX 20
#define PM8XXX_CHANNEL_MPP_SCALE3_IDX 40
@@ -218,7 +218,6 @@
/**
* struct pm8xxx_adc_linear_graph - Represent ADC characteristics
- * @offset: Offset with respect to the actual curve
* @dy: Numerator slope to calculate the gain
* @dx: Denominator slope to calculate the gain
* @adc_vref: A/D word of the voltage reference used for the channel
@@ -228,11 +227,10 @@
* to calibrate the device.
*/
struct pm8xxx_adc_linear_graph {
- int32_t offset;
- int32_t dy;
- int32_t dx;
- int32_t adc_vref;
- int32_t adc_gnd;
+ int64_t dy;
+ int64_t dx;
+ int64_t adc_vref;
+ int64_t adc_gnd;
};
/**
@@ -292,6 +290,10 @@
* here is a number relative to a reference of a given ADC
* @physical: The data meaningful for each individual channel whether it is
* voltage, current, temperature, etc.
+ * All voltage units are represented in micro - volts.
+ * -Battery temperature units are represented as 0.1 DegC
+ * -PA Therm temperature units are represented as DegC
+ * -PMIC Die temperature units are represented as 0.001 DegC
*/
struct pm8xxx_adc_chan_result {
uint32_t chan;
@@ -379,6 +381,21 @@
const struct pm8xxx_adc_properties *adc_prop,
const struct pm8xxx_adc_chan_properties *chan_prop,
struct pm8xxx_adc_chan_result *chan_rslt);
+/**
+ * pm8xxx_adc_scale_batt_id() - Scales the pre-calibrated digital output
+ * of an ADC to the ADC reference and compensates for the
+ * gain and offset.
+ * @adc_code: pre-calibrated digital ouput of the ADC.
+ * @adc_prop: adc properties of the pm8xxx adc such as bit resolution,
+ * reference voltage.
+ * @chan_prop: individual channel properties to compensate the i/p scaling,
+ * slope and offset.
+ * @chan_rslt: physical result to be stored.
+ */
+int32_t pm8xxx_adc_scale_batt_id(int32_t adc_code,
+ const struct pm8xxx_adc_properties *adc_prop,
+ const struct pm8xxx_adc_chan_properties *chan_prop,
+ struct pm8xxx_adc_chan_result *chan_rslt);
#else
static inline int32_t pm8xxx_adc_scale_default(int32_t adc_code,
const struct pm8xxx_adc_properties *adc_prop,
@@ -405,6 +422,11 @@
const struct pm8xxx_adc_chan_properties *chan_prop,
struct pm8xxx_adc_chan_result *chan_rslt)
{ return -ENXIO; }
+static inline int32_t pm8xxx_adc_scale_batt_id(int32_t adc_code,
+ const struct pm8xxx_adc_properties *adc_prop,
+ const struct pm8xxx_adc_chan_properties *chan_prop,
+ struct pm8xxx_adc_chan_result *chan_rslt)
+{ return -ENXIO; }
#endif
/**
diff --git a/include/linux/mfd/pm8xxx/regulator.h b/include/linux/mfd/pm8xxx/regulator.h
new file mode 100644
index 0000000..38700cb
--- /dev/null
+++ b/include/linux/mfd/pm8xxx/regulator.h
@@ -0,0 +1,258 @@
+/* Copyright (c) 2011, 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 __MFD_PM8XXX_REGULATOR_H__
+#define __MFD_PM8XXX_REGULATOR_H__
+
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/pm8xxx-regulator.h>
+
+/**
+ * enum pm8xxx_regulator_type - possible PM8XXX voltage regulator types
+ * %PM8XXX_REGULATOR_TYPE_PLDO: PMOS low drop-out linear regulator
+ * %PM8XXX_REGULATOR_TYPE_NLDO: NMOS low drop-out linear regulator
+ * %PM8XXX_REGULATOR_TYPE_NLDO1200: NMOS low drop-out linear regulator
+ * capable of supplying up to 1200 mA
+ * %PM8XXX_REGULATOR_TYPE_SMPS: switched-mode power supply (buck)
+ * %PM8XXX_REGULATOR_TYPE_FTSMPS: fast transient switched-mode power
+ * supply (buck)
+ * %PM8XXX_REGULATOR_TYPE_VS: voltage switch capable of sourcing 100mA
+ * %PM8XXX_REGULATOR_TYPE_VS300: voltage switch capable of sourcing 300mA
+ * %PM8XXX_REGULATOR_TYPE_NCP: negative charge pump
+ * %PM8XXX_REGULATOR_TYPE_MAX: used internally for error checking; not
+ * a valid regulator type.
+ *
+ * Each of these has a different register control interface.
+ */
+enum pm8xxx_regulator_type {
+ PM8XXX_REGULATOR_TYPE_PLDO,
+ PM8XXX_REGULATOR_TYPE_NLDO,
+ PM8XXX_REGULATOR_TYPE_NLDO1200,
+ PM8XXX_REGULATOR_TYPE_SMPS,
+ PM8XXX_REGULATOR_TYPE_FTSMPS,
+ PM8XXX_REGULATOR_TYPE_VS,
+ PM8XXX_REGULATOR_TYPE_VS300,
+ PM8XXX_REGULATOR_TYPE_NCP,
+ PM8XXX_REGULATOR_TYPE_MAX,
+};
+
+/**
+ * struct pm8xxx_vreg - regulator configuration and state data used by the
+ * pm8xxx-regulator driver
+ * @rdesc: regulator description
+ * @rdesc_pc: pin control regulator description. rdesc_pc.name == NULL
+ * implies that there is no pin control version of this
+ * regulator.
+ * @type: regulator type
+ * @hpm_min_load: minimum load in uA that will result in the regulator
+ * being set to high power mode
+ * @ctrl_addr: control register SSBI address
+ * @test_addr: test register SSBI address (not needed for all types)
+ * @clk_ctrl_addr: clock control register SSBI address (only used by SMPS
+ * type regulators)
+ * @sleep_ctrl_addr: sleep control register SSBI address (only used by SMPS
+ * type regulators)
+ * @pfm_ctrl_addr: pulse-frequency modulation control register SSBI address
+ * (only used by FTSMPS type regulators)
+ * @pwr_cnfg_addr: power configuration register SSBI address (only used by
+ * FTSMPS type regulators)
+ * @pdata: this platform data struct is filled based using the
+ * platform data pointed to in a core platform data struct
+ * @rdev: pointer to regulator device which is created with
+ * regulator_register
+ * @rdev_pc: pointer to pin controlled regulator device which is
+ * created with regulator_register
+ * @dev: pointer to pm8xxx-regulator device
+ * @dev_pc: pointer to pin control pm8xxx-regulator device
+ * @pc_lock: mutex lock to handle sharing between pin controlled and
+ * non-pin controlled versions of a given regulator. Note,
+ * this lock must be initialized in the PMIC core driver.)
+ * @save_uV: current regulator voltage in uV
+ * @mode: current mode of the regulator
+ * @write_count: number of SSBI writes that have taken place for this
+ * regulator. This is used for debug printing to determine
+ * if a given operation is redundant.
+ * @prev_write_count: number of SSBI writes that have taken place for this
+ * regulator at the start of an operation. This is used for
+ * debug printing to determine if a given operation is
+ * redundant.
+ * @is_enabled: true if the regulator is currently enabled, false if not
+ * @is_enabled_pc: true if the pin controlled version of the regulator is
+ * currently enabled (i.e. pin control is active), false if
+ * not
+ * @test_reg: last value read from or written to each of the banks of
+ * the test register
+ * @ctrl_reg: last value read from or written to the control register
+ * @clk_ctrl_reg: last value read from or written to the clock control
+ * register
+ * @sleep_ctrl_reg: last value read from or written to the sleep control
+ * register
+ * @pfm_ctrl_reg: last value read from or written to the PFM control
+ * register
+ * @pwr_cnfg_reg: last value read from or written to the power
+ * configuration register
+ *
+ * This data structure should only need to be instantiated in a PMIC core driver
+ * It is used to specify PMIC specific as opposed to board specific
+ * configuration data. It is also used to hold all state variables needed by
+ * the pm8xxx-regulator driver as these variables need to be shared between
+ * pin controlled and non-pin controlled versions of a given regulator, which
+ * are probed separately.
+ */
+struct pm8xxx_vreg {
+ /* Configuration data */
+ struct regulator_desc rdesc;
+ struct regulator_desc rdesc_pc;
+ enum pm8xxx_regulator_type type;
+ const int hpm_min_load;
+ const u16 ctrl_addr;
+ const u16 test_addr;
+ const u16 clk_ctrl_addr;
+ const u16 sleep_ctrl_addr;
+ const u16 pfm_ctrl_addr;
+ const u16 pwr_cnfg_addr;
+ /* State data */
+ struct pm8xxx_regulator_platform_data pdata;
+ struct regulator_dev *rdev;
+ struct regulator_dev *rdev_pc;
+ struct device *dev;
+ struct device *dev_pc;
+ struct mutex pc_lock;
+ int save_uV;
+ int mode;
+ u32 write_count;
+ u32 prev_write_count;
+ bool is_enabled;
+ bool is_enabled_pc;
+ u8 test_reg[REGULATOR_TEST_BANKS_MAX];
+ u8 ctrl_reg;
+ u8 clk_ctrl_reg;
+ u8 sleep_ctrl_reg;
+ u8 pfm_ctrl_reg;
+ u8 pwr_cnfg_reg;
+};
+
+/**
+ * struct pm8xxx_regulator_core_platform_data - platform data specified in a
+ * PMIC core driver and utilized in the pm8xxx-regulator driver
+* @vreg: pointer to pm8xxx_vreg data structure that may be shared
+* between pin controlled and non-pin controlled versions
+* of a given regulator. Note that this data must persist
+* as long as the regulator device is in use.
+* @pdata: pointer to platform data passed in from a board file
+* @is_pin_controlled: true if the regulator driver represents the pin control
+* portion of a regulator, false if not.
+*
+* This data structure should only be needed in a PMIC core driver.
+*/
+struct pm8xxx_regulator_core_platform_data {
+ struct pm8xxx_vreg *vreg;
+ struct pm8xxx_regulator_platform_data *pdata;
+ bool is_pin_controlled;
+};
+
+/* Helper macros */
+#define PLDO(_name, _pc_name, _ctrl_addr, _test_addr, _hpm_min_load) \
+ { \
+ .type = PM8XXX_REGULATOR_TYPE_PLDO, \
+ .ctrl_addr = _ctrl_addr, \
+ .test_addr = _test_addr, \
+ .hpm_min_load = PM8XXX_VREG_##_hpm_min_load##_HPM_MIN_LOAD, \
+ .rdesc.name = _name, \
+ .rdesc_pc.name = _pc_name, \
+ .write_count = 0, \
+ .prev_write_count = -1, \
+ }
+
+#define NLDO(_name, _pc_name, _ctrl_addr, _test_addr, _hpm_min_load) \
+ { \
+ .type = PM8XXX_REGULATOR_TYPE_NLDO, \
+ .ctrl_addr = _ctrl_addr, \
+ .test_addr = _test_addr, \
+ .hpm_min_load = PM8XXX_VREG_##_hpm_min_load##_HPM_MIN_LOAD, \
+ .rdesc.name = _name, \
+ .rdesc_pc.name = _pc_name, \
+ .write_count = 0, \
+ .prev_write_count = -1, \
+ }
+
+#define NLDO1200(_name, _ctrl_addr, _test_addr, _hpm_min_load) \
+ { \
+ .type = PM8XXX_REGULATOR_TYPE_NLDO1200, \
+ .ctrl_addr = _ctrl_addr, \
+ .test_addr = _test_addr, \
+ .hpm_min_load = PM8XXX_VREG_##_hpm_min_load##_HPM_MIN_LOAD, \
+ .rdesc.name = _name, \
+ .write_count = 0, \
+ .prev_write_count = -1, \
+ }
+
+#define SMPS(_name, _pc_name, _ctrl_addr, _test_addr, _clk_ctrl_addr, \
+ _sleep_ctrl_addr, _hpm_min_load) \
+ { \
+ .type = PM8XXX_REGULATOR_TYPE_SMPS, \
+ .ctrl_addr = _ctrl_addr, \
+ .test_addr = _test_addr, \
+ .clk_ctrl_addr = _clk_ctrl_addr, \
+ .sleep_ctrl_addr = _sleep_ctrl_addr, \
+ .hpm_min_load = PM8XXX_VREG_##_hpm_min_load##_HPM_MIN_LOAD, \
+ .rdesc.name = _name, \
+ .rdesc_pc.name = _pc_name, \
+ .write_count = 0, \
+ .prev_write_count = -1, \
+ }
+
+#define FTSMPS(_name, _pwm_ctrl_addr, _fts_cnfg1_addr, _pfm_ctrl_addr, \
+ _pwr_cnfg_addr, _hpm_min_load) \
+ { \
+ .type = PM8XXX_REGULATOR_TYPE_FTSMPS, \
+ .ctrl_addr = _pwm_ctrl_addr, \
+ .test_addr = _fts_cnfg1_addr, \
+ .pfm_ctrl_addr = _pfm_ctrl_addr, \
+ .pwr_cnfg_addr = _pwr_cnfg_addr, \
+ .hpm_min_load = PM8XXX_VREG_##_hpm_min_load##_HPM_MIN_LOAD, \
+ .rdesc.name = _name, \
+ .write_count = 0, \
+ .prev_write_count = -1, \
+ }
+
+#define VS(_name, _pc_name, _ctrl_addr) \
+ { \
+ .type = PM8XXX_REGULATOR_TYPE_VS, \
+ .ctrl_addr = _ctrl_addr, \
+ .rdesc.name = _name, \
+ .rdesc_pc.name = _pc_name, \
+ .write_count = 0, \
+ .prev_write_count = -1, \
+ }
+
+#define VS300(_name, _ctrl_addr) \
+ { \
+ .type = PM8XXX_REGULATOR_TYPE_VS300, \
+ .ctrl_addr = _ctrl_addr, \
+ .rdesc.name = _name, \
+ .write_count = 0, \
+ .prev_write_count = -1, \
+ }
+
+#define NCP(_name, _ctrl_addr) \
+ { \
+ .type = PM8XXX_REGULATOR_TYPE_NCP, \
+ .ctrl_addr = _ctrl_addr, \
+ .rdesc.name = _name, \
+ .write_count = 0, \
+ .prev_write_count = -1, \
+ }
+
+#endif
diff --git a/include/linux/mfd/pmic8058.h b/include/linux/mfd/pmic8058.h
index cf753b5d..ff7a329 100644
--- a/include/linux/mfd/pmic8058.h
+++ b/include/linux/mfd/pmic8058.h
@@ -108,20 +108,6 @@
bool charger_data_valid;
};
-enum pon_config{
- DISABLE_HARD_RESET = 0,
- SHUTDOWN_ON_HARD_RESET,
- RESTART_ON_HARD_RESET,
- MAX_PON_CONFIG,
-};
-
-enum pm8058_smpl_delay {
- PM8058_SMPL_DELAY_0p5,
- PM8058_SMPL_DELAY_1p0,
- PM8058_SMPL_DELAY_1p5,
- PM8058_SMPL_DELAY_2p0,
-};
-
struct pm8058_platform_data {
struct pm8xxx_mpp_platform_data *mpp_pdata;
struct pm8xxx_keypad_platform_data *keypad_pdata;
@@ -144,64 +130,4 @@
struct pmic8058_charger_data *charger_pdata;
};
-#ifdef CONFIG_PMIC8058
-int pm8058_reset_pwr_off(int reset);
-#else
-static inline int pm8058_reset_pwr_off(int reset) { return 0; }
-#endif
-
-
-int pm8058_hard_reset_config(enum pon_config config);
-
-/**
- * pm8058_smpl_control - enables/disables SMPL detection
- * @enable: 0 = shutdown PMIC on power loss, 1 = reset PMIC on power loss
- *
- * This function enables or disables the Sudden Momentary Power Loss detection
- * module. If SMPL detection is enabled, then when a sufficiently long power
- * loss event occurs, the PMIC will automatically reset itself. If SMPL
- * detection is disabled, then the PMIC will shutdown when power loss occurs.
- *
- * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
- */
-int pm8058_smpl_control(int enable);
-
-/**
- * pm8058_smpl_set_delay - sets the SMPL detection time delay
- * @delay: enum value corresponding to delay time
- *
- * This function sets the time delay of the SMPL detection module. If power
- * is reapplied within this interval, then the PMIC reset automatically. The
- * SMPL detection module must be enabled for this delay time to take effect.
- *
- * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
- */
-int pm8058_smpl_set_delay(enum pm8058_smpl_delay delay);
-
-/**
- * pm8058_watchdog_reset_control - enables/disables watchdog reset detection
- * @enable: 0 = shutdown when PS_HOLD goes low, 1 = reset when PS_HOLD goes low
- *
- * This function enables or disables the PMIC watchdog reset detection feature.
- * If watchdog reset detection is enabled, then the PMIC will reset itself
- * when PS_HOLD goes low. If it is not enabled, then the PMIC will shutdown
- * when PS_HOLD goes low.
- *
- * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
- */
-int pm8058_watchdog_reset_control(int enable);
-
-/**
- * pm8058_stay_on - enables stay_on feature
- *
- * PMIC stay-on feature allows PMIC to ignore MSM PS_HOLD=low
- * signal so that some special functions like debugging could be
- * performed.
- *
- * This feature should not be used in any product release.
- *
- * RETURNS: an appropriate -ERRNO error value on error, or zero for success.
- */
-int pm8058_stay_on(void);
-
#endif /* __MFD_PMIC8058_H__ */
diff --git a/include/linux/mfd/pmic8901.h b/include/linux/mfd/pmic8901.h
index 932f8da..f5b34be 100644
--- a/include/linux/mfd/pmic8901.h
+++ b/include/linux/mfd/pmic8901.h
@@ -23,6 +23,7 @@
#include <linux/mfd/pm8xxx/mpp.h>
#include <linux/mfd/pm8xxx/tm.h>
#include <linux/regulator/pmic8901-regulator.h>
+#include <linux/mfd/pm8xxx/misc.h>
#define PM8901_IRQ_BLOCK_BIT(block, bit) ((block) * 8 + (bit))
@@ -37,19 +38,12 @@
#define PM8901_TEMPSTAT_IRQ PM8901_IRQ_BLOCK_BIT(6, 4)
#define PM8901_OVERTEMP_IRQ PM8901_IRQ_BLOCK_BIT(6, 5)
-struct pm8901_chip;
-
struct pm8901_platform_data {
- struct pm8xxx_irq_platform_data *irq_pdata;
- struct pm8xxx_mpp_platform_data *mpp_pdata;
- struct pm8901_vreg_pdata *regulator_pdatas;
- int num_regulators;
+ struct pm8xxx_irq_platform_data *irq_pdata;
+ struct pm8xxx_mpp_platform_data *mpp_pdata;
+ struct pm8xxx_misc_platform_data *misc_pdata;
+ struct pm8901_vreg_pdata *regulator_pdatas;
+ int num_regulators;
};
-#ifdef CONFIG_PMIC8901
-int pm8901_reset_pwr_off(int reset);
-#else
-static inline int pm8901_reset_pwr_off(int reset) { return 0; }
-#endif
-
#endif /* __PMIC8901_H__ */
diff --git a/include/linux/msm_vidc_dec.h b/include/linux/msm_vidc_dec.h
index 768fb3b..9c742b5 100644
--- a/include/linux/msm_vidc_dec.h
+++ b/include/linux/msm_vidc_dec.h
@@ -198,6 +198,15 @@
#define VDEC_IOCTL_SET_CONT_ON_RECONFIG \
_IO(VDEC_IOCTL_MAGIC, 34)
+#define VDEC_IOCTL_SET_DISABLE_DMX \
+ _IOW(VDEC_IOCTL_MAGIC, 35, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_GET_DISABLE_DMX \
+ _IOR(VDEC_IOCTL_MAGIC, 36, struct vdec_ioctl_msg)
+
+#define VDEC_IOCTL_GET_DISABLE_DMX_SUPPORT \
+ _IOR(VDEC_IOCTL_MAGIC, 37, struct vdec_ioctl_msg)
+
enum vdec_picture {
PICTURE_TYPE_I,
PICTURE_TYPE_P,
@@ -494,6 +503,8 @@
void *client_data;
int pmem_fd;
size_t pmem_offset;
+ void __user *desc_addr;
+ uint32_t desc_size;
};
struct vdec_framesize {
diff --git a/include/linux/msm_vidc_enc.h b/include/linux/msm_vidc_enc.h
index 12c7afd..1f264e9 100644
--- a/include/linux/msm_vidc_enc.h
+++ b/include/linux/msm_vidc_enc.h
@@ -92,16 +92,17 @@
#define VEN_LEVEL_H264_2p2 0x10/* H.264 Level 2.2 */
#define VEN_LEVEL_H264_3 0x11/* H.264 Level 3 */
#define VEN_LEVEL_H264_3p1 0x12/* H.264 Level 3.1 */
-#define VEN_LEVEL_H264_4 0x13/* H.264 Level 4 */
+#define VEN_LEVEL_H264_3p2 0x13/* H.264 Level 3.2 */
+#define VEN_LEVEL_H264_4 0x14/* H.264 Level 4 */
-#define VEN_LEVEL_H263_10 0x14/* H.263 Level 10 */
-#define VEN_LEVEL_H263_20 0x15/* H.263 Level 20 */
-#define VEN_LEVEL_H263_30 0x16/* H.263 Level 30 */
-#define VEN_LEVEL_H263_40 0x17/* H.263 Level 40 */
-#define VEN_LEVEL_H263_45 0x18/* H.263 Level 45 */
-#define VEN_LEVEL_H263_50 0x19/* H.263 Level 50 */
-#define VEN_LEVEL_H263_60 0x1A/* H.263 Level 60 */
-#define VEN_LEVEL_H263_70 0x1B/* H.263 Level 70 */
+#define VEN_LEVEL_H263_10 0x15/* H.263 Level 10 */
+#define VEN_LEVEL_H263_20 0x16/* H.263 Level 20 */
+#define VEN_LEVEL_H263_30 0x17/* H.263 Level 30 */
+#define VEN_LEVEL_H263_40 0x18/* H.263 Level 40 */
+#define VEN_LEVEL_H263_45 0x19/* H.263 Level 45 */
+#define VEN_LEVEL_H263_50 0x1A/* H.263 Level 50 */
+#define VEN_LEVEL_H263_60 0x1B/* H.263 Level 60 */
+#define VEN_LEVEL_H263_70 0x1C/* H.263 Level 70 */
/*Entropy coding model selection for H.264 encoder.*/
#define VEN_ENTROPY_MODEL_CAVLC 1
@@ -435,6 +436,9 @@
#define VEN_IOCTL_GET_NUMBER_INSTANCES \
_IOR(VEN_IOCTLBASE_ENC, 46, struct venc_ioctl_msg)
+#define VEN_IOCTL_SET_METABUFFER_MODE \
+ _IOW(VEN_IOCTLBASE_ENC, 47, struct venc_ioctl_msg)
+
struct venc_switch{
unsigned char status;
};
diff --git a/include/linux/platform_data/qcom_crypto_device.h b/include/linux/platform_data/qcom_crypto_device.h
index c6ef40a..08aa784 100644
--- a/include/linux/platform_data/qcom_crypto_device.h
+++ b/include/linux/platform_data/qcom_crypto_device.h
@@ -18,6 +18,7 @@
uint32_t shared_ce_resource;
uint32_t hw_key_support;
uint32_t sha_hmac;
+ void *bus_scale_table;
};
#endif /* __QCOM_CRYPTO_DEVICE__H */
diff --git a/include/linux/regulator/pm8xxx-regulator.h b/include/linux/regulator/pm8xxx-regulator.h
new file mode 100644
index 0000000..7c8d778
--- /dev/null
+++ b/include/linux/regulator/pm8xxx-regulator.h
@@ -0,0 +1,80 @@
+/* Copyright (c) 2011, 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 __REGULATOR_PM8XXX_REGULATOR_H__
+#define __REGULATOR_PM8XXX_REGULATOR_H__
+
+#include <linux/regulator/machine.h>
+
+#define PM8XXX_REGULATOR_DEV_NAME "pm8xxx-regulator"
+
+/* Pin control input pins. */
+#define PM8XXX_VREG_PIN_CTRL_NONE 0x00
+#define PM8XXX_VREG_PIN_CTRL_EN0 0x01
+#define PM8XXX_VREG_PIN_CTRL_EN1 0x02
+#define PM8XXX_VREG_PIN_CTRL_EN2 0x04
+#define PM8XXX_VREG_PIN_CTRL_EN3 0x08
+#define PM8XXX_VREG_PIN_CTRL_ALL 0x0F
+
+#define PM8921_VREG_PIN_CTRL_NONE PM8XXX_VREG_PIN_CTRL_NONE
+#define PM8921_VREG_PIN_CTRL_D1 PM8XXX_VREG_PIN_CTRL_EN0
+#define PM8921_VREG_PIN_CTRL_A0 PM8XXX_VREG_PIN_CTRL_EN1
+#define PM8921_VREG_PIN_CTRL_A1 PM8XXX_VREG_PIN_CTRL_EN2
+#define PM8921_VREG_PIN_CTRL_A2 PM8XXX_VREG_PIN_CTRL_EN3
+
+/* Minimum high power mode loads in uA. */
+#define PM8XXX_VREG_LDO_50_HPM_MIN_LOAD 5000
+#define PM8XXX_VREG_LDO_150_HPM_MIN_LOAD 10000
+#define PM8XXX_VREG_LDO_300_HPM_MIN_LOAD 10000
+#define PM8XXX_VREG_LDO_600_HPM_MIN_LOAD 10000
+#define PM8XXX_VREG_LDO_1200_HPM_MIN_LOAD 10000
+#define PM8XXX_VREG_SMPS_1500_HPM_MIN_LOAD 100000
+#define PM8XXX_VREG_SMPS_2000_HPM_MIN_LOAD 100000
+
+#define REGULATOR_TEST_BANKS_MAX 8
+
+/**
+ * enum pm8xxx_vreg_pin_function - action to perform when pin control is active
+ * %PM8XXX_VREG_PIN_FN_ENABLE: pin control enables the regulator
+ * %PM8XXX_VREG_PIN_FN_MODE: pin control changes mode from LPM to HPM
+ */
+enum pm8xxx_vreg_pin_function {
+ PM8XXX_VREG_PIN_FN_ENABLE = 0,
+ PM8XXX_VREG_PIN_FN_MODE,
+};
+
+/**
+ * struct pm8xxx_regulator_platform_data - PMIC 8921 regulator platform data
+ * @init_data: regulator constraints
+ * @id: regulator id. Any value unique among pm8xxx_regulator
+ * devices is acceptable.
+ * @pull_down_enable: 0 = no pulldown, 1 = pulldown when regulator disabled
+ * @pin_ctrl: pin control inputs to use for the regulator; should be
+ * a combination of PM8XXX_VREG_PIN_CTRL_* values
+ * @pin_fn: action to perform when pin control pin is active
+ * @system_uA: current drawn from regulator not accounted for by any
+ * regulator framework consumer
+ * @enable_time: time in us taken to enable a regulator to the maximum
+ * allowed voltage for the system. This is dependent upon
+ * the load and capacitance for a regulator on the board.
+ */
+struct pm8xxx_regulator_platform_data {
+ struct regulator_init_data init_data;
+ int id;
+ unsigned pull_down_enable;
+ unsigned pin_ctrl;
+ enum pm8xxx_vreg_pin_function pin_fn;
+ int system_uA;
+ int enable_time;
+};
+
+#endif
diff --git a/include/linux/tspp.h b/include/linux/tspp.h
new file mode 100644
index 0000000..d5a5ffc
--- /dev/null
+++ b/include/linux/tspp.h
@@ -0,0 +1,84 @@
+#ifndef _TSPP_H_
+#define _TSPP_H_
+
+#include <linux/ioctl.h>
+
+#define TSPP_NUM_SYSTEM_KEYS 8
+
+enum tspp_key_parity {
+ TSPP_KEY_PARITY_EVEN,
+ TSPP_KEY_PARITY_ODD
+};
+
+enum tspp_source {
+ TSPP_SOURCE_TSIF0,
+ TSPP_SOURCE_TSIF1,
+ TSPP_SOURCE_MEM,
+ TSPP_SOURCE_NONE = -1
+};
+
+enum tspp_mode {
+ TSPP_MODE_DISABLED,
+ TSPP_MODE_PES,
+ TSPP_MODE_RAW,
+ TSPP_MODE_RAW_NO_SUFFIX
+};
+
+struct tspp_filter {
+ int pid;
+ int mask;
+ enum tspp_mode mode;
+ int priority; /* 0 - 15 */
+ int decrypt;
+ enum tspp_source source;
+};
+
+struct tspp_select_source {
+ enum tspp_source source;
+};
+
+struct tspp_pid {
+ int pid;
+};
+
+struct tspp_key {
+ enum tspp_key_parity parity;
+ int lsb;
+ int msb;
+};
+
+struct tspp_iv {
+ int data[2];
+};
+
+struct tspp_system_keys {
+ int data[TSPP_NUM_SYSTEM_KEYS];
+};
+
+struct tspp_buffer {
+ int size;
+};
+
+/* defines for IOCTL functions */
+/* read Documentation/ioctl-number.txt */
+/* some random number to avoid coinciding with other ioctl numbers */
+#define TSPP_IOCTL_BASE 0xAA
+#define TSPP_IOCTL_SELECT_SOURCE \
+ _IOW(TSPP_IOCTL_BASE, 0, struct tspp_select_source)
+#define TSPP_IOCTL_ADD_FILTER \
+ _IOW(TSPP_IOCTL_BASE, 1, struct tspp_filter)
+#define TSPP_IOCTL_REMOVE_FILTER \
+ _IOW(TSPP_IOCTL_BASE, 2, struct tspp_pid)
+#define TSPP_IOCTL_SET_KEY \
+ _IOW(TSPP_IOCTL_BASE, 3, struct tspp_key)
+#define TSPP_IOCTL_SET_IV \
+ _IOW(TSPP_IOCTL_BASE, 4, struct tspp_iv)
+#define TSPP_IOCTL_SET_SYSTEM_KEYS \
+ _IOW(TSPP_IOCTL_BASE, 5, struct tspp_system_keys)
+#define TSPP_IOCTL_BUFFER_SIZE \
+ _IOW(TSPP_IOCTL_BASE, 6, struct tspp_buffer)
+#define TSPP_IOCTL_LOOPBACK \
+ _IOW(TSPP_IOCTL_BASE, 0xFF, int)
+
+
+#endif /* _TSPP_H_ */
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index c83035d..47e8427 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -57,6 +57,7 @@
* Note that for writes (IN transfers) some data bytes may still
* reside in a device-side FIFO when the request is reported as
* complete.
+ *@udc_priv: Vendor private data in usage by the UDC.
*
* These are allocated/freed through the endpoint they're used with. The
* hardware's driver can add extra per-request data to the memory it returns,
@@ -92,6 +93,7 @@
int status;
unsigned actual;
+ unsigned udc_priv;
};
/*-------------------------------------------------------------------------*/
@@ -111,7 +113,6 @@
struct usb_request *(*alloc_request) (struct usb_ep *ep,
gfp_t gfp_flags);
void (*free_request) (struct usb_ep *ep, struct usb_request *req);
-
int (*queue) (struct usb_ep *ep, struct usb_request *req,
gfp_t gfp_flags);
int (*dequeue) (struct usb_ep *ep, struct usb_request *req);
diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h
index 68fc67c..eb2c543 100644
--- a/include/linux/usb/msm_hsusb.h
+++ b/include/linux/usb/msm_hsusb.h
@@ -132,6 +132,19 @@
};
/**
+ * SPS Pipes direction.
+ *
+ * USB_TO_PEER_PERIPHERAL USB (as Producer) to other
+ * peer peripheral.
+ * PEER_PERIPHERAL_TO_USB Other Peripheral to
+ * USB (as consumer).
+ */
+enum usb_bam_pipe_dir {
+ USB_TO_PEER_PERIPHERAL,
+ PEER_PERIPHERAL_TO_USB,
+};
+
+/**
* struct msm_otg_platform_data - platform device data
* for msm_otg driver.
* @phy_init_seq: PHY configuration sequence. val, reg pairs
@@ -258,4 +271,21 @@
unsigned hub_reset;
};
+struct usb_bam_pipe_connect {
+ u32 src_phy_addr;
+ int src_pipe_index;
+ u32 dst_phy_addr;
+ int dst_pipe_index;
+ u32 data_fifo_base_offset;
+ u32 data_fifo_size;
+ u32 desc_fifo_base_offset;
+ u32 desc_fifo_size;
+};
+
+struct msm_usb_bam_platform_data {
+ struct usb_bam_pipe_connect *connections;
+ unsigned long usb_bam_phy_base;
+ unsigned long usb_bam_phy_size;
+ int usb_bam_num_pipes;
+};
#endif
diff --git a/include/media/msm_camera.h b/include/media/msm_camera.h
index 4d5d697..423a849 100644
--- a/include/media/msm_camera.h
+++ b/include/media/msm_camera.h
@@ -159,6 +159,15 @@
#define MSM_CAM_IOCTL_PICT_PP_DIVERT_DONE \
_IOR(MSM_CAM_IOCTL_MAGIC, 47, struct msm_pp_frame *)
+#define MSM_CAM_IOCTL_SENSOR_V4l2_S_CTRL \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 48, struct v4l2_control)
+
+#define MSM_CAM_IOCTL_SENSOR_V4l2_QUERY_CTRL \
+ _IOR(MSM_CAM_IOCTL_MAGIC, 49, struct v4l2_queryctrl)
+
+#define MSM_CAM_IOCTL_GET_KERNEL_SYSTEM_TIME \
+ _IOW(MSM_CAM_IOCTL_MAGIC, 50, struct timeval *)
+
struct msm_mctl_pp_cmd {
int32_t id;
uint16_t length;
@@ -821,6 +830,73 @@
#define CAMERA_EXPOSURE_COMPENSATION_LV3 -6
#define CAMERA_EXPOSURE_COMPENSATION_LV4 -12
+enum msm_v4l2_saturation_level {
+ MSM_V4L2_SATURATION_L0,
+ MSM_V4L2_SATURATION_L1,
+ MSM_V4L2_SATURATION_L2,
+ MSM_V4L2_SATURATION_L3,
+ MSM_V4L2_SATURATION_L4,
+ MSM_V4L2_SATURATION_L5,
+ MSM_V4L2_SATURATION_L6,
+ MSM_V4L2_SATURATION_L7,
+ MSM_V4L2_SATURATION_L8,
+ MSM_V4L2_SATURATION_L9,
+ MSM_V4L2_SATURATION_L10,
+};
+
+enum msm_v4l2_exposure_level {
+ MSM_V4L2_EXPOSURE_N2,
+ MSM_V4L2_EXPOSURE_N1,
+ MSM_V4L2_EXPOSURE_D,
+ MSM_V4L2_EXPOSURE_P1,
+ MSM_V4L2_EXPOSURE_P2,
+};
+
+enum msm_v4l2_sharpness_level {
+ MSM_V4L2_SHARPNESS_L0,
+ MSM_V4L2_SHARPNESS_L1,
+ MSM_V4L2_SHARPNESS_L2,
+ MSM_V4L2_SHARPNESS_L3,
+ MSM_V4L2_SHARPNESS_L4,
+ MSM_V4L2_SHARPNESS_L5,
+ MSM_V4L2_SHARPNESS_L6,
+};
+
+enum msm_v4l2_expo_metering_mode {
+ MSM_V4L2_EXP_FRAME_AVERAGE,
+ MSM_V4L2_EXP_CENTER_WEIGHTED,
+ MSM_V4L2_EXP_SPOT_METERING,
+};
+
+enum msm_v4l2_iso_mode {
+ MSM_V4L2_ISO_AUTO = 0,
+ MSM_V4L2_ISO_DEBLUR,
+ MSM_V4L2_ISO_100,
+ MSM_V4L2_ISO_200,
+ MSM_V4L2_ISO_400,
+ MSM_V4L2_ISO_800,
+ MSM_V4L2_ISO_1600,
+};
+
+enum msm_v4l2_wb_mode {
+ MSM_V4L2_WB_MIN_MINUS_1,
+ MSM_V4L2_WB_AUTO = 1,
+ MSM_V4L2_WB_CUSTOM,
+ MSM_V4L2_WB_INCANDESCENT,
+ MSM_V4L2_WB_FLUORESCENT,
+ MSM_V4L2_WB_DAYLIGHT,
+ MSM_V4L2_WB_CLOUDY_DAYLIGHT,
+ MSM_V4L2_WB_TWILIGHT,
+ MSM_V4L2_WB_SHADE,
+ MSM_V4L2_WB_OFF,
+};
+
+enum msm_v4l2_power_line_frequency {
+ MSM_V4L2_POWER_LINE_OFF,
+ MSM_V4L2_POWER_LINE_60HZ,
+ MSM_V4L2_POWER_LINE_50HZ,
+ MSM_V4L2_POWER_LINE_AUTO,
+};
struct sensor_pict_fps {
uint16_t prevfps;
diff --git a/include/media/tavarua.h b/include/media/tavarua.h
index d40829e..b7380bc 100644
--- a/include/media/tavarua.h
+++ b/include/media/tavarua.h
@@ -147,15 +147,26 @@
V4L2_CID_PRIVATE_TAVARUA_AF_JUMP,
V4L2_CID_PRIVATE_TAVARUA_RSSI_DELTA,
V4L2_CID_PRIVATE_TAVARUA_HLSI,
+
/*
* Here we have IOCTl's that are specific to IRIS
- * (V4L2_CID_PRIVATE_BASE + 0x1E to V4L2_CID_PRIVATE_BASE + 0x27)
+ * (V4L2_CID_PRIVATE_BASE + 0x1E to V4L2_CID_PRIVATE_BASE + 0x28)
*/
- V4L2_CID_PRIVATE_TAVARUA_SET_NOTCH_FILTER =
- V4L2_CID_PRIVATE_BASE + 0x28, /* IRIS specific command */
- V4L2_CID_PRIVATE_TAVARUA_SET_AUDIO_PATH,
- V4L2_CID_PRIVATE_TAVARUA_DO_CALIBRATION, /* IRIS specific command */
- V4L2_CID_PRIVATE_TAVARUA_SRCH_ALGORITHM,
+ V4L2_CID_PRIVATE_SOFT_MUTE, /* 0x800001E*/
+ V4L2_CID_PRIVATE_RIVA_ACCS_ADDR,
+ V4L2_CID_PRIVATE_RIVA_ACCS_LEN,
+ V4L2_CID_PRIVATE_RIVA_PEEK,
+ V4L2_CID_PRIVATE_RIVA_POKE,
+ V4L2_CID_PRIVATE_SSBI_ACCS_ADDR,
+ V4L2_CID_PRIVATE_SSBI_PEEK,
+ V4L2_CID_PRIVATE_SSBI_POKE,
+ V4L2_CID_PRIVATE_TX_TONE,
+ V4L2_CID_PRIVATE_RDS_GRP_COUNTERS,
+ V4L2_CID_PRIVATE_SET_NOTCH_FILTER, /* 0x8000028 */
+
+ V4L2_CID_PRIVATE_TAVARUA_SET_AUDIO_PATH, /* 0x8000029 */
+ V4L2_CID_PRIVATE_TAVARUA_DO_CALIBRATION, /* 0x800002A : IRIS command */
+ V4L2_CID_PRIVATE_TAVARUA_SRCH_ALGORITHM, /* 0x800002B */
V4L2_CID_PRIVATE_TAVARUA_ON_CHANNEL_THRESHOLD =
V4L2_CTRL_CLASS_USER + 0x92B,
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 396e8fc..e95d3ac 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -536,6 +536,11 @@
* This number should increase every time the list of stations
* changes, i.e. when a station is added or removed, so that
* userspace can tell whether it got a consistent snapshot.
+ * @assoc_req_ies: IEs from (Re)Association Request.
+ * This is used only when in AP mode with drivers that do not use
+ * user space MLME/SME implementation. The information is provided for
+ * the cfg80211_new_sta() calls to notify user space of the IEs.
+ * @assoc_req_ies_len: Length of assoc_req_ies buffer in octets.
*/
struct station_info {
u32 filled;
@@ -558,6 +563,14 @@
struct sta_bss_parameters bss_param;
int generation;
+
+ const u8 *assoc_req_ies;
+ size_t assoc_req_ies_len;
+
+ /*
+ * Note: Add a new enum station_info_flags value for each new field and
+ * use it to check which fields are initialized.
+ */
};
/**
diff --git a/include/sound/q6afe.h b/include/sound/q6afe.h
index e913e15..4be1abe 100644
--- a/include/sound/q6afe.h
+++ b/include/sound/q6afe.h
@@ -93,4 +93,6 @@
*/
int afe_convert_virtual_to_portid(u16 port_id);
+int afe_pseudo_port_start_nowait(u16 port_id);
+int afe_pseudo_port_stop_nowait(u16 port_id);
#endif /* __Q6AFE_H__ */
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index 2adc6b5..fe1f47a 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -26,7 +26,7 @@
int irq_set_chip(unsigned int irq, struct irq_chip *chip)
{
unsigned long flags;
- struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+ struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
if (!desc)
return -EINVAL;
@@ -54,7 +54,7 @@
int irq_set_irq_type(unsigned int irq, unsigned int type)
{
unsigned long flags;
- struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
+ struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
int ret = 0;
if (!desc)
@@ -78,7 +78,7 @@
int irq_set_handler_data(unsigned int irq, void *data)
{
unsigned long flags;
- struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+ struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
if (!desc)
return -EINVAL;
@@ -98,7 +98,7 @@
int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry)
{
unsigned long flags;
- struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+ struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
if (!desc)
return -EINVAL;
@@ -119,7 +119,7 @@
int irq_set_chip_data(unsigned int irq, void *data)
{
unsigned long flags;
- struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+ struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
if (!desc)
return -EINVAL;
@@ -204,6 +204,24 @@
}
}
+void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu)
+{
+ if (desc->irq_data.chip->irq_enable)
+ desc->irq_data.chip->irq_enable(&desc->irq_data);
+ else
+ desc->irq_data.chip->irq_unmask(&desc->irq_data);
+ cpumask_set_cpu(cpu, desc->percpu_enabled);
+}
+
+void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu)
+{
+ if (desc->irq_data.chip->irq_disable)
+ desc->irq_data.chip->irq_disable(&desc->irq_data);
+ else
+ desc->irq_data.chip->irq_mask(&desc->irq_data);
+ cpumask_clear_cpu(cpu, desc->percpu_enabled);
+}
+
static inline void mask_ack_irq(struct irq_desc *desc)
{
if (desc->irq_data.chip->irq_mask_ack)
@@ -553,12 +571,44 @@
chip->irq_eoi(&desc->irq_data);
}
+/**
+ * handle_percpu_devid_irq - Per CPU local irq handler with per cpu dev ids
+ * @irq: the interrupt number
+ * @desc: the interrupt description structure for this irq
+ *
+ * Per CPU interrupts on SMP machines without locking requirements. Same as
+ * handle_percpu_irq() above but with the following extras:
+ *
+ * action->percpu_dev_id is a pointer to percpu variables which
+ * contain the real device id for the cpu on which this handler is
+ * called
+ */
+void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct irqaction *action = desc->action;
+ void *dev_id = __this_cpu_ptr(action->percpu_dev_id);
+ irqreturn_t res;
+
+ kstat_incr_irqs_this_cpu(irq, desc);
+
+ if (chip->irq_ack)
+ chip->irq_ack(&desc->irq_data);
+
+ trace_irq_handler_entry(irq, action);
+ res = action->handler(irq, dev_id);
+ trace_irq_handler_exit(irq, action, res);
+
+ if (chip->irq_eoi)
+ chip->irq_eoi(&desc->irq_data);
+}
+
void
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
const char *name)
{
unsigned long flags;
- struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
+ struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
if (!desc)
return;
@@ -602,7 +652,7 @@
void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
{
unsigned long flags;
- struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+ struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
if (!desc)
return;
diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h
index 6546431..a73dd6c 100644
--- a/kernel/irq/internals.h
+++ b/kernel/irq/internals.h
@@ -71,6 +71,8 @@
extern void irq_shutdown(struct irq_desc *desc);
extern void irq_enable(struct irq_desc *desc);
extern void irq_disable(struct irq_desc *desc);
+extern void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu);
+extern void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu);
extern void mask_irq(struct irq_desc *desc);
extern void unmask_irq(struct irq_desc *desc);
@@ -114,14 +116,21 @@
desc->irq_data.chip->irq_bus_sync_unlock(&desc->irq_data);
}
+#define _IRQ_DESC_CHECK (1 << 0)
+#define _IRQ_DESC_PERCPU (1 << 1)
+
+#define IRQ_GET_DESC_CHECK_GLOBAL (_IRQ_DESC_CHECK)
+#define IRQ_GET_DESC_CHECK_PERCPU (_IRQ_DESC_CHECK | _IRQ_DESC_PERCPU)
+
struct irq_desc *
-__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus);
+__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
+ unsigned int check);
void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus);
static inline struct irq_desc *
-irq_get_desc_buslock(unsigned int irq, unsigned long *flags)
+irq_get_desc_buslock(unsigned int irq, unsigned long *flags, unsigned int check)
{
- return __irq_get_desc_lock(irq, flags, true);
+ return __irq_get_desc_lock(irq, flags, true, check);
}
static inline void
@@ -131,9 +140,9 @@
}
static inline struct irq_desc *
-irq_get_desc_lock(unsigned int irq, unsigned long *flags)
+irq_get_desc_lock(unsigned int irq, unsigned long *flags, unsigned int check)
{
- return __irq_get_desc_lock(irq, flags, false);
+ return __irq_get_desc_lock(irq, flags, false, check);
}
static inline void
diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c
index 4c60a50..028e377 100644
--- a/kernel/irq/irqdesc.c
+++ b/kernel/irq/irqdesc.c
@@ -70,7 +70,8 @@
static inline int desc_node(struct irq_desc *desc) { return 0; }
#endif
-static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node)
+static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node,
+ struct module *owner)
{
int cpu;
@@ -86,6 +87,7 @@
desc->irq_count = 0;
desc->irqs_unhandled = 0;
desc->name = NULL;
+ desc->owner = owner;
for_each_possible_cpu(cpu)
*per_cpu_ptr(desc->kstat_irqs, cpu) = 0;
desc_smp_init(desc, node);
@@ -128,7 +130,7 @@
static inline void free_masks(struct irq_desc *desc) { }
#endif
-static struct irq_desc *alloc_desc(int irq, int node)
+static struct irq_desc *alloc_desc(int irq, int node, struct module *owner)
{
struct irq_desc *desc;
gfp_t gfp = GFP_KERNEL;
@@ -147,7 +149,7 @@
raw_spin_lock_init(&desc->lock);
lockdep_set_class(&desc->lock, &irq_desc_lock_class);
- desc_set_defaults(irq, desc, node);
+ desc_set_defaults(irq, desc, node, owner);
return desc;
@@ -173,13 +175,14 @@
kfree(desc);
}
-static int alloc_descs(unsigned int start, unsigned int cnt, int node)
+static int alloc_descs(unsigned int start, unsigned int cnt, int node,
+ struct module *owner)
{
struct irq_desc *desc;
int i;
for (i = 0; i < cnt; i++) {
- desc = alloc_desc(start + i, node);
+ desc = alloc_desc(start + i, node, owner);
if (!desc)
goto err;
mutex_lock(&sparse_irq_lock);
@@ -227,7 +230,7 @@
nr_irqs = initcnt;
for (i = 0; i < initcnt; i++) {
- desc = alloc_desc(i, node);
+ desc = alloc_desc(i, node, NULL);
set_bit(i, allocated_irqs);
irq_insert_desc(i, desc);
}
@@ -261,7 +264,7 @@
alloc_masks(&desc[i], GFP_KERNEL, node);
raw_spin_lock_init(&desc[i].lock);
lockdep_set_class(&desc[i].lock, &irq_desc_lock_class);
- desc_set_defaults(i, &desc[i], node);
+ desc_set_defaults(i, &desc[i], node, NULL);
}
return arch_early_irq_init();
}
@@ -276,8 +279,16 @@
dynamic_irq_cleanup(irq);
}
-static inline int alloc_descs(unsigned int start, unsigned int cnt, int node)
+static inline int alloc_descs(unsigned int start, unsigned int cnt, int node,
+ struct module *owner)
{
+ u32 i;
+
+ for (i = 0; i < cnt; i++) {
+ struct irq_desc *desc = irq_to_desc(start + i);
+
+ desc->owner = owner;
+ }
return start;
}
@@ -337,7 +348,8 @@
* Returns the first irq number or error code
*/
int __ref
-irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node)
+__irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,
+ struct module *owner)
{
int start, ret;
@@ -366,13 +378,13 @@
bitmap_set(allocated_irqs, start, cnt);
mutex_unlock(&sparse_irq_lock);
- return alloc_descs(start, cnt, node);
+ return alloc_descs(start, cnt, node, owner);
err:
mutex_unlock(&sparse_irq_lock);
return ret;
}
-EXPORT_SYMBOL_GPL(irq_alloc_descs);
+EXPORT_SYMBOL_GPL(__irq_alloc_descs);
/**
* irq_reserve_irqs - mark irqs allocated
@@ -411,11 +423,22 @@
}
struct irq_desc *
-__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus)
+__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus,
+ unsigned int check)
{
struct irq_desc *desc = irq_to_desc(irq);
if (desc) {
+ if (check & _IRQ_DESC_CHECK) {
+ if ((check & _IRQ_DESC_PERCPU) &&
+ !irq_settings_is_per_cpu_devid(desc))
+ return NULL;
+
+ if (!(check & _IRQ_DESC_PERCPU) &&
+ irq_settings_is_per_cpu_devid(desc))
+ return NULL;
+ }
+
if (bus)
chip_bus_lock(desc);
raw_spin_lock_irqsave(&desc->lock, *flags);
@@ -430,6 +453,25 @@
chip_bus_sync_unlock(desc);
}
+int irq_set_percpu_devid(unsigned int irq)
+{
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ if (!desc)
+ return -EINVAL;
+
+ if (desc->percpu_enabled)
+ return -EINVAL;
+
+ desc->percpu_enabled = kzalloc(sizeof(*desc->percpu_enabled), GFP_KERNEL);
+
+ if (!desc->percpu_enabled)
+ return -ENOMEM;
+
+ irq_set_percpu_devid_flags(irq);
+ return 0;
+}
+
/**
* dynamic_irq_cleanup - cleanup a dynamically allocated irq
* @irq: irq number to initialize
@@ -440,7 +482,7 @@
unsigned long flags;
raw_spin_lock_irqsave(&desc->lock, flags);
- desc_set_defaults(irq, desc, desc_node(desc));
+ desc_set_defaults(irq, desc, desc_node(desc), NULL);
raw_spin_unlock_irqrestore(&desc->lock, flags);
}
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index b495711..1db1dc6 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -195,7 +195,7 @@
int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m)
{
unsigned long flags;
- struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+ struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
if (!desc)
return -EINVAL;
@@ -356,7 +356,7 @@
static int __disable_irq_nosync(unsigned int irq)
{
unsigned long flags;
- struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
+ struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
if (!desc)
return -EINVAL;
@@ -448,7 +448,7 @@
void enable_irq(unsigned int irq)
{
unsigned long flags;
- struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
+ struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
if (!desc)
return;
@@ -488,7 +488,7 @@
int irq_set_irq_wake(unsigned int irq, unsigned int on)
{
unsigned long flags;
- struct irq_desc *desc = irq_get_desc_buslock(irq, &flags);
+ struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
int ret = 0;
if (!desc)
@@ -555,7 +555,7 @@
int can_request_irq(unsigned int irq, unsigned long irqflags)
{
unsigned long flags;
- struct irq_desc *desc = irq_get_desc_lock(irq, &flags);
+ struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
int canrequest = 0;
if (!desc)
@@ -909,6 +909,8 @@
if (desc->irq_data.chip == &no_irq_chip)
return -ENOSYS;
+ if (!try_module_get(desc->owner))
+ return -ENODEV;
/*
* Some drivers like serial.c use request_irq() heavily,
* so we have to be careful not to interfere with a
@@ -932,8 +934,10 @@
*/
nested = irq_settings_is_nested_thread(desc);
if (nested) {
- if (!new->thread_fn)
- return -EINVAL;
+ if (!new->thread_fn) {
+ ret = -EINVAL;
+ goto out_mput;
+ }
/*
* Replace the primary handler which was provided from
* the driver for non nested interrupt handling by the
@@ -955,8 +959,10 @@
t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
new->name);
- if (IS_ERR(t))
- return PTR_ERR(t);
+ if (IS_ERR(t)) {
+ ret = PTR_ERR(t);
+ goto out_mput;
+ }
/*
* We keep the reference to the task struct even if
* the thread dies to avoid that the interrupt code
@@ -1121,6 +1127,8 @@
kthread_stop(t);
put_task_struct(t);
}
+out_mput:
+ module_put(desc->owner);
return ret;
}
@@ -1136,6 +1144,8 @@
int retval;
struct irq_desc *desc = irq_to_desc(irq);
+ if (WARN_ON(irq_settings_is_per_cpu_devid(desc)))
+ return -EINVAL;
chip_bus_lock(desc);
retval = __setup_irq(irq, desc, act);
chip_bus_sync_unlock(desc);
@@ -1144,7 +1154,7 @@
}
EXPORT_SYMBOL_GPL(setup_irq);
- /*
+/*
* Internal function to unregister an irqaction - used to free
* regular and special interrupts that are part of the architecture.
*/
@@ -1236,6 +1246,7 @@
put_task_struct(action->thread);
}
+ module_put(desc->owner);
return action;
}
@@ -1248,7 +1259,10 @@
*/
void remove_irq(unsigned int irq, struct irqaction *act)
{
- __free_irq(irq, act->dev_id);
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ if (desc && !WARN_ON(irq_settings_is_per_cpu_devid(desc)))
+ __free_irq(irq, act->dev_id);
}
EXPORT_SYMBOL_GPL(remove_irq);
@@ -1270,7 +1284,7 @@
{
struct irq_desc *desc = irq_to_desc(irq);
- if (!desc)
+ if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return;
#ifdef CONFIG_SMP
@@ -1348,7 +1362,8 @@
if (!desc)
return -EINVAL;
- if (!irq_settings_can_request(desc))
+ if (!irq_settings_can_request(desc) ||
+ WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
if (!handler) {
@@ -1446,3 +1461,194 @@
}
}
EXPORT_SYMBOL_GPL(irq_set_pending);
+
+void enable_percpu_irq(unsigned int irq, unsigned int type)
+{
+ unsigned int cpu = smp_processor_id();
+ unsigned long flags;
+ struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_PERCPU);
+
+ if (!desc)
+ return;
+
+ type &= IRQ_TYPE_SENSE_MASK;
+ if (type != IRQ_TYPE_NONE) {
+ int ret;
+
+ ret = __irq_set_trigger(desc, irq, type);
+
+ if (ret) {
+ WARN(1, "failed to set type for IRQ%d\n", irq);
+ goto out;
+ }
+ }
+
+ irq_percpu_enable(desc, cpu);
+out:
+ irq_put_desc_unlock(desc, flags);
+}
+
+void disable_percpu_irq(unsigned int irq)
+{
+ unsigned int cpu = smp_processor_id();
+ unsigned long flags;
+ struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_PERCPU);
+
+ if (!desc)
+ return;
+
+ irq_percpu_disable(desc, cpu);
+ irq_put_desc_unlock(desc, flags);
+}
+
+/*
+ * Internal function to unregister a percpu irqaction.
+ */
+static struct irqaction *__free_percpu_irq(unsigned int irq, void __percpu *dev_id)
+{
+ struct irq_desc *desc = irq_to_desc(irq);
+ struct irqaction *action;
+ unsigned long flags;
+
+ WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq);
+
+ if (!desc)
+ return NULL;
+
+ raw_spin_lock_irqsave(&desc->lock, flags);
+
+ action = desc->action;
+ if (!action || action->percpu_dev_id != dev_id) {
+ WARN(1, "Trying to free already-free IRQ %d\n", irq);
+ goto bad;
+ }
+
+ if (!cpumask_empty(desc->percpu_enabled)) {
+ WARN(1, "percpu IRQ %d still enabled on CPU%d!\n",
+ irq, cpumask_first(desc->percpu_enabled));
+ goto bad;
+ }
+
+ /* Found it - now remove it from the list of entries: */
+ desc->action = NULL;
+
+ raw_spin_unlock_irqrestore(&desc->lock, flags);
+
+ unregister_handler_proc(irq, action);
+
+ module_put(desc->owner);
+ return action;
+
+bad:
+ raw_spin_unlock_irqrestore(&desc->lock, flags);
+ return NULL;
+}
+
+/**
+ * remove_percpu_irq - free a per-cpu interrupt
+ * @irq: Interrupt line to free
+ * @act: irqaction for the interrupt
+ *
+ * Used to remove interrupts statically setup by the early boot process.
+ */
+void remove_percpu_irq(unsigned int irq, struct irqaction *act)
+{
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ if (desc && irq_settings_is_per_cpu_devid(desc))
+ __free_percpu_irq(irq, act->percpu_dev_id);
+}
+
+/**
+ * free_percpu_irq - free an interrupt allocated with request_percpu_irq
+ * @irq: Interrupt line to free
+ * @dev_id: Device identity to free
+ *
+ * Remove a percpu interrupt handler. The handler is removed, but
+ * the interrupt line is not disabled. This must be done on each
+ * CPU before calling this function. The function does not return
+ * until any executing interrupts for this IRQ have completed.
+ *
+ * This function must not be called from interrupt context.
+ */
+void free_percpu_irq(unsigned int irq, void __percpu *dev_id)
+{
+ struct irq_desc *desc = irq_to_desc(irq);
+
+ if (!desc || !irq_settings_is_per_cpu_devid(desc))
+ return;
+
+ chip_bus_lock(desc);
+ kfree(__free_percpu_irq(irq, dev_id));
+ chip_bus_sync_unlock(desc);
+}
+
+/**
+ * setup_percpu_irq - setup a per-cpu interrupt
+ * @irq: Interrupt line to setup
+ * @act: irqaction for the interrupt
+ *
+ * Used to statically setup per-cpu interrupts in the early boot process.
+ */
+int setup_percpu_irq(unsigned int irq, struct irqaction *act)
+{
+ struct irq_desc *desc = irq_to_desc(irq);
+ int retval;
+
+ if (!desc || !irq_settings_is_per_cpu_devid(desc))
+ return -EINVAL;
+ chip_bus_lock(desc);
+ retval = __setup_irq(irq, desc, act);
+ chip_bus_sync_unlock(desc);
+
+ return retval;
+}
+
+/**
+ * request_percpu_irq - allocate a percpu interrupt line
+ * @irq: Interrupt line to allocate
+ * @handler: Function to be called when the IRQ occurs.
+ * @devname: An ascii name for the claiming device
+ * @dev_id: A percpu cookie passed back to the handler function
+ *
+ * This call allocates interrupt resources, but doesn't
+ * automatically enable the interrupt. It has to be done on each
+ * CPU using enable_percpu_irq().
+ *
+ * Dev_id must be globally unique. It is a per-cpu variable, and
+ * the handler gets called with the interrupted CPU's instance of
+ * that variable.
+ */
+int request_percpu_irq(unsigned int irq, irq_handler_t handler,
+ const char *devname, void __percpu *dev_id)
+{
+ struct irqaction *action;
+ struct irq_desc *desc;
+ int retval;
+
+ if (!dev_id)
+ return -EINVAL;
+
+ desc = irq_to_desc(irq);
+ if (!desc || !irq_settings_can_request(desc) ||
+ !irq_settings_is_per_cpu_devid(desc))
+ return -EINVAL;
+
+ action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
+ if (!action)
+ return -ENOMEM;
+
+ action->handler = handler;
+ action->flags = IRQF_PERCPU;
+ action->name = devname;
+ action->percpu_dev_id = dev_id;
+
+ chip_bus_lock(desc);
+ retval = __setup_irq(irq, desc, action);
+ chip_bus_sync_unlock(desc);
+
+ if (retval)
+ kfree(action);
+
+ return retval;
+}
diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h
index f1667833..1162f10 100644
--- a/kernel/irq/settings.h
+++ b/kernel/irq/settings.h
@@ -13,6 +13,7 @@
_IRQ_MOVE_PCNTXT = IRQ_MOVE_PCNTXT,
_IRQ_NO_BALANCING = IRQ_NO_BALANCING,
_IRQ_NESTED_THREAD = IRQ_NESTED_THREAD,
+ _IRQ_PER_CPU_DEVID = IRQ_PER_CPU_DEVID,
_IRQF_MODIFY_MASK = IRQF_MODIFY_MASK,
};
@@ -24,6 +25,7 @@
#define IRQ_NOTHREAD GOT_YOU_MORON
#define IRQ_NOAUTOEN GOT_YOU_MORON
#define IRQ_NESTED_THREAD GOT_YOU_MORON
+#define IRQ_PER_CPU_DEVID GOT_YOU_MORON
#undef IRQF_MODIFY_MASK
#define IRQF_MODIFY_MASK GOT_YOU_MORON
@@ -39,6 +41,11 @@
return desc->status_use_accessors & _IRQ_PER_CPU;
}
+static inline bool irq_settings_is_per_cpu_devid(struct irq_desc *desc)
+{
+ return desc->status_use_accessors & _IRQ_PER_CPU_DEVID;
+}
+
static inline void irq_settings_set_per_cpu(struct irq_desc *desc)
{
desc->status_use_accessors |= _IRQ_PER_CPU;
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 6a9e368..75b5d6b 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -1079,7 +1079,7 @@
depends on FAULT_INJECTION_DEBUG_FS && STACKTRACE_SUPPORT
depends on !X86_64
select STACKTRACE
- select FRAME_POINTER if !PPC && !S390 && !MICROBLAZE
+ select FRAME_POINTER if !PPC && !S390 && !MICROBLAZE && !ARM_UNWIND
help
Provide stacktrace filter for fault-injection capabilities
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index 95718bbc..25b80df 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -459,6 +459,8 @@
if (hdev->notify)
hdev->notify(hdev, HCI_NOTIFY_CONN_DEL);
+ tasklet_schedule(&hdev->tx_task);
+
tasklet_enable(&hdev->tx_task);
skb_queue_purge(&conn->data_q);
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 13d9b71..45aa228 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -2708,6 +2708,11 @@
ie->data.ssp_mode = (ev->features[0] & 0x01);
conn->ssp_mode = (ev->features[0] & 0x01);
+ /*In case if remote device ssp supported/2.0 device
+ reduce the security level to MEDIUM if it is HIGH*/
+ if (!conn->ssp_mode &&
+ (conn->pending_sec_level == BT_SECURITY_HIGH))
+ conn->pending_sec_level = BT_SECURITY_MEDIUM;
}
if (conn->state != BT_CONFIG)
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index a3f3563..31a726a 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -569,6 +569,7 @@
} else
sk->sk_state_change(sk);
+ sk->sk_send_head = NULL;
skb_queue_purge(TX_QUEUE(sk));
if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) {
@@ -782,6 +783,7 @@
if (!conn)
return;
+ sk->sk_send_head = NULL;
skb_queue_purge(TX_QUEUE(sk));
if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) {
@@ -4515,6 +4517,7 @@
if (sk->sk_state != BT_DISCONN) {
sk->sk_shutdown = SHUTDOWN_MASK;
+ sk->sk_send_head = NULL;
skb_queue_purge(TX_QUEUE(sk));
if (l2cap_pi(sk)->mode == L2CAP_MODE_ERTM) {
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index ca7bf10..3ff633e 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -334,6 +334,7 @@
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
+ memset(&sinfo, 0, sizeof(sinfo));
sinfo.filled = 0;
sinfo.generation = local->sta_generation;
cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
diff --git a/net/socket.c b/net/socket.c
index 1ad42d3..129d9ae 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -467,7 +467,7 @@
struct inode *inode;
struct socket *sock;
- inode = new_inode(sock_mnt->mnt_sb);
+ inode = new_inode_pseudo(sock_mnt->mnt_sb);
if (!inode)
return NULL;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 584a7cd..971fbe7 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -2209,6 +2209,10 @@
}
nla_nest_end(msg, sinfoattr);
+ if (sinfo->assoc_req_ies)
+ NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
+ sinfo->assoc_req_ies);
+
return genlmsg_end(msg, hdr);
nla_put_failure:
@@ -2236,6 +2240,7 @@
}
while (1) {
+ memset(&sinfo, 0, sizeof(sinfo));
err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
mac_addr, &sinfo);
if (err == -ENOENT)
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 3545934..de7900ef 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -1984,6 +1984,7 @@
void exit_sel_fs(void)
{
kobject_put(selinuxfs_kobj);
+ kern_unmount(selinuxfs_mount);
unregister_filesystem(&sel_fs_type);
}
#endif
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index 5003ab5..75864702 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -122,6 +122,11 @@
*/
struct work_struct hphlocp_work; /* reporting left hph ocp off */
struct work_struct hphrocp_work; /* reporting right hph ocp off */
+
+ /* pm_cnt holds number of sleep lock holders + 1
+ * so if pm_cnt is 1 system is sleep-able. */
+ atomic_t pm_cnt;
+ wait_queue_head_t pm_wq;
};
#ifdef CONFIG_DEBUG_FS
@@ -1603,6 +1608,14 @@
return 0;
}
+static void tabla_snd_soc_jack_report(struct tabla_priv *tabla,
+ struct snd_soc_jack *jack, int status,
+ int mask)
+{
+ /* XXX: wake_lock_timeout()? */
+ snd_soc_jack_report(jack, status, mask);
+}
+
static void hphocp_off_report(struct tabla_priv *tabla,
u32 jack_status, int irq)
{
@@ -1613,8 +1626,9 @@
codec = tabla->codec;
tabla->hph_status &= ~jack_status;
if (tabla->headset_jack)
- snd_soc_jack_report(tabla->headset_jack,
- tabla->hph_status, TABLA_JACK_MASK);
+ tabla_snd_soc_jack_report(tabla, tabla->headset_jack,
+ tabla->hph_status,
+ TABLA_JACK_MASK);
snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10,
0x00);
snd_soc_update_bits(codec, TABLA_A_RX_HPH_OCP_CTL, 0x10,
@@ -1717,6 +1731,7 @@
default:
/* Should never reach here */
pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
+ return;
}
micbias_regs->cfilt_sel = cfilt;
@@ -3069,6 +3084,24 @@
return 0;
}
+static void tabla_lock_sleep(struct tabla_priv *tabla)
+{
+ int ret;
+ while (!(ret = wait_event_timeout(tabla->pm_wq,
+ atomic_inc_not_zero(&tabla->pm_cnt),
+ 2 * HZ))) {
+ pr_err("%s: didn't wake up for 2000ms (%d), pm_cnt %d\n",
+ __func__, ret, atomic_read(&tabla->pm_cnt));
+ WARN_ON_ONCE(1);
+ }
+}
+
+static void tabla_unlock_sleep(struct tabla_priv *tabla)
+{
+ atomic_dec(&tabla->pm_cnt);
+ wake_up(&tabla->pm_wq);
+}
+
static void btn0_lpress_fn(struct work_struct *work)
{
struct delayed_work *delayed_work;
@@ -3083,13 +3116,15 @@
if (tabla->button_jack) {
pr_debug("%s: Reporting long button press event\n",
__func__);
- snd_soc_jack_report(tabla->button_jack, SND_JACK_BTN_0,
- SND_JACK_BTN_0);
+ tabla_snd_soc_jack_report(tabla, tabla->button_jack,
+ SND_JACK_BTN_0,
+ SND_JACK_BTN_0);
}
} else {
pr_err("%s: Bad tabla private data\n", __func__);
}
+ tabla_unlock_sleep(tabla);
}
int tabla_hs_detect(struct snd_soc_codec *codec,
@@ -3139,6 +3174,7 @@
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
+ tabla_lock_sleep(priv);
bias_value = tabla_codec_read_dce_result(codec);
pr_debug("%s: button press interrupt, bias value(DCE Read)=%d\n",
@@ -3155,7 +3191,11 @@
msleep(100);
- schedule_delayed_work(&priv->btn0_dwork, msecs_to_jiffies(400));
+ if (schedule_delayed_work(&priv->btn0_dwork,
+ msecs_to_jiffies(400)) == 0) {
+ WARN(1, "Button pressed twice without release event\n");
+ tabla_unlock_sleep(priv);
+ }
return IRQ_HANDLED;
}
@@ -3168,6 +3208,7 @@
pr_debug("%s\n", __func__);
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE);
+ tabla_lock_sleep(priv);
mic_voltage = tabla_codec_read_dce_result(codec);
pr_debug("%s: Microphone Voltage on release(DCE Read) = %d\n",
@@ -3181,12 +3222,15 @@
pr_debug("%s: Reporting long button release event\n",
__func__);
if (priv->button_jack) {
- snd_soc_jack_report(priv->button_jack, 0,
- SND_JACK_BTN_0);
+ tabla_snd_soc_jack_report(priv,
+ priv->button_jack, 0,
+ SND_JACK_BTN_0);
}
} else {
-
+ /* if scheduled btn0_dwork is canceled from here,
+ * we have to unlock from here instead btn0_work */
+ tabla_unlock_sleep(priv);
mic_voltage =
tabla_codec_measure_micbias_voltage(codec, 0);
pr_debug("%s: Mic Voltage on release(new STA) = %d\n",
@@ -3201,10 +3245,12 @@
pr_debug("%s:reporting short button press and release\n",
__func__);
- snd_soc_jack_report(priv->button_jack,
+ tabla_snd_soc_jack_report(priv,
+ priv->button_jack,
SND_JACK_BTN_0, SND_JACK_BTN_0);
- snd_soc_jack_report(priv->button_jack,
- 0, SND_JACK_BTN_0);
+ tabla_snd_soc_jack_report(priv,
+ priv->button_jack,
+ 0, SND_JACK_BTN_0);
}
}
}
@@ -3213,7 +3259,7 @@
}
tabla_codec_start_hs_polling(codec);
-
+ tabla_unlock_sleep(priv);
return IRQ_HANDLED;
}
@@ -3268,8 +3314,9 @@
TABLA_IRQ_HPH_PA_OCPL_FAULT);
tabla->hph_status |= SND_JACK_OC_HPHL;
if (tabla->headset_jack) {
- snd_soc_jack_report(tabla->headset_jack,
- tabla->hph_status, TABLA_JACK_MASK);
+ tabla_snd_soc_jack_report(tabla, tabla->headset_jack,
+ tabla->hph_status,
+ TABLA_JACK_MASK);
}
} else {
pr_err("%s: Bad tabla private data\n", __func__);
@@ -3291,8 +3338,9 @@
TABLA_IRQ_HPH_PA_OCPR_FAULT);
tabla->hph_status |= SND_JACK_OC_HPHR;
if (tabla->headset_jack) {
- snd_soc_jack_report(tabla->headset_jack,
- tabla->hph_status, TABLA_JACK_MASK);
+ tabla_snd_soc_jack_report(tabla, tabla->headset_jack,
+ tabla->hph_status,
+ TABLA_JACK_MASK);
}
} else {
pr_err("%s: Bad tabla private data\n", __func__);
@@ -3311,9 +3359,10 @@
short threshold_fake_insert = 0xFD30;
u8 is_removal;
-
pr_debug("%s\n", __func__);
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_INSERTION);
+ tabla_lock_sleep(priv);
+
is_removal = snd_soc_read(codec, TABLA_A_CDC_MBHC_INT_CTL) & 0x02;
snd_soc_update_bits(codec, TABLA_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
@@ -3361,11 +3410,13 @@
priv->hph_status &= ~SND_JACK_HEADPHONE;
if (priv->headset_jack) {
pr_debug("%s: Reporting removal\n", __func__);
- snd_soc_jack_report(priv->headset_jack,
- priv->hph_status, TABLA_JACK_MASK);
+ tabla_snd_soc_jack_report(priv, priv->headset_jack,
+ priv->hph_status,
+ TABLA_JACK_MASK);
}
tabla_codec_shutdown_hs_removal_detect(codec);
tabla_codec_enable_hs_detect(codec, 1);
+ tabla_unlock_sleep(priv);
return IRQ_HANDLED;
}
@@ -3400,8 +3451,9 @@
if (priv->headset_jack) {
pr_debug("%s: Reporting insertion %d\n", __func__,
SND_JACK_HEADPHONE);
- snd_soc_jack_report(priv->headset_jack,
- priv->hph_status, TABLA_JACK_MASK);
+ tabla_snd_soc_jack_report(priv, priv->headset_jack,
+ priv->hph_status,
+ TABLA_JACK_MASK);
}
tabla_codec_shutdown_hs_polling(codec);
tabla_codec_enable_hs_detect(codec, 0);
@@ -3413,12 +3465,14 @@
if (priv->headset_jack) {
pr_debug("%s: Reporting insertion %d\n", __func__,
SND_JACK_HEADSET);
- snd_soc_jack_report(priv->headset_jack,
- priv->hph_status, TABLA_JACK_MASK);
+ tabla_snd_soc_jack_report(priv, priv->headset_jack,
+ priv->hph_status,
+ TABLA_JACK_MASK);
}
tabla_codec_start_hs_polling(codec);
}
+ tabla_unlock_sleep(priv);
return IRQ_HANDLED;
}
@@ -3431,6 +3485,7 @@
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_REMOVAL);
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_POTENTIAL);
tabla_disable_irq(codec->control_data, TABLA_IRQ_MBHC_RELEASE);
+ tabla_lock_sleep(priv);
usleep_range(priv->calibration->shutdown_plug_removal,
priv->calibration->shutdown_plug_removal);
@@ -3452,13 +3507,15 @@
priv->hph_status &= ~SND_JACK_HEADSET;
if (priv->headset_jack) {
pr_debug("%s: Reporting removal\n", __func__);
- snd_soc_jack_report(priv->headset_jack, 0,
- TABLA_JACK_MASK);
+ tabla_snd_soc_jack_report(priv, priv->headset_jack, 0,
+ TABLA_JACK_MASK);
}
tabla_codec_shutdown_hs_polling(codec);
tabla_codec_enable_hs_detect(codec, 1);
}
+
+ tabla_unlock_sleep(priv);
return IRQ_HANDLED;
}
@@ -3471,6 +3528,8 @@
int i, j;
u8 val;
+ tabla_lock_sleep(priv);
+
for (i = 0; i < TABLA_SLIM_NUM_PORT_REG; i++) {
slimbus_value = tabla_interface_reg_read(codec->control_data,
TABLA_SLIM_PGD_PORT_INT_STATUS0 + i);
@@ -3488,6 +3547,7 @@
TABLA_SLIM_PGD_PORT_INT_CLR0 + i, 0xFF);
}
+ tabla_unlock_sleep(priv);
return IRQ_HANDLED;
}
@@ -3765,6 +3825,8 @@
tabla->codec = codec;
tabla->pdata = dev_get_platdata(codec->dev->parent);
tabla->intf_type = tabla_get_intf_type();
+ atomic_set(&tabla->pm_cnt, 1);
+ init_waitqueue_head(&tabla->pm_wq);
tabla_update_reg_defaults(codec);
tabla_codec_init_reg(codec);
@@ -3869,6 +3931,7 @@
TABLA_IRQ_HPH_PA_OCPL_FAULT);
goto err_hphl_ocp_irq;
}
+ tabla_disable_irq(codec->control_data, TABLA_IRQ_HPH_PA_OCPL_FAULT);
ret = tabla_request_irq(codec->control_data,
TABLA_IRQ_HPH_PA_OCPR_FAULT, tabla_hphr_ocp_irq,
@@ -3878,6 +3941,7 @@
TABLA_IRQ_HPH_PA_OCPR_FAULT);
goto err_hphr_ocp_irq;
}
+ tabla_disable_irq(codec->control_data, TABLA_IRQ_HPH_PA_OCPR_FAULT);
#ifdef CONFIG_DEBUG_FS
debug_tabla_priv = tabla;
@@ -3966,6 +4030,64 @@
};
#endif
+#ifdef CONFIG_PM
+static int tabla_suspend(struct device *dev)
+{
+ int ret = 0, cnt;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tabla_priv *tabla = platform_get_drvdata(pdev);
+
+ cnt = atomic_read(&tabla->pm_cnt);
+ if (cnt > 0) {
+ if (wait_event_timeout(tabla->pm_wq,
+ (atomic_cmpxchg(&tabla->pm_cnt, 1, 0)
+ == 1), 5 * HZ)) {
+ dev_dbg(dev, "system suspend pm_cnt %d\n",
+ atomic_read(&tabla->pm_cnt));
+ } else {
+ dev_err(dev, "%s timed out pm_cnt = %d\n",
+ __func__, atomic_read(&tabla->pm_cnt));
+ WARN_ON_ONCE(1);
+ ret = -EBUSY;
+ }
+ } else if (cnt == 0)
+ dev_warn(dev, "system is already in suspend, pm_cnt %d\n",
+ atomic_read(&tabla->pm_cnt));
+ else {
+ WARN(1, "unexpected pm_cnt %d\n", cnt);
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+static int tabla_resume(struct device *dev)
+{
+ int ret = 0, cnt;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tabla_priv *tabla = platform_get_drvdata(pdev);
+
+ cnt = atomic_cmpxchg(&tabla->pm_cnt, 0, 1);
+ if (cnt == 0) {
+ dev_dbg(dev, "system resume, pm_cnt %d\n",
+ atomic_read(&tabla->pm_cnt));
+ wake_up_all(&tabla->pm_wq);
+ } else if (cnt > 0)
+ dev_warn(dev, "system is already awake, pm_cnt %d\n", cnt);
+ else {
+ WARN(1, "unexpected pm_cnt %d\n", cnt);
+ ret = -EFAULT;
+ }
+
+ return ret;
+}
+
+static const struct dev_pm_ops tabla_pm_ops = {
+ .suspend = tabla_suspend,
+ .resume = tabla_resume,
+};
+#endif
+
static int __devinit tabla_probe(struct platform_device *pdev)
{
int ret = 0;
@@ -3997,6 +4119,9 @@
.driver = {
.name = "tabla_codec",
.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &tabla_pm_ops,
+#endif
},
};
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
index 3b257a6..c583ce2 100644
--- a/sound/soc/msm/Makefile
+++ b/sound/soc/msm/Makefile
@@ -61,7 +61,7 @@
snd-soc-qdsp6-objs += msm-pcm-lpa.o msm-pcm-afe.o
obj-$(CONFIG_SND_SOC_QDSP6) += snd-soc-qdsp6.o
-snd-soc-msm8960-objs := msm8960.o
+snd-soc-msm8960-objs := msm8960.o apq8064.o
obj-$(CONFIG_SND_SOC_MSM8960) += snd-soc-msm8960.o
# Generic MSM drivers
diff --git a/sound/soc/msm/apq8064.c b/sound/soc/msm/apq8064.c
new file mode 100644
index 0000000..870bd20
--- /dev/null
+++ b/sound/soc/msm/apq8064.c
@@ -0,0 +1,1161 @@
+/* Copyright (c) 2011, 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/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-dsp.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <asm/mach-types.h>
+#include <mach/socinfo.h>
+#include "msm-pcm-routing.h"
+#include "../codecs/wcd9310.h"
+
+/* 8064 machine driver */
+
+#define PM8921_GPIO_BASE NR_GPIO_IRQS
+#define PM8921_GPIO_PM_TO_SYS(pm_gpio) (pm_gpio - 1 + PM8921_GPIO_BASE)
+
+#define MSM8064_SPK_ON 1
+#define MSM8064_SPK_OFF 0
+
+#define MSM_SLIM_0_RX_MAX_CHANNELS 2
+#define MSM_SLIM_0_TX_MAX_CHANNELS 4
+
+#define BTSCO_RATE_8KHZ 8000
+#define BTSCO_RATE_16KHZ 16000
+
+#define BOTTOM_SPK_AMP_POS 0x1
+#define BOTTOM_SPK_AMP_NEG 0x2
+#define TOP_SPK_AMP_POS 0x4
+#define TOP_SPK_AMP_NEG 0x8
+
+#define GPIO_AUX_PCM_DOUT 43
+#define GPIO_AUX_PCM_DIN 44
+#define GPIO_AUX_PCM_SYNC 45
+#define GPIO_AUX_PCM_CLK 46
+
+static u32 top_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(18);
+static u32 bottom_spk_pamp_gpio = PM8921_GPIO_PM_TO_SYS(19);
+static int msm_spk_control;
+static int msm_ext_bottom_spk_pamp;
+static int msm_ext_top_spk_pamp;
+static int msm_slim_0_rx_ch = 1;
+static int msm_slim_0_tx_ch = 1;
+
+static int msm_btsco_rate = BTSCO_RATE_8KHZ;
+static int msm_btsco_ch = 1;
+
+struct tabla_mbhc_calibration tabla_calib = {
+ .bias = TABLA_MICBIAS2,
+ .tldoh = 100,
+ .bg_fast_settle = 100,
+ .mic_current = TABLA_PID_MIC_5_UA,
+ .mic_pid = 100,
+ .hph_current = TABLA_PID_MIC_5_UA,
+ .setup_plug_removal_delay = 1000000,
+ .shutdown_plug_removal = 100000,
+};
+
+static struct clk *codec_clk;
+static int clk_users;
+
+static int msm_headset_gpios_configured;
+
+static struct snd_soc_jack hs_jack;
+static struct snd_soc_jack button_jack;
+
+static void msm_enable_ext_spk_amp_gpio(u32 spk_amp_gpio)
+{
+ int ret = 0;
+
+ struct pm_gpio param = {
+ .direction = PM_GPIO_DIR_OUT,
+ .output_buffer = PM_GPIO_OUT_BUF_CMOS,
+ .output_value = 1,
+ .pull = PM_GPIO_PULL_NO,
+ .vin_sel = PM_GPIO_VIN_S4,
+ .out_strength = PM_GPIO_STRENGTH_MED,
+ .
+ function = PM_GPIO_FUNC_NORMAL,
+ };
+
+ if (spk_amp_gpio == bottom_spk_pamp_gpio) {
+
+ ret = gpio_request(bottom_spk_pamp_gpio, "BOTTOM_SPK_AMP");
+ if (ret) {
+ pr_err("%s: Error requesting BOTTOM SPK AMP GPIO %u\n",
+ __func__, bottom_spk_pamp_gpio);
+ return;
+ }
+ ret = pm8xxx_gpio_config(bottom_spk_pamp_gpio, ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure Bottom Spk Ampl"
+ " gpio %u\n", __func__, bottom_spk_pamp_gpio);
+ else {
+ pr_debug("%s: enable Bottom spkr amp gpio\n", __func__);
+ gpio_direction_output(bottom_spk_pamp_gpio, 1);
+ }
+
+ } else if (spk_amp_gpio == top_spk_pamp_gpio) {
+
+ ret = gpio_request(top_spk_pamp_gpio, "TOP_SPK_AMP");
+ if (ret) {
+ pr_err("%s: Error requesting GPIO %d\n", __func__,
+ top_spk_pamp_gpio);
+ return;
+ }
+ ret = pm8xxx_gpio_config(top_spk_pamp_gpio, ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure Top Spk Ampl"
+ " gpio %u\n", __func__, top_spk_pamp_gpio);
+ else {
+ pr_debug("%s: enable Top spkr amp gpio\n", __func__);
+ gpio_direction_output(top_spk_pamp_gpio, 1);
+ }
+ } else {
+ pr_err("%s: ERROR : Invalid External Speaker Ampl GPIO."
+ " gpio = %u\n", __func__, spk_amp_gpio);
+ return;
+ }
+}
+
+static void msm_ext_spk_power_amp_on(u32 spk)
+{
+ if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
+
+ if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
+ (msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
+
+ pr_debug("%s() External Bottom Speaker Ampl already "
+ "turned on. spk = 0x%08x\n", __func__, spk);
+ return;
+ }
+
+ msm_ext_bottom_spk_pamp |= spk;
+
+ if ((msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_POS) &&
+ (msm_ext_bottom_spk_pamp & BOTTOM_SPK_AMP_NEG)) {
+
+ msm_enable_ext_spk_amp_gpio(bottom_spk_pamp_gpio);
+ pr_debug("%s: slepping 4 ms after turning on external "
+ " Bottom Speaker Ampl\n", __func__);
+ usleep_range(4000, 4000);
+ }
+
+ } else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
+
+ if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
+ (msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
+
+ pr_debug("%s() External Top Speaker Ampl already"
+ "turned on. spk = 0x%08x\n", __func__, spk);
+ return;
+ }
+
+ msm_ext_top_spk_pamp |= spk;
+
+ if ((msm_ext_top_spk_pamp & TOP_SPK_AMP_POS) &&
+ (msm_ext_top_spk_pamp & TOP_SPK_AMP_NEG)) {
+
+ msm_enable_ext_spk_amp_gpio(top_spk_pamp_gpio);
+ pr_debug("%s: sleeping 4 ms after turning on "
+ " external Top Speaker Ampl\n", __func__);
+ usleep_range(4000, 4000);
+ }
+ } else {
+
+ pr_err("%s: ERROR : Invalid External Speaker Ampl. spk = 0x%08x\n",
+ __func__, spk);
+ return;
+ }
+}
+
+static void msm_ext_spk_power_amp_off(u32 spk)
+{
+ if (spk & (BOTTOM_SPK_AMP_POS | BOTTOM_SPK_AMP_NEG)) {
+
+ if (!msm_ext_bottom_spk_pamp)
+ return;
+
+ gpio_direction_output(bottom_spk_pamp_gpio, 0);
+ gpio_free(bottom_spk_pamp_gpio);
+ msm_ext_bottom_spk_pamp = 0;
+
+ pr_debug("%s: sleeping 4 ms after turning off external Bottom"
+ " Speaker Ampl\n", __func__);
+
+ usleep_range(4000, 4000);
+
+ } else if (spk & (TOP_SPK_AMP_POS | TOP_SPK_AMP_NEG)) {
+
+ if (!msm_ext_top_spk_pamp)
+ return;
+
+ gpio_direction_output(top_spk_pamp_gpio, 0);
+ gpio_free(top_spk_pamp_gpio);
+ msm_ext_top_spk_pamp = 0;
+
+ pr_debug("%s: sleeping 4 ms after turning off external Top"
+ " Spkaker Ampl\n", __func__);
+
+ usleep_range(4000, 4000);
+ } else {
+
+ pr_err("%s: ERROR : Invalid Ext Spk Ampl. spk = 0x%08x\n",
+ __func__, spk);
+ return;
+ }
+}
+
+static void msm_ext_control(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
+ if (msm_spk_control == MSM8064_SPK_ON) {
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
+ } else {
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Pos");
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk Bottom Neg");
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Pos");
+ snd_soc_dapm_disable_pin(dapm, "Ext Spk Top Neg");
+ }
+
+ snd_soc_dapm_sync(dapm);
+}
+
+static int msm_get_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_spk_control = %d", __func__, msm_spk_control);
+ ucontrol->value.integer.value[0] = msm_spk_control;
+ return 0;
+}
+static int msm_set_spk(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+ pr_debug("%s()\n", __func__);
+ if (msm_spk_control == ucontrol->value.integer.value[0])
+ return 0;
+
+ msm_spk_control = ucontrol->value.integer.value[0];
+ msm_ext_control(codec);
+ return 1;
+}
+static int msm_spkramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ pr_debug("%s() %x\n", __func__, SND_SOC_DAPM_EVENT_ON(event));
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
+ msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_POS);
+ else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
+ msm_ext_spk_power_amp_on(BOTTOM_SPK_AMP_NEG);
+ else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
+ msm_ext_spk_power_amp_on(TOP_SPK_AMP_POS);
+ else if (!strncmp(w->name, "Ext Spk Top Neg", 15))
+ msm_ext_spk_power_amp_on(TOP_SPK_AMP_NEG);
+ else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+
+ } else {
+ if (!strncmp(w->name, "Ext Spk Bottom Pos", 18))
+ msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_POS);
+ else if (!strncmp(w->name, "Ext Spk Bottom Neg", 18))
+ msm_ext_spk_power_amp_off(BOTTOM_SPK_AMP_NEG);
+ else if (!strncmp(w->name, "Ext Spk Top Pos", 15))
+ msm_ext_spk_power_amp_off(TOP_SPK_AMP_POS);
+ else if (!strncmp(w->name, "Ext Spk Top Neg", 15))
+ msm_ext_spk_power_amp_off(TOP_SPK_AMP_NEG);
+ else {
+ pr_err("%s() Invalid Speaker Widget = %s\n",
+ __func__, w->name);
+ return -EINVAL;
+ }
+ }
+ return 0;
+}
+
+static int msm_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+
+ clk_users++;
+ pr_debug("%s: clk_users = %d\n", __func__, clk_users);
+
+ if (clk_users != 1)
+ return 0;
+
+ codec_clk = clk_get(NULL, "i2s_spkr_osr_clk");
+ if (codec_clk) {
+ clk_set_rate(codec_clk, 12288000);
+ clk_enable(codec_clk);
+ tabla_mclk_enable(w->codec, 1);
+
+ } else {
+ pr_err("%s: Error setting Tabla MCLK\n", __func__);
+ clk_users--;
+ return -EINVAL;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+
+ pr_debug("%s: clk_users = %d\n", __func__, clk_users);
+
+ if (clk_users == 0)
+ return 0;
+
+ clk_users--;
+
+ if (!clk_users) {
+ pr_debug("%s: disabling MCLK. clk_users = %d\n",
+ __func__, clk_users);
+
+ clk_disable(codec_clk);
+ clk_put(codec_clk);
+ tabla_mclk_enable(w->codec, 0);
+ }
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget msm_dapm_widgets[] = {
+
+ SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
+ msm_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_SPK("Ext Spk Bottom Pos", msm_spkramp_event),
+ SND_SOC_DAPM_SPK("Ext Spk Bottom Neg", msm_spkramp_event),
+
+ SND_SOC_DAPM_SPK("Ext Spk Top Pos", msm_spkramp_event),
+ SND_SOC_DAPM_SPK("Ext Spk Top Neg", msm_spkramp_event),
+
+ SND_SOC_DAPM_MIC("Handset Mic", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+
+ SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic3", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic4", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+ SND_SOC_DAPM_MIC("Digital Mic6", NULL),
+
+};
+
+static const struct snd_soc_dapm_route common_audio_map[] = {
+
+ {"RX_BIAS", NULL, "MCLK"},
+ {"LDO_H", NULL, "MCLK"},
+
+ /* Speaker path */
+ {"Ext Spk Bottom Pos", NULL, "LINEOUT1"},
+ {"Ext Spk Bottom Neg", NULL, "LINEOUT3"},
+
+ {"Ext Spk Top Pos", NULL, "LINEOUT2"},
+ {"Ext Spk Top Neg", NULL, "LINEOUT4"},
+
+ /* Microphone path */
+ {"AMIC1", NULL, "MIC BIAS1 Internal1"},
+ {"MIC BIAS1 Internal1", NULL, "Handset Mic"},
+
+ {"AMIC2", NULL, "MIC BIAS2 External"},
+ {"MIC BIAS2 External", NULL, "Headset Mic"},
+
+ /**
+ * AMIC3 and AMIC4 inputs are connected to ANC microphones
+ * These mics are biased differently on CDP and FLUID
+ * routing entries below are based on bias arrangement
+ * on FLUID.
+ */
+ {"AMIC3", NULL, "MIC BIAS3 Internal1"},
+ {"MIC BIAS3 Internal1", NULL, "ANCRight Headset Mic"},
+
+ {"AMIC4", NULL, "MIC BIAS1 Internal2"},
+ {"MIC BIAS1 Internal2", NULL, "ANCLeft Headset Mic"},
+
+ {"HEADPHONE", NULL, "LDO_H"},
+
+ /**
+ * The digital Mic routes are setup considering
+ * fluid as default device.
+ */
+
+ /**
+ * Digital Mic1. Front Bottom left Digital Mic on Fluid and MTP.
+ * Digital Mic GM5 on CDP mainboard.
+ * Conncted to DMIC2 Input on Tabla codec.
+ */
+ {"DMIC2", NULL, "MIC BIAS1 External"},
+ {"MIC BIAS1 External", NULL, "Digital Mic1"},
+
+ /**
+ * Digital Mic2. Front Bottom right Digital Mic on Fluid and MTP.
+ * Digital Mic GM6 on CDP mainboard.
+ * Conncted to DMIC1 Input on Tabla codec.
+ */
+ {"DMIC1", NULL, "MIC BIAS1 External"},
+ {"MIC BIAS1 External", NULL, "Digital Mic2"},
+
+ /**
+ * Digital Mic3. Back Bottom Digital Mic on Fluid.
+ * Digital Mic GM1 on CDP mainboard.
+ * Conncted to DMIC4 Input on Tabla codec.
+ */
+ {"DMIC4", NULL, "MIC BIAS3 External"},
+ {"MIC BIAS3 External", NULL, "Digital Mic3"},
+
+ /**
+ * Digital Mic4. Back top Digital Mic on Fluid.
+ * Digital Mic GM2 on CDP mainboard.
+ * Conncted to DMIC3 Input on Tabla codec.
+ */
+ {"DMIC3", NULL, "MIC BIAS3 External"},
+ {"MIC BIAS3 External", NULL, "Digital Mic4"},
+
+ /**
+ * Digital Mic5. Front top Digital Mic on Fluid.
+ * Digital Mic GM3 on CDP mainboard.
+ * Conncted to DMIC5 Input on Tabla codec.
+ */
+ {"DMIC5", NULL, "MIC BIAS4 External"},
+ {"MIC BIAS4 External", NULL, "Digital Mic5"},
+
+ /* Tabla digital Mic6 - back bottom digital Mic on Liquid and
+ * bottom mic on CDP. FLUID/MTP do not have dmic6 installed.
+ */
+ {"DMIC6", NULL, "MIC BIAS4 External"},
+ {"MIC BIAS4 External", NULL, "Digital Mic6"},
+};
+
+static const char *spk_function[] = {"Off", "On"};
+static const char *slim0_rx_ch_text[] = {"One", "Two"};
+static const char *slim0_tx_ch_text[] = {"One", "Two", "Three", "Four"};
+
+static const struct soc_enum msm_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, spk_function),
+ SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
+ SOC_ENUM_SINGLE_EXT(4, slim0_tx_ch_text),
+};
+
+static const char *btsco_rate_text[] = {"8000", "16000"};
+static const struct soc_enum msm_btsco_enum[] = {
+ SOC_ENUM_SINGLE_EXT(2, btsco_rate_text),
+};
+
+static int msm_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
+ msm_slim_0_rx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_0_rx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
+ msm_slim_0_rx_ch);
+ return 1;
+}
+
+static int msm_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__,
+ msm_slim_0_tx_ch);
+ ucontrol->value.integer.value[0] = msm_slim_0_tx_ch - 1;
+ return 0;
+}
+
+static int msm_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+ pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__,
+ msm_slim_0_tx_ch);
+ return 1;
+}
+
+static int msm_btsco_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ pr_debug("%s: msm_btsco_rate = %d", __func__,
+ msm_btsco_rate);
+ ucontrol->value.integer.value[0] = msm_btsco_rate;
+ return 0;
+}
+
+static int msm_btsco_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_btsco_rate = BTSCO_RATE_8KHZ;
+ break;
+ case 1:
+ msm_btsco_rate = BTSCO_RATE_16KHZ;
+ break;
+ default:
+ msm_btsco_rate = BTSCO_RATE_8KHZ;
+ break;
+ }
+ pr_debug("%s: msm_btsco_rate = %d\n", __func__,
+ msm_btsco_rate);
+ return 0;
+}
+
+static const struct snd_kcontrol_new tabla_msm_controls[] = {
+ SOC_ENUM_EXT("Speaker Function", msm_enum[0], msm_get_spk,
+ msm_set_spk),
+ SOC_ENUM_EXT("SLIM_0_RX Channels", msm_enum[1],
+ msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
+ SOC_ENUM_EXT("SLIM_0_TX Channels", msm_enum[2],
+ msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put),
+};
+
+static const struct snd_kcontrol_new int_btsco_rate_mixer_controls[] = {
+ SOC_ENUM_EXT("Internal BTSCO SampleRate", msm_btsco_enum[0],
+ msm_btsco_rate_get, msm_btsco_rate_put),
+};
+
+static int msm_btsco_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int err = 0;
+ struct snd_soc_platform *platform = rtd->platform;
+
+ err = snd_soc_add_platform_controls(platform,
+ int_btsco_rate_mixer_controls,
+ ARRAY_SIZE(int_btsco_rate_mixer_controls));
+ if (err < 0)
+ return err;
+ return 0;
+}
+
+static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+ int err;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+
+ pr_debug("%s()\n", __func__);
+
+ /*if (machine_is_msm_liquid()) {
+ top_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(19));
+ bottom_spk_pamp_gpio = (PM8921_GPIO_PM_TO_SYS(18));
+ }*/
+
+ rtd->pmdown_time = 0;
+
+ err = snd_soc_add_controls(codec, tabla_msm_controls,
+ ARRAY_SIZE(tabla_msm_controls));
+ if (err < 0)
+ return err;
+
+ snd_soc_dapm_new_controls(dapm, msm_dapm_widgets,
+ ARRAY_SIZE(msm_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, common_audio_map,
+ ARRAY_SIZE(common_audio_map));
+
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Pos");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Bottom Neg");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Pos");
+ snd_soc_dapm_enable_pin(dapm, "Ext Spk Top Neg");
+
+ snd_soc_dapm_sync(dapm);
+
+ err = snd_soc_jack_new(codec, "Headset Jack",
+ (SND_JACK_HEADSET | SND_JACK_OC_HPHL | SND_JACK_OC_HPHR),
+ &hs_jack);
+ if (err) {
+ pr_err("failed to create new jack\n");
+ return err;
+ }
+
+ err = snd_soc_jack_new(codec, "Button Jack",
+ SND_JACK_BTN_0, &button_jack);
+ if (err) {
+ pr_err("failed to create new jack\n");
+ return err;
+ }
+
+ tabla_hs_detect(codec, &hs_jack, &button_jack, &tabla_calib);
+
+ return 0;
+}
+
+static struct snd_soc_dsp_link lpa_fe_media = {
+ .playback = true,
+ .trigger = {
+ SND_SOC_DSP_TRIGGER_POST,
+ SND_SOC_DSP_TRIGGER_POST
+ },
+};
+
+static struct snd_soc_dsp_link fe_media = {
+ .playback = true,
+ .capture = true,
+ .trigger = {
+ SND_SOC_DSP_TRIGGER_POST,
+ SND_SOC_DSP_TRIGGER_POST
+ },
+};
+
+static struct snd_soc_dsp_link slimbus0_hl_media = {
+ .playback = true,
+ .capture = true,
+ .trigger = {
+ SND_SOC_DSP_TRIGGER_POST,
+ SND_SOC_DSP_TRIGGER_POST
+ },
+};
+
+static struct snd_soc_dsp_link int_fm_hl_media = {
+ .playback = true,
+ .capture = true,
+ .trigger = {
+ SND_SOC_DSP_TRIGGER_POST,
+ SND_SOC_DSP_TRIGGER_POST
+ },
+};
+
+static int msm_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = msm_slim_0_rx_ch;
+
+ return 0;
+}
+
+static int msm_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = msm_slim_0_tx_ch;
+
+ return 0;
+}
+
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ pr_debug("%s()\n", __func__);
+ rate->min = rate->max = 48000;
+
+ return 0;
+}
+
+static int msm_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ rate->min = rate->max = msm_btsco_rate;
+ channels->min = channels->max = msm_btsco_ch;
+
+ return 0;
+}
+static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* PCM only supports mono output with 8khz sample rate */
+ rate->min = rate->max = 8000;
+ channels->min = channels->max = 1;
+
+ return 0;
+}
+static int msm_aux_pcm_get_gpios(void)
+{
+ int ret = 0;
+
+ pr_debug("%s\n", __func__);
+
+ ret = gpio_request(GPIO_AUX_PCM_DOUT, "AUX PCM DOUT");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM DOUT",
+ __func__, GPIO_AUX_PCM_DOUT);
+ goto fail_dout;
+ }
+
+ ret = gpio_request(GPIO_AUX_PCM_DIN, "AUX PCM DIN");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM DIN",
+ __func__, GPIO_AUX_PCM_DIN);
+ goto fail_din;
+ }
+
+ ret = gpio_request(GPIO_AUX_PCM_SYNC, "AUX PCM SYNC");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM SYNC",
+ __func__, GPIO_AUX_PCM_SYNC);
+ goto fail_sync;
+ }
+ ret = gpio_request(GPIO_AUX_PCM_CLK, "AUX PCM CLK");
+ if (ret < 0) {
+ pr_err("%s: Failed to request gpio(%d): AUX PCM CLK",
+ __func__, GPIO_AUX_PCM_CLK);
+ goto fail_clk;
+ }
+
+ return 0;
+
+fail_clk:
+ gpio_free(GPIO_AUX_PCM_SYNC);
+fail_sync:
+ gpio_free(GPIO_AUX_PCM_DIN);
+fail_din:
+ gpio_free(GPIO_AUX_PCM_DOUT);
+fail_dout:
+
+ return ret;
+}
+
+static int msm_aux_pcm_free_gpios(void)
+{
+ gpio_free(GPIO_AUX_PCM_DIN);
+ gpio_free(GPIO_AUX_PCM_DOUT);
+ gpio_free(GPIO_AUX_PCM_SYNC);
+ gpio_free(GPIO_AUX_PCM_CLK);
+
+ return 0;
+}
+static int msm_startup(struct snd_pcm_substream *substream)
+{
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+ return 0;
+}
+
+static int msm_auxpcm_startup(struct snd_pcm_substream *substream)
+{
+ int ret = 0;
+
+ pr_debug("%s(): substream = %s\n", __func__, substream->name);
+ ret = msm_aux_pcm_get_gpios();
+ if (ret < 0) {
+ pr_err("%s: Aux PCM GPIO request failed\n", __func__);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void msm_auxpcm_shutdown(struct snd_pcm_substream *substream)
+{
+
+ pr_debug("%s(): substream = %s\n", __func__, substream->name);
+ msm_aux_pcm_free_gpios();
+}
+
+static void msm_shutdown(struct snd_pcm_substream *substream)
+{
+ pr_debug("%s(): substream = %s stream = %d\n", __func__,
+ substream->name, substream->stream);
+}
+
+static struct snd_soc_ops msm_be_ops = {
+ .startup = msm_startup,
+ .shutdown = msm_shutdown,
+};
+
+static struct snd_soc_ops msm_auxpcm_be_ops = {
+ .startup = msm_auxpcm_startup,
+ .shutdown = msm_auxpcm_shutdown,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm_dai[] = {
+ /* FrontEnd DAI Links */
+ {
+ .name = "MSM8960 Media1",
+ .stream_name = "MultiMedia1",
+ .cpu_dai_name = "MultiMedia1",
+ .platform_name = "msm-pcm-dsp",
+ .dynamic = 1,
+ .dsp_link = &fe_media,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+ },
+ {
+ .name = "MSM8960 Media2",
+ .stream_name = "MultiMedia2",
+ .cpu_dai_name = "MultiMedia2",
+ .platform_name = "msm-pcm-dsp",
+ .dynamic = 1,
+ .dsp_link = &fe_media,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+ },
+ {
+ .name = "Circuit-Switch Voice",
+ .stream_name = "CS-Voice",
+ .cpu_dai_name = "CS-VOICE",
+ .platform_name = "msm-pcm-voice",
+ .dynamic = 1,
+ .dsp_link = &fe_media,
+ .be_id = MSM_FRONTEND_DAI_CS_VOICE,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "MSM VoIP",
+ .stream_name = "VoIP",
+ .cpu_dai_name = "VoIP",
+ .platform_name = "msm-voip-dsp",
+ .dynamic = 1,
+ .dsp_link = &fe_media,
+ .be_id = MSM_FRONTEND_DAI_VOIP,
+ },
+ {
+ .name = "MSM8960 LPA",
+ .stream_name = "LPA",
+ .cpu_dai_name = "MultiMedia3",
+ .platform_name = "msm-pcm-lpa",
+ .dynamic = 1,
+ .dsp_link = &lpa_fe_media,
+ .be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+ },
+ /* Hostless PMC purpose */
+ {
+ .name = "SLIMBUS_0 Hostless",
+ .stream_name = "SLIMBUS_0 Hostless",
+ .cpu_dai_name = "SLIMBUS0_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dsp_link = &slimbus0_hl_media,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* .be_id = do not care */
+ },
+ {
+ .name = "INT_FM Hostless",
+ .stream_name = "INT_FM Hostless",
+ .cpu_dai_name = "INT_FM_HOSTLESS",
+ .platform_name = "msm-pcm-hostless",
+ .dynamic = 1,
+ .dsp_link = &int_fm_hl_media,
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ /* .be_id = do not care */
+ },
+ {
+ .name = "MSM AFE-PCM RX",
+ .stream_name = "AFE-PROXY RX",
+ .cpu_dai_name = "msm-dai-q6.241",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ },
+ {
+ .name = "MSM AFE-PCM TX",
+ .stream_name = "AFE-PROXY TX",
+ .cpu_dai_name = "msm-dai-q6.240",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .platform_name = "msm-pcm-afe",
+ .ignore_suspend = 1,
+ },
+ /* Backend DAI Links */
+ {
+ .name = LPASS_BE_SLIMBUS_0_RX,
+ .stream_name = "Slimbus Playback",
+ .cpu_dai_name = "msm-dai-q6.16384",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tabla_codec",
+ .codec_dai_name = "tabla_rx1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+ .init = &msm_audrx_init,
+ .be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ },
+ {
+ .name = LPASS_BE_SLIMBUS_0_TX,
+ .stream_name = "Slimbus Capture",
+ .cpu_dai_name = "msm-dai-q6.16385",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tabla_codec",
+ .codec_dai_name = "tabla_tx1",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+ .be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+ .ops = &msm_be_ops,
+ },
+ /* Backend BT/FM DAI Links */
+ {
+ .name = LPASS_BE_INT_BT_SCO_RX,
+ .stream_name = "Internal BT-SCO Playback",
+ .cpu_dai_name = "msm-dai-q6.12288",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .init = &msm_btsco_init,
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_BT_SCO_RX,
+ .be_hw_params_fixup = msm_btsco_be_hw_params_fixup,
+ },
+ {
+ .name = LPASS_BE_INT_BT_SCO_TX,
+ .stream_name = "Internal BT-SCO Capture",
+ .cpu_dai_name = "msm-dai-q6.12289",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_BT_SCO_TX,
+ .be_hw_params_fixup = msm_btsco_be_hw_params_fixup,
+ },
+ {
+ .name = LPASS_BE_INT_FM_RX,
+ .stream_name = "Internal FM Playback",
+ .cpu_dai_name = "msm-dai-q6.12292",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_FM_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ },
+ {
+ .name = LPASS_BE_INT_FM_TX,
+ .stream_name = "Internal FM Capture",
+ .cpu_dai_name = "msm-dai-q6.12293",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_INT_FM_TX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ },
+ /* HDMI BACK END DAI Link */
+ {
+ .name = LPASS_BE_HDMI,
+ .stream_name = "HDMI Playback",
+ .cpu_dai_name = "msm-dai-q6.8",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .no_codec = 1,
+ .be_id = MSM_BACKEND_DAI_HDMI_RX,
+ .be_hw_params_fixup = msm_be_hw_params_fixup,
+ },
+ /* Backend AFE DAI Links */
+ {
+ .name = LPASS_BE_AFE_PCM_RX,
+ .stream_name = "AFE Playback",
+ .cpu_dai_name = "msm-dai-q6.224",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_codec = 1,
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+ },
+ {
+ .name = LPASS_BE_AFE_PCM_TX,
+ .stream_name = "AFE Capture",
+ .cpu_dai_name = "msm-dai-q6.225",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_codec = 1,
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
+ },
+ /* AUX PCM Backend DAI Links */
+ {
+ .name = LPASS_BE_AUXPCM_RX,
+ .stream_name = "AUX PCM Playback",
+ .cpu_dai_name = "msm-dai-q6.2",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ .ops = &msm_auxpcm_be_ops,
+ },
+ {
+ .name = LPASS_BE_AUXPCM_TX,
+ .stream_name = "AUX PCM Capture",
+ .cpu_dai_name = "msm-dai-q6.3",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+ .be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+ },
+};
+
+struct snd_soc_card snd_soc_card_msm = {
+ .name = "msm-snd-card",
+ .dai_link = msm_dai,
+ .num_links = ARRAY_SIZE(msm_dai),
+};
+
+static struct platform_device *msm_snd_device;
+
+static int msm_configure_headset_mic_gpios(void)
+{
+ int ret;
+ struct pm_gpio param = {
+ .direction = PM_GPIO_DIR_OUT,
+ .output_buffer = PM_GPIO_OUT_BUF_CMOS,
+ .output_value = 1,
+ .pull = PM_GPIO_PULL_NO,
+ .vin_sel = PM_GPIO_VIN_S4,
+ .out_strength = PM_GPIO_STRENGTH_MED,
+ .function = PM_GPIO_FUNC_NORMAL,
+ };
+
+ ret = gpio_request(PM8921_GPIO_PM_TO_SYS(23), "AV_SWITCH");
+ if (ret) {
+ pr_err("%s: Failed to request gpio %d\n", __func__,
+ PM8921_GPIO_PM_TO_SYS(23));
+ return ret;
+ }
+
+ ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(23), ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure gpio %d\n", __func__,
+ PM8921_GPIO_PM_TO_SYS(23));
+ else
+ gpio_direction_output(PM8921_GPIO_PM_TO_SYS(23), 0);
+
+ ret = gpio_request(PM8921_GPIO_PM_TO_SYS(35), "US_EURO_SWITCH");
+ if (ret) {
+ pr_err("%s: Failed to request gpio %d\n", __func__,
+ PM8921_GPIO_PM_TO_SYS(35));
+ gpio_free(PM8921_GPIO_PM_TO_SYS(23));
+ return ret;
+ }
+ ret = pm8xxx_gpio_config(PM8921_GPIO_PM_TO_SYS(35), ¶m);
+ if (ret)
+ pr_err("%s: Failed to configure gpio %d\n", __func__,
+ PM8921_GPIO_PM_TO_SYS(35));
+ else
+ gpio_direction_output(PM8921_GPIO_PM_TO_SYS(35), 0);
+
+ return 0;
+}
+static void msm_free_headset_mic_gpios(void)
+{
+ if (msm_headset_gpios_configured) {
+ gpio_free(PM8921_GPIO_PM_TO_SYS(23));
+ gpio_free(PM8921_GPIO_PM_TO_SYS(35));
+ }
+}
+
+static int __init msm_audio_init(void)
+{
+ int ret;
+
+ if (!cpu_is_apq8064()) {
+ pr_err("%s: Not the right machine type\n", __func__);
+ return -ENODEV;
+ }
+
+ msm_snd_device = platform_device_alloc("soc-audio", 0);
+ if (!msm_snd_device) {
+ pr_err("Platform device allocation failed\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(msm_snd_device, &snd_soc_card_msm);
+ ret = platform_device_add(msm_snd_device);
+ if (ret) {
+ platform_device_put(msm_snd_device);
+ return ret;
+ }
+
+ if (msm_configure_headset_mic_gpios()) {
+ pr_err("%s Fail to configure headset mic gpios\n", __func__);
+ msm_headset_gpios_configured = 0;
+ } else
+ msm_headset_gpios_configured = 1;
+
+ return ret;
+
+}
+module_init(msm_audio_init);
+
+static void __exit msm_audio_exit(void)
+{
+ if (!cpu_is_apq8064()) {
+ pr_err("%s: Not the right machine type\n", __func__);
+ return ;
+ }
+ msm_free_headset_mic_gpios();
+ platform_device_unregister(msm_snd_device);
+}
+module_exit(msm_audio_exit);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-dai-q6.c b/sound/soc/msm/msm-dai-q6.c
index 5e02797..4af4f06 100644
--- a/sound/soc/msm/msm-dai-q6.c
+++ b/sound/soc/msm/msm-dai-q6.c
@@ -300,6 +300,11 @@
case RT_PROXY_DAI_002_RX:
rc = msm_dai_q6_afe_rtproxy_hw_params(params, dai);
break;
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ rc = 0;
+ break;
default:
dev_err(dai->dev, "invalid AFE port ID\n");
rc = -EINVAL;
@@ -343,7 +348,18 @@
int rc = 0;
if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
- rc = afe_close(dai->id); /* can block */
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ pr_debug("%s, stop pseudo port:%d\n",
+ __func__, dai->id);
+ rc = afe_stop_pseudo_port(dai->id);
+ break;
+ default:
+ rc = afe_close(dai->id); /* can block */
+ break;
+ }
if (IS_ERR_VALUE(rc))
dev_err(dai->dev, "fail to close AFE port\n");
pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
@@ -478,8 +494,17 @@
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
- afe_port_start_nowait(dai->id, &dai_data->port_config,
- dai_data->rate);
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ afe_pseudo_port_start_nowait(dai->id);
+ break;
+ default:
+ afe_port_start_nowait(dai->id,
+ &dai_data->port_config, dai_data->rate);
+ break;
+ }
set_bit(STATUS_PORT_STARTED,
dai_data->status_mask);
}
@@ -488,7 +513,16 @@
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
- afe_port_stop_nowait(dai->id);
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ afe_pseudo_port_stop_nowait(dai->id);
+ break;
+ default:
+ afe_port_stop_nowait(dai->id);
+ break;
+ }
clear_bit(STATUS_PORT_STARTED,
dai_data->status_mask);
}
@@ -586,7 +620,17 @@
/* If AFE port is still up, close it */
if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
- rc = afe_close(dai->id); /* can block */
+ switch (dai->id) {
+ case VOICE_PLAYBACK_TX:
+ case VOICE_RECORD_TX:
+ case VOICE_RECORD_RX:
+ pr_debug("%s, stop pseudo port:%d\n",
+ __func__, dai->id);
+ rc = afe_stop_pseudo_port(dai->id);
+ break;
+ default:
+ rc = afe_close(dai->id); /* can block */
+ }
if (IS_ERR_VALUE(rc))
dev_err(dai->dev, "fail to close AFE port\n");
clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
@@ -705,6 +749,21 @@
.remove = msm_dai_q6_dai_remove,
};
+static struct snd_soc_dai_driver msm_dai_q6_voice_playback_tx_dai = {
+ .playback = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_max = 48000,
+ .rate_min = 8000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai = {
.playback = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
@@ -735,6 +794,21 @@
.remove = msm_dai_q6_dai_remove,
};
+static struct snd_soc_dai_driver msm_dai_q6_incall_record_dai = {
+ .capture = {
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+ SNDRV_PCM_RATE_16000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 48000,
+ },
+ .ops = &msm_dai_q6_ops,
+ .probe = msm_dai_q6_dai_probe,
+ .remove = msm_dai_q6_dai_remove,
+};
+
static struct snd_soc_dai_driver msm_dai_q6_bt_sco_rx_dai = {
.playback = {
.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
@@ -872,6 +946,15 @@
case RT_PROXY_DAI_002_TX:
rc = snd_soc_register_dai(&pdev->dev, &msm_dai_q6_afe_tx_dai);
break;
+ case VOICE_PLAYBACK_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_voice_playback_tx_dai);
+ break;
+ case VOICE_RECORD_RX:
+ case VOICE_RECORD_TX:
+ rc = snd_soc_register_dai(&pdev->dev,
+ &msm_dai_q6_incall_record_dai);
+ break;
default:
rc = -ENODEV;
break;
diff --git a/sound/soc/msm/msm-pcm-q6.c b/sound/soc/msm/msm-pcm-q6.c
index 738e024..0f3ac4a 100644
--- a/sound/soc/msm/msm-pcm-q6.c
+++ b/sound/soc/msm/msm-pcm-q6.c
@@ -51,11 +51,11 @@
.rate_max = 48000,
.channels_min = 1,
.channels_max = 2,
- .buffer_bytes_max = 320 * 8,
+ .buffer_bytes_max = 320 * 16,
.period_bytes_min = 320,
.period_bytes_max = 320,
- .periods_min = 8,
- .periods_max = 8,
+ .periods_min = 16,
+ .periods_max = 16,
.fifo_size = 0,
};
diff --git a/sound/soc/msm/msm-pcm-routing.c b/sound/soc/msm/msm-pcm-routing.c
index 3573169..453bce4 100644
--- a/sound/soc/msm/msm-pcm-routing.c
+++ b/sound/soc/msm/msm-pcm-routing.c
@@ -114,11 +114,14 @@
{ RT_PROXY_PORT_001_TX, 0, NULL, 0, 0},
{ PCM_RX, 0, NULL, 0, 0},
{ PCM_TX, 0, NULL, 0, 0},
+ { VOICE_PLAYBACK_TX, 0, NULL, 0, 0},
+ { VOICE_RECORD_RX, 0, NULL, 0, 0},
+ { VOICE_RECORD_TX, 0, NULL, 0, 0},
};
/* Track ASM playback & capture sessions of DAI */
-static int fe_dai_map[MSM_FRONTEND_DAI_MAX][2] = {
+static int fe_dai_map[MSM_FRONTEND_DAI_MM_SIZE][2] = {
/* MULTIMEDIA1 */
{INVALID_SESSION, INVALID_SESSION},
/* MULTIMEDIA2 */
@@ -158,6 +161,12 @@
int i, session_type, path_type, port_type;
struct route_payload payload;
+ if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
session_type = SESSION_TYPE_RX;
path_type = ADM_PATH_PLAYBACK;
@@ -200,6 +209,12 @@
{
int i, port_type, session_type;
+ if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* bad ID assigned in machine driver */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
port_type = MSM_AFE_PORT_TYPE_RX;
session_type = SESSION_TYPE_RX;
@@ -229,6 +244,12 @@
pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
+ if (val > MSM_FRONTEND_DAI_MM_MAX_ID) {
+ /* recheck FE ID in the mixer control defined in this file */
+ pr_err("%s: bad MM ID\n", __func__);
+ return;
+ }
+
if (afe_get_port_type(msm_bedais[reg].port_id) ==
MSM_AFE_PORT_TYPE_RX) {
session_type = SESSION_TYPE_RX;
@@ -241,6 +262,10 @@
mutex_lock(&routing_lock);
if (set) {
+ if (!test_bit(val, &msm_bedais[reg].fe_sessions) &&
+ (msm_bedais[reg].port_id == VOICE_PLAYBACK_TX))
+ voc_start_playback(set);
+
set_bit(val, &msm_bedais[reg].fe_sessions);
if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
INVALID_SESSION) {
@@ -252,6 +277,9 @@
fe_dai_map[val][session_type], path_type);
}
} else {
+ if (test_bit(val, &msm_bedais[reg].fe_sessions) &&
+ (msm_bedais[reg].port_id == VOICE_PLAYBACK_TX))
+ voc_start_playback(set);
clear_bit(val, &msm_bedais[reg].fe_sessions);
if (msm_bedais[reg].active && fe_dai_map[val][session_type] !=
INVALID_SESSION) {
@@ -260,6 +288,9 @@
fe_dai_map[val][session_type], path_type);
}
}
+ if ((msm_bedais[reg].port_id == VOICE_RECORD_RX)
+ || (msm_bedais[reg].port_id == VOICE_RECORD_TX))
+ voc_start_record(msm_bedais[reg].port_id, set);
mutex_unlock(&routing_lock);
}
@@ -518,9 +549,11 @@
int eq_idx = ((struct soc_multi_mixer_control *)
kcontrol->private_value)->reg;
+ ucontrol->value.integer.value[0] = eq_data[eq_idx].enable;
+
pr_debug("%s: EQ #%d enable %d\n", __func__,
eq_idx, eq_data[eq_idx].enable);
- return eq_data[eq_idx].enable;
+ return 0;
}
static int msm_routing_put_eq_enable_mixer(struct snd_kcontrol *kcontrol,
@@ -545,6 +578,8 @@
int eq_idx = ((struct soc_multi_mixer_control *)
kcontrol->private_value)->reg;
+ ucontrol->value.integer.value[0] = eq_data[eq_idx].num_bands;
+
pr_debug("%s: EQ #%d bands %d\n", __func__,
eq_idx, eq_data[eq_idx].num_bands);
return eq_data[eq_idx].num_bands;
@@ -558,7 +593,6 @@
kcontrol->private_value)->reg;
int value = ucontrol->value.integer.value[0];
-
pr_debug("%s: EQ #%d bands %d\n", __func__,
eq_idx, value);
eq_data[eq_idx].num_bands = value;
@@ -573,6 +607,17 @@
int band_idx = ((struct soc_multi_mixer_control *)
kcontrol->private_value)->shift;
+ ucontrol->value.integer.value[0] =
+ eq_data[eq_idx].eq_bands[band_idx].band_idx;
+ ucontrol->value.integer.value[1] =
+ eq_data[eq_idx].eq_bands[band_idx].filter_type;
+ ucontrol->value.integer.value[2] =
+ eq_data[eq_idx].eq_bands[band_idx].center_freq_hz;
+ ucontrol->value.integer.value[3] =
+ eq_data[eq_idx].eq_bands[band_idx].filter_gain;
+ ucontrol->value.integer.value[4] =
+ eq_data[eq_idx].eq_bands[band_idx].q_factor;
+
pr_debug("%s: band_idx = %d\n", __func__,
eq_data[eq_idx].eq_bands[band_idx].band_idx);
pr_debug("%s: filter_type = %d\n", __func__,
@@ -651,6 +696,15 @@
MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
};
+ /* incall music delivery mixer */
+static const struct snd_kcontrol_new incall_music_delivery_mixer_controls[] = {
+ SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+};
static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_BT_SCO_RX,
@@ -731,6 +785,12 @@
SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
+ SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+ msm_routing_put_audio_mixer),
};
static const struct snd_kcontrol_new mmul2_mixer_controls[] = {
@@ -1071,6 +1131,13 @@
SND_SOC_DAPM_AIF_OUT("PCM_RX", "AFE Playback",
0, 0, 0 , 0),
SND_SOC_DAPM_AIF_IN("PCM_TX", "AFE Capture",
+ 0, 0, 0 , 0),
+ /* incall */
+ SND_SOC_DAPM_AIF_OUT("VOICE_PLAYBACK_TX", "Voice Farend Playback",
+ 0, 0, 0 , 0),
+ SND_SOC_DAPM_AIF_IN("INCALL_RECORD_TX", "Voice Uplink Capture",
+ 0, 0, 0, 0),
+ SND_SOC_DAPM_AIF_IN("INCALL_RECORD_RX", "Voice Downlink Capture",
0, 0, 0, 0),
SND_SOC_DAPM_AIF_OUT("AUX_PCM_RX", "AUX PCM Playback", 0, 0, 0, 0),
SND_SOC_DAPM_AIF_IN("AUX_PCM_TX", "AUX PCM Capture", 0, 0, 0, 0),
@@ -1090,6 +1157,10 @@
mmul2_mixer_controls, ARRAY_SIZE(mmul2_mixer_controls)),
SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)),
+ /* incall */
+ SND_SOC_DAPM_MIXER("Incall_Music Audio Mixer", SND_SOC_NOPM, 0, 0,
+ incall_music_delivery_mixer_controls,
+ ARRAY_SIZE(incall_music_delivery_mixer_controls)),
/* Voice Mixer */
SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
@@ -1150,6 +1221,14 @@
{"HDMI Mixer", "MultiMedia4", "MM_DL4"},
{"HDMI", NULL, "HDMI Mixer"},
+ /* incall */
+ {"Incall_Music Audio Mixer", "MultiMedia1", "MM_DL1"},
+ {"Incall_Music Audio Mixer", "MultiMedia2", "MM_DL2"},
+ {"VOICE_PLAYBACK_TX", NULL, "Incall_Music Audio Mixer"},
+
+ {"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+ {"MultiMedia1 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+
{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
{"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
{"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
@@ -1239,6 +1318,11 @@
struct snd_soc_pcm_runtime *rtd = substream->private_data;
unsigned int be_id = rtd->dai_link->be_id;
+ if (be_id >= MSM_BACKEND_DAI_MAX) {
+ pr_err("%s: unexpected be_id %d\n", __func__, be_id);
+ return -EINVAL;
+ }
+
mutex_lock(&routing_lock);
msm_bedais[be_id].hw_params = params;
mutex_unlock(&routing_lock);
@@ -1264,7 +1348,7 @@
mutex_lock(&routing_lock);
- for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MAX) {
+ for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_MAX_ID) {
if (fe_dai_map[i][session_type] != INVALID_SESSION)
adm_close(bedai->port_id);
}
@@ -1318,7 +1402,7 @@
*/
bedai->active = 1;
- for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MAX) {
+ for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_MAX_ID) {
if (fe_dai_map[i][session_type] != INVALID_SESSION) {
adm_open(bedai->port_id, path_type,
params_rate(bedai->hw_params),
diff --git a/sound/soc/msm/msm-pcm-routing.h b/sound/soc/msm/msm-pcm-routing.h
index b7fc82a..2020939 100644
--- a/sound/soc/msm/msm-pcm-routing.h
+++ b/sound/soc/msm/msm-pcm-routing.h
@@ -26,6 +26,16 @@
#define LPASS_BE_AFE_PCM_TX "(Backend) RT_PROXY_DAI_002_TX"
#define LPASS_BE_AUXPCM_RX "(Backend) AUX_PCM_RX"
#define LPASS_BE_AUXPCM_TX "(Backend) AUX_PCM_TX"
+#define LPASS_BE_VOICE_PLAYBACK_TX "(Backend) VOICE_PLAYBACK_TX"
+#define LPASS_BE_INCALL_RECORD_RX "(Backend) INCALL_RECORD_TX"
+#define LPASS_BE_INCALL_RECORD_TX "(Backend) INCALL_RECORD_RX"
+
+
+/* For multimedia front-ends, asm session is allocated dynamically.
+ * Hence, asm session/multimedia front-end mapping has to be maintained.
+ * Due to this reason, additional multimedia front-end must be placed before
+ * non-multimedia front-ends.
+ */
enum {
MSM_FRONTEND_DAI_MULTIMEDIA1 = 0,
@@ -39,6 +49,9 @@
MSM_FRONTEND_DAI_MAX,
};
+#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA4 + 1)
+#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA4
+
enum {
MSM_BACKEND_DAI_PRI_I2S_RX = 0,
MSM_BACKEND_DAI_PRI_I2S_TX,
@@ -53,6 +66,9 @@
MSM_BACKEND_DAI_AFE_PCM_TX,
MSM_BACKEND_DAI_AUXPCM_RX,
MSM_BACKEND_DAI_AUXPCM_TX,
+ MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ MSM_BACKEND_DAI_INCALL_RECORD_TX,
MSM_BACKEND_DAI_MAX,
};
diff --git a/sound/soc/msm/msm8960.c b/sound/soc/msm/msm8960.c
index bfc004e..dd8224c 100644
--- a/sound/soc/msm/msm8960.c
+++ b/sound/soc/msm/msm8960.c
@@ -24,6 +24,7 @@
#include <sound/pcm.h>
#include <sound/jack.h>
#include <asm/mach-types.h>
+#include <mach/socinfo.h>
#include "msm-pcm-routing.h"
#include "../codecs/wcd9310.h"
@@ -1060,6 +1061,45 @@
.be_id = MSM_BACKEND_DAI_AUXPCM_TX,
.be_hw_params_fixup = msm8960_auxpcm_be_params_fixup,
},
+ /* Incall Music BACK END DAI Link */
+ {
+ .name = LPASS_BE_VOICE_PLAYBACK_TX,
+ .stream_name = "Voice Farend Playback",
+ .cpu_dai_name = "msm-dai-q6.32773",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-rx",
+ .no_pcm = 1,
+ .no_codec = 1,
+ .be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+ .be_hw_params_fixup = msm8960_be_hw_params_fixup,
+ },
+ /* Incall Record Uplink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_TX,
+ .stream_name = "Voice Uplink Capture",
+ .cpu_dai_name = "msm-dai-q6.32772",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .no_codec = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+ .be_hw_params_fixup = msm8960_be_hw_params_fixup,
+ },
+ /* Incall Record Downlink BACK END DAI Link */
+ {
+ .name = LPASS_BE_INCALL_RECORD_RX,
+ .stream_name = "Voice Downlink Capture",
+ .cpu_dai_name = "msm-dai-q6.32771",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "msm-stub-codec.1",
+ .codec_dai_name = "msm-stub-tx",
+ .no_pcm = 1,
+ .no_codec = 1,
+ .be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+ .be_hw_params_fixup = msm8960_be_hw_params_fixup,
+ },
};
struct snd_soc_card snd_soc_card_msm8960 = {
@@ -1125,6 +1165,10 @@
{
int ret;
+ if (!cpu_is_msm8960()) {
+ pr_err("%s: Not the right machine type\n", __func__);
+ return -ENODEV ;
+ }
msm8960_snd_device = platform_device_alloc("soc-audio", 0);
if (!msm8960_snd_device) {
pr_err("Platform device allocation failed\n");
@@ -1151,6 +1195,10 @@
static void __exit msm8960_audio_exit(void)
{
+ if (!cpu_is_msm8960()) {
+ pr_err("%s: Not the right machine type\n", __func__);
+ return ;
+ }
msm8960_free_headset_mic_gpios();
platform_device_unregister(msm8960_snd_device);
}
diff --git a/sound/soc/msm/qdsp6/q6afe.c b/sound/soc/msm/qdsp6/q6afe.c
index 017c1b6..21bbcf2 100644
--- a/sound/soc/msm/qdsp6/q6afe.c
+++ b/sound/soc/msm/qdsp6/q6afe.c
@@ -677,6 +677,39 @@
fail_cmd:
return ret;
}
+
+int afe_pseudo_port_start_nowait(u16 port_id)
+{
+ int ret = 0;
+ struct afe_pseudoport_start_command start;
+
+ pr_debug("%s: port_id=%d\n", __func__, port_id);
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE APR is not registered\n", __func__);
+ return -ENODEV;
+ }
+
+
+ start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ start.hdr.pkt_size = sizeof(start);
+ start.hdr.src_port = 0;
+ start.hdr.dest_port = 0;
+ start.hdr.token = 0;
+ start.hdr.opcode = AFE_PSEUDOPORT_CMD_START;
+ start.port_id = port_id;
+ start.timing = 1;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &start);
+ if (ret < 0) {
+ pr_err("%s: AFE enable for port %d failed %d\n",
+ __func__, port_id, ret);
+ return -EINVAL;
+ }
+ return 0;
+}
+
int afe_start_pseudo_port(u16 port_id)
{
int ret = 0;
@@ -703,8 +736,7 @@
if (ret < 0) {
pr_err("%s: AFE enable for port %d failed %d\n",
__func__, port_id, ret);
- ret = -EINVAL;
- return ret;
+ return -EINVAL;
}
ret = wait_event_timeout(this_afe.wait,
@@ -712,24 +744,22 @@
msecs_to_jiffies(TIMEOUT_MS));
if (!ret) {
pr_err("%s: wait_event timeout\n", __func__);
- ret = -EINVAL;
- return ret;
+ return -EINVAL;
}
return 0;
}
-int afe_stop_pseudo_port(u16 port_id)
+int afe_pseudo_port_stop_nowait(u16 port_id)
{
int ret = 0;
struct afe_pseudoport_stop_command stop;
- pr_info("%s: port_id=%d\n", __func__, port_id);
+ pr_debug("%s: port_id=%d\n", __func__, port_id);
if (this_afe.apr == NULL) {
pr_err("%s: AFE is already closed\n", __func__);
- ret = -EINVAL;
- return ret;
+ return -EINVAL;
}
stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
@@ -746,8 +776,40 @@
ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
if (ret < 0) {
pr_err("%s: AFE close failed %d\n", __func__, ret);
- ret = -EINVAL;
- return ret;
+ return -EINVAL;
+ }
+
+ return 0;
+
+}
+
+int afe_stop_pseudo_port(u16 port_id)
+{
+ int ret = 0;
+ struct afe_pseudoport_stop_command stop;
+
+ pr_info("%s: port_id=%d\n", __func__, port_id);
+
+ if (this_afe.apr == NULL) {
+ pr_err("%s: AFE is already closed\n", __func__);
+ return -EINVAL;
+ }
+
+ stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ stop.hdr.pkt_size = sizeof(stop);
+ stop.hdr.src_port = 0;
+ stop.hdr.dest_port = 0;
+ stop.hdr.token = 0;
+ stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP;
+ stop.port_id = port_id;
+ stop.reserved = 0;
+
+ atomic_set(&this_afe.state, 1);
+ ret = apr_send_pkt(this_afe.apr, (uint32_t *) &stop);
+ if (ret < 0) {
+ pr_err("%s: AFE close failed %d\n", __func__, ret);
+ return -EINVAL;
}
ret = wait_event_timeout(this_afe.wait,
@@ -755,8 +817,7 @@
msecs_to_jiffies(TIMEOUT_MS));
if (!ret) {
pr_err("%s: wait_event timeout\n", __func__);
- ret = -EINVAL;
- return ret;
+ return -EINVAL;
}
return 0;
diff --git a/sound/soc/msm/qdsp6/q6voice.c b/sound/soc/msm/qdsp6/q6voice.c
index 965211d..de63fa0 100644
--- a/sound/soc/msm/qdsp6/q6voice.c
+++ b/sound/soc/msm/qdsp6/q6voice.c
@@ -54,6 +54,10 @@
static int voice_send_set_widevoice_enable_cmd(struct voice_data *v);
static int voice_send_set_slowtalk_enable_cmd(struct voice_data *v);
+static int voice_cvs_stop_playback(struct voice_data *v);
+static int voice_cvs_start_playback(struct voice_data *v);
+static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode);
+static int voice_cvs_stop_record(struct voice_data *v);
static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv);
static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv);
@@ -1919,6 +1923,14 @@
if (is_voip_session(v->session_id))
voice_send_netid_timing_cmd(v);
+ /* Start in-call music delivery if this feature is enabled */
+ if (v->music_info.play_enable)
+ voice_cvs_start_playback(v);
+
+ /* Start in-call recording if this feature is enabled */
+ if (v->rec_info.rec_enable)
+ voice_cvs_start_record(v, v->rec_info.rec_mode);
+
rtac_add_voice(voice_get_cvs_handle(v),
voice_get_cvp_handle(v),
v->dev_rx.port_id, v->dev_tx.port_id,
@@ -2150,6 +2162,10 @@
mvm_handle = voice_get_mvm_handle(v);
cvp_handle = voice_get_cvp_handle(v);
+ /* stop playback or recording */
+ v->music_info.force = 1;
+ voice_cvs_stop_playback(v);
+ voice_cvs_stop_record(v);
/* send stop voice cmd */
voice_send_stop_voice_cmd(v);
@@ -2322,6 +2338,423 @@
return 0;
}
+static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode)
+{
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+
+ struct cvs_start_record_cmd cvs_start_record;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ if (!v->rec_info.recording) {
+ cvs_start_record.hdr.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_start_record.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_start_record) - APR_HDR_SIZE);
+ cvs_start_record.hdr.src_port = v->session_id;
+ cvs_start_record.hdr.dest_port = cvs_handle;
+ cvs_start_record.hdr.token = 0;
+ cvs_start_record.hdr.opcode = VSS_ISTREAM_CMD_START_RECORD;
+
+ if (rec_mode == VOC_REC_UPLINK) {
+ cvs_start_record.rec_mode.rx_tap_point =
+ VSS_TAP_POINT_NONE;
+ cvs_start_record.rec_mode.tx_tap_point =
+ VSS_TAP_POINT_STREAM_END;
+ } else if (rec_mode == VOC_REC_DOWNLINK) {
+ cvs_start_record.rec_mode.rx_tap_point =
+ VSS_TAP_POINT_STREAM_END;
+ cvs_start_record.rec_mode.tx_tap_point =
+ VSS_TAP_POINT_NONE;
+ } else if (rec_mode == VOC_REC_BOTH) {
+ cvs_start_record.rec_mode.rx_tap_point =
+ VSS_TAP_POINT_STREAM_END;
+ cvs_start_record.rec_mode.tx_tap_point =
+ VSS_TAP_POINT_STREAM_END;
+ } else {
+ pr_err("%s: Invalid in-call rec_mode %d\n", __func__,
+ rec_mode);
+
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_record);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending START_RECORD\n", __func__,
+ ret);
+
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ goto fail;
+ }
+ v->rec_info.recording = 1;
+ } else {
+ pr_debug("%s: Start record already sent\n", __func__);
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int voice_cvs_stop_record(struct voice_data *v)
+{
+ int ret = 0;
+ void *apr_cvs;
+ u16 cvs_handle;
+ struct apr_hdr cvs_stop_record;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ if (v->rec_info.recording) {
+ cvs_stop_record.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvs_stop_record.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_stop_record) - APR_HDR_SIZE);
+ cvs_stop_record.src_port = v->session_id;
+ cvs_stop_record.dest_port = cvs_handle;
+ cvs_stop_record.token = 0;
+ cvs_stop_record.opcode = VSS_ISTREAM_CMD_STOP_RECORD;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_record);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending STOP_RECORD\n",
+ __func__, ret);
+
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ goto fail;
+ }
+ v->rec_info.recording = 0;
+ } else {
+ pr_debug("%s: Stop record already sent\n", __func__);
+ }
+
+ return 0;
+
+fail:
+
+ return ret;
+}
+
+int voc_start_record(uint32_t port_id, uint32_t set)
+{
+ int ret = 0;
+ int rec_mode = 0;
+ u16 cvs_handle;
+ int i, rec_set = 0;
+
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ struct voice_data *v = &common.voice[i];
+ pr_debug("%s: i:%d port_id: %d, set: %d\n",
+ __func__, i, port_id, set);
+
+ mutex_lock(&v->lock);
+ rec_mode = v->rec_info.rec_mode;
+ rec_set = set;
+ if (set) {
+ if ((v->rec_route_state.ul_flag != 0) &&
+ (v->rec_route_state.dl_flag != 0)) {
+ pr_debug("%s: i=%d, rec mode already set.\n",
+ __func__, i);
+ mutex_unlock(&v->lock);
+ if (i < MAX_VOC_SESSIONS)
+ continue;
+ else
+ return 0;
+ }
+
+ if (port_id == VOICE_RECORD_TX) {
+ if ((v->rec_route_state.ul_flag == 0)
+ && (v->rec_route_state.dl_flag == 0)) {
+ rec_mode = VOC_REC_UPLINK;
+ v->rec_route_state.ul_flag = 1;
+ } else if ((v->rec_route_state.ul_flag == 0)
+ && (v->rec_route_state.dl_flag != 0)) {
+ voice_cvs_stop_record(v);
+ rec_mode = VOC_REC_BOTH;
+ v->rec_route_state.ul_flag = 1;
+ }
+ } else if (port_id == VOICE_RECORD_RX) {
+ if ((v->rec_route_state.ul_flag == 0)
+ && (v->rec_route_state.dl_flag == 0)) {
+ rec_mode = VOC_REC_DOWNLINK;
+ v->rec_route_state.dl_flag = 1;
+ } else if ((v->rec_route_state.ul_flag != 0)
+ && (v->rec_route_state.dl_flag == 0)) {
+ voice_cvs_stop_record(v);
+ rec_mode = VOC_REC_BOTH;
+ v->rec_route_state.dl_flag = 1;
+ }
+ }
+ rec_set = 1;
+ } else {
+ if ((v->rec_route_state.ul_flag == 0) &&
+ (v->rec_route_state.dl_flag == 0)) {
+ pr_debug("%s: i=%d, rec already stops.\n",
+ __func__, i);
+ mutex_unlock(&v->lock);
+ if (i < MAX_VOC_SESSIONS)
+ continue;
+ else
+ return 0;
+ }
+
+ if (port_id == VOICE_RECORD_TX) {
+ if ((v->rec_route_state.ul_flag != 0)
+ && (v->rec_route_state.dl_flag == 0)) {
+ v->rec_route_state.ul_flag = 0;
+ rec_set = 0;
+ } else if ((v->rec_route_state.ul_flag != 0)
+ && (v->rec_route_state.dl_flag != 0)) {
+ voice_cvs_stop_record(v);
+ v->rec_route_state.ul_flag = 0;
+ rec_mode = VOC_REC_DOWNLINK;
+ rec_set = 1;
+ }
+ } else if (port_id == VOICE_RECORD_RX) {
+ if ((v->rec_route_state.ul_flag == 0)
+ && (v->rec_route_state.dl_flag != 0)) {
+ v->rec_route_state.dl_flag = 0;
+ rec_set = 0;
+ } else if ((v->rec_route_state.ul_flag != 0)
+ && (v->rec_route_state.dl_flag != 0)) {
+ voice_cvs_stop_record(v);
+ v->rec_route_state.dl_flag = 0;
+ rec_mode = VOC_REC_UPLINK;
+ rec_set = 1;
+ }
+ }
+ }
+ pr_debug("%s: i=%d, mode =%d, set =%d\n", __func__,
+ i, rec_mode, rec_set);
+ cvs_handle = voice_get_cvs_handle(v);
+
+ if (cvs_handle != 0) {
+ if (rec_set)
+ ret = voice_cvs_start_record(v, rec_mode);
+ else
+ ret = voice_cvs_stop_record(v);
+ }
+
+ /* Cache the value */
+ v->rec_info.rec_enable = rec_set;
+ v->rec_info.rec_mode = rec_mode;
+
+ mutex_unlock(&v->lock);
+ }
+
+ return ret;
+}
+
+static int voice_cvs_start_playback(struct voice_data *v)
+{
+ int ret = 0;
+ struct apr_hdr cvs_start_playback;
+ void *apr_cvs;
+ u16 cvs_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ if (!v->music_info.playing && v->music_info.count) {
+ cvs_start_playback.hdr_field = APR_HDR_FIELD(
+ APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE),
+ APR_PKT_VER);
+ cvs_start_playback.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_start_playback) - APR_HDR_SIZE);
+ cvs_start_playback.src_port = v->session_id;
+ cvs_start_playback.dest_port = cvs_handle;
+ cvs_start_playback.token = 0;
+ cvs_start_playback.opcode = VSS_ISTREAM_CMD_START_PLAYBACK;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_playback);
+
+ if (ret < 0) {
+ pr_err("%s: Error %d sending START_PLAYBACK\n",
+ __func__, ret);
+
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ goto fail;
+ }
+
+ v->music_info.playing = 1;
+ } else {
+ pr_debug("%s: Start playback already sent\n", __func__);
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+static int voice_cvs_stop_playback(struct voice_data *v)
+{
+ int ret = 0;
+ struct apr_hdr cvs_stop_playback;
+ void *apr_cvs;
+ u16 cvs_handle;
+
+ if (v == NULL) {
+ pr_err("%s: v is NULL\n", __func__);
+ return -EINVAL;
+ }
+ apr_cvs = common.apr_q6_cvs;
+
+ if (!apr_cvs) {
+ pr_err("%s: apr_cvs is NULL.\n", __func__);
+ return -EINVAL;
+ }
+
+ cvs_handle = voice_get_cvs_handle(v);
+
+ if (v->music_info.playing && ((!v->music_info.count) ||
+ (v->music_info.force))) {
+ cvs_stop_playback.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ cvs_stop_playback.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+ sizeof(cvs_stop_playback) - APR_HDR_SIZE);
+ cvs_stop_playback.src_port = v->session_id;
+ cvs_stop_playback.dest_port = cvs_handle;
+ cvs_stop_playback.token = 0;
+
+ cvs_stop_playback.opcode = VSS_ISTREAM_CMD_STOP_PLAYBACK;
+
+ v->cvs_state = CMD_STATUS_FAIL;
+
+ ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_playback);
+ if (ret < 0) {
+ pr_err("%s: Error %d sending STOP_PLAYBACK\n",
+ __func__, ret);
+
+
+ goto fail;
+ }
+
+ ret = wait_event_timeout(v->cvs_wait,
+ (v->cvs_state == CMD_STATUS_SUCCESS),
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!ret) {
+ pr_err("%s: wait_event timeout\n", __func__);
+
+ goto fail;
+ }
+
+ v->music_info.playing = 0;
+ v->music_info.force = 0;
+ } else {
+ pr_debug("%s: Stop playback already sent\n", __func__);
+ }
+
+ return 0;
+
+fail:
+ return ret;
+}
+
+int voc_start_playback(uint32_t set)
+{
+ int ret = 0;
+ u16 cvs_handle;
+ int i;
+
+
+ for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+ struct voice_data *v = &common.voice[i];
+
+ mutex_lock(&v->lock);
+ v->music_info.play_enable = set;
+ if (set)
+ v->music_info.count++;
+ else
+ v->music_info.count--;
+ pr_debug("%s: music_info count =%d\n", __func__,
+ v->music_info.count);
+
+ cvs_handle = voice_get_cvs_handle(v);
+ if (cvs_handle != 0) {
+ if (set)
+ ret = voice_cvs_start_playback(v);
+ else
+ ret = voice_cvs_stop_playback(v);
+ }
+
+ mutex_unlock(&v->lock);
+ }
+
+ return ret;
+}
+
int voc_disable_cvp(uint16_t session_id)
{
struct voice_data *v = voice_get_session(session_id);
@@ -2336,7 +2769,10 @@
mutex_lock(&v->lock);
if (v->voc_state == VOC_RUN) {
- afe_sidetone(v->dev_tx.port_id, v->dev_rx.port_id, 0, 0);
+ if (v->dev_tx.port_id != RT_PROXY_PORT_001_TX &&
+ v->dev_rx.port_id != RT_PROXY_PORT_001_RX)
+ afe_sidetone(v->dev_tx.port_id, v->dev_rx.port_id,
+ 0, 0);
rtac_remove_voice(voice_get_cvs_handle(v));
/* send cmd to dsp to disable vocproc */
@@ -2403,12 +2839,17 @@
voice_send_set_slowtalk_enable_cmd(v);
get_sidetone_cal(&sidetone_cal_data);
- ret = afe_sidetone(v->dev_tx.port_id, v->dev_rx.port_id,
- sidetone_cal_data.enable,
- sidetone_cal_data.gain);
+ if (v->dev_tx.port_id != RT_PROXY_PORT_001_TX &&
+ v->dev_rx.port_id != RT_PROXY_PORT_001_RX) {
+ ret = afe_sidetone(v->dev_tx.port_id,
+ v->dev_rx.port_id,
+ sidetone_cal_data.enable,
+ sidetone_cal_data.gain);
- if (ret < 0)
- pr_err("%s: AFE command sidetone failed\n", __func__);
+ if (ret < 0)
+ pr_err("%s: AFE command sidetone failed\n",
+ __func__);
+ }
rtac_add_voice(voice_get_cvs_handle(v),
voice_get_cvp_handle(v),
@@ -2683,7 +3124,10 @@
mutex_lock(&v->lock);
if (v->voc_state == VOC_RUN) {
- afe_sidetone(v->dev_tx.port_id, v->dev_rx.port_id, 0, 0);
+ if (v->dev_tx.port_id != RT_PROXY_PORT_001_TX &&
+ v->dev_rx.port_id != RT_PROXY_PORT_001_RX)
+ afe_sidetone(v->dev_tx.port_id, v->dev_rx.port_id,
+ 0, 0);
ret = voice_destroy_vocproc(v);
if (ret < 0)
pr_err("%s: destroy voice failed\n", __func__);
@@ -2732,12 +3176,15 @@
goto fail;
}
get_sidetone_cal(&sidetone_cal_data);
- ret = afe_sidetone(v->dev_tx.port_id,
+ if (v->dev_tx.port_id != RT_PROXY_PORT_001_TX &&
+ v->dev_rx.port_id != RT_PROXY_PORT_001_RX) {
+ ret = afe_sidetone(v->dev_tx.port_id,
v->dev_rx.port_id,
sidetone_cal_data.enable,
sidetone_cal_data.gain);
- if (ret < 0)
- pr_err("AFE command sidetone failed\n");
+ if (ret < 0)
+ pr_err("AFE command sidetone failed\n");
+ }
v->voc_state = VOC_RUN;
}
@@ -2923,6 +3370,10 @@
case VSS_ICOMMON_CMD_MAP_MEMORY:
case VSS_ICOMMON_CMD_UNMAP_MEMORY:
case VSS_ICOMMON_CMD_SET_UI_PROPERTY:
+ case VSS_ISTREAM_CMD_START_PLAYBACK:
+ case VSS_ISTREAM_CMD_STOP_PLAYBACK:
+ case VSS_ISTREAM_CMD_START_RECORD:
+ case VSS_ISTREAM_CMD_STOP_RECORD:
pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
v->cvs_state = CMD_STATUS_SUCCESS;
wake_up(&v->cvs_wait);
diff --git a/sound/soc/msm/qdsp6/q6voice.h b/sound/soc/msm/qdsp6/q6voice.h
index edde0aa..d330ada 100644
--- a/sound/soc/msm/qdsp6/q6voice.h
+++ b/sound/soc/msm/qdsp6/q6voice.h
@@ -17,6 +17,10 @@
#define MAX_VOC_PKT_SIZE 642
#define SESSION_NAME_LEN 20
+#define VOC_REC_UPLINK 0x00
+#define VOC_REC_DOWNLINK 0x01
+#define VOC_REC_BOTH 0x02
+
struct voice_header {
uint32_t id;
uint32_t data_len;
@@ -43,6 +47,11 @@
u16 tx_route_flag;
};
+struct voice_rec_route_state {
+ u16 ul_flag;
+ u16 dl_flag;
+};
+
enum {
VOC_INIT = 0,
VOC_RUN,
@@ -318,6 +327,36 @@
#define VOICE_PARAM_MOD_ENABLE 0x00010E00
#define MOD_ENABLE_PARAM_LEN 4
+#define VSS_ISTREAM_CMD_START_PLAYBACK 0x00011238
+/* Start in-call music delivery on the Tx voice path. */
+
+#define VSS_ISTREAM_CMD_STOP_PLAYBACK 0x00011239
+/* Stop the in-call music delivery on the Tx voice path. */
+
+#define VSS_ISTREAM_CMD_START_RECORD 0x00011236
+/* Start in-call conversation recording. */
+#define VSS_ISTREAM_CMD_STOP_RECORD 0x00011237
+/* Stop in-call conversation recording. */
+
+#define VSS_TAP_POINT_NONE 0x00010F78
+/* Indicates no tapping for specified path. */
+
+#define VSS_TAP_POINT_STREAM_END 0x00010F79
+/* Indicates that specified path should be tapped at the end of the stream. */
+
+struct vss_istream_cmd_start_record_t {
+ uint32_t rx_tap_point;
+ /* Tap point to use on the Rx path. Supported values are:
+ * VSS_TAP_POINT_NONE : Do not record Rx path.
+ * VSS_TAP_POINT_STREAM_END : Rx tap point is at the end of the stream.
+ */
+ uint32_t tx_tap_point;
+ /* Tap point to use on the Tx path. Supported values are:
+ * VSS_TAP_POINT_NONE : Do not record tx path.
+ * VSS_TAP_POINT_STREAM_END : Tx tap point is at the end of the stream.
+ */
+} __packed;
+
struct vss_istream_cmd_create_passive_control_session_t {
char name[SESSION_NAME_LEN];
/**<
@@ -538,6 +577,10 @@
struct apr_hdr hdr;
struct vss_icommon_cmd_set_ui_property_st_enable_t vss_set_st;
} __packed;
+struct cvs_start_record_cmd {
+ struct apr_hdr hdr;
+ struct vss_istream_cmd_start_record_t rec_mode;
+} __packed;
/* TO CVP commands */
@@ -747,13 +790,16 @@
};
struct incall_rec_info {
- uint32_t pending;
+ uint32_t rec_enable;
uint32_t rec_mode;
+ uint32_t recording;
};
struct incall_music_info {
- uint32_t pending;
+ uint32_t play_enable;
uint32_t playing;
+ int count;
+ int force;
};
struct voice_data {
@@ -790,6 +836,12 @@
struct voice_dev_route_state voc_route_state;
u16 session_id;
+
+ struct incall_rec_info rec_info;
+
+ struct incall_music_info music_info;
+
+ struct voice_rec_route_state rec_route_state;
};
#define MAX_VOC_SESSIONS 2
@@ -856,4 +908,6 @@
#define VOIP_SESSION_NAME "VoIP session"
uint16_t voc_get_session_id(char *name);
+int voc_start_playback(uint32_t set);
+int voc_start_record(uint32_t port_id, uint32_t set);
#endif