Merge changes I4d5d73ab,I6e076344,I6cf6035d,I29faa348 into msm-3.0
* changes:
Perf: Use CPU PM notifiers to save and restore PMU regs
ARM: cpu_pm: Add cpu power management notifiers
ARM: Perfevents: Add mode exclusion support for Krait P2
Perf: Fix detection of Krait implementation events
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 34ffd9e..3eeac92 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1410,6 +1410,12 @@
help
This option enables support for the ARM system coherency unit
+config ARM_ARCH_TIMER
+ bool "Architected timer support"
+ select TICK_ONESHOT
+ help
+ This option enables support for the ARM architected timer
+
config HAVE_ARM_TWD
bool
depends on SMP
diff --git a/arch/arm/configs/msm7627a-perf_defconfig b/arch/arm/configs/msm7627a-perf_defconfig
index 90d4c4a..f81cc3d 100644
--- a/arch/arm/configs/msm7627a-perf_defconfig
+++ b/arch/arm/configs/msm7627a-perf_defconfig
@@ -230,6 +230,8 @@
CONFIG_MSM_CAMERA_FLASH_SC628A=y
CONFIG_IMX072=y
CONFIG_RADIO_TAVARUA=y
+CONFIG_GENLOCK=y
+CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_MSM_KGSL=y
CONFIG_FB=y
CONFIG_FB_MSM=y
diff --git a/arch/arm/configs/msm7627a_defconfig b/arch/arm/configs/msm7627a_defconfig
index cf35373..4836af0 100644
--- a/arch/arm/configs/msm7627a_defconfig
+++ b/arch/arm/configs/msm7627a_defconfig
@@ -228,6 +228,8 @@
CONFIG_MSM_CAMERA_FLASH_SC628A=y
CONFIG_IMX072=y
CONFIG_RADIO_TAVARUA=y
+CONFIG_GENLOCK=y
+CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_MSM_KGSL=y
CONFIG_FB=y
CONFIG_FB_MSM=y
diff --git a/arch/arm/configs/msm7630-perf_defconfig b/arch/arm/configs/msm7630-perf_defconfig
index 2cd7b97..3791d0e 100644
--- a/arch/arm/configs/msm7630-perf_defconfig
+++ b/arch/arm/configs/msm7630-perf_defconfig
@@ -262,6 +262,8 @@
CONFIG_MT9E013=y
CONFIG_MSM_GEMINI=y
CONFIG_RADIO_TAVARUA=y
+CONFIG_GENLOCK=y
+CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_MSM_KGSL=y
CONFIG_VIDEO_OUTPUT_CONTROL=y
CONFIG_FB=y
diff --git a/arch/arm/configs/msm7630_defconfig b/arch/arm/configs/msm7630_defconfig
index c2d180f..bd60092 100644
--- a/arch/arm/configs/msm7630_defconfig
+++ b/arch/arm/configs/msm7630_defconfig
@@ -253,6 +253,8 @@
# CONFIG_MFD_PM8XXX_DEBUG is not set
# CONFIG_MFD_PM8XXX_PWM is not set
# CONFIG_MFD_PM8XXX_MISC is not set
+CONFIG_GENLOCK=y
+CONFIG_GENLOCK_MISCDEVICE=y
CONFIG_MSM_KGSL=y
CONFIG_VIDEO_OUTPUT_CONTROL=y
CONFIG_FB=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 3af0419..e136a0a 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -269,7 +269,7 @@
CONFIG_ISL9519_CHARGER=y
CONFIG_PM8921_CHARGER=y
CONFIG_PM8921_BMS=y
-CONFIG_SENSORS_PM8921_ADC=y
+CONFIG_SENSORS_PM8XXX_ADC=y
CONFIG_THERMAL=y
CONFIG_THERMAL_TSENS8960=y
CONFIG_THERMAL_PM8XXX=y
@@ -341,7 +341,7 @@
CONFIG_USB_SERIAL_QUALCOMM=y
CONFIG_USB_EHSET_TEST_FIXTURE=y
CONFIG_USB_QCOM_DIAG_BRIDGE=y
-CONFIG_USB_QCOM_DUN_BRIDGE=y
+CONFIG_USB_QCOM_MDM_BRIDGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_GADGET_DEBUG_FILES=y
CONFIG_USB_GADGET_CI13XXX_MSM=y
diff --git a/arch/arm/configs/msm9615_defconfig b/arch/arm/configs/msm9615_defconfig
index 141bfde..e1b45e1 100644
--- a/arch/arm/configs/msm9615_defconfig
+++ b/arch/arm/configs/msm9615_defconfig
@@ -97,6 +97,8 @@
# CONFIG_MSM_RMNET is not set
CONFIG_MSM_RMNET_BAM=y
# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_EVBUG=m
# CONFIG_INPUT_KEYBOARD is not set
# CONFIG_INPUT_MOUSE is not set
CONFIG_INPUT_MISC=y
diff --git a/arch/arm/include/asm/arch_timer.h b/arch/arm/include/asm/arch_timer.h
new file mode 100644
index 0000000..e82217f
--- /dev/null
+++ b/arch/arm/include/asm/arch_timer.h
@@ -0,0 +1,8 @@
+#ifndef __ASMARM_ARCH_TIMER_H
+#define __ASMARM_ARCH_TIMER_H
+
+struct resource;
+
+int arch_timer_register(struct resource *res, int res_nr);
+
+#endif
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index a5b31af..c77c2fb 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -34,6 +34,7 @@
obj-$(CONFIG_SMP) += smp.o smp_tlb.o
obj-$(CONFIG_HAVE_ARM_SCU) += smp_scu.o
obj-$(CONFIG_HAVE_ARM_TWD) += smp_twd.o
+obj-$(CONFIG_ARM_ARCH_TIMER) += arch_timer.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
diff --git a/arch/arm/kernel/arch_timer.c b/arch/arm/kernel/arch_timer.c
new file mode 100644
index 0000000..5b76911
--- /dev/null
+++ b/arch/arm/kernel/arch_timer.c
@@ -0,0 +1,319 @@
+/*
+ * linux/arch/arm/kernel/arch_timer.c
+ *
+ * Copyright (C) 2011 ARM Ltd.
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <linux/jiffies.h>
+#include <linux/clockchips.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <asm/cputype.h>
+#include <asm/hardware/gic.h>
+
+static unsigned long arch_timer_rate;
+static int arch_timer_ppi;
+static int arch_timer_ppi2;
+static DEFINE_CLOCK_DATA(cd);
+
+static struct clock_event_device __percpu *arch_timer_evt;
+
+/*
+ * Architected system timer support.
+ */
+
+#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
+#define ARCH_TIMER_CTRL_IT_MASK (1 << 1)
+
+#define ARCH_TIMER_REG_CTRL 0
+#define ARCH_TIMER_REG_FREQ 1
+#define ARCH_TIMER_REG_TVAL 2
+
+static void arch_timer_reg_write(int reg, u32 val)
+{
+ switch (reg) {
+ case ARCH_TIMER_REG_CTRL:
+ asm volatile("mcr p15, 0, %0, c14, c2, 1" : : "r" (val));
+ break;
+ case ARCH_TIMER_REG_TVAL:
+ asm volatile("mcr p15, 0, %0, c14, c2, 0" : : "r" (val));
+ break;
+ }
+
+ isb();
+}
+
+static u32 arch_timer_reg_read(int reg)
+{
+ u32 val;
+
+ switch (reg) {
+ case ARCH_TIMER_REG_CTRL:
+ asm volatile("mrc p15, 0, %0, c14, c2, 1" : "=r" (val));
+ break;
+ case ARCH_TIMER_REG_FREQ:
+ asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r" (val));
+ break;
+ case ARCH_TIMER_REG_TVAL:
+ asm volatile("mrc p15, 0, %0, c14, c2, 0" : "=r" (val));
+ break;
+ default:
+ BUG();
+ }
+
+ return val;
+}
+
+static irqreturn_t arch_timer_handler(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+ unsigned long ctrl;
+
+ ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+ if (ctrl & 0x4) {
+ ctrl |= ARCH_TIMER_CTRL_IT_MASK;
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+ evt->event_handler(evt);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static void arch_timer_stop(void)
+{
+ unsigned long ctrl;
+
+ ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+ ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+}
+
+static void arch_timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *clk)
+{
+ switch (mode) {
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ arch_timer_stop();
+ break;
+ default:
+ break;
+ }
+}
+
+static int arch_timer_set_next_event(unsigned long evt,
+ struct clock_event_device *unused)
+{
+ unsigned long ctrl;
+
+ ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+ ctrl |= ARCH_TIMER_CTRL_ENABLE;
+ ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
+
+ arch_timer_reg_write(ARCH_TIMER_REG_TVAL, evt);
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+
+ return 0;
+}
+
+static void __cpuinit arch_timer_setup(void *data)
+{
+ struct clock_event_device *clk = data;
+ int err;
+
+ /* Be safe... */
+ arch_timer_stop();
+
+ clk->features = CLOCK_EVT_FEAT_ONESHOT;
+ clk->name = "arch_sys_timer";
+ clk->rating = 450;
+ clk->set_mode = arch_timer_set_mode;
+ clk->set_next_event = arch_timer_set_next_event;
+ clk->irq = arch_timer_ppi;
+ clk->cpumask = cpumask_of(smp_processor_id());
+
+ clockevents_config_and_register(clk, arch_timer_rate,
+ 0xf, 0x7fffffff);
+
+ err = gic_request_ppi(clk->irq, arch_timer_handler, clk);
+ if (err) {
+ pr_err("%s: can't register interrupt %d on cpu %d (%d)\n",
+ clk->name, clk->irq, smp_processor_id(), err);
+ return;
+ }
+
+ if (arch_timer_ppi2 >= 0) {
+ err = gic_request_ppi(arch_timer_ppi2, arch_timer_handler, clk);
+ if (err) {
+ pr_warn("%s: can't register interrupt %d on cpu %d (%d)\n",
+ clk->name, arch_timer_ppi2, smp_processor_id(), err);
+ }
+ }
+}
+
+/* Is the optional system timer available? */
+static int local_timer_is_architected(void)
+{
+ return (cpu_architecture() >= CPU_ARCH_ARMv7) &&
+ ((read_cpuid_ext(CPUID_EXT_PFR1) >> 16) & 0xf) == 1;
+}
+
+static int arch_timer_available(void)
+{
+ unsigned long freq;
+
+ if (!local_timer_is_architected())
+ return -ENXIO;
+
+ if (arch_timer_rate == 0) {
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, 0);
+ freq = arch_timer_reg_read(ARCH_TIMER_REG_FREQ);
+
+ /* Check the timer frequency. */
+ if (freq == 0) {
+ pr_warn("Architected timer frequency not available\n");
+ return -EINVAL;
+ }
+
+ arch_timer_rate = freq;
+ pr_info("Architected local timer running at %lu.%02luMHz.\n",
+ arch_timer_rate / 1000000, (arch_timer_rate % 100000) / 100);
+ }
+
+ return 0;
+}
+
+static inline cycle_t arch_counter_get_cntpct(void)
+{
+ u32 cvall, cvalh;
+
+ asm volatile("mrrc p15, 0, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
+
+ return ((u64) cvalh << 32) | cvall;
+}
+
+static inline cycle_t arch_counter_get_cntvct(void)
+{
+ u32 cvall, cvalh;
+
+ asm volatile("mrrc p15, 1, %0, %1, c14" : "=r" (cvall), "=r" (cvalh));
+
+ return ((u64) cvalh << 32) | cvall;
+}
+
+static cycle_t arch_counter_read(struct clocksource *cs)
+{
+ return arch_counter_get_cntpct();
+}
+
+static struct clocksource clocksource_counter = {
+ .name = "arch_sys_counter",
+ .rating = 400,
+ .read = arch_counter_read,
+ .mask = CLOCKSOURCE_MASK(56),
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static u32 arch_counter_get_cntvct32(void)
+{
+ cycle_t cntvct;
+
+ cntvct = arch_counter_get_cntvct();
+
+ /*
+ * The sched_clock infrastructure only knows about counters
+ * with at most 32bits. Forget about the upper 24 bits for the
+ * time being...
+ */
+ return (u32)(cntvct & (u32)~0);
+}
+
+DEFINE_SCHED_CLOCK_FUNC(arch_timer_sched_clock)
+{
+ return cyc_to_sched_clock(&cd, arch_counter_get_cntvct32(), (u32)~0);
+}
+
+static void notrace arch_timer_update_sched_clock(void)
+{
+ update_sched_clock(&cd, arch_counter_get_cntvct32(), (u32)~0);
+}
+
+static void __cpuinit arch_timer_teardown(void *data)
+{
+ struct clock_event_device *clk = data;
+ pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n",
+ clk->irq, smp_processor_id());
+ gic_free_ppi(clk->irq, clk);
+ if (arch_timer_ppi2 >= 0)
+ gic_free_ppi(arch_timer_ppi2, clk);
+ arch_timer_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
+}
+
+static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *data)
+{
+ int cpu = (int)data;
+ struct clock_event_device *clk = per_cpu_ptr(arch_timer_evt, cpu);
+
+ switch(action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ smp_call_function_single(cpu, arch_timer_setup, clk, 1);
+ break;
+
+ case CPU_DOWN_PREPARE:
+ case CPU_DOWN_PREPARE_FROZEN:
+ smp_call_function_single(cpu, arch_timer_teardown, clk, 1);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata arch_timer_cpu_nb = {
+ .notifier_call = arch_timer_cpu_notify,
+};
+
+int arch_timer_register(struct resource *res, int res_nr)
+{
+ int err;
+
+ if (!res_nr || res[0].start < 0 || !(res[0].flags & IORESOURCE_IRQ))
+ return -EINVAL;
+
+ err = arch_timer_available();
+ if (err)
+ return err;
+
+ arch_timer_evt = alloc_percpu(struct clock_event_device);
+ if (!arch_timer_evt)
+ return -ENOMEM;
+
+ arch_timer_ppi = res[0].start;
+ if (res_nr > 1 && (res[1].flags & IORESOURCE_IRQ))
+ arch_timer_ppi2 = res[1].start;
+
+ clocksource_register_hz(&clocksource_counter, arch_timer_rate);
+
+ init_arch_sched_clock(&cd, arch_timer_update_sched_clock,
+ arch_timer_sched_clock, 32, arch_timer_rate);
+
+ /* Immediately configure the timer on the boot CPU */
+ arch_timer_setup(per_cpu_ptr(arch_timer_evt, smp_processor_id()));
+
+ register_cpu_notifier(&arch_timer_cpu_nb);
+
+ return 0;
+}
diff --git a/arch/arm/mach-msm/acpuclock-7201.c b/arch/arm/mach-msm/acpuclock-7201.c
index 4702e51..0338d53 100644
--- a/arch/arm/mach-msm/acpuclock-7201.c
+++ b/arch/arm/mach-msm/acpuclock-7201.c
@@ -341,10 +341,86 @@
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
};
+/* 7x27a pll2 at 1200mhz with GSM capable modem */
+static struct clkctl_acpu_speed pll0_960_pll1_737_pll2_1200_pll4_800[] = {
+ { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
+ { 0, 61440, ACPU_PLL_1, 1, 11, 7680, 3, 1, 61440 },
+ { 1, 122880, ACPU_PLL_1, 1, 5, 15360, 3, 2, 61440 },
+ { 1, 245760, ACPU_PLL_1, 1, 2, 30720, 3, 3, 61440 },
+ { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 150000 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 },
+ { 0, 400000, ACPU_PLL_4, 6, 1, 50000, 3, 4, 122880 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 },
+ { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 200000 },
+ { 1, 800000, ACPU_PLL_4, 6, 0, 100000, 3, 7, 200000 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
+};
+
+/* 7x27a pll2 at 1200mhz with CDMA only modem */
+static struct clkctl_acpu_speed pll0_960_pll1_589_pll2_1200_pll4_800[] = {
+ { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
+ { 0, 65536, ACPU_PLL_1, 1, 8, 8192, 3, 1, 49152 },
+ { 1, 98304, ACPU_PLL_1, 1, 5, 12288, 3, 2, 49152 },
+ { 1, 196608, ACPU_PLL_1, 1, 2, 24576, 3, 3, 98304 },
+ { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 120000 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 120000 },
+ { 0, 400000, ACPU_PLL_4, 6, 1, 50000, 3, 4, 120000 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 120000 },
+ { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 200000 },
+ { 1, 800000, ACPU_PLL_4, 6, 0, 100000, 3, 7, 200000 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
+};
+
+/* 7x27aa pll4 at 1008mhz with GSM capable modem */
+static struct clkctl_acpu_speed pll0_960_pll1_737_pll2_1200_pll4_1008[] = {
+ { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
+ { 0, 61440, ACPU_PLL_1, 1, 11, 7680, 3, 1, 61440 },
+ { 1, 122880, ACPU_PLL_1, 1, 5, 15360, 3, 2, 61440 },
+ { 1, 245760, ACPU_PLL_1, 1, 2, 30720, 3, 3, 61440 },
+ { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 150000 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 },
+ { 0, 504000, ACPU_PLL_4, 6, 1, 63000, 3, 6, 200000 },
+ { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 200000 },
+ { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 7, 200000},
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
+};
+
+/* 7x27aa pll4 at 1008mhz with CDMA capable modem */
+static struct clkctl_acpu_speed pll0_960_pll1_589_pll2_1200_pll4_1008[] = {
+ { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 24576 },
+ { 0, 65536, ACPU_PLL_1, 1, 8, 8192, 3, 1, 49152 },
+ { 1, 98304, ACPU_PLL_1, 1, 5, 12288, 3, 2, 49152 },
+ { 1, 196608, ACPU_PLL_1, 1, 2, 24576, 3, 3, 98304 },
+ { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 150000 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 },
+ { 0, 504000, ACPU_PLL_4, 6, 1, 63000, 3, 6, 200000 },
+ { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 200000 },
+ { 1, 1008000, ACPU_PLL_4, 6, 0, 126000, 3, 7, 200000},
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
+};
+
+/* 7x25a pll2 at 1200mhz with GSM capable modem */
+static struct clkctl_acpu_speed pll0_960_pll1_737_pll2_1200_pll4_800_25a[] = {
+ { 0, 19200, ACPU_PLL_TCXO, 0, 0, 2400, 3, 0, 30720 },
+ { 0, 61440, ACPU_PLL_1, 1, 11, 7680, 3, 1, 61440 },
+ { 1, 122880, ACPU_PLL_1, 1, 5, 15360, 3, 2, 61440 },
+ { 1, 245760, ACPU_PLL_1, 1, 2, 30720, 3, 3, 61440 },
+ { 0, 300000, ACPU_PLL_2, 2, 3, 37500, 3, 4, 150000 },
+ { 1, 320000, ACPU_PLL_0, 4, 2, 40000, 3, 4, 122880 },
+ { 0, 400000, ACPU_PLL_4, 6, 1, 50000, 3, 4, 122880 },
+ { 1, 480000, ACPU_PLL_0, 4, 1, 60000, 3, 5, 122880 },
+ { 1, 600000, ACPU_PLL_2, 2, 1, 75000, 3, 6, 200000 },
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0, 0, 0, 0}, {0, 0, 0, 0} }
+};
+
#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
@@ -380,6 +456,10 @@
PLL_CONFIG(960, 196, 1200, 800),
PLL_CONFIG(960, 245, 1200, 1008),
PLL_CONFIG(960, 196, 1200, 1008),
+ PLL_CONFIG(960, 737, 1200, 800),
+ PLL_CONFIG(960, 589, 1200, 800),
+ PLL_CONFIG(960, 737, 1200, 1008),
+ PLL_CONFIG(960, 589, 1200, 1008),
{ 0, 0, 0, 0, 0 }
};
@@ -868,6 +948,9 @@
if (pll1_l == PLL_245_MHZ) {
acpu_freq_tbl =
pll0_960_pll1_245_pll2_1200_pll4_800_25a;
+ } else if (pll1_l == PLL_737_MHZ) {
+ acpu_freq_tbl =
+ pll0_960_pll1_737_pll2_1200_pll4_800_25a;
}
} else {
/* Select the right table to use. */
diff --git a/arch/arm/mach-msm/bam_dmux.c b/arch/arm/mach-msm/bam_dmux.c
index c70e1b5..ede298d 100644
--- a/arch/arm/mach-msm/bam_dmux.c
+++ b/arch/arm/mach-msm/bam_dmux.c
@@ -55,6 +55,7 @@
static uint32_t bam_dmux_write_cnt;
static uint32_t bam_dmux_write_cpy_cnt;
static uint32_t bam_dmux_write_cpy_bytes;
+static uint32_t bam_dmux_tx_sps_failure_cnt;
#define DBG(x...) do { \
if (msm_bam_dmux_debug_enable) \
@@ -83,11 +84,17 @@
__func__, bam_dmux_write_cpy_cnt, \
bam_dmux_write_cpy_bytes); \
} while (0)
+
+#define DBG_INC_TX_SPS_FAILURE_CNT() do { \
+ bam_dmux_tx_sps_failure_cnt++; \
+} while (0)
+
#else
#define DBG(x...) do { } while (0)
#define DBG_INC_READ_CNT(x...) do { } while (0)
#define DBG_INC_WRITE_CNT(x...) do { } while (0)
#define DBG_INC_WRITE_CPY(x...) do { } while (0)
+#define DBG_INC_TX_SPS_FAILURE_CNT() do { } while (0)
#endif
struct bam_ch_info {
@@ -377,6 +384,7 @@
DBG("%s sps_transfer_one failed rc=%d\n", __func__, rc);
spin_lock(&bam_tx_pool_spinlock);
list_del(&pkt->list_node);
+ DBG_INC_TX_SPS_FAILURE_CNT();
spin_unlock(&bam_tx_pool_spinlock);
kfree(pkt);
}
@@ -509,6 +517,7 @@
DBG("%s sps_transfer_one failed rc=%d\n", __func__, rc);
spin_lock(&bam_tx_pool_spinlock);
list_del(&pkt->list_node);
+ DBG_INC_TX_SPS_FAILURE_CNT();
spin_unlock(&bam_tx_pool_spinlock);
kfree(pkt);
}
@@ -815,6 +824,37 @@
return i;
}
+static int debug_ul_pkt_cnt(char *buf, int max)
+{
+ struct list_head *p;
+ unsigned long flags;
+ int n = 0;
+
+ spin_lock_irqsave(&bam_tx_pool_spinlock, flags);
+ __list_for_each(p, &bam_tx_pool) {
+ ++n;
+ }
+ spin_unlock_irqrestore(&bam_tx_pool_spinlock, flags);
+
+ return scnprintf(buf, max, "Number of UL packets in flight: %d\n", n);
+}
+
+static int debug_stats(char *buf, int max)
+{
+ int i = 0;
+
+ i += scnprintf(buf + i, max - i,
+ "skb copy cnt: %u\n"
+ "skb copy bytes: %u\n"
+ "sps tx failures: %u\n",
+ bam_dmux_write_cpy_cnt,
+ bam_dmux_write_cpy_bytes,
+ bam_dmux_tx_sps_failure_cnt
+ );
+
+ return i;
+}
+
#define DEBUG_BUFMAX 4096
static char debug_buffer[DEBUG_BUFMAX];
@@ -877,9 +917,17 @@
static void ul_timeout(struct work_struct *work)
{
+ unsigned long flags;
+ int ret;
+
if (in_global_reset)
return;
- write_lock(&ul_wakeup_lock);
+ ret = write_trylock_irqsave(&ul_wakeup_lock, flags);
+ if (!ret) { /* failed to grab lock, reschedule and bail */
+ schedule_delayed_work(&ul_timeout_work,
+ msecs_to_jiffies(UL_TIMEOUT_DELAY));
+ return;
+ }
if (ul_packet_written) {
ul_packet_written = 0;
schedule_delayed_work(&ul_timeout_work,
@@ -889,7 +937,7 @@
bam_is_connected = 0;
notify_all(BAM_DMUX_UL_DISCONNECTED, (unsigned long)(NULL));
}
- write_unlock(&ul_wakeup_lock);
+ write_unlock_irqrestore(&ul_wakeup_lock, flags);
}
static void ul_wakeup(void)
{
@@ -1299,8 +1347,11 @@
struct dentry *dent;
dent = debugfs_create_dir("bam_dmux", 0);
- if (!IS_ERR(dent))
+ if (!IS_ERR(dent)) {
debug_create("tbl", 0444, dent, debug_tbl);
+ debug_create("ul_pkt_cnt", 0444, dent, debug_ul_pkt_cnt);
+ debug_create("stats", 0444, dent, debug_stats);
+ }
#endif
subsys_notif_register_notifier("modem", &restart_notifier);
return platform_driver_register(&bam_dmux_driver);
diff --git a/arch/arm/mach-msm/board-apq8064.c b/arch/arm/mach-msm/board-apq8064.c
index 00886c6..53c290e 100644
--- a/arch/arm/mach-msm/board-apq8064.c
+++ b/arch/arm/mach-msm/board-apq8064.c
@@ -661,6 +661,7 @@
static struct platform_device *rumi3_devices[] __initdata = {
&apq8064_device_uart_gsbi1,
+ &msm_device_sps_apq8064,
};
static struct msm_spi_platform_data apq8064_qup_spi_gsbi5_pdata = {
diff --git a/arch/arm/mach-msm/board-msm8960-regulator.c b/arch/arm/mach-msm/board-msm8960-regulator.c
index 6aee8e7..559312c 100644
--- a/arch/arm/mach-msm/board-msm8960-regulator.c
+++ b/arch/arm/mach-msm/board-msm8960-regulator.c
@@ -79,7 +79,7 @@
};
VREG_CONSUMERS(L14) = {
REGULATOR_SUPPLY("8921_l14", NULL),
- REGULATOR_SUPPLY("pa_therm", "pm8921-adc"),
+ REGULATOR_SUPPLY("pa_therm", "pm8xxx-adc"),
};
VREG_CONSUMERS(L15) = {
REGULATOR_SUPPLY("8921_l15", NULL),
diff --git a/arch/arm/mach-msm/board-msm8960.c b/arch/arm/mach-msm/board-msm8960.c
index e1e922e..61423a6 100644
--- a/arch/arm/mach-msm/board-msm8960.c
+++ b/arch/arm/mach-msm/board-msm8960.c
@@ -21,7 +21,7 @@
#include <linux/msm_ssbi.h>
#include <linux/regulator/gpio-regulator.h>
#include <linux/mfd/pm8xxx/pm8921.h>
-#include <linux/mfd/pm8xxx/pm8921-adc.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
#include <linux/slimbus/slimbus.h>
@@ -187,7 +187,7 @@
static struct pm8xxx_mpp_init pm8921_mpps[] __initdata = {
/* External 5V regulator enable; shared by HDMI and USB_OTG switches. */
PM8XXX_MPP_INIT(7, D_INPUT, PM8921_MPP_DIG_LEVEL_VPH, DIN_TO_INT),
- PM8XXX_MPP_INIT(PM8921_AMUX_MPP_8, A_INPUT, PM8XXX_MPP_AIN_AMUX_CH8,
+ PM8XXX_MPP_INIT(PM8XXX_AMUX_MPP_8, A_INPUT, PM8XXX_MPP_AIN_AMUX_CH8,
DOUT_CTRL_LOW),
};
@@ -2858,7 +2858,7 @@
#define MSM_SHARED_RAM_PHYS 0x80000000
-static struct pm8921_adc_amux pm8921_adc_channels_data[] = {
+static struct pm8xxx_adc_amux pm8xxx_adc_channels_data[] = {
{"vcoin", CHANNEL_VCOIN, CHAN_PATH_SCALING2, AMUX_RSV1,
ADC_DECIMATION_TYPE2, ADC_SCALE_DEFAULT},
{"vbat", CHANNEL_VBAT, CHAN_PATH_SCALING2, AMUX_RSV1,
@@ -2893,16 +2893,16 @@
ADC_DECIMATION_TYPE2, ADC_SCALE_PA_THERM},
};
-static struct pm8921_adc_properties pm8921_adc_data = {
+static struct pm8xxx_adc_properties pm8xxx_adc_data = {
.adc_vdd_reference = 1800, /* milli-voltage for this adc */
.bitresolution = 15,
.bipolar = 0,
};
-static struct pm8921_adc_platform_data pm8921_adc_pdata = {
- .adc_channel = pm8921_adc_channels_data,
- .adc_num_board_channel = ARRAY_SIZE(pm8921_adc_channels_data),
- .adc_prop = &pm8921_adc_data,
+static struct pm8xxx_adc_platform_data pm8xxx_adc_pdata = {
+ .adc_channel = pm8xxx_adc_channels_data,
+ .adc_num_board_channel = ARRAY_SIZE(pm8xxx_adc_channels_data),
+ .adc_prop = &pm8xxx_adc_data,
.adc_mpp_base = PM8921_MPP_PM_TO_SYS(1),
};
@@ -4201,10 +4201,8 @@
if (SOCINFO_VERSION_MAJOR(soc_platform_version) == 1) {
struct kgsl_device_platform_data *kgsl_3d0_pdata =
msm_kgsl_3d0.dev.platform_data;
- kgsl_3d0_pdata->pwr_data.pwrlevel[0].gpu_freq =
- 320000000;
- kgsl_3d0_pdata->pwr_data.pwrlevel[1].gpu_freq =
- 266667000;
+ kgsl_3d0_pdata->pwrlevel[0].gpu_freq = 320000000;
+ kgsl_3d0_pdata->pwrlevel[1].gpu_freq = 266667000;
}
}
@@ -4539,7 +4537,7 @@
.regulator_pdatas = msm_pm8921_regulator_pdata,
.charger_pdata = &pm8921_chg_pdata,
.bms_pdata = &pm8921_bms_pdata,
- .adc_pdata = &pm8921_adc_pdata,
+ .adc_pdata = &pm8xxx_adc_pdata,
.leds_pdata = &pm8xxx_leds_pdata,
.ccadc_pdata = &pm8xxx_ccadc_pdata,
};
diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c
index 0d2bd82..c22d54c 100644
--- a/arch/arm/mach-msm/board-msm8x60.c
+++ b/arch/arm/mach-msm/board-msm8x60.c
@@ -9292,7 +9292,7 @@
goto out;
} else {
/* set ldo0 to LPM */
- rc = regulator_set_optimum_mode(ldo0, 9000);
+ rc = regulator_set_optimum_mode(ldo0, 1000);
if (rc < 0)
goto out;
}
diff --git a/arch/arm/mach-msm/board-qrd7627a.c b/arch/arm/mach-msm/board-qrd7627a.c
index 31767c3..d192faf 100644
--- a/arch/arm/mach-msm/board-qrd7627a.c
+++ b/arch/arm/mach-msm/board-qrd7627a.c
@@ -36,7 +36,6 @@
#include <mach/rpc_hsusb.h>
#include <mach/rpc_pmapp.h>
#include <mach/usbdiag.h>
-#include <mach/usb_gadget_fserial.h>
#include <mach/msm_memtypes.h>
#include <mach/msm_serial_hs.h>
#include <mach/pmic.h>
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 5899773..a4c8b2a2 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -4921,9 +4921,9 @@
{ TEST_LPA_HS(0x00), &q6_func_clk },
- { TEST_CPUL2(0x1), &l2_m_clk },
- { TEST_CPUL2(0x2), &krait0_m_clk },
- { TEST_CPUL2(0x3), &krait1_m_clk },
+ { TEST_CPUL2(0x2), &l2_m_clk },
+ { TEST_CPUL2(0x0), &krait0_m_clk },
+ { TEST_CPUL2(0x1), &krait1_m_clk },
};
static struct measure_sel *find_measure_sel(struct clk *clk)
@@ -5245,7 +5245,7 @@
CLK_LOOKUP("core_clk", rot_clk.c, "msm_rotator.0"),
CLK_LOOKUP("core_clk", rot_clk.c, "footswitch-8x60.6"),
CLK_DUMMY("tv_src_clk", TV_SRC_CLK, NULL, OFF),
- CLK_LOOKUP("core_clk", vcodec_clk.c, NULL),
+ CLK_LOOKUP("core_clk", vcodec_clk.c, "msm_vidc.0"),
CLK_LOOKUP("core_clk", vcodec_clk.c, "footswitch-8x60.7"),
CLK_DUMMY("mdp_tv_clk", MDP_TV_CLK, NULL, OFF),
CLK_DUMMY("hdmi_clk", HDMI_TV_CLK, NULL, OFF),
@@ -5281,7 +5281,7 @@
CLK_LOOKUP("iface_clk", smmu_p_clk.c, NULL),
CLK_LOOKUP("iface_clk", rot_p_clk.c, "msm_rotator.0"),
CLK_LOOKUP("iface_clk", rot_p_clk.c, "footswitch-8x60.6"),
- CLK_LOOKUP("iface_clk", vcodec_p_clk.c, NULL),
+ CLK_LOOKUP("iface_clk", vcodec_p_clk.c, "msm_vidc.0"),
CLK_LOOKUP("iface_clk", vcodec_p_clk.c, "footswitch-8x60.7"),
CLK_LOOKUP("vfe_pclk", vfe_p_clk.c, NULL),
CLK_LOOKUP("iface_clk", vfe_p_clk.c, "footswitch-8x60.8"),
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index 2391f84..8aee414 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -911,3 +911,97 @@
{
return branch_reset(&to_branch_clk(clk)->b, action);
}
+
+static int cdiv_clk_enable(struct clk *c)
+{
+ unsigned long flags;
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ __branch_clk_enable_reg(&clk->b, clk->c.dbg_name);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+
+ return 0;
+}
+
+static void cdiv_clk_disable(struct clk *c)
+{
+ unsigned long flags;
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+
+ spin_lock_irqsave(&local_clock_reg_lock, flags);
+ __branch_clk_disable_reg(&clk->b, clk->c.dbg_name);
+ spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+}
+
+static int cdiv_clk_set_rate(struct clk *c, unsigned long rate)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ u32 reg_val;
+
+ if (rate > clk->max_div)
+ return -EINVAL;
+ /* Check if frequency is actually changed. */
+ if (rate == clk->cur_div)
+ return 0;
+
+ spin_lock(&local_clock_reg_lock);
+ reg_val = readl_relaxed(clk->ns_reg);
+ reg_val &= ~(clk->ext_mask | (clk->max_div - 1) << clk->div_offset);
+ /* Non-zero rates mean set a divider, zero means use external input */
+ if (rate)
+ reg_val |= (rate - 1) << clk->div_offset;
+ else
+ reg_val |= clk->ext_mask;
+ writel_relaxed(reg_val, clk->ns_reg);
+ spin_unlock(&local_clock_reg_lock);
+
+ clk->cur_div = rate;
+ return 0;
+}
+
+static unsigned long cdiv_clk_get_rate(struct clk *c)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ return clk->cur_div;
+}
+
+static long cdiv_clk_round_rate(struct clk *c, unsigned long rate)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ return rate > clk->max_div ? -EPERM : rate;
+}
+
+static int cdiv_clk_list_rate(struct clk *c, unsigned n)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ return n > clk->max_div ? -ENXIO : n;
+}
+
+static int cdiv_clk_handoff(struct clk *c)
+{
+ struct cdiv_clk *clk = to_cdiv_clk(c);
+ u32 reg_val;
+
+ reg_val = readl_relaxed(clk->ns_reg);
+ if (reg_val & clk->ext_mask) {
+ clk->cur_div = 0;
+ } else {
+ reg_val >>= clk->div_offset;
+ clk->cur_div = (reg_val & (clk->max_div - 1)) + 1;
+ }
+
+ return 0;
+}
+
+struct clk_ops clk_ops_cdiv = {
+ .enable = cdiv_clk_enable,
+ .disable = cdiv_clk_disable,
+ .auto_off = cdiv_clk_disable,
+ .handoff = cdiv_clk_handoff,
+ .set_rate = cdiv_clk_set_rate,
+ .get_rate = cdiv_clk_get_rate,
+ .list_rate = cdiv_clk_list_rate,
+ .round_rate = cdiv_clk_round_rate,
+ .is_local = local_clk_is_local,
+};
diff --git a/arch/arm/mach-msm/clock-local.h b/arch/arm/mach-msm/clock-local.h
index 2107567..e651d47 100644
--- a/arch/arm/mach-msm/clock-local.h
+++ b/arch/arm/mach-msm/clock-local.h
@@ -153,6 +153,35 @@
int rcg_clk_handoff(struct clk *c);
/**
+ * struct cdiv_clk - integer divider clock with external source selection
+ * @ns_reg: source select and divider settings register
+ * @ext_mask: bit to set to select an external source
+ * @cur_div: current divider setting (or 0 for external source)
+ * @max_div: maximum divider value supported (must be power of 2)
+ * @div_offset: number of bits to shift divider left by in @ns_reg
+ * @b: branch
+ * @c: clock
+ */
+struct cdiv_clk {
+ void __iomem *const ns_reg;
+ u32 ext_mask;
+
+ unsigned long cur_div;
+ u8 div_offset;
+ u32 max_div;
+
+ struct branch b;
+ struct clk c;
+};
+
+static inline struct cdiv_clk *to_cdiv_clk(struct clk *clk)
+{
+ return container_of(clk, struct cdiv_clk, c);
+}
+
+extern struct clk_ops clk_ops_cdiv;
+
+/**
* struct fixed_clk - fixed rate clock (used for crystal oscillators)
* @rate: output rate
* @c: clk
diff --git a/arch/arm/mach-msm/devices-8960.c b/arch/arm/mach-msm/devices-8960.c
index 0a300ad..2add8f9 100644
--- a/arch/arm/mach-msm/devices-8960.c
+++ b/arch/arm/mach-msm/devices-8960.c
@@ -2052,52 +2052,41 @@
};
static struct kgsl_device_platform_data kgsl_3d0_pdata = {
- .pwr_data = {
- .pwrlevel = {
- {
- .gpu_freq = 400000000,
- .bus_freq = 4,
- .io_fraction = 0,
- },
- {
- .gpu_freq = 300000000,
- .bus_freq = 3,
- .io_fraction = 33,
- },
- {
- .gpu_freq = 200000000,
- .bus_freq = 2,
- .io_fraction = 100,
- },
- {
- .gpu_freq = 128000000,
- .bus_freq = 1,
- .io_fraction = 100,
- },
- {
- .gpu_freq = 27000000,
- .bus_freq = 0,
- },
+ .pwrlevel = {
+ {
+ .gpu_freq = 400000000,
+ .bus_freq = 4,
+ .io_fraction = 0,
},
- .init_level = 0,
- .num_levels = 5,
- .set_grp_async = NULL,
- .idle_timeout = HZ/5,
- .nap_allowed = true,
+ {
+ .gpu_freq = 300000000,
+ .bus_freq = 3,
+ .io_fraction = 33,
+ },
+ {
+ .gpu_freq = 200000000,
+ .bus_freq = 2,
+ .io_fraction = 100,
+ },
+ {
+ .gpu_freq = 128000000,
+ .bus_freq = 1,
+ .io_fraction = 100,
+ },
+ {
+ .gpu_freq = 27000000,
+ .bus_freq = 0,
+ },
},
- .clk = {
- .name = {
- .clk = "core_clk",
- .pclk = "iface_clk",
- },
+ .init_level = 0,
+ .num_levels = 5,
+ .set_grp_async = NULL,
+ .idle_timeout = HZ/5,
+ .nap_allowed = true,
+ .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
- .bus_scale_table = &grp3d_bus_scale_pdata,
+ .bus_scale_table = &grp3d_bus_scale_pdata,
#endif
- },
- .imem_clk_name = {
- .clk = NULL,
- .pclk = "mem_iface_clk",
- },
.iommu_user_ctx_name = "gfx3d_user",
.iommu_priv_ctx_name = NULL,
};
@@ -2128,33 +2117,25 @@
};
static struct kgsl_device_platform_data kgsl_2d0_pdata = {
- .pwr_data = {
- .pwrlevel = {
- {
- .gpu_freq = 200000000,
- .bus_freq = 1,
- },
- {
- .gpu_freq = 200000000,
- .bus_freq = 0,
- },
+ .pwrlevel = {
+ {
+ .gpu_freq = 200000000,
+ .bus_freq = 1,
},
- .init_level = 0,
- .num_levels = 2,
- .set_grp_async = NULL,
- .idle_timeout = HZ/10,
- .nap_allowed = true,
+ {
+ .gpu_freq = 200000000,
+ .bus_freq = 0,
+ },
},
- .clk = {
- .name = {
- /* note: 2d clocks disabled on v1 */
- .clk = "core_clk",
- .pclk = "iface_clk",
- },
+ .init_level = 0,
+ .num_levels = 2,
+ .set_grp_async = NULL,
+ .idle_timeout = HZ/10,
+ .nap_allowed = true,
+ .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
- .bus_scale_table = &grp2d0_bus_scale_pdata,
+ .bus_scale_table = &grp2d0_bus_scale_pdata,
#endif
- },
.iommu_user_ctx_name = "gfx2d0_2d0",
.iommu_priv_ctx_name = NULL,
};
@@ -2185,32 +2166,25 @@
};
static struct kgsl_device_platform_data kgsl_2d1_pdata = {
- .pwr_data = {
- .pwrlevel = {
- {
- .gpu_freq = 200000000,
- .bus_freq = 1,
- },
- {
- .gpu_freq = 200000000,
- .bus_freq = 0,
- },
+ .pwrlevel = {
+ {
+ .gpu_freq = 200000000,
+ .bus_freq = 1,
},
- .init_level = 0,
- .num_levels = 2,
- .set_grp_async = NULL,
- .idle_timeout = HZ/10,
- .nap_allowed = true,
+ {
+ .gpu_freq = 200000000,
+ .bus_freq = 0,
+ },
},
- .clk = {
- .name = {
- .clk = "core_clk",
- .pclk = "iface_clk",
- },
+ .init_level = 0,
+ .num_levels = 2,
+ .set_grp_async = NULL,
+ .idle_timeout = HZ/10,
+ .nap_allowed = true,
+ .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
- .bus_scale_table = &grp2d1_bus_scale_pdata,
+ .bus_scale_table = &grp2d1_bus_scale_pdata,
#endif
- },
.iommu_user_ctx_name = "gfx2d1_2d1",
.iommu_priv_ctx_name = NULL,
};
diff --git a/arch/arm/mach-msm/devices-msm7x25.c b/arch/arm/mach-msm/devices-msm7x25.c
index d918315..71b57b4 100644
--- a/arch/arm/mach-msm/devices-msm7x25.c
+++ b/arch/arm/mach-msm/devices-msm7x25.c
@@ -31,7 +31,6 @@
#include <asm/mach/mmc.h>
#include <mach/msm_hsusb.h>
#include <mach/usbdiag.h>
-#include <mach/usb_gadget_fserial.h>
#include <mach/rpc_hsusb.h>
#include "clock-pcom.h"
diff --git a/arch/arm/mach-msm/devices-msm7x27.c b/arch/arm/mach-msm/devices-msm7x27.c
index 1bb9a21..0fb64dc 100644
--- a/arch/arm/mach-msm/devices-msm7x27.c
+++ b/arch/arm/mach-msm/devices-msm7x27.c
@@ -807,32 +807,22 @@
};
static struct kgsl_device_platform_data kgsl_3d0_pdata = {
- .pwr_data = {
- /* bus_freq has been set to 160000 for power savings.
- * OEMs may modify the value at their discretion for performance
- * The appropriate maximum replacement for 160000 is:
- * msm7x2x_clock_data.max_axi_khz
- */
- .pwrlevel = {
- {
- .gpu_freq = 0,
- .bus_freq = 160000000,
- },
- },
- .init_level = 0,
- .num_levels = 1,
- .set_grp_async = NULL,
- .idle_timeout = HZ/5,
- },
- .clk = {
- .name = {
- .clk = "core_clk",
- .pclk = "iface_clk",
+ /* bus_freq has been set to 160000 for power savings.
+ * OEMs may modify the value at their discretion for performance
+ * The appropriate maximum replacement for 160000 is:
+ * msm7x2x_clock_data.max_axi_khz
+ */
+ .pwrlevel = {
+ {
+ .gpu_freq = 0,
+ .bus_freq = 160000000,
},
},
- .imem_clk_name = {
- .clk = "mem_clk",
- },
+ .init_level = 0,
+ .num_levels = 1,
+ .set_grp_async = NULL,
+ .idle_timeout = HZ/5,
+ .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM,
};
struct platform_device msm_kgsl_3d0 = {
diff --git a/arch/arm/mach-msm/devices-msm7x27a.c b/arch/arm/mach-msm/devices-msm7x27a.c
index 7008bd5..2b08098 100644
--- a/arch/arm/mach-msm/devices-msm7x27a.c
+++ b/arch/arm/mach-msm/devices-msm7x27a.c
@@ -588,34 +588,22 @@
};
static struct kgsl_device_platform_data kgsl_3d0_pdata = {
- .pwr_data = {
- .pwrlevel = {
- {
- .gpu_freq = 245760000,
- .bus_freq = 200000000,
- },
- {
- .gpu_freq = 133330000,
- .bus_freq = 0,
- },
+ .pwrlevel = {
+ {
+ .gpu_freq = 245760000,
+ .bus_freq = 200000000,
},
- .init_level = 0,
- .num_levels = 2,
- .set_grp_async = set_grp_xbar_async,
- .idle_timeout = HZ/5,
- .nap_allowed = false,
- },
- .clk = {
- .name = {
- .clk = "core_clk",
- .pclk = "iface_clk",
+ {
+ .gpu_freq = 133330000,
+ .bus_freq = 0,
},
},
- .imem_clk_name = {
- .clk = "mem_clk",
- .pclk = NULL,
- },
-
+ .init_level = 0,
+ .num_levels = 2,
+ .set_grp_async = set_grp_xbar_async,
+ .idle_timeout = HZ/5,
+ .nap_allowed = false,
+ .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM,
};
struct platform_device msm_kgsl_3d0 = {
@@ -631,10 +619,10 @@
void __init msm7x25a_kgsl_3d0_init(void)
{
if (cpu_is_msm7x25a() || cpu_is_msm7x25aa()) {
- kgsl_3d0_pdata.pwr_data.pwrlevel[0].gpu_freq = 133330000;
- kgsl_3d0_pdata.pwr_data.pwrlevel[0].bus_freq = 160000000;
- kgsl_3d0_pdata.pwr_data.pwrlevel[1].gpu_freq = 96000000;
- kgsl_3d0_pdata.pwr_data.pwrlevel[1].bus_freq = 0;
+ kgsl_3d0_pdata.pwrlevel[0].gpu_freq = 133330000;
+ kgsl_3d0_pdata.pwrlevel[0].bus_freq = 160000000;
+ kgsl_3d0_pdata.pwrlevel[1].gpu_freq = 96000000;
+ kgsl_3d0_pdata.pwrlevel[1].bus_freq = 0;
}
}
diff --git a/arch/arm/mach-msm/devices-msm7x30.c b/arch/arm/mach-msm/devices-msm7x30.c
index 017eed9..41136d2 100644
--- a/arch/arm/mach-msm/devices-msm7x30.c
+++ b/arch/arm/mach-msm/devices-msm7x30.c
@@ -1102,37 +1102,27 @@
};
static struct kgsl_device_platform_data kgsl_3d0_pdata = {
- .pwr_data = {
- .pwrlevel = {
- {
- .gpu_freq = 245760000,
- .bus_freq = 192000000,
- },
- {
- .gpu_freq = 192000000,
- .bus_freq = 152000000,
- },
- {
- .gpu_freq = 192000000,
- .bus_freq = 0,
- },
+ .pwrlevel = {
+ {
+ .gpu_freq = 245760000,
+ .bus_freq = 192000000,
},
- .init_level = 0,
- .num_levels = 3,
- .set_grp_async = set_grp3d_async,
- .idle_timeout = HZ/20,
- .nap_allowed = true,
- },
- .clk = {
- .name = {
- .clk = "core_clk",
- .pclk = "iface_clk",
+ {
+ .gpu_freq = 192000000,
+ .bus_freq = 152000000,
+ },
+ {
+ .gpu_freq = 192000000,
+ .bus_freq = 0,
},
},
- .imem_clk_name = {
- .clk = "mem_clk",
- .pclk = NULL,
- },
+ .init_level = 0,
+ .num_levels = 3,
+ .set_grp_async = set_grp3d_async,
+ .idle_timeout = HZ/20,
+ .nap_allowed = true,
+ .clk_map = KGSL_CLK_SRC | KGSL_CLK_CORE |
+ KGSL_CLK_IFACE | KGSL_CLK_MEM,
};
struct platform_device msm_kgsl_3d0 = {
@@ -1161,26 +1151,19 @@
};
static struct kgsl_device_platform_data kgsl_2d0_pdata = {
- .pwr_data = {
- .pwrlevel = {
- {
- .gpu_freq = 0,
- .bus_freq = 192000000,
- },
- },
- .init_level = 0,
- .num_levels = 1,
- /* HW workaround, run Z180 SYNC @ 192 MHZ */
- .set_grp_async = NULL,
- .idle_timeout = HZ/10,
- .nap_allowed = true,
- },
- .clk = {
- .name = {
- .clk = "core_clk",
- .pclk = "iface_clk",
+ .pwrlevel = {
+ {
+ .gpu_freq = 0,
+ .bus_freq = 192000000,
},
},
+ .init_level = 0,
+ .num_levels = 1,
+ /* HW workaround, run Z180 SYNC @ 192 MHZ */
+ .set_grp_async = NULL,
+ .idle_timeout = HZ/10,
+ .nap_allowed = true,
+ .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE,
};
struct platform_device msm_kgsl_2d0 = {
diff --git a/arch/arm/mach-msm/devices-msm8x60.c b/arch/arm/mach-msm/devices-msm8x60.c
index 43c13bc..d89c9d7 100644
--- a/arch/arm/mach-msm/devices-msm8x60.c
+++ b/arch/arm/mach-msm/devices-msm8x60.c
@@ -653,52 +653,41 @@
};
static struct kgsl_device_platform_data kgsl_3d0_pdata = {
- .pwr_data = {
- .pwrlevel = {
- {
- .gpu_freq = 266667000,
- .bus_freq = 4,
- .io_fraction = 0,
- },
- {
- .gpu_freq = 228571000,
- .bus_freq = 3,
- .io_fraction = 33,
- },
- {
- .gpu_freq = 200000000,
- .bus_freq = 2,
- .io_fraction = 100,
- },
- {
- .gpu_freq = 177778000,
- .bus_freq = 1,
- .io_fraction = 100,
- },
- {
- .gpu_freq = 27000000,
- .bus_freq = 0,
- },
+ .pwrlevel = {
+ {
+ .gpu_freq = 266667000,
+ .bus_freq = 4,
+ .io_fraction = 0,
},
- .init_level = 0,
- .num_levels = 5,
- .set_grp_async = NULL,
- .idle_timeout = HZ/5,
- .nap_allowed = true,
+ {
+ .gpu_freq = 228571000,
+ .bus_freq = 3,
+ .io_fraction = 33,
+ },
+ {
+ .gpu_freq = 200000000,
+ .bus_freq = 2,
+ .io_fraction = 100,
+ },
+ {
+ .gpu_freq = 177778000,
+ .bus_freq = 1,
+ .io_fraction = 100,
+ },
+ {
+ .gpu_freq = 27000000,
+ .bus_freq = 0,
+ },
},
- .clk = {
- .name = {
- .clk = "core_clk",
- .pclk = "iface_clk",
- },
+ .init_level = 0,
+ .num_levels = 5,
+ .set_grp_async = NULL,
+ .idle_timeout = HZ/5,
+ .nap_allowed = true,
+ .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE | KGSL_CLK_MEM_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
- .bus_scale_table = &grp3d_bus_scale_pdata,
+ .bus_scale_table = &grp3d_bus_scale_pdata,
#endif
- },
- .imem_clk_name = {
- .clk = NULL,
- .pclk = "mem_iface_clk",
- },
};
struct platform_device msm_kgsl_3d0 = {
@@ -727,33 +716,25 @@
};
static struct kgsl_device_platform_data kgsl_2d0_pdata = {
- .pwr_data = {
- .pwrlevel = {
- {
- .gpu_freq = 200000000,
- .bus_freq = 1,
- },
- {
- .gpu_freq = 200000000,
- .bus_freq = 0,
- },
+ .pwrlevel = {
+ {
+ .gpu_freq = 200000000,
+ .bus_freq = 1,
},
- .init_level = 0,
- .num_levels = 2,
- .set_grp_async = NULL,
- .idle_timeout = HZ/10,
- .nap_allowed = true,
+ {
+ .gpu_freq = 200000000,
+ .bus_freq = 0,
+ },
},
- .clk = {
- .name = {
- /* note: 2d clocks disabled on v1 */
- .clk = "core_clk",
- .pclk = "iface_clk",
- },
+ .init_level = 0,
+ .num_levels = 2,
+ .set_grp_async = NULL,
+ .idle_timeout = HZ/10,
+ .nap_allowed = true,
+ .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
- .bus_scale_table = &grp2d0_bus_scale_pdata,
+ .bus_scale_table = &grp2d0_bus_scale_pdata,
#endif
- },
};
struct platform_device msm_kgsl_2d0 = {
@@ -782,32 +763,25 @@
};
static struct kgsl_device_platform_data kgsl_2d1_pdata = {
- .pwr_data = {
- .pwrlevel = {
- {
- .gpu_freq = 200000000,
- .bus_freq = 1,
- },
- {
- .gpu_freq = 200000000,
- .bus_freq = 0,
- },
+ .pwrlevel = {
+ {
+ .gpu_freq = 200000000,
+ .bus_freq = 1,
},
- .init_level = 0,
- .num_levels = 2,
- .set_grp_async = NULL,
- .idle_timeout = HZ/10,
- .nap_allowed = true,
+ {
+ .gpu_freq = 200000000,
+ .bus_freq = 0,
+ },
},
- .clk = {
- .name = {
- .clk = "gfx2d1_clk",
- .pclk = "gfx2d1_pclk",
- },
+ .init_level = 0,
+ .num_levels = 2,
+ .set_grp_async = NULL,
+ .idle_timeout = HZ/10,
+ .nap_allowed = true,
+ .clk_map = KGSL_CLK_CORE | KGSL_CLK_IFACE,
#ifdef CONFIG_MSM_BUS_SCALING
- .bus_scale_table = &grp2d1_bus_scale_pdata,
+ .bus_scale_table = &grp2d1_bus_scale_pdata,
#endif
- },
};
struct platform_device msm_kgsl_2d1 = {
@@ -831,8 +805,7 @@
if ((SOCINFO_VERSION_MAJOR(socinfo_get_version()) == 1) &&
(SOCINFO_VERSION_MINOR(socinfo_get_version()) == 0)) {
printk(KERN_WARNING "kgsl: 2D cores disabled on 8660v1\n");
- kgsl_2d0_pdata.clk.name.clk = NULL;
- kgsl_2d1_pdata.clk.name.clk = NULL;
+ kgsl_2d0_pdata.clk_map = 0;
}
}
diff --git a/arch/arm/mach-msm/devices-qsd8x50.c b/arch/arm/mach-msm/devices-qsd8x50.c
index 2367719..05efcdf 100644
--- a/arch/arm/mach-msm/devices-qsd8x50.c
+++ b/arch/arm/mach-msm/devices-qsd8x50.c
@@ -32,7 +32,6 @@
#include <asm/mach/mmc.h>
#include <mach/msm_hsusb.h>
#include <mach/usbdiag.h>
-#include <mach/usb_gadget_fserial.h>
#include <mach/rpc_hsusb.h>
static struct resource resources_uart1[] = {
@@ -911,26 +910,17 @@
};
static struct kgsl_device_platform_data kgsl_3d0_pdata = {
- .pwr_data = {
- .pwrlevel = {
- {
- .gpu_freq = 0,
- .bus_freq = 128000000,
- },
- },
- .init_level = 0,
- .num_levels = 1,
- .set_grp_async = NULL,
- .idle_timeout = HZ/5,
- },
- .clk = {
- .name = {
- .clk = "core_clk",
+ .pwrlevel = {
+ {
+ .gpu_freq = 0,
+ .bus_freq = 128000000,
},
},
- .imem_clk_name = {
- .clk = "mem_clk",
- },
+ .init_level = 0,
+ .num_levels = 1,
+ .set_grp_async = NULL,
+ .idle_timeout = HZ/5,
+ .clk_map = KGSL_CLK_CORE | KGSL_CLK_MEM,
};
struct platform_device msm_kgsl_3d0 = {
diff --git a/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h b/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h
index 3965a75..562a305 100644
--- a/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h
+++ b/arch/arm/mach-msm/include/mach/msm_hdmi_audio.h
@@ -13,7 +13,19 @@
#ifndef __MSM_HDMI_AUDIO_H
#define __MSM_HDMI_AUDIO_H
+enum hdmi_supported_sample_rates {
+ HDMI_SAMPLE_RATE_32KHZ,
+ HDMI_SAMPLE_RATE_44_1KHZ,
+ HDMI_SAMPLE_RATE_48KHZ,
+ HDMI_SAMPLE_RATE_88_2KHZ,
+ HDMI_SAMPLE_RATE_96KHZ,
+ HDMI_SAMPLE_RATE_176_4KHZ,
+ HDMI_SAMPLE_RATE_192KHZ
+};
+
int hdmi_audio_enable(bool on , u32 fifo_water_mark);
int hdmi_audio_packet_enable(bool on);
+void hdmi_msm_audio_sample_rate_reset(int rate);
+int hdmi_msm_audio_get_sample_rate(void);
#endif /* __MSM_HDMI_AUDIO_H*/
diff --git a/arch/arm/mach-msm/include/mach/usb_bridge.h b/arch/arm/mach-msm/include/mach/usb_bridge.h
new file mode 100644
index 0000000..2b7e754
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/usb_bridge.h
@@ -0,0 +1,122 @@
+/* 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 __LINUX_USB_BRIDGE_H__
+#define __LINUX_USB_BRIDGE_H__
+
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+/* bridge device 0: DUN
+ * bridge device 1 : Tethered RMNET
+ */
+#define MAX_BRIDGE_DEVICES 2
+
+/*PID 9001*/
+#define DUN_IFACE_NUM 2
+#define TETHERED_RMNET_IFACE_NUM 3
+
+struct bridge_ops {
+ int (*send_pkt)(void *, void *, size_t actual);
+ void (*send_cbits)(void *, unsigned int);
+
+ /* flow control */
+ void (*unthrottle_tx)(void *);
+};
+
+#define TX_THROTTLED BIT(0)
+#define RX_THROTTLED BIT(1)
+
+struct bridge {
+ /* context of the gadget port using bridge driver */
+ void *ctx;
+
+ /* bridge device array index mapped to the gadget port array index.
+ * data bridge[ch_id] <-- bridge --> gadget port[ch_id]
+ */
+ unsigned int ch_id;
+
+ /* flow control bits */
+ unsigned long flags;
+
+ /* data/ctrl bridge callbacks */
+ struct bridge_ops ops;
+};
+
+#if defined(CONFIG_USB_QCOM_MDM_BRIDGE) || \
+ defined(CONFIG_USB_QCOM_MDM_BRIDGE_MODULE)
+
+/* Bridge APIs called by gadget driver */
+int ctrl_bridge_open(struct bridge *);
+void ctrl_bridge_close(unsigned int);
+int ctrl_bridge_write(unsigned int, char *, size_t);
+int ctrl_bridge_set_cbits(unsigned int, unsigned int);
+unsigned int ctrl_bridge_get_cbits_tohost(unsigned int);
+int data_bridge_open(struct bridge *brdg);
+void data_bridge_close(unsigned int);
+int data_bridge_write(unsigned int , struct sk_buff *);
+int data_bridge_unthrottle_rx(unsigned int);
+
+/* defined in control bridge */
+int ctrl_bridge_probe(struct usb_interface *, struct usb_host_endpoint *, int);
+void ctrl_bridge_disconnect(unsigned int);
+int ctrl_bridge_resume(unsigned int);
+int ctrl_bridge_suspend(unsigned int);
+
+#else
+
+static inline int __maybe_unused ctrl_bridge_open(struct bridge *brdg)
+{
+ return -ENODEV;
+}
+
+static inline void __maybe_unused ctrl_bridge_close(unsigned int id) { }
+
+static inline int __maybe_unused ctrl_bridge_write(unsigned int id,
+ char *data, size_t size)
+{
+ return -ENODEV;
+}
+
+static inline int __maybe_unused ctrl_bridge_set_cbits(unsigned int id,
+ unsigned int cbits)
+{
+ return -ENODEV;
+}
+
+static inline unsigned int __maybe_unused
+ctrl_bridge_get_cbits_tohost(unsigned int id)
+{
+ return -ENODEV;
+}
+
+static inline int __maybe_unused data_bridge_open(struct bridge *brdg)
+{
+ return -ENODEV;
+}
+
+static inline void __maybe_unused data_bridge_close(unsigned int id) { }
+
+static inline int __maybe_unused data_bridge_write(unsigned int id,
+ struct sk_buff *skb)
+{
+ return -ENODEV;
+}
+
+static inline int __maybe_unused data_bridge_unthrottle_rx(unsigned int id)
+{
+ return -ENODEV;
+}
+
+#endif
+#endif
diff --git a/arch/arm/mach-msm/include/mach/usb_dun_bridge.h b/arch/arm/mach-msm/include/mach/usb_dun_bridge.h
deleted file mode 100644
index b4a8eef..0000000
--- a/arch/arm/mach-msm/include/mach/usb_dun_bridge.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/* 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_DUN_BRIDGE_H
-#define __USB_DUN_BRIDGE_H
-
-/**
- * struct dun_bridge_ops - context and callbacks for DUN bridge
- *
- * @ctxt: caller private context
- * @read_complete: called when read is completed. buf and len correspond
- * to original passed-in values. actual length of buffer returned, or
- * negative error value.
- * @write_complete: called when write is completed. buf and len correspond
- * to original passed-in values. actual length of buffer returned, or
- * negative error value.
- * @ctrl_status: asynchronous notification of control status. ctrl_bits
- * is a bitfield of CDC ACM control status bits.
- */
-struct dun_bridge_ops {
- void *ctxt;
- void (*read_complete)(void *ctxt, char *buf, size_t len, size_t actual);
- void (*write_complete)(void *ctxt, char *buf,
- size_t len, size_t actual);
- void (*ctrl_status)(void *ctxt, unsigned int ctrl_bits);
-};
-
-#ifdef CONFIG_USB_QCOM_DUN_BRIDGE
-
-/**
- * dun_bridge_open - Open the DUN bridge
- *
- * @ops: pointer to ops struct containing private context and callback
- * pointers
- */
-int dun_bridge_open(struct dun_bridge_ops *ops);
-
-/**
- * dun_bridge_close - Closes the DUN bridge
- */
-int dun_bridge_close(void);
-
-/**
- * dun_bridge_read - Request to read data from the DUN bridge. This call is
- * asynchronous: user's read callback (ops->read_complete) will be called
- * when data is returned.
- *
- * @data: pointer to caller-allocated buffer to fill in
- * @len: size of the buffer
- */
-int dun_bridge_read(void *data, int len);
-
-/**
- * dun_bridge_write - Request to write data to the DUN bridge. This call is
- * asynchronous: user's write callback (ops->write_complete) will be called
- * upon completion of the write indicating status and number of bytes
- * written.
- *
- * @data: pointer to caller-allocated buffer to write
- * @len: length of the data in buffer
- */
-int dun_bridge_write(void *data, int len);
-
-/**
- * dun_bridge_send_ctrl_bits - Request to write line control data to the DUN
- * bridge. This call is asynchronous, however no callback will be issued
- * upon completion.
- *
- * @ctrl_bits: CDC ACM line control bits
- */
-int dun_bridge_send_ctrl_bits(unsigned ctrl_bits);
-
-#else
-
-#include <linux/errno.h>
-
-static int __maybe_unused dun_bridge_open(struct dun_bridge_ops *ops)
-{
- return -ENODEV;
-}
-
-static int __maybe_unused dun_bridge_close(void)
-{
- return -ENODEV;
-}
-
-static int __maybe_unused dun_bridge_read(void *data, int len)
-{
- return -ENODEV;
-}
-
-static int __maybe_unused dun_bridge_write(void *data, int len)
-{
- return -ENODEV;
-}
-
-static int __maybe_unused dun_bridge_send_ctrl_bits(unsigned ctrl_bits)
-{
- return -ENODEV;
-}
-
-#endif
-
-#endif
diff --git a/arch/arm/mach-msm/include/mach/usb_gadget_fserial.h b/arch/arm/mach-msm/include/mach/usb_gadget_fserial.h
deleted file mode 100644
index 4112885..0000000
--- a/arch/arm/mach-msm/include/mach/usb_gadget_fserial.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/* 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 __LINUX_USB_GADGET_FSERIAL_H__
-#define __LINUX_USB_GADGET_FSERIAL_H__
-
-#include <linux/platform_device.h>
-
-enum transport_type {
- USB_GADGET_FSERIAL_TRANSPORT_TTY,
- USB_GADGET_FSERIAL_TRANSPORT_SDIO,
- USB_GADGET_FSERIAL_TRANSPORT_SMD,
-};
-
-#define GSERIAL_NO_PORTS 2
-#endif
diff --git a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
new file mode 100644
index 0000000..54566dc
--- /dev/null
+++ b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
@@ -0,0 +1,84 @@
+/* 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 __LINUX_USB_GADGET_XPORT_H__
+#define __LINUX_USB_GADGET_XPORT_H__
+
+enum transport_type {
+ USB_GADGET_XPORT_UNDEF,
+ USB_GADGET_XPORT_TTY,
+ USB_GADGET_XPORT_SDIO,
+ USB_GADGET_XPORT_SMD,
+ USB_GADGET_XPORT_BAM,
+ USB_GADGET_XPORT_HSIC,
+ USB_GADGET_XPORT_NONE,
+};
+
+#define XPORT_STR_LEN 10
+
+static char *xport_to_str(enum transport_type t)
+{
+ switch (t) {
+ case USB_GADGET_XPORT_TTY:
+ return "TTY";
+ case USB_GADGET_XPORT_SDIO:
+ return "SDIO";
+ case USB_GADGET_XPORT_SMD:
+ return "SMD";
+ case USB_GADGET_XPORT_BAM:
+ return "BAM";
+ case USB_GADGET_XPORT_HSIC:
+ return "HSIC";
+ case USB_GADGET_XPORT_NONE:
+ return "NONE";
+ default:
+ return "UNDEFINED";
+ }
+}
+
+static enum transport_type str_to_xport(const char *name)
+{
+ if (!strncasecmp("TTY", name, XPORT_STR_LEN))
+ return USB_GADGET_XPORT_TTY;
+ if (!strncasecmp("SDIO", name, XPORT_STR_LEN))
+ return USB_GADGET_XPORT_SDIO;
+ if (!strncasecmp("SMD", name, XPORT_STR_LEN))
+ return USB_GADGET_XPORT_SMD;
+ if (!strncasecmp("BAM", name, XPORT_STR_LEN))
+ return USB_GADGET_XPORT_BAM;
+ if (!strncasecmp("HSIC", name, XPORT_STR_LEN))
+ return USB_GADGET_XPORT_HSIC;
+ if (!strncasecmp("", name, XPORT_STR_LEN))
+ return USB_GADGET_XPORT_NONE;
+
+ return USB_GADGET_XPORT_UNDEF;
+}
+
+enum gadget_type {
+ USB_GADGET_SERIAL,
+ USB_GADGET_RMNET,
+};
+
+#define NUM_RMNET_HSIC_PORTS 1
+#define NUM_DUN_HSIC_PORTS 1
+#define NUM_PORTS (NUM_RMNET_HSIC_PORTS \
+ + NUM_DUN_HSIC_PORTS)
+
+int ghsic_ctrl_connect(void *, int);
+void ghsic_ctrl_disconnect(void *, int);
+int ghsic_ctrl_setup(unsigned int, enum gadget_type);
+int ghsic_data_connect(void *, int);
+void ghsic_data_disconnect(void *, int);
+int ghsic_data_setup(unsigned int, enum gadget_type);
+
+#endif
diff --git a/arch/arm/mach-msm/qdsp6v2/lpa_if_hdmi.c b/arch/arm/mach-msm/qdsp6v2/lpa_if_hdmi.c
index f978554..64344ef 100644
--- a/arch/arm/mach-msm/qdsp6v2/lpa_if_hdmi.c
+++ b/arch/arm/mach-msm/qdsp6v2/lpa_if_hdmi.c
@@ -184,13 +184,18 @@
rc = -EFAULT;
}
break;
- case AUDIO_SET_CONFIG:
+ case AUDIO_SET_CONFIG: {
+ /* Setting default rate as 48khz */
+ unsigned int cur_sample_rate =
+ HDMI_SAMPLE_RATE_48KHZ;
+ struct msm_audio_config config;
+
pr_debug("AUDIO_SET_CONFIG\n");
- if (copy_from_user(&lpa_if->dma_period_sz, (void *) arg,
- sizeof(unsigned int))) {
- pr_debug("%s:failed to copy from user\n", __func__);
+ if (copy_from_user(&config, (void *)arg, sizeof(config))) {
rc = -EFAULT;
+ break;
}
+ lpa_if->dma_period_sz = config.buffer_size;
if ((lpa_if->dma_period_sz * lpa_if->num_periods) >
DMA_ALLOC_BUF_SZ) {
pr_err("Dma buffer size greater than allocated size\n");
@@ -213,7 +218,40 @@
lpa_if->audio_buf[i].size = lpa_if->dma_period_sz;
lpa_if->audio_buf[i].used = 0;
}
+
+ pr_debug("Sample rate %d\n", config.sample_rate);
+ switch (config.sample_rate) {
+ case 48000:
+ cur_sample_rate = HDMI_SAMPLE_RATE_48KHZ;
+ break;
+ case 44100:
+ cur_sample_rate = HDMI_SAMPLE_RATE_44_1KHZ;
+ break;
+ case 32000:
+ cur_sample_rate = HDMI_SAMPLE_RATE_32KHZ;
+ break;
+ case 88200:
+ cur_sample_rate = HDMI_SAMPLE_RATE_88_2KHZ;
+ break;
+ case 96000:
+ cur_sample_rate = HDMI_SAMPLE_RATE_96KHZ;
+ break;
+ case 176400:
+ cur_sample_rate = HDMI_SAMPLE_RATE_176_4KHZ;
+ break;
+ case 192000:
+ cur_sample_rate = HDMI_SAMPLE_RATE_192KHZ;
+ break;
+ default:
+ cur_sample_rate = HDMI_SAMPLE_RATE_48KHZ;
+ }
+ if (cur_sample_rate != hdmi_msm_audio_get_sample_rate())
+ hdmi_msm_audio_sample_rate_reset(cur_sample_rate);
+ else
+ pr_debug("Previous sample rate and current"
+ "sample rate are same\n");
break;
+ }
default:
pr_err("UnKnown Ioctl\n");
rc = -EINVAL;
diff --git a/arch/arm/mach-msm/qdss-ptm.c b/arch/arm/mach-msm/qdss-ptm.c
index 0d971d7..205e2b8 100644
--- a/arch/arm/mach-msm/qdss-ptm.c
+++ b/arch/arm/mach-msm/qdss-ptm.c
@@ -357,6 +357,7 @@
ptm_writel(ptm, cpu, ptm.cfg.extnd_ext_input_sel, ETMEXTINSELR);
ptm_writel(ptm, cpu, ptm.cfg.ts_event, ETMTSEVR);
ptm_writel(ptm, cpu, ptm.cfg.aux_control, ETMAUXCR);
+ ptm_writel(ptm, cpu, cpu+1, ETMTRACEIDR);
ptm_writel(ptm, cpu, ptm.cfg.vmid_comp_value, ETMVMIDCVR);
ptm_clear_prog(cpu);
@@ -640,6 +641,7 @@
ptm.state[i++] = ptm_readl(ptm, cpu, ETMEXTINSELR);
ptm.state[i++] = ptm_readl(ptm, cpu, ETMTSEVR);
ptm.state[i++] = ptm_readl(ptm, cpu, ETMAUXCR);
+ ptm.state[i++] = ptm_readl(ptm, cpu, ETMTRACEIDR);
ptm.state[i++] = ptm_readl(ptm, cpu, ETMVMIDCVR);
ptm.state[i++] = ptm_readl(ptm, cpu, CS_CLAIMSET);
ptm.state[i++] = ptm_readl(ptm, cpu, CS_CLAIMCLR);
@@ -696,6 +698,7 @@
ptm_writel(ptm, cpu, ptm.state[i++], ETMEXTINSELR);
ptm_writel(ptm, cpu, ptm.state[i++], ETMTSEVR);
ptm_writel(ptm, cpu, ptm.state[i++], ETMAUXCR);
+ ptm_writel(ptm, cpu, ptm.state[i++], ETMTRACEIDR);
ptm_writel(ptm, cpu, ptm.state[i++], ETMVMIDCVR);
ptm_writel(ptm, cpu, ptm.state[i++], CS_CLAIMSET);
ptm_writel(ptm, cpu, ptm.state[i++], CS_CLAIMCLR);
diff --git a/arch/arm/mach-msm/rpm-regulator.c b/arch/arm/mach-msm/rpm-regulator.c
index 5c2bb8e..c10281b 100644
--- a/arch/arm/mach-msm/rpm-regulator.c
+++ b/arch/arm/mach-msm/rpm-regulator.c
@@ -51,14 +51,15 @@
#define SET_PART(_vreg, _part, _val) \
_vreg->req[_vreg->part->_part.word].value \
= (_vreg->req[_vreg->part->_part.word].value \
- & ~vreg->part->_part.mask) \
- | (((_val) << vreg->part->_part.shift) & vreg->part->_part.mask)
+ & ~_vreg->part->_part.mask) \
+ | (((_val) << _vreg->part->_part.shift) \
+ & _vreg->part->_part.mask)
#define GET_PART(_vreg, _part) \
- ((_vreg->req[_vreg->part->_part.word].value & vreg->part->_part.mask) \
- >> vreg->part->_part.shift)
+ ((_vreg->req[_vreg->part->_part.word].value & _vreg->part->_part.mask) \
+ >> _vreg->part->_part.shift)
-#define USES_PART(_vreg, _part) (vreg->part->_part.mask)
+#define USES_PART(_vreg, _part) (_vreg->part->_part.mask)
#define vreg_err(vreg, fmt, ...) \
pr_err("%s: " fmt, vreg->rdesc.name, ##__VA_ARGS__)
diff --git a/arch/arm/mach-msm/subsystem_map.c b/arch/arm/mach-msm/subsystem_map.c
index db9ab30..b5ff244 100644
--- a/arch/arm/mach-msm/subsystem_map.c
+++ b/arch/arm/mach-msm/subsystem_map.c
@@ -440,6 +440,9 @@
int i, j, ret;
unsigned long temp_va;
+ if (IS_ERR_OR_NULL(buf))
+ goto out;
+
if (buf->vaddr)
node = find_buffer(buf->vaddr);
else
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index 8f24d81..9647646 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -235,11 +235,14 @@
t1 = __raw_readl(addr);
t2 = __raw_readl(addr);
t3 = __raw_readl(addr);
+ cpu_relax();
if ((t3-t2) <= 1)
return t3;
if ((t2-t1) <= 1)
return t2;
- if (++loop_count == 10) {
+ if (((t3-t2) == (t2-t1)) && (t3-t2) <= 8)
+ return t3;
+ if (++loop_count == 5) {
pr_err("msm_read_timer_count timer %s did not "
"stabilize: %u -> %u -> %u\n",
clock->clockevent.name, t1, t2, t3);
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index cac5c81..58c9b9a 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -43,7 +43,7 @@
unsigned char diag_debug_buf[1024];
static unsigned int buf_tbl_size = 8; /*Number of entries in table of buffers */
struct diag_master_table entry;
-
+smd_channel_t *ch_temp;
struct diag_send_desc_type send = { NULL, NULL, DIAG_STATE_START, 0 };
struct diag_hdlc_dest_type enc = { NULL, NULL, 0 };
@@ -1149,8 +1149,12 @@
if (event == SMD_EVENT_CLOSE) {
pr_info("diag: clean modem registration\n");
diag_clear_reg(MODEM_PROC);
- } else
- queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
+ driver->ch = 0;
+ return;
+ } else if (event == SMD_EVENT_OPEN) {
+ driver->ch = ch_temp;
+ }
+ queue_work(driver->diag_wq, &(driver->diag_read_smd_work));
}
#if defined(CONFIG_MSM_N_WAY_SMD)
@@ -1169,8 +1173,10 @@
{
int r = 0;
- if (pdev->id == SMD_APPS_MODEM)
+ if (pdev->id == SMD_APPS_MODEM) {
r = smd_open("DIAG", &driver->ch, driver, diag_smd_notify);
+ ch_temp = driver->ch;
+ }
#if defined(CONFIG_MSM_N_WAY_SMD)
if (pdev->id == SMD_APPS_QDSP)
r = smd_named_open_on_edge("DIAG", SMD_APPS_QDSP
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index 50420ba..48dc9c0 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -1170,6 +1170,8 @@
{
struct ion_flush_data data;
unsigned long start, end;
+ struct ion_handle *handle = NULL;
+ int ret;
if (copy_from_user(&data, (void __user *)arg,
sizeof(struct ion_flush_data)))
@@ -1184,8 +1186,24 @@
return -EINVAL;
}
- return ion_do_cache_op(client, data.handle, data.vaddr,
- data.offset, data.length, cmd);
+ if (!data.handle) {
+ handle = ion_import_fd(client, data.fd);
+ if (IS_ERR_OR_NULL(handle)) {
+ pr_info("%s: Could not import handle: %d\n",
+ __func__, (int)handle);
+ return -EINVAL;
+ }
+ }
+
+ ret = ion_do_cache_op(client,
+ data.handle ? data.handle : handle,
+ data.vaddr, data.offset, data.length,
+ cmd);
+
+ if (!data.handle)
+ ion_free(client, handle);
+
+ break;
}
case ION_IOC_GET_FLAGS:
diff --git a/drivers/gpu/msm/a2xx_reg.h b/drivers/gpu/msm/a2xx_reg.h
index d859d61..5ffdea1 100644
--- a/drivers/gpu/msm/a2xx_reg.h
+++ b/drivers/gpu/msm/a2xx_reg.h
@@ -413,6 +413,7 @@
#define REG_A225_RB_COLOR_INFO3 0x2005
#define REG_A225_PC_MULTI_PRIM_IB_RESET_INDX 0x2103
#define REG_A225_GRAS_UCP0X 0x2340
+#define REG_A225_GRAS_UCP5W 0x2357
#define REG_A225_GRAS_UCP_ENABLED 0x2360
#endif /* __A200_REG_H */
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index d485cd2..23ecfb3 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -93,7 +93,6 @@
.pwrctrl = {
.regulator_name = "fs_gfx3d",
.irq_name = KGSL_3D0_IRQ,
- .src_clk_name = "src_clk",
},
.mutex = __MUTEX_INITIALIZER(device_3d0.dev.mutex),
.state = KGSL_STATE_INIT,
@@ -1133,9 +1132,14 @@
mutex_lock(&device->mutex);
if (status > 0) {
+ /*completed before the wait finished */
status = 0;
goto done;
+ } else if (status < 0) {
+ /*an error occurred*/
+ goto done;
}
+ /*this wait timed out*/
}
}
if (!kgsl_check_timestamp(device, timestamp)) {
diff --git a/drivers/gpu/msm/adreno_a2xx.c b/drivers/gpu/msm/adreno_a2xx.c
index 5acf23d..7b27f61 100644
--- a/drivers/gpu/msm/adreno_a2xx.c
+++ b/drivers/gpu/msm/adreno_a2xx.c
@@ -408,7 +408,8 @@
REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL,
REG_A220_PC_VERTEX_REUSE_BLOCK_CNTL,
REG_RB_COPY_CONTROL, REG_RB_DEPTH_CLEAR,
- REG_A225_GRAS_UCP0X, REG_A225_GRAS_UCP_ENABLED
+ REG_A225_GRAS_UCP0X, REG_A225_GRAS_UCP5W,
+ REG_A225_GRAS_UCP_ENABLED, REG_A225_GRAS_UCP_ENABLED
};
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 7034fd8..dd7b1d6 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -27,6 +27,34 @@
#define UPDATE_BUSY_VAL 1000000
#define UPDATE_BUSY 50
+struct clk_pair {
+ const char *name;
+ uint map;
+};
+
+struct clk_pair clks[KGSL_MAX_CLKS] = {
+ {
+ .name = "src_clk",
+ .map = KGSL_CLK_SRC,
+ },
+ {
+ .name = "core_clk",
+ .map = KGSL_CLK_CORE,
+ },
+ {
+ .name = "iface_clk",
+ .map = KGSL_CLK_IFACE,
+ },
+ {
+ .name = "mem_clk",
+ .map = KGSL_CLK_MEM,
+ },
+ {
+ .name = "mem_iface_clk",
+ .map = KGSL_CLK_MEM_IFACE,
+ },
+};
+
void kgsl_pwrctrl_pwrlevel_change(struct kgsl_device *device,
unsigned int new_level)
{
@@ -434,49 +462,43 @@
struct platform_device *pdev =
container_of(device->parentdev, struct platform_device, dev);
struct kgsl_pwrctrl *pwr = &device->pwrctrl;
- struct kgsl_device_platform_data *pdata_dev = pdev->dev.platform_data;
- struct kgsl_device_pwr_data *pdata_pwr = &pdata_dev->pwr_data;
- const char *clk_names[KGSL_MAX_CLKS] = {pwr->src_clk_name,
- pdata_dev->clk.name.clk,
- pdata_dev->clk.name.pclk,
- pdata_dev->imem_clk_name.clk,
- pdata_dev->imem_clk_name.pclk};
+ struct kgsl_device_platform_data *pdata = pdev->dev.platform_data;
/*acquire clocks */
- for (i = 1; i < KGSL_MAX_CLKS; i++) {
- if (clk_names[i]) {
- clk = clk_get(&pdev->dev, clk_names[i]);
+ for (i = 0; i < KGSL_MAX_CLKS; i++) {
+ if (pdata->clk_map & clks[i].map) {
+ clk = clk_get(&pdev->dev, clks[i].name);
if (IS_ERR(clk))
goto clk_err;
pwr->grp_clks[i] = clk;
}
}
/* Make sure we have a source clk for freq setting */
- clk = clk_get(&pdev->dev, clk_names[0]);
- pwr->grp_clks[0] = (IS_ERR(clk)) ? pwr->grp_clks[1] : clk;
+ if (pwr->grp_clks[0] == NULL)
+ pwr->grp_clks[0] = pwr->grp_clks[1];
/* put the AXI bus into asynchronous mode with the graphics cores */
- if (pdata_pwr->set_grp_async != NULL)
- pdata_pwr->set_grp_async();
+ if (pdata->set_grp_async != NULL)
+ pdata->set_grp_async();
- if (pdata_pwr->num_levels > KGSL_MAX_PWRLEVELS) {
+ if (pdata->num_levels > KGSL_MAX_PWRLEVELS) {
KGSL_PWR_ERR(device, "invalid power level count: %d\n",
- pdata_pwr->num_levels);
+ pdata->num_levels);
result = -EINVAL;
goto done;
}
- pwr->num_pwrlevels = pdata_pwr->num_levels;
- pwr->active_pwrlevel = pdata_pwr->init_level;
- for (i = 0; i < pdata_pwr->num_levels; i++) {
+ pwr->num_pwrlevels = pdata->num_levels;
+ pwr->active_pwrlevel = pdata->init_level;
+ for (i = 0; i < pdata->num_levels; i++) {
pwr->pwrlevels[i].gpu_freq =
- (pdata_pwr->pwrlevel[i].gpu_freq > 0) ?
+ (pdata->pwrlevel[i].gpu_freq > 0) ?
clk_round_rate(pwr->grp_clks[0],
- pdata_pwr->pwrlevel[i].
+ pdata->pwrlevel[i].
gpu_freq) : 0;
pwr->pwrlevels[i].bus_freq =
- pdata_pwr->pwrlevel[i].bus_freq;
+ pdata->pwrlevel[i].bus_freq;
pwr->pwrlevels[i].io_fraction =
- pdata_pwr->pwrlevel[i].io_fraction;
+ pdata->pwrlevel[i].io_fraction;
}
/* Do not set_rate for targets in sync with AXI */
if (pwr->pwrlevels[0].gpu_freq > 0)
@@ -489,8 +511,8 @@
pwr->power_flags = 0;
- pwr->nap_allowed = pdata_pwr->nap_allowed;
- pwr->interval_timeout = pdata_pwr->idle_timeout;
+ pwr->nap_allowed = pdata->nap_allowed;
+ pwr->interval_timeout = pdata->idle_timeout;
pwr->ebi1_clk = clk_get(&pdev->dev, "bus_clk");
if (IS_ERR(pwr->ebi1_clk))
pwr->ebi1_clk = NULL;
@@ -498,15 +520,14 @@
clk_set_rate(pwr->ebi1_clk,
pwr->pwrlevels[pwr->active_pwrlevel].
bus_freq);
- if (pdata_dev->clk.bus_scale_table != NULL) {
- pwr->pcl =
- msm_bus_scale_register_client(pdata_dev->clk.
+ if (pdata->bus_scale_table != NULL) {
+ pwr->pcl = msm_bus_scale_register_client(pdata->
bus_scale_table);
if (!pwr->pcl) {
KGSL_PWR_ERR(device,
"msm_bus_scale_register_client failed: "
"id %d table %p", device->id,
- pdata_dev->clk.bus_scale_table);
+ pdata->bus_scale_table);
result = -EINVAL;
goto done;
}
@@ -529,7 +550,7 @@
clk_err:
result = PTR_ERR(clk);
KGSL_PWR_ERR(device, "clk_get(%s) failed: %d\n",
- clk_names[i], result);
+ clks[i].name, result);
done:
return result;
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 9bf3c0f..8b33fcd 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -52,7 +52,6 @@
unsigned int nap_allowed;
const char *regulator_name;
const char *irq_name;
- const char *src_clk_name;
s64 time;
struct kgsl_busy busy;
unsigned int restore_slumber;
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index f3cd0ad..92fd1d78 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -788,18 +788,9 @@
internally uses an array of LTC2499 and EPM ADCs in a differential
configuration to provide a flat set of channels that can be addressed.
-config SENSORS_PM8921_ADC
- tristate "Support for Qualcomm PM8921 ADC"
- depends on MFD_PM8921_CORE
- help
- This is the ADC arbiter driver for Qualcomm PM8921 Chip.
-
- The driver supports reading the HKADC, XOADC and support to set and receive
- temperature threshold notifications using the Battery temperature module.
-
config SENSORS_PM8XXX_ADC
tristate "Support for Qualcomm PM8XXX ADC"
- depends on MFD_PM8018_CORE
+ depends on MFD_PM8018_CORE || MFD_PM8921_CORE
help
This is the ADC arbiter driver for Qualcomm PM8XXX Chip.
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 10d0699..137f469 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -120,7 +120,6 @@
obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o
obj-$(CONFIG_SENSORS_WPCE775X) += wpce775x.o
obj-$(CONFIG_SENSORS_MSM_ADC) += msm_adc.o m_adcproc.o
-obj-$(CONFIG_SENSORS_PM8921_ADC) += pm8921-adc.o msmproc_adc.o
obj-$(CONFIG_SENSORS_PM8XXX_ADC) += pm8xxx-adc.o pm8xxx-adc-scale.o
# PMBus drivers
diff --git a/drivers/hwmon/msmproc_adc.c b/drivers/hwmon/msmproc_adc.c
deleted file mode 100644
index fa6949e..0000000
--- a/drivers/hwmon/msmproc_adc.c
+++ /dev/null
@@ -1,638 +0,0 @@
-/*
- * 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/module.h>
-#include <linux/mfd/pm8xxx/pm8921-adc.h>
-#define KELVINMIL_DEGMIL 273160
-
-static const struct pm8921_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}
-};
-
-static const struct pm8921_adc_map_pt adcmap_btm_threshold[] = {
- {-30, 1642},
- {-20, 1544},
- {-10, 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}
-};
-
-static const struct pm8921_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}
-};
-
-static const struct pm8921_adc_map_pt adcmap_ntcg_104ef_104fb[] = {
- {696483, -40960},
- {649148, -39936},
- {605368, -38912},
- {564809, -37888},
- {527215, -36864},
- {492322, -35840},
- {460007, -34816},
- {429982, -33792},
- {402099, -32768},
- {376192, -31744},
- {352075, -30720},
- {329714, -29696},
- {308876, -28672},
- {289480, -27648},
- {271417, -26624},
- {254574, -25600},
- {238903, -24576},
- {224276, -23552},
- {210631, -22528},
- {197896, -21504},
- {186007, -20480},
- {174899, -19456},
- {164521, -18432},
- {154818, -17408},
- {145744, -16384},
- {137265, -15360},
- {129307, -14336},
- {121866, -13312},
- {114896, -12288},
- {108365, -11264},
- {102252, -10240},
- {96499, -9216},
- {91111, -8192},
- {86055, -7168},
- {81308, -6144},
- {76857, -5120},
- {72660, -4096},
- {68722, -3072},
- {65020, -2048},
- {61538, -1024},
- {58261, 0},
- {55177, 1024},
- {52274, 2048},
- {49538, 3072},
- {46962, 4096},
- {44531, 5120},
- {42243, 6144},
- {40083, 7168},
- {38045, 8192},
- {36122, 9216},
- {34308, 10240},
- {32592, 11264},
- {30972, 12288},
- {29442, 13312},
- {27995, 14336},
- {26624, 15360},
- {25333, 16384},
- {24109, 17408},
- {22951, 18432},
- {21854, 19456},
- {20807, 20480},
- {19831, 21504},
- {18899, 22528},
- {18016, 23552},
- {17178, 24576},
- {16384, 25600},
- {15631, 26624},
- {14916, 27648},
- {14237, 28672},
- {13593, 29696},
- {12976, 30720},
- {12400, 31744},
- {11848, 32768},
- {11324, 33792},
- {10825, 34816},
- {10354, 35840},
- {9900, 36864},
- {9471, 37888},
- {9062, 38912},
- {8674, 39936},
- {8306, 40960},
- {7951, 41984},
- {7616, 43008},
- {7296, 44032},
- {6991, 45056},
- {6701, 46080},
- {6424, 47104},
- {6160, 48128},
- {5908, 49152},
- {5667, 50176},
- {5439, 51200},
- {5219, 52224},
- {5010, 53248},
- {4810, 54272},
- {4619, 55296},
- {4440, 56320},
- {4263, 57344},
- {4097, 58368},
- {3938, 59392},
- {3785, 60416},
- {3637, 61440},
- {3501, 62464},
- {3368, 63488},
- {3240, 64512},
- {3118, 65536},
- {2998, 66560},
- {2889, 67584},
- {2782, 68608},
- {2680, 69632},
- {2581, 70656},
- {2490, 71680},
- {2397, 72704},
- {2310, 73728},
- {2227, 74752},
- {2147, 75776},
- {2064, 76800},
- {1998, 77824},
- {1927, 78848},
- {1860, 79872},
- {1795, 80896},
- {1736, 81920},
- {1673, 82944},
- {1615, 83968},
- {1560, 84992},
- {1507, 86016},
- {1456, 87040},
- {1407, 88064},
- {1360, 89088},
- {1314, 90112},
- {1271, 91136},
- {1228, 92160},
- {1189, 93184},
- {1150, 94208},
- {1112, 95232},
- {1076, 96256},
- {1042, 97280},
- {1008, 98304},
- {976, 99328},
- {945, 100352},
- {915, 101376},
- {886, 102400},
- {859, 103424},
- {832, 104448},
- {807, 105472},
- {782, 106496},
- {756, 107520},
- {735, 108544},
- {712, 109568},
- {691, 110592},
- {670, 111616},
- {650, 112640},
- {631, 113664},
- {612, 114688},
- {594, 115712},
- {577, 116736},
- {560, 117760},
- {544, 118784},
- {528, 119808},
- {513, 120832},
- {498, 121856},
- {483, 122880},
- {470, 123904},
- {457, 124928},
- {444, 125952},
- {431, 126976},
- {419, 128000}
-};
-
-static int32_t pm8921_adc_map_linear(const struct pm8921_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].x < pts[1].x)
- descending = 0;
- }
-
- while (i < tablesize) {
- if ((descending == 1) && (pts[i].x < input)) {
- /* table entry is less than measured
- value and table is descending, stop */
- break;
- } else if ((descending == 0) &&
- (pts[i].x > input)) {
- /* table entry is greater than measured
- value and table is ascending, stop */
- break;
- } else {
- i++;
- }
- }
-
- if (i == 0)
- *output = pts[0].y;
- else if (i == tablesize)
- *output = pts[tablesize-1].y;
- else {
- /* result is between search_index and search_index-1 */
- /* interpolate linearly */
- *output = (((int32_t) ((pts[i].y - pts[i-1].y)*
- (input - pts[i-1].x))/
- (pts[i].x - pts[i-1].x))+
- pts[i-1].y);
- }
-
- return 0;
-}
-
-int32_t pm8921_adc_scale_default(int32_t adc_code,
- const struct pm8921_adc_properties *adc_properties,
- const struct pm8921_adc_chan_properties *chan_properties,
- struct pm8921_adc_chan_result *adc_chan_result)
-{
- bool negative_rawfromoffset = 0;
- int32_t rawfromoffset = 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;
-
- adc_chan_result->adc_code = adc_code;
- if (rawfromoffset < 0) {
- if (adc_properties->bipolar) {
- rawfromoffset = -rawfromoffset;
- negative_rawfromoffset = 1;
- } else {
- rawfromoffset = 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 *
- 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)
- adc_chan_result->measurement = -adc_chan_result->measurement;
-
- /* Note: adc_chan_result->measurement is in the unit of
- * 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;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pm8921_adc_scale_default);
-
-int32_t pm8921_adc_scale_batt_therm(int32_t adc_code,
- const struct pm8921_adc_properties *adc_properties,
- const struct pm8921_adc_chan_properties *chan_properties,
- struct pm8921_adc_chan_result *adc_chan_result)
-{
- /* convert mV ---> degC using the table */
- return pm8921_adc_map_linear(
- adcmap_batttherm,
- ARRAY_SIZE(adcmap_batttherm),
- adc_code,
- &adc_chan_result->physical);
-}
-EXPORT_SYMBOL_GPL(pm8921_adc_scale_batt_therm);
-
-int32_t pm8921_adc_scale_pa_therm(int32_t adc_code,
- const struct pm8921_adc_properties *adc_properties,
- const struct pm8921_adc_chan_properties *chan_properties,
- struct pm8921_adc_chan_result *adc_chan_result)
-{
- /* convert mV ---> degC using the table */
- return pm8921_adc_map_linear(
- adcmap_pa_therm,
- ARRAY_SIZE(adcmap_pa_therm),
- adc_code,
- &adc_chan_result->physical);
-}
-EXPORT_SYMBOL_GPL(pm8921_adc_scale_pa_therm);
-
-int32_t pm8921_adc_scale_pmic_therm(int32_t adc_code,
- const struct pm8921_adc_properties *adc_properties,
- const struct pm8921_adc_chan_properties *chan_properties,
- struct pm8921_adc_chan_result *adc_chan_result)
-{
- int32_t rawfromoffset;
-
- 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;
- /* 2mV/K */
- adc_chan_result->measurement = (int64_t)rawfromoffset*
- chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dx *
- chan_properties->offset_gain_denominator * 1000;
-
- do_div(adc_chan_result->measurement,
- chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].dy *
- 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;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pm8921_adc_scale_pmic_therm);
-
-/* Scales the ADC code to 0.001 degrees C using the map
- * table for the XO thermistor.
- */
-int32_t pm8921_adc_tdkntcg_therm(int32_t adc_code,
- const struct pm8921_adc_properties *adc_properties,
- const struct pm8921_adc_chan_properties *chan_properties,
- struct pm8921_adc_chan_result *adc_chan_result)
-{
- int32_t rt_r25;
- int32_t offset = chan_properties->adc_graph[ADC_CALIB_ABSOLUTE].offset;
-
- rt_r25 = adc_code - offset;
-
- pm8921_adc_map_linear(adcmap_ntcg_104ef_104fb,
- ARRAY_SIZE(adcmap_ntcg_104ef_104fb),
- rt_r25, &adc_chan_result->physical);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(pm8921_adc_tdkntcg_therm);
-
-int32_t pm8921_adc_batt_scaler(struct pm8921_adc_arb_btm_param *btm_param,
- const struct pm8921_adc_properties *adc_properties,
- const struct pm8921_adc_chan_properties *chan_properties)
-{
- int rc;
-
- rc = pm8921_adc_map_linear(
- adcmap_btm_threshold,
- ARRAY_SIZE(adcmap_btm_threshold),
- btm_param->low_thr_temp,
- &btm_param->low_thr_voltage);
- if (rc)
- return rc;
-
- btm_param->low_thr_voltage *=
- chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].dy;
- do_div(btm_param->low_thr_voltage, adc_properties->adc_vdd_reference);
- btm_param->low_thr_voltage +=
- chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].adc_gnd;
-
- rc = pm8921_adc_map_linear(
- adcmap_btm_threshold,
- ARRAY_SIZE(adcmap_btm_threshold),
- btm_param->high_thr_temp,
- &btm_param->high_thr_voltage);
- if (rc)
- return rc;
-
- btm_param->high_thr_voltage *=
- chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].dy;
- do_div(btm_param->high_thr_voltage, adc_properties->adc_vdd_reference);
- btm_param->high_thr_voltage +=
- chan_properties->adc_graph[ADC_CALIB_RATIOMETRIC].adc_gnd;
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(pm8921_adc_batt_scaler);
diff --git a/drivers/hwmon/pm8921-adc.c b/drivers/hwmon/pm8921-adc.c
deleted file mode 100644
index 38e18de..0000000
--- a/drivers/hwmon/pm8921-adc.c
+++ /dev/null
@@ -1,1359 +0,0 @@
-/*
- * 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.
- *
- * Qualcomm's PM8921 ADC Arbiter driver
- */
-#define pr_fmt(fmt) "%s: " fmt, __func__
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/mutex.h>
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/interrupt.h>
-#include <linux/completion.h>
-#include <linux/platform_device.h>
-#include <linux/mfd/pm8xxx/core.h>
-#include <linux/mfd/pm8xxx/mpp.h>
-#include <linux/debugfs.h>
-#include <linux/regulator/consumer.h>
-#include <linux/mfd/pm8xxx/pm8921-adc.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
-#include <linux/wakelock.h>
-
-/* User Bank register set */
-#define PM8921_ADC_ARB_USRP_CNTRL1 0x197
-#define PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB BIT(0)
-#define PM8921_ADC_ARB_USRP_CNTRL1_RSV1 BIT(1)
-#define PM8921_ADC_ARB_USRP_CNTRL1_RSV2 BIT(2)
-#define PM8921_ADC_ARB_USRP_CNTRL1_RSV3 BIT(3)
-#define PM8921_ADC_ARB_USRP_CNTRL1_RSV4 BIT(4)
-#define PM8921_ADC_ARB_USRP_CNTRL1_RSV5 BIT(5)
-#define PM8921_ADC_ARB_USRP_CNTRL1_EOC BIT(6)
-#define PM8921_ADC_ARB_USRP_CNTRL1_REQ BIT(7)
-
-#define PM8921_ADC_ARB_USRP_AMUX_CNTRL 0x198
-#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_RSV0 BIT(0)
-#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_RSV1 BIT(1)
-#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_PREMUX0 BIT(2)
-#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_PREMUX1 BIT(3)
-#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_SEL0 BIT(4)
-#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_SEL1 BIT(5)
-#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_SEL2 BIT(6)
-#define PM8921_ADC_ARB_USRP_AMUX_CNTRL_SEL3 BIT(7)
-
-#define PM8921_ADC_ARB_USRP_ANA_PARAM 0x199
-#define PM8921_ADC_ARB_USRP_DIG_PARAM 0x19A
-#define PM8921_ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT0 BIT(0)
-#define PM8921_ADC_ARB_USRP_DIG_PARAM_SEL_SHIFT1 BIT(1)
-#define PM8921_ADC_ARB_USRP_DIG_PARAM_CLK_RATE0 BIT(2)
-#define PM8921_ADC_ARB_USRP_DIG_PARAM_CLK_RATE1 BIT(3)
-#define PM8921_ADC_ARB_USRP_DIG_PARAM_EOC BIT(4)
-#define PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0 BIT(5)
-#define PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE1 BIT(6)
-#define PM8921_ADC_ARB_USRP_DIG_PARAM_EN BIT(7)
-
-#define PM8921_ADC_ARB_USRP_RSV 0x19B
-#define PM8921_ADC_ARB_USRP_RSV_RST BIT(0)
-#define PM8921_ADC_ARB_USRP_RSV_DTEST0 BIT(1)
-#define PM8921_ADC_ARB_USRP_RSV_DTEST1 BIT(2)
-#define PM8921_ADC_ARB_USRP_RSV_OP BIT(3)
-#define PM8921_ADC_ARB_USRP_RSV_IP_SEL0 BIT(4)
-#define PM8921_ADC_ARB_USRP_RSV_IP_SEL1 BIT(5)
-#define PM8921_ADC_ARB_USRP_RSV_IP_SEL2 BIT(6)
-#define PM8921_ADC_ARB_USRP_RSV_TRM BIT(7)
-
-#define PM8921_ADC_ARB_USRP_DATA0 0x19D
-#define PM8921_ADC_ARB_USRP_DATA1 0x19C
-
-#define PM8921_ADC_ARB_BTM_CNTRL1 0x17e
-#define PM8921_ADC_ARB_BTM_CNTRL1_EN_BTM BIT(0)
-#define PM8921_ADC_ARB_BTM_CNTRL1_SEL_OP_MODE BIT(1)
-#define PM8921_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL1 BIT(2)
-#define PM8921_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL2 BIT(3)
-#define PM8921_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL3 BIT(4)
-#define PM8921_ADC_ARB_BTM_CNTRL1_MEAS_INTERVAL4 BIT(5)
-#define PM8921_ADC_ARB_BTM_CNTRL1_EOC BIT(6)
-#define PM8921_ADC_ARB_BTM_CNTRL1_REQ BIT(7)
-
-#define PM8921_ADC_ARB_BTM_CNTRL2 0x18c
-#define PM8921_ADC_ARB_BTM_AMUX_CNTRL 0x17f
-#define PM8921_ADC_ARB_BTM_ANA_PARAM 0x180
-#define PM8921_ADC_ARB_BTM_DIG_PARAM 0x181
-#define PM8921_ADC_ARB_BTM_RSV 0x182
-#define PM8921_ADC_ARB_BTM_DATA1 0x183
-#define PM8921_ADC_ARB_BTM_DATA0 0x184
-#define PM8921_ADC_ARB_BTM_BAT_COOL_THR1 0x185
-#define PM8921_ADC_ARB_BTM_BAT_COOL_THR0 0x186
-#define PM8921_ADC_ARB_BTM_BAT_WARM_THR1 0x187
-#define PM8921_ADC_ARB_BTM_BAT_WARM_THR0 0x188
-
-#define PM8921_ADC_ARB_ANA_DIG 0xa0
-#define PM8921_ADC_BTM_RSV 0x10
-#define PM8921_ADC_AMUX_MPP_SEL 2
-#define PM8921_ADC_AMUX_SEL 4
-#define PM8921_ADC_RSV_IP_SEL 4
-#define PM8921_ADC_BTM_CHANNEL_SEL 4
-#define PM8921_MAX_CHANNEL_PROPERTIES 2
-#define PM8921_ADC_IRQ_0 0
-#define PM8921_ADC_IRQ_1 1
-#define PM8921_ADC_IRQ_2 2
-#define PM8921_ADC_BTM_INTERVAL_SEL_MASK 0xF
-#define PM8921_ADC_BTM_INTERVAL_SEL_SHIFT 2
-#define PM8921_ADC_BTM_DECIMATION_SEL 5
-#define PM8921_ADC_MUL 10
-#define PM8921_ADC_CONV_TIME_MIN 2000
-#define PM8921_ADC_CONV_TIME_MAX 2100
-#define PM8921_ADC_MPP_SETTLE_TIME_MIN 200
-#define PM8921_ADC_MPP_SETTLE_TIME_MAX 200
-#define PM8921_ADC_PA_THERM_VREG_UV_MIN 1800000
-#define PM8921_ADC_PA_THERM_VREG_UV_MAX 1800000
-#define PM8921_ADC_PA_THERM_VREG_UA_LOAD 100000
-
-struct pm8921_adc {
- struct device *dev;
- struct pm8921_adc_properties *adc_prop;
- int adc_irq;
- struct mutex adc_lock;
- struct mutex mpp_adc_lock;
- spinlock_t btm_lock;
- uint32_t adc_num_channel;
- uint32_t adc_num_board_channel;
- struct completion adc_rslt_completion;
- struct pm8921_adc_amux *adc_channel;
- struct pm8921_adc_amux_properties *conv;
- struct pm8921_adc_arb_btm_param *batt;
- int btm_warm_irq;
- int btm_cool_irq;
- struct dentry *dent;
- struct work_struct warm_work;
- struct work_struct cool_work;
- uint32_t mpp_base;
- struct sensor_device_attribute *sens_attr;
- struct device *hwmon;
- struct wake_lock adc_wakelock;
- int msm_suspend_check;
-};
-
-struct pm8921_adc_amux_properties {
- uint32_t amux_channel;
- uint32_t decimation;
- uint32_t amux_ip_rsv;
- uint32_t amux_mpp_channel;
- struct pm8921_adc_chan_properties *chan_prop;
-};
-
-static const struct pm8921_adc_scaling_ratio pm8921_amux_scaling_ratio[] = {
- {1, 1},
- {1, 3},
- {1, 4},
- {1, 6}
-};
-
-static struct pm8921_adc *pmic_adc;
-
-static struct pm8921_adc_scale_fn adc_scale_fn[] = {
- [ADC_SCALE_DEFAULT] = {pm8921_adc_scale_default},
- [ADC_SCALE_BATT_THERM] = {pm8921_adc_scale_batt_therm},
- [ADC_SCALE_PA_THERM] = {pm8921_adc_scale_pa_therm},
- [ADC_SCALE_PMIC_THERM] = {pm8921_adc_scale_pmic_therm},
- [ADC_SCALE_XOTHERM] = {pm8921_adc_tdkntcg_therm},
-};
-
-/* MPP 8 is mapped to AMUX8 and is common between remote processor's */
-
-static struct pm8xxx_mpp_config_data pm8921_adc_mpp_config = {
- .type = PM8XXX_MPP_TYPE_A_INPUT,
- /* AMUX6 is dedicated to be used for apps processor */
- .level = PM8XXX_MPP_AIN_AMUX_CH6,
- .control = PM8XXX_MPP_AOUT_CTRL_DISABLE,
-};
-
-/* MPP Configuration for default settings */
-static struct pm8xxx_mpp_config_data pm8921_adc_mpp_unconfig = {
- .type = PM8XXX_MPP_TYPE_SINK,
- .level = PM8XXX_MPP_AIN_AMUX_CH5,
- .control = PM8XXX_MPP_AOUT_CTRL_DISABLE,
-};
-
-static bool pm8921_adc_calib_first_adc;
-static bool pm8921_adc_initialized, pm8921_adc_calib_device_init;
-
-static int32_t pm8921_adc_arb_cntrl(uint32_t arb_cntrl, uint32_t channel)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- int i, rc;
- u8 data_arb_cntrl = 0;
-
- if (arb_cntrl) {
- if (adc_pmic->msm_suspend_check)
- pr_err("PM8921 ADC request being made after suspend "
- "irq with channel id:%d\n", channel);
- data_arb_cntrl |= PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB;
- }
-
- /* Write twice to the CNTRL register for the arbiter settings
- to take into effect */
- for (i = 0; i < 2; i++) {
- rc = pm8xxx_writeb(adc_pmic->dev->parent,
- PM8921_ADC_ARB_USRP_CNTRL1, data_arb_cntrl);
- if (rc < 0) {
- pr_err("PM8921 arb cntrl write failed with %d\n", rc);
- return rc;
- }
- }
-
- if (arb_cntrl) {
- data_arb_cntrl |= PM8921_ADC_ARB_USRP_CNTRL1_REQ;
- rc = pm8xxx_writeb(adc_pmic->dev->parent,
- PM8921_ADC_ARB_USRP_CNTRL1, data_arb_cntrl);
- if (rc < 0) {
- pr_err("PM8921 arb cntrl write failed with %d\n", rc);
- return rc;
- }
- wake_lock(&adc_pmic->adc_wakelock);
- } else {
- wake_unlock(&adc_pmic->adc_wakelock);
- }
-
- return 0;
-}
-
-static int32_t pm8921_adc_patherm_power(bool on)
-{
- static struct regulator *pa_therm;
- struct pm8921_adc *adc_pmic = pmic_adc;
- int rc = 0;
- if (on) {
- pa_therm = regulator_get(adc_pmic->dev,
- "pa_therm");
- if (IS_ERR(pa_therm)) {
- rc = PTR_ERR(pa_therm);
- pr_err("failed to request pa_therm vreg "
- "with error %d\n", rc);
- return rc;
- }
-
- rc = regulator_set_voltage(pa_therm,
- PM8921_ADC_PA_THERM_VREG_UV_MIN,
- PM8921_ADC_PA_THERM_VREG_UV_MAX);
- if (rc < 0) {
- pr_err("failed to set the voltage for "
- "pa_therm with error %d\n", rc);
- goto fail;
- }
-
- rc = regulator_set_optimum_mode(pa_therm,
- PM8921_ADC_PA_THERM_VREG_UA_LOAD);
- if (rc < 0) {
- pr_err("failed to set optimum mode for "
- "pa_therm with error %d\n", rc);
- goto fail;
- }
-
- if (regulator_enable(pa_therm)) {
- pr_err("failed to enable pa_therm vreg with "
- "error %d\n", rc);
- goto fail;
- }
- } else {
- if (pa_therm != NULL) {
- regulator_disable(pa_therm);
- regulator_put(pa_therm);
- }
- }
-
- return rc;
-fail:
- regulator_put(pa_therm);
- return rc;
-}
-
-static int32_t pm8921_adc_channel_power_enable(uint32_t channel,
- bool power_cntrl)
-{
- int rc = 0;
-
- switch (channel)
- case ADC_MPP_1_AMUX8:
- pm8921_adc_patherm_power(power_cntrl);
-
- return rc;
-}
-
-
-static uint32_t pm8921_adc_read_reg(uint32_t reg, u8 *data)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- int rc;
-
- rc = pm8xxx_readb(adc_pmic->dev->parent, reg, data);
- if (rc < 0) {
- pr_err("PM8921 adc read reg %d failed with %d\n", reg, rc);
- return rc;
- }
-
- return 0;
-}
-
-static uint32_t pm8921_adc_write_reg(uint32_t reg, u8 data)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- int rc;
-
- rc = pm8xxx_writeb(adc_pmic->dev->parent, reg, data);
- if (rc < 0) {
- pr_err("PM8921 adc write reg %d failed with %d\n", reg, rc);
- return rc;
- }
-
- return 0;
-}
-
-static int32_t pm8921_adc_configure(
- struct pm8921_adc_amux_properties *chan_prop)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- u8 data_amux_chan = 0, data_arb_rsv = 0, data_dig_param = 0;
- int rc;
-
- data_amux_chan |= chan_prop->amux_channel << PM8921_ADC_AMUX_SEL;
-
- if (chan_prop->amux_mpp_channel)
- data_amux_chan |= chan_prop->amux_mpp_channel <<
- PM8921_ADC_AMUX_MPP_SEL;
-
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_USRP_AMUX_CNTRL,
- data_amux_chan);
- if (rc < 0)
- return rc;
-
- data_arb_rsv &= (PM8921_ADC_ARB_USRP_RSV_RST |
- PM8921_ADC_ARB_USRP_RSV_DTEST0 |
- PM8921_ADC_ARB_USRP_RSV_DTEST1 |
- PM8921_ADC_ARB_USRP_RSV_OP |
- PM8921_ADC_ARB_USRP_RSV_TRM);
- data_arb_rsv |= chan_prop->amux_ip_rsv << PM8921_ADC_RSV_IP_SEL;
-
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_USRP_RSV, data_arb_rsv);
- if (rc < 0)
- return rc;
-
- rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_DIG_PARAM,
- &data_dig_param);
- if (rc < 0)
- return rc;
-
- /* Default 2.4Mhz clock rate */
- /* Client chooses the decimation */
- switch (chan_prop->decimation) {
- case ADC_DECIMATION_TYPE1:
- data_dig_param |= PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0;
- break;
- case ADC_DECIMATION_TYPE2:
- data_dig_param |= (PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0
- | PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE1);
- break;
- default:
- data_dig_param |= PM8921_ADC_ARB_USRP_DIG_PARAM_DEC_RATE0;
- break;
- }
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_USRP_DIG_PARAM,
- PM8921_ADC_ARB_ANA_DIG);
- if (rc < 0)
- return rc;
-
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_USRP_ANA_PARAM,
- PM8921_ADC_ARB_ANA_DIG);
- if (rc < 0)
- return rc;
-
- if (!pm8921_adc_calib_first_adc)
- enable_irq(adc_pmic->adc_irq);
-
- rc = pm8921_adc_arb_cntrl(1, chan_prop->amux_mpp_channel);
- if (rc < 0) {
- pr_err("Configuring ADC Arbiter"
- "enable failed with %d\n", rc);
- return rc;
- }
-
- return 0;
-}
-
-static uint32_t pm8921_adc_read_adc_code(int32_t *data)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- uint8_t rslt_lsb, rslt_msb;
- int32_t rc, max_ideal_adc_code = 1 << adc_pmic->adc_prop->bitresolution;
-
- rc = pm8xxx_readb(adc_pmic->dev->parent,
- PM8921_ADC_ARB_USRP_DATA0, &rslt_lsb);
- if (rc < 0) {
- pr_err("PM8921 adc result read failed with %d\n", rc);
- return rc;
- }
-
- rc = pm8xxx_readb(adc_pmic->dev->parent,
- PM8921_ADC_ARB_USRP_DATA1, &rslt_msb);
- if (rc < 0) {
- pr_err("PM8921 adc result read failed with %d\n", rc);
- return rc;
- }
-
- *data = (rslt_msb << 8) | rslt_lsb;
-
- /* Use the midpoint to determine underflow or overflow */
- if (*data > max_ideal_adc_code + (max_ideal_adc_code >> 1))
- *data |= ((1 << (8 * sizeof(*data) -
- adc_pmic->adc_prop->bitresolution)) - 1) <<
- adc_pmic->adc_prop->bitresolution;
-
- /* Default value for switching off the arbiter after reading
- the ADC value. Bit 0 set to 0. */
- rc = pm8921_adc_arb_cntrl(0, CHANNEL_MUXOFF);
- if (rc < 0) {
- pr_err("%s: Configuring ADC Arbiter disable"
- "failed\n", __func__);
- return rc;
- }
-
- return 0;
-}
-
-static void pm8921_adc_btm_warm_scheduler_fn(struct work_struct *work)
-{
- struct pm8921_adc *adc_pmic = container_of(work, struct pm8921_adc,
- warm_work);
- unsigned long flags = 0;
- bool warm_status;
-
- spin_lock_irqsave(&adc_pmic->btm_lock, flags);
- warm_status = irq_read_line(adc_pmic->btm_warm_irq);
- if (adc_pmic->batt->btm_warm_fn != NULL)
- adc_pmic->batt->btm_warm_fn(warm_status);
- spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
-}
-
-static void pm8921_adc_btm_cool_scheduler_fn(struct work_struct *work)
-{
- struct pm8921_adc *adc_pmic = container_of(work, struct pm8921_adc,
- cool_work);
- unsigned long flags = 0;
- bool cool_status;
-
- spin_lock_irqsave(&adc_pmic->btm_lock, flags);
- cool_status = irq_read_line(adc_pmic->btm_cool_irq);
- if (adc_pmic->batt->btm_cool_fn != NULL)
- adc_pmic->batt->btm_cool_fn(cool_status);
- spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
-}
-
-static irqreturn_t pm8921_adc_isr(int irq, void *dev_id)
-{
- struct pm8921_adc *adc_8921 = dev_id;
-
- disable_irq_nosync(adc_8921->adc_irq);
-
- if (pm8921_adc_calib_first_adc)
- return IRQ_HANDLED;
- /* TODO Handle spurius interrupt condition */
- complete(&adc_8921->adc_rslt_completion);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t pm8921_btm_warm_isr(int irq, void *dev_id)
-{
- struct pm8921_adc *btm_8921 = dev_id;
-
- schedule_work(&btm_8921->warm_work);
-
- return IRQ_HANDLED;
-}
-
-static irqreturn_t pm8921_btm_cool_isr(int irq, void *dev_id)
-{
- struct pm8921_adc *btm_8921 = dev_id;
-
- schedule_work(&btm_8921->cool_work);
-
- return IRQ_HANDLED;
-}
-
-static uint32_t pm8921_adc_calib_device(void)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- struct pm8921_adc_amux_properties conv;
- int rc, offset_adc, slope_adc, calib_read_1, calib_read_2;
- u8 data_arb_usrp_cntrl1 = 0;
-
- conv.amux_channel = CHANNEL_125V;
- conv.decimation = ADC_DECIMATION_TYPE2;
- conv.amux_ip_rsv = AMUX_RSV1;
- conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
- pm8921_adc_calib_first_adc = true;
- rc = pm8921_adc_configure(&conv);
- if (rc) {
- pr_err("pm8921_adc configure failed with %d\n", rc);
- goto calib_fail;
- }
-
- while (data_arb_usrp_cntrl1 != (PM8921_ADC_ARB_USRP_CNTRL1_EOC |
- PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
- rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_CNTRL1,
- &data_arb_usrp_cntrl1);
- if (rc < 0)
- return rc;
- usleep_range(PM8921_ADC_CONV_TIME_MIN,
- PM8921_ADC_CONV_TIME_MAX);
- }
- data_arb_usrp_cntrl1 = 0;
-
- rc = pm8921_adc_read_adc_code(&calib_read_1);
- if (rc) {
- pr_err("pm8921_adc read adc failed with %d\n", rc);
- pm8921_adc_calib_first_adc = false;
- goto calib_fail;
- }
- pm8921_adc_calib_first_adc = false;
-
- conv.amux_channel = CHANNEL_625MV;
- conv.decimation = ADC_DECIMATION_TYPE2;
- conv.amux_ip_rsv = AMUX_RSV1;
- conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
- pm8921_adc_calib_first_adc = true;
- rc = pm8921_adc_configure(&conv);
- if (rc) {
- pr_err("pm8921_adc configure failed with %d\n", rc);
- goto calib_fail;
- }
-
- while (data_arb_usrp_cntrl1 != (PM8921_ADC_ARB_USRP_CNTRL1_EOC |
- PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
- rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_CNTRL1,
- &data_arb_usrp_cntrl1);
- if (rc < 0)
- return rc;
- usleep_range(PM8921_ADC_CONV_TIME_MIN,
- PM8921_ADC_CONV_TIME_MAX);
- }
- data_arb_usrp_cntrl1 = 0;
-
- rc = pm8921_adc_read_adc_code(&calib_read_2);
- if (rc) {
- pr_err("pm8921_adc read adc failed with %d\n", rc);
- pm8921_adc_calib_first_adc = false;
- goto calib_fail;
- }
- pm8921_adc_calib_first_adc = false;
-
- slope_adc = (((calib_read_1 - calib_read_2) << PM8921_ADC_MUL)/
- PM8921_CHANNEL_ADC_625_MV);
- offset_adc = calib_read_2 -
- ((slope_adc * PM8921_CHANNEL_ADC_625_MV) >>
- PM8921_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
- = PM8921_CHANNEL_ADC_625_MV;
- rc = pm8921_adc_arb_cntrl(0, CHANNEL_MUXOFF);
- if (rc < 0) {
- pr_err("%s: Configuring ADC Arbiter disable"
- "failed\n", __func__);
- return rc;
- }
- /* Ratiometric Calibration */
- conv.amux_channel = CHANNEL_MUXOFF;
- conv.decimation = ADC_DECIMATION_TYPE2;
- conv.amux_ip_rsv = AMUX_RSV5;
- conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
- pm8921_adc_calib_first_adc = true;
- rc = pm8921_adc_configure(&conv);
- if (rc) {
- pr_err("pm8921_adc configure failed with %d\n", rc);
- goto calib_fail;
- }
-
- while (data_arb_usrp_cntrl1 != (PM8921_ADC_ARB_USRP_CNTRL1_EOC |
- PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
- rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_CNTRL1,
- &data_arb_usrp_cntrl1);
- if (rc < 0)
- return rc;
- usleep_range(PM8921_ADC_CONV_TIME_MIN,
- PM8921_ADC_CONV_TIME_MAX);
- }
- data_arb_usrp_cntrl1 = 0;
-
- rc = pm8921_adc_read_adc_code(&calib_read_1);
- if (rc) {
- pr_err("pm8921_adc read adc failed with %d\n", rc);
- pm8921_adc_calib_first_adc = false;
- goto calib_fail;
- }
- pm8921_adc_calib_first_adc = false;
-
- conv.amux_channel = CHANNEL_MUXOFF;
- conv.decimation = ADC_DECIMATION_TYPE2;
- conv.amux_ip_rsv = AMUX_RSV4;
- conv.amux_mpp_channel = PREMUX_MPP_SCALE_0;
- pm8921_adc_calib_first_adc = true;
- rc = pm8921_adc_configure(&conv);
- if (rc) {
- pr_err("pm8921_adc configure failed with %d\n", rc);
- goto calib_fail;
- }
-
- while (data_arb_usrp_cntrl1 != (PM8921_ADC_ARB_USRP_CNTRL1_EOC |
- PM8921_ADC_ARB_USRP_CNTRL1_EN_ARB)) {
- rc = pm8921_adc_read_reg(PM8921_ADC_ARB_USRP_CNTRL1,
- &data_arb_usrp_cntrl1);
- if (rc < 0)
- return rc;
- usleep_range(PM8921_ADC_CONV_TIME_MIN,
- PM8921_ADC_CONV_TIME_MAX);
- }
- data_arb_usrp_cntrl1 = 0;
-
- rc = pm8921_adc_read_adc_code(&calib_read_2);
- if (rc) {
- pr_err("pm8921_adc read adc failed with %d\n", rc);
- pm8921_adc_calib_first_adc = false;
- goto calib_fail;
- }
- pm8921_adc_calib_first_adc = false;
-
- slope_adc = (((calib_read_1 - calib_read_2) << PM8921_ADC_MUL)/
- adc_pmic->adc_prop->adc_vdd_reference);
- offset_adc = calib_read_2 -
- ((slope_adc * adc_pmic->adc_prop->adc_vdd_reference)
- >> PM8921_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 =
- adc_pmic->adc_prop->adc_vdd_reference;
- adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].adc_vref =
- calib_read_1;
- adc_pmic->conv->chan_prop->adc_graph[ADC_CALIB_RATIOMETRIC].adc_gnd =
- calib_read_2;
-calib_fail:
- rc = pm8921_adc_arb_cntrl(0, CHANNEL_MUXOFF);
- if (rc < 0) {
- pr_err("%s: Configuring ADC Arbiter disable"
- "failed\n", __func__);
- }
-
- return rc;
-}
-
-uint32_t pm8921_adc_read(enum pm8921_adc_channels channel,
- struct pm8921_adc_chan_result *result)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- int i = 0, rc = 0, rc_fail, amux_prescaling, scale_type;
- enum pm8921_adc_premux_mpp_scale_type mpp_scale;
-
- if (!pm8921_adc_initialized)
- return -ENODEV;
-
- if (!pm8921_adc_calib_device_init) {
- if (pm8921_adc_calib_device() == 0)
- pm8921_adc_calib_device_init = true;
- }
-
- mutex_lock(&adc_pmic->adc_lock);
-
- for (i = 0; i < adc_pmic->adc_num_channel; i++) {
- if (channel == adc_pmic->adc_channel[i].channel_name)
- break;
- }
-
- if (i == adc_pmic->adc_num_channel) {
- rc = -EBADF;
- goto fail_unlock;
- }
-
- if (channel < PM8921_CHANNEL_MPP_SCALE1_IDX) {
- mpp_scale = PREMUX_MPP_SCALE_0;
- adc_pmic->conv->amux_channel = channel;
- } else if (channel >= PM8921_CHANNEL_MPP_SCALE1_IDX) {
- mpp_scale = PREMUX_MPP_SCALE_1;
- adc_pmic->conv->amux_channel = channel %
- PM8921_CHANNEL_MPP_SCALE1_IDX;
- } else if (channel >= PM8921_CHANNEL_MPP_SCALE3_IDX) {
- mpp_scale = PREMUX_MPP_SCALE_1_DIV3;
- adc_pmic->conv->amux_channel = channel %
- PM8921_CHANNEL_MPP_SCALE3_IDX;
- }
-
- adc_pmic->conv->amux_mpp_channel = mpp_scale;
- adc_pmic->conv->amux_ip_rsv = adc_pmic->adc_channel[i].adc_rsv;
- adc_pmic->conv->decimation = adc_pmic->adc_channel[i].adc_decimation;
- amux_prescaling = adc_pmic->adc_channel[i].chan_path_prescaling;
-
- adc_pmic->conv->chan_prop->offset_gain_numerator =
- pm8921_amux_scaling_ratio[amux_prescaling].num;
- adc_pmic->conv->chan_prop->offset_gain_denominator =
- pm8921_amux_scaling_ratio[amux_prescaling].den;
-
- rc = pm8921_adc_channel_power_enable(channel, true);
- if (rc) {
- rc = -EINVAL;
- goto fail_unlock;
- }
-
- rc = pm8921_adc_configure(adc_pmic->conv);
- if (rc) {
- rc = -EINVAL;
- goto fail;
- }
-
- wait_for_completion(&adc_pmic->adc_rslt_completion);
-
- rc = pm8921_adc_read_adc_code(&result->adc_code);
- if (rc) {
- rc = -EINVAL;
- goto fail;
- }
-
- scale_type = adc_pmic->adc_channel[i].adc_scale_fn;
- if (scale_type >= ADC_SCALE_NONE) {
- rc = -EBADF;
- goto fail;
- }
-
- adc_scale_fn[scale_type].chan(result->adc_code,
- adc_pmic->adc_prop, adc_pmic->conv->chan_prop, result);
-
- rc = pm8921_adc_channel_power_enable(channel, false);
- if (rc) {
- rc = -EINVAL;
- goto fail_unlock;
- }
-
- mutex_unlock(&adc_pmic->adc_lock);
-
- return 0;
-fail:
- rc_fail = pm8921_adc_channel_power_enable(channel, false);
- if (rc_fail)
- pr_err("pm8921 adc power disable failed\n");
-fail_unlock:
- mutex_unlock(&adc_pmic->adc_lock);
- pr_err("pm8921 adc error with %d\n", rc);
- return rc;
-}
-EXPORT_SYMBOL_GPL(pm8921_adc_read);
-
-uint32_t pm8921_adc_mpp_config_read(uint32_t mpp_num,
- enum pm8921_adc_channels channel,
- struct pm8921_adc_chan_result *result)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- int rc = 0;
-
- if (!adc_pmic->mpp_base) {
- rc = -EINVAL;
- pr_info("PM8921 MPP base invalid with error %d\n", rc);
- return rc;
- }
-
- if (mpp_num == PM8921_AMUX_MPP_8) {
- rc = -EINVAL;
- pr_info("PM8921 MPP8 is already configured "
- "to AMUX8. Use pm8921_adc_read() instead.\n");
- return rc;
- }
-
- mutex_lock(&adc_pmic->mpp_adc_lock);
-
- rc = pm8xxx_mpp_config(((mpp_num - 1) + adc_pmic->mpp_base),
- &pm8921_adc_mpp_config);
- if (rc < 0) {
- pr_err("pm8921 adc mpp config error with %d\n", rc);
- goto fail;
- }
-
- usleep_range(PM8921_ADC_MPP_SETTLE_TIME_MIN,
- PM8921_ADC_MPP_SETTLE_TIME_MAX);
-
- rc = pm8921_adc_read(channel, result);
- if (rc < 0)
- pr_err("pm8921 adc read error with %d\n", rc);
-
- rc = pm8xxx_mpp_config(((mpp_num - 1) + adc_pmic->mpp_base),
- &pm8921_adc_mpp_unconfig);
- if (rc < 0)
- pr_err("pm8921 adc mpp config error with %d\n", rc);
-fail:
- mutex_unlock(&adc_pmic->mpp_adc_lock);
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(pm8921_adc_mpp_config_read);
-
-uint32_t pm8921_adc_btm_configure(struct pm8921_adc_arb_btm_param *btm_param)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- u8 data_btm_cool_thr0, data_btm_cool_thr1;
- u8 data_btm_warm_thr0, data_btm_warm_thr1;
- u8 arb_btm_cntrl1;
- unsigned long flags = 0;
- int rc;
-
- if (adc_pmic == NULL) {
- pr_err("PMIC ADC not valid\n");
- return -EINVAL;
- }
-
- if ((btm_param->btm_cool_fn == NULL) &&
- (btm_param->btm_warm_fn == NULL)) {
- pr_err("No BTM warm/cool notification??\n");
- return -EINVAL;
- }
-
- rc = pm8921_adc_batt_scaler(btm_param, adc_pmic->adc_prop,
- adc_pmic->conv->chan_prop);
- if (rc < 0) {
- pr_err("Failed to lookup the BTM thresholds\n");
- return rc;
- }
-
- spin_lock_irqsave(&adc_pmic->btm_lock, flags);
-
- data_btm_cool_thr0 = ((btm_param->low_thr_voltage << 24) >> 24);
- data_btm_cool_thr1 = ((btm_param->low_thr_voltage << 16) >> 24);
- data_btm_warm_thr0 = ((btm_param->high_thr_voltage << 24) >> 24);
- data_btm_warm_thr1 = ((btm_param->high_thr_voltage << 16) >> 24);
-
- if (btm_param->btm_cool_fn != NULL) {
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_BAT_COOL_THR0,
- data_btm_cool_thr0);
- if (rc < 0)
- goto write_err;
-
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_BAT_COOL_THR1,
- data_btm_cool_thr1);
- if (rc < 0)
- goto write_err;
-
- adc_pmic->batt->btm_cool_fn = btm_param->btm_cool_fn;
- }
-
- if (btm_param->btm_warm_fn != NULL) {
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_BAT_WARM_THR0,
- data_btm_warm_thr0);
- if (rc < 0)
- goto write_err;
-
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_BAT_WARM_THR1,
- data_btm_warm_thr1);
- if (rc < 0)
- goto write_err;
-
- adc_pmic->batt->btm_warm_fn = btm_param->btm_warm_fn;
- }
-
- rc = pm8921_adc_read_reg(PM8921_ADC_ARB_BTM_CNTRL1, &arb_btm_cntrl1);
- if (rc < 0)
- goto bail_out;
-
- btm_param->interval &= PM8921_ADC_BTM_INTERVAL_SEL_MASK;
- arb_btm_cntrl1 |=
- btm_param->interval << PM8921_ADC_BTM_INTERVAL_SEL_SHIFT;
-
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_CNTRL1, arb_btm_cntrl1);
- if (rc < 0)
- goto write_err;
-
- spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
-
- return rc;
-bail_out:
-write_err:
- spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
- pr_debug("%s: with error code %d\n", __func__, rc);
- return rc;
-}
-EXPORT_SYMBOL_GPL(pm8921_adc_btm_configure);
-
-static uint32_t pm8921_adc_btm_read(uint32_t channel)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- int rc, i;
- u8 arb_btm_dig_param, arb_btm_ana_param, arb_btm_rsv;
- u8 arb_btm_amux_cntrl, data_arb_btm_cntrl = 0;
- unsigned long flags;
-
- arb_btm_amux_cntrl = channel << PM8921_ADC_BTM_CHANNEL_SEL;
- arb_btm_rsv = adc_pmic->adc_channel[channel].adc_rsv;
- arb_btm_dig_param = arb_btm_ana_param = PM8921_ADC_ARB_ANA_DIG;
-
- spin_lock_irqsave(&adc_pmic->btm_lock, flags);
-
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_AMUX_CNTRL,
- arb_btm_amux_cntrl);
- if (rc < 0)
- goto write_err;
-
- arb_btm_rsv = PM8921_ADC_BTM_RSV;
-
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_RSV, arb_btm_rsv);
- if (rc < 0)
- goto write_err;
-
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_DIG_PARAM,
- arb_btm_dig_param);
- if (rc < 0)
- goto write_err;
-
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_ANA_PARAM,
- arb_btm_ana_param);
- if (rc < 0)
- goto write_err;
-
- data_arb_btm_cntrl |= PM8921_ADC_ARB_BTM_CNTRL1_EN_BTM;
-
- for (i = 0; i < 2; i++) {
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_CNTRL1,
- data_arb_btm_cntrl);
- if (rc < 0)
- goto write_err;
- }
-
- data_arb_btm_cntrl |= PM8921_ADC_ARB_BTM_CNTRL1_REQ
- | PM8921_ADC_ARB_BTM_CNTRL1_SEL_OP_MODE;
-
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_CNTRL1,
- data_arb_btm_cntrl);
- if (rc < 0)
- goto write_err;
-
- if (pmic_adc->batt->btm_warm_fn != NULL)
- enable_irq(adc_pmic->btm_warm_irq);
-
- if (pmic_adc->batt->btm_cool_fn != NULL)
- enable_irq(adc_pmic->btm_cool_irq);
-
-write_err:
- spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
- return rc;
-}
-
-uint32_t pm8921_adc_btm_start(void)
-{
- return pm8921_adc_btm_read(CHANNEL_BATT_THERM);
-}
-EXPORT_SYMBOL_GPL(pm8921_adc_btm_start);
-
-uint32_t pm8921_adc_btm_end(void)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- int i, rc;
- u8 data_arb_btm_cntrl;
- 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 = pm8921_adc_read_reg(PM8921_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 |= ~PM8921_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++) {
- rc = pm8921_adc_write_reg(PM8921_ADC_ARB_BTM_CNTRL1,
- data_arb_btm_cntrl);
- if (rc < 0) {
- spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
- return rc;
- }
- }
-
- spin_unlock_irqrestore(&adc_pmic->btm_lock, flags);
-
- return rc;
-}
-EXPORT_SYMBOL_GPL(pm8921_adc_btm_end);
-
-static ssize_t pm8921_adc_show(struct device *dev,
- struct device_attribute *devattr, char *buf)
-{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct pm8921_adc *adc_pmic = pmic_adc;
- struct pm8921_adc_chan_result result;
- int rc = -1;
-
- if (attr->index < adc_pmic->adc_num_channel)
- rc = pm8921_adc_read(attr->index, &result);
-
- if (rc)
- return 0;
-
- return snprintf(buf, sizeof(struct pm8921_adc_chan_result),
- "Result:%lld Raw:%d\n",
- result.physical, result.adc_code);
-}
-
-static int get_adc(void *data, u64 *val)
-{
- struct pm8921_adc_chan_result result;
- int i = (int)data;
- int rc;
-
- rc = pm8921_adc_read(i, &result);
- if (!rc)
- pr_info("ADC value raw:%x physical:%lld\n",
- result.adc_code, result.physical);
- *val = result.physical;
-
- return 0;
-}
-DEFINE_SIMPLE_ATTRIBUTE(reg_fops, get_adc, NULL, "%llu\n");
-
-static int get_mpp_adc(void *data, u64 *val)
-{
- struct pm8921_adc_chan_result result;
- int i = (int)data;
- int rc;
-
- rc = pm8921_adc_mpp_config_read(i,
- ADC_MPP_1_AMUX6, &result);
- if (!rc)
- pr_info("ADC MPP value raw:%x physical:%lld\n",
- result.adc_code, result.physical);
- *val = result.physical;
-
- return 0;
-}
-DEFINE_SIMPLE_ATTRIBUTE(reg_mpp_fops, get_mpp_adc, NULL, "%llu\n");
-
-#ifdef CONFIG_DEBUG_FS
-static void create_debugfs_entries(void)
-{
- pmic_adc->dent = debugfs_create_dir("pm8921_adc", NULL);
-
- if (IS_ERR(pmic_adc->dent)) {
- pr_err("pmic adc debugfs dir not created\n");
- return;
- }
-
- debugfs_create_file("vbat", 0644, pmic_adc->dent,
- (void *)CHANNEL_VBAT, ®_fops);
- debugfs_create_file("625mv", 0644, pmic_adc->dent,
- (void *)CHANNEL_625MV, ®_fops);
- debugfs_create_file("125v", 0644, pmic_adc->dent,
- (void *)CHANNEL_125V, ®_fops);
- debugfs_create_file("die_temp", 0644, pmic_adc->dent,
- (void *)CHANNEL_DIE_TEMP, ®_fops);
- debugfs_create_file("vcoin", 0644, pmic_adc->dent,
- (void *)CHANNEL_VCOIN, ®_fops);
- debugfs_create_file("dc_in", 0644, pmic_adc->dent,
- (void *)CHANNEL_DCIN, ®_fops);
- debugfs_create_file("vph_pwr", 0644, pmic_adc->dent,
- (void *)CHANNEL_VPH_PWR, ®_fops);
- debugfs_create_file("usb_in", 0644, pmic_adc->dent,
- (void *)CHANNEL_USBIN, ®_fops);
- debugfs_create_file("batt_therm", 0644, pmic_adc->dent,
- (void *)CHANNEL_BATT_THERM, ®_fops);
- debugfs_create_file("batt_id", 0644, pmic_adc->dent,
- (void *)CHANNEL_BATT_ID, ®_fops);
- debugfs_create_file("chg_temp", 0644, pmic_adc->dent,
- (void *)CHANNEL_CHG_TEMP, ®_fops);
- debugfs_create_file("charger_current", 0644, pmic_adc->dent,
- (void *)CHANNEL_ICHG, ®_fops);
- debugfs_create_file("ibat", 0644, pmic_adc->dent,
- (void *)CHANNEL_IBAT, ®_fops);
- debugfs_create_file("pa_therm1", 0644, pmic_adc->dent,
- (void *)ADC_MPP_1_AMUX8, ®_fops);
- debugfs_create_file("xo_therm", 0644, pmic_adc->dent,
- (void *)CHANNEL_MUXOFF, ®_fops);
- debugfs_create_file("pa_therm0", 0644, pmic_adc->dent,
- (void *)ADC_MPP_1_AMUX3, ®_fops);
-}
-#else
-static inline void create_debugfs_entries(void)
-{
-}
-#endif
-static struct sensor_device_attribute pm8921_adc_attr =
- SENSOR_ATTR(NULL, S_IRUGO, pm8921_adc_show, NULL, 0);
-
-static int32_t pm8921_adc_init_hwmon(struct platform_device *pdev)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- int rc = 0, i;
-
- adc_pmic->sens_attr = kzalloc(pmic_adc->adc_num_board_channel *
- sizeof(struct sensor_device_attribute), GFP_KERNEL);
-
- if (!adc_pmic->sens_attr) {
- dev_err(&pdev->dev, "Unable to allocate memory\n");
- rc = -ENOMEM;
- goto hwmon_err_sens;
- }
-
- for (i = 0; i < pmic_adc->adc_num_board_channel; i++) {
- pm8921_adc_attr.index = adc_pmic->adc_channel[i].channel_name;
- pm8921_adc_attr.dev_attr.attr.name =
- adc_pmic->adc_channel[i].name;
- memcpy(&adc_pmic->sens_attr[i], &pm8921_adc_attr,
- sizeof(pm8921_adc_attr));
- rc = device_create_file(&pdev->dev,
- &adc_pmic->sens_attr[i].dev_attr);
- if (rc) {
- dev_err(&pdev->dev, "device_create_file failed for "
- "dev %s\n",
- adc_pmic->adc_channel[i].name);
- goto hwmon_err_sens;
- }
- }
- return 0;
-
-hwmon_err_sens:
- pr_info("Init HWMON failed for pm8921_adc with %d\n", rc);
- return rc;
-}
-
-#ifdef CONFIG_PM
-static int pm8921_adc_suspend_noirq(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct pm8921_adc *adc_pmic = platform_get_drvdata(pdev);
-
- adc_pmic->msm_suspend_check = 1;
-
- return 0;
-}
-
-static int pm8921_adc_resume_noirq(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- struct pm8921_adc *adc_pmic = platform_get_drvdata(pdev);
-
- adc_pmic->msm_suspend_check = 0;
-
- return 0;
-}
-
-static const struct dev_pm_ops pm8921_adc_dev_pm_ops = {
- .suspend_noirq = pm8921_adc_suspend_noirq,
- .resume_noirq = pm8921_adc_resume_noirq,
-};
-
-#define PM8921_ADC_DEV_PM_OPS (&pm8921_adc_dev_pm_ops)
-#else
-#define PM8921_ADC_DEV_PM_OPS NULL
-#endif
-
-static int __devexit pm8921_adc_teardown(struct platform_device *pdev)
-{
- struct pm8921_adc *adc_pmic = pmic_adc;
- int i;
-
- wake_lock_destroy(&adc_pmic->adc_wakelock);
- free_irq(adc_pmic->adc_irq, adc_pmic);
- free_irq(adc_pmic->btm_warm_irq, adc_pmic);
- free_irq(adc_pmic->btm_cool_irq, adc_pmic);
- platform_set_drvdata(pdev, NULL);
- pmic_adc = NULL;
- kfree(adc_pmic->conv->chan_prop);
- kfree(adc_pmic->adc_channel);
- kfree(adc_pmic->batt);
- for (i = 0; i < adc_pmic->adc_num_board_channel; i++)
- device_remove_file(adc_pmic->dev,
- &adc_pmic->sens_attr[i].dev_attr);
- kfree(adc_pmic->sens_attr);
- kfree(adc_pmic);
- pm8921_adc_initialized = false;
-
- return 0;
-}
-
-static int __devinit pm8921_adc_probe(struct platform_device *pdev)
-{
- const struct pm8921_adc_platform_data *pdata = pdev->dev.platform_data;
- struct pm8921_adc *adc_pmic;
- struct pm8921_adc_amux_properties *adc_amux_prop;
- struct pm8921_adc_chan_properties *adc_pmic_chanprop;
- struct pm8921_adc_arb_btm_param *adc_btm;
- int rc = 0;
-
- if (!pdata) {
- dev_err(&pdev->dev, "no platform data?\n");
- return -EINVAL;
- }
-
- adc_pmic = kzalloc(sizeof(struct pm8921_adc),
- GFP_KERNEL);
- if (!adc_pmic) {
- dev_err(&pdev->dev, "Unable to allocate memory\n");
- return -ENOMEM;
- }
-
- adc_amux_prop = kzalloc(sizeof(struct pm8921_adc_amux_properties),
- GFP_KERNEL);
- if (!adc_amux_prop) {
- dev_err(&pdev->dev, "Unable to allocate memory\n");
- return -ENOMEM;
- }
-
- adc_pmic_chanprop = kzalloc(sizeof(struct pm8921_adc_chan_properties),
- GFP_KERNEL);
- if (!adc_pmic_chanprop) {
- dev_err(&pdev->dev, "Unable to allocate memory\n");
- return -ENOMEM;
- }
-
- adc_btm = kzalloc(sizeof(struct pm8921_adc_arb_btm_param),
- GFP_KERNEL);
- if (!adc_btm) {
- dev_err(&pdev->dev, "Unable to allocate memory\n");
- return -ENOMEM;
- }
-
- adc_pmic->dev = &pdev->dev;
- adc_pmic->adc_prop = pdata->adc_prop;
- adc_pmic->conv = adc_amux_prop;
- adc_pmic->conv->chan_prop = adc_pmic_chanprop;
- adc_pmic->batt = adc_btm;
-
- init_completion(&adc_pmic->adc_rslt_completion);
- adc_pmic->adc_channel = pdata->adc_channel;
- adc_pmic->adc_num_board_channel = pdata->adc_num_board_channel;
- adc_pmic->adc_num_channel = ADC_MPP_2_CHANNEL_NONE;
- adc_pmic->mpp_base = pdata->adc_mpp_base;
-
- mutex_init(&adc_pmic->adc_lock);
- mutex_init(&adc_pmic->mpp_adc_lock);
- spin_lock_init(&adc_pmic->btm_lock);
-
- adc_pmic->adc_irq = platform_get_irq(pdev, PM8921_ADC_IRQ_0);
- if (adc_pmic->adc_irq < 0) {
- rc = -ENXIO;
- goto err_cleanup;
- }
-
- rc = request_irq(adc_pmic->adc_irq,
- pm8921_adc_isr,
- IRQF_TRIGGER_RISING, "pm8921_adc_interrupt", adc_pmic);
- if (rc) {
- dev_err(&pdev->dev, "failed to request adc irq "
- "with error %d\n", rc);
- goto err_cleanup;
- }
-
- disable_irq_nosync(adc_pmic->adc_irq);
-
- adc_pmic->btm_warm_irq = platform_get_irq(pdev, PM8921_ADC_IRQ_1);
- if (adc_pmic->btm_warm_irq < 0) {
- rc = -ENXIO;
- goto err_cleanup;
- }
-
- rc = request_irq(adc_pmic->btm_warm_irq,
- pm8921_btm_warm_isr,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "pm8921_btm_warm_interrupt", adc_pmic);
- if (rc) {
- pr_err("btm warm irq failed %d with interrupt number %d\n",
- rc, adc_pmic->btm_warm_irq);
- dev_err(&pdev->dev, "failed to request btm irq\n");
- goto err_cleanup;
- }
-
- disable_irq_nosync(adc_pmic->btm_warm_irq);
-
- adc_pmic->btm_cool_irq = platform_get_irq(pdev, PM8921_ADC_IRQ_2);
- if (adc_pmic->btm_cool_irq < 0) {
- rc = -ENXIO;
- goto err_cleanup;
- }
-
- rc = request_irq(adc_pmic->btm_cool_irq,
- pm8921_btm_cool_isr,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "pm8921_btm_cool_interrupt", adc_pmic);
- if (rc) {
- pr_err("btm cool irq failed with return %d and number %d\n",
- rc, adc_pmic->btm_cool_irq);
- dev_err(&pdev->dev, "failed to request btm irq\n");
- goto err_cleanup;
- }
-
- disable_irq_nosync(adc_pmic->btm_cool_irq);
- platform_set_drvdata(pdev, adc_pmic);
- pmic_adc = adc_pmic;
-
- INIT_WORK(&adc_pmic->warm_work, pm8921_adc_btm_warm_scheduler_fn);
- INIT_WORK(&adc_pmic->cool_work, pm8921_adc_btm_cool_scheduler_fn);
- wake_lock_init(&adc_pmic->adc_wakelock, WAKE_LOCK_SUSPEND,
- "pm8921_adc_wakelock");
- create_debugfs_entries();
- pm8921_adc_calib_first_adc = false;
- pm8921_adc_calib_device_init = false;
- pm8921_adc_initialized = true;
-
- rc = pm8921_adc_init_hwmon(pdev);
- if (rc) {
- pr_err("pm8921 adc init hwmon failed with %d\n", rc);
- goto err_cleanup;
- }
- adc_pmic->hwmon = hwmon_device_register(adc_pmic->dev);
- return 0;
-
-err_cleanup:
- pm8921_adc_teardown(pdev);
- return rc;
-}
-
-static struct platform_driver pm8921_adc_driver = {
- .probe = pm8921_adc_probe,
- .remove = __devexit_p(pm8921_adc_teardown),
- .driver = {
- .name = PM8921_ADC_DEV_NAME,
- .owner = THIS_MODULE,
- .pm = PM8921_ADC_DEV_PM_OPS,
- },
-};
-
-static int __init pm8921_adc_init(void)
-{
- return platform_driver_register(&pm8921_adc_driver);
-}
-module_init(pm8921_adc_init);
-
-static void __exit pm8921_adc_exit(void)
-{
- platform_driver_unregister(&pm8921_adc_driver);
-}
-module_exit(pm8921_adc_exit);
-
-MODULE_ALIAS("platform:" PM8921_ADC_DEV_NAME);
-MODULE_DESCRIPTION("PMIC8921 ADC driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index e4430ad..5e70607 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -1352,18 +1352,30 @@
return ret;
}
-static int hci_fm_set_cal_req(struct radio_hci_dev *hdev,
+static int hci_fm_set_cal_req_proc(struct radio_hci_dev *hdev,
unsigned long param)
{
u16 opcode = 0;
- struct hci_fm_set_cal_req *cal_req =
- (struct hci_fm_set_cal_req *)param;
+ struct hci_fm_set_cal_req_proc *cal_req =
+ (struct hci_fm_set_cal_req_proc *)param;
opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
HCI_OCF_FM_SET_CALIBRATION);
- return radio_hci_send_cmd(hdev, opcode, sizeof((*hci_fm_set_cal_req)),
+ return radio_hci_send_cmd(hdev, opcode, sizeof(*cal_req),
cal_req);
+}
+static int hci_fm_set_cal_req_dc(struct radio_hci_dev *hdev,
+ unsigned long param)
+{
+ u16 opcode = 0;
+ struct hci_fm_set_cal_req_dc *cal_req =
+ (struct hci_fm_set_cal_req_dc *)param;
+
+ opcode = hci_opcode_pack(HCI_OGF_FM_COMMON_CTRL_CMD_REQ,
+ HCI_OCF_FM_SET_CALIBRATION);
+ return radio_hci_send_cmd(hdev, opcode, sizeof(*cal_req),
+ cal_req);
}
static int hci_fm_do_cal_req(struct radio_hci_dev *hdev,
@@ -2525,7 +2537,8 @@
struct hci_fm_tx_ps tx_ps;
struct hci_fm_tx_rt tx_rt;
struct hci_fm_def_data_wr_req default_data;
- struct hci_fm_set_cal_req cal_req;
+ struct hci_fm_set_cal_req_proc proc_cal_req;
+ struct hci_fm_set_cal_req_dc dc_cal_req;
struct iris_device *radio = video_get_drvdata(video_devdata(file));
char *data = NULL;
@@ -2579,24 +2592,33 @@
retval = hci_def_data_write(&default_data, radio->fm_hdev);
break;
case V4L2_CID_PRIVATE_IRIS_SET_CALIBRATION:
- FMDERR("In Set Calibration");
data = (ctrl->controls[0]).string;
bytes_to_copy = (ctrl->controls[0]).size;
- memset(cal_req.data, 0, MAX_CALIB_SIZE);
- cal_req.mode = PROCS_CALIB_MODE;
- if (copy_from_user(&cal_req.data[0],
- data, PROCS_CALIB_SIZE))
+ if (bytes_to_copy < (PROCS_CALIB_SIZE + DC_CALIB_SIZE)) {
+ FMDERR("data is less than required size");
+ return -EFAULT;
+ }
+ memset(proc_cal_req.data, 0, PROCS_CALIB_SIZE);
+ proc_cal_req.mode = PROCS_CALIB_MODE;
+ if (copy_from_user(&proc_cal_req.data[0],
+ data, sizeof(proc_cal_req.data)))
return -EFAULT;
- retval = radio_hci_request(radio->fm_hdev, hci_fm_set_cal_req,
- (unsigned long)&cal_req, RADIO_HCI_TIMEOUT);
- if (retval < 0)
+ retval = radio_hci_request(radio->fm_hdev,
+ hci_fm_set_cal_req_proc,
+ (unsigned long)&proc_cal_req,
+ RADIO_HCI_TIMEOUT);
+ if (retval < 0) {
FMDERR("Set Process calibration failed %d", retval);
- if (copy_from_user(&cal_req.data[PROCS_CALIB_SIZE],
- data, DC_CALIB_SIZE))
+ return retval;
+ }
+ memset(dc_cal_req.data, 0, DC_CALIB_SIZE);
+ if (copy_from_user(&dc_cal_req.data[0], &data[PROCS_CALIB_SIZE],
+ sizeof(dc_cal_req.data)))
return -EFAULT;
- cal_req.mode = DC_CALIB_MODE;
- retval = radio_hci_request(radio->fm_hdev, hci_fm_set_cal_req,
- (unsigned long)&cal_req, RADIO_HCI_TIMEOUT);
+ dc_cal_req.mode = DC_CALIB_MODE;
+ retval = radio_hci_request(radio->fm_hdev,
+ hci_fm_set_cal_req_dc,
+ (unsigned long)&dc_cal_req, RADIO_HCI_TIMEOUT);
if (retval < 0)
FMDERR("Set DC calibration failed %d", retval);
break;
@@ -3188,6 +3210,9 @@
if ((i == IRIS_BUF_RAW_RDS) || (i == IRIS_BUF_PEEK))
kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
rds_buf*3, GFP_KERNEL);
+ else if (i == IRIS_BUF_CAL_DATA)
+ kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
+ STD_BUF_SIZE*2, GFP_KERNEL);
else
kfifo_alloc_rc = kfifo_alloc(&radio->data_buf[i],
STD_BUF_SIZE, GFP_KERNEL);
diff --git a/drivers/mfd/pm8921-core.c b/drivers/mfd/pm8921-core.c
index d225dfa..51d2224 100644
--- a/drivers/mfd/pm8921-core.c
+++ b/drivers/mfd/pm8921-core.c
@@ -154,7 +154,7 @@
};
static struct mfd_cell adc_cell __devinitdata = {
- .name = PM8921_ADC_DEV_NAME,
+ .name = PM8XXX_ADC_DEV_NAME,
.id = -1,
.resources = adc_cell_resources,
.num_resources = ARRAY_SIZE(adc_cell_resources),
@@ -304,7 +304,7 @@
static struct pm8xxx_tm_core_data thermal_alarm_cdata = {
.adc_channel = CHANNEL_DIE_TEMP,
- .adc_type = PM8XXX_TM_ADC_PM8921_ADC,
+ .adc_type = PM8XXX_TM_ADC_PM8XXX_ADC,
.reg_addr_temp_alarm_ctrl = REG_TEMP_ALARM_CTRL,
.reg_addr_temp_alarm_pwm = REG_TEMP_ALARM_PWM,
.tm_name = "pm8921_tz",
@@ -461,7 +461,7 @@
if (pdata->adc_pdata) {
adc_cell.platform_data = pdata->adc_pdata;
adc_cell.pdata_size =
- sizeof(struct pm8921_adc_platform_data);
+ sizeof(struct pm8xxx_adc_platform_data);
ret = mfd_add_devices(pmic->dev, 0, &adc_cell, 1, NULL,
irq_base);
if (ret) {
@@ -616,6 +616,7 @@
[PM8XXX_REVISION_8921_1p0] = "1.0",
[PM8XXX_REVISION_8921_1p1] = "1.1",
[PM8XXX_REVISION_8921_2p0] = "2.0",
+ [PM8XXX_REVISION_8921_3p0] = "3.0",
};
static int __devinit pm8921_probe(struct platform_device *pdev)
diff --git a/drivers/mfd/pmic8901.c b/drivers/mfd/pmic8901.c
index 07bba8b..3d87f0b 100644
--- a/drivers/mfd/pmic8901.c
+++ b/drivers/mfd/pmic8901.c
@@ -19,6 +19,7 @@
#include <linux/mfd/pmic8901.h>
#include <linux/platform_device.h>
#include <linux/debugfs.h>
+#include <linux/delay.h>
/* PMIC8901 Revision */
#define SSBI_REG_REV 0x002 /* PMIC4 revision */
@@ -65,6 +66,10 @@
#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
+
struct pm8901_chip {
struct pm8901_platform_data pdata;
struct device *dev;
@@ -225,10 +230,12 @@
"\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);
diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c
index 1af828b..fa91b53 100644
--- a/drivers/mmc/host/msm_sdcc.c
+++ b/drivers/mmc/host/msm_sdcc.c
@@ -68,8 +68,6 @@
#define SPS_SDCC_CONSUMER_PIPE_INDEX 2
#define SPS_CONS_PERIPHERAL 0
#define SPS_PROD_PERIPHERAL 1
-/* 16 KB */
-#define SPS_MAX_DESC_SIZE (16 * 1024)
#if defined(CONFIG_DEBUG_FS)
static void msmsdcc_dbg_createhost(struct msmsdcc_host *);
@@ -134,6 +132,42 @@
u32 c);
static inline void msmsdcc_delay(struct msmsdcc_host *host);
+static inline unsigned short msmsdcc_get_nr_sg(struct msmsdcc_host *host)
+{
+ unsigned short ret = NR_SG;
+
+ if (host->is_sps_mode) {
+ if (NR_SG > MAX_NR_SG_SPS)
+ ret = MAX_NR_SG_SPS;
+ } else { /* DMA or PIO mode */
+ if (NR_SG > MAX_NR_SG_DMA_PIO)
+ ret = MAX_NR_SG_DMA_PIO;
+ }
+
+ return ret;
+}
+
+static inline unsigned int msmsdcc_get_max_seg_size(struct msmsdcc_host *host)
+{
+ unsigned int max_seg_size;
+
+ /*
+ * SPS BAM has limitation of max. number of descriptors.
+ * max. # of descriptors = SPS_MAX_DESCS
+ * each descriptor can point to SPS_MAX_DESC_SIZE (16KB)
+ * So (nr_sg * max_seg_size) should be limited to the
+ * max. size that all of the descriptors can point to.
+ * i.e., (nr_sg * max_seg_size) = (SPS_MAX_DESCS * SPS_MAX_DESC_SIZE).
+ */
+ if (host->is_sps_mode) {
+ max_seg_size = (SPS_MAX_DESCS * SPS_MAX_DESC_SIZE) /
+ msmsdcc_get_nr_sg(host);
+ } else { /* DMA or PIO mode */
+ max_seg_size = MMC_MAX_REQ_SIZE;
+ }
+
+ return max_seg_size;
+}
#ifdef CONFIG_MMC_MSM_SPS_SUPPORT
static int msmsdcc_sps_reset_ep(struct msmsdcc_host *host,
@@ -717,8 +751,9 @@
dmov_box *box;
uint32_t rows;
unsigned int n;
- int i;
+ int i, err = 0, box_cmd_cnt = 0;
struct scatterlist *sg = data->sg;
+ unsigned int len, offset;
if ((host->dma.channel == -1) || (host->dma.crci == -1))
return -ENOENT;
@@ -728,7 +763,8 @@
host->dma.sg = data->sg;
host->dma.num_ents = data->sg_len;
- BUG_ON(host->dma.num_ents > NR_SG); /* Prevent memory corruption */
+ /* Prevent memory corruption */
+ BUG_ON(host->dma.num_ents > msmsdcc_get_nr_sg(host));
nc = host->dma.nc;
@@ -737,59 +773,8 @@
else
host->dma.dir = DMA_TO_DEVICE;
- /* host->curr.user_pages = (data->flags & MMC_DATA_USERPAGE); */
- host->curr.user_pages = 0;
- box = &nc->cmd[0];
- for (i = 0; i < host->dma.num_ents; i++) {
- box->cmd = CMD_MODE_BOX;
-
- /* Initialize sg dma address */
- sg->dma_address = pfn_to_dma(mmc_dev(host->mmc),
- page_to_pfn(sg_page(sg)))
- + sg->offset;
-
- if (i == (host->dma.num_ents - 1))
- box->cmd |= CMD_LC;
- rows = (sg_dma_len(sg) % MCI_FIFOSIZE) ?
- (sg_dma_len(sg) / MCI_FIFOSIZE) + 1 :
- (sg_dma_len(sg) / MCI_FIFOSIZE) ;
-
- if (data->flags & MMC_DATA_READ) {
- box->src_row_addr = msmsdcc_fifo_addr(host);
- box->dst_row_addr = sg_dma_address(sg);
-
- box->src_dst_len = (MCI_FIFOSIZE << 16) |
- (MCI_FIFOSIZE);
- box->row_offset = MCI_FIFOSIZE;
-
- box->num_rows = rows * ((1 << 16) + 1);
- box->cmd |= CMD_SRC_CRCI(host->dma.crci);
- } else {
- box->src_row_addr = sg_dma_address(sg);
- box->dst_row_addr = msmsdcc_fifo_addr(host);
-
- box->src_dst_len = (MCI_FIFOSIZE << 16) |
- (MCI_FIFOSIZE);
- box->row_offset = (MCI_FIFOSIZE << 16);
-
- box->num_rows = rows * ((1 << 16) + 1);
- box->cmd |= CMD_DST_CRCI(host->dma.crci);
- }
- box++;
- sg++;
- }
-
- /* location of command block must be 64 bit aligned */
- BUG_ON(host->dma.cmd_busaddr & 0x07);
-
- nc->cmdptr = (host->dma.cmd_busaddr >> 3) | CMD_PTR_LP;
- host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST |
- DMOV_CMD_ADDR(host->dma.cmdptr_busaddr);
- host->dma.hdr.complete_func = msmsdcc_dma_complete_func;
-
n = dma_map_sg(mmc_dev(host->mmc), host->dma.sg,
host->dma.num_ents, host->dma.dir);
- /* dsb inside dma_map_sg will write nc out to mem as well */
if (n != host->dma.num_ents) {
pr_err("%s: Unable to map in all sg elements\n",
@@ -799,7 +784,79 @@
return -ENOMEM;
}
- return 0;
+ /* host->curr.user_pages = (data->flags & MMC_DATA_USERPAGE); */
+ host->curr.user_pages = 0;
+ box = &nc->cmd[0];
+ for (i = 0; i < host->dma.num_ents; i++) {
+ len = sg_dma_len(sg);
+ offset = 0;
+
+ do {
+ /* Check if we can do DMA */
+ if (!len || (box_cmd_cnt >= MMC_MAX_DMA_CMDS)) {
+ err = -ENOTSUPP;
+ goto unmap;
+ }
+
+ box->cmd = CMD_MODE_BOX;
+
+ if (len >= MMC_MAX_DMA_BOX_LENGTH) {
+ len = MMC_MAX_DMA_BOX_LENGTH;
+ len -= len % data->blksz;
+ }
+ rows = (len % MCI_FIFOSIZE) ?
+ (len / MCI_FIFOSIZE) + 1 :
+ (len / MCI_FIFOSIZE);
+
+ if (data->flags & MMC_DATA_READ) {
+ box->src_row_addr = msmsdcc_fifo_addr(host);
+ box->dst_row_addr = sg_dma_address(sg) + offset;
+ box->src_dst_len = (MCI_FIFOSIZE << 16) |
+ (MCI_FIFOSIZE);
+ box->row_offset = MCI_FIFOSIZE;
+ box->num_rows = rows * ((1 << 16) + 1);
+ box->cmd |= CMD_SRC_CRCI(host->dma.crci);
+ } else {
+ box->src_row_addr = sg_dma_address(sg) + offset;
+ box->dst_row_addr = msmsdcc_fifo_addr(host);
+ box->src_dst_len = (MCI_FIFOSIZE << 16) |
+ (MCI_FIFOSIZE);
+ box->row_offset = (MCI_FIFOSIZE << 16);
+ box->num_rows = rows * ((1 << 16) + 1);
+ box->cmd |= CMD_DST_CRCI(host->dma.crci);
+ }
+
+ offset += len;
+ len = sg_dma_len(sg) - offset;
+ box++;
+ box_cmd_cnt++;
+ } while (len);
+ sg++;
+ }
+ /* Mark last command */
+ box--;
+ box->cmd |= CMD_LC;
+
+ /* location of command block must be 64 bit aligned */
+ BUG_ON(host->dma.cmd_busaddr & 0x07);
+
+ nc->cmdptr = (host->dma.cmd_busaddr >> 3) | CMD_PTR_LP;
+ host->dma.hdr.cmdptr = DMOV_CMD_PTR_LIST |
+ DMOV_CMD_ADDR(host->dma.cmdptr_busaddr);
+ host->dma.hdr.complete_func = msmsdcc_dma_complete_func;
+
+ /* Flush all data to memory before starting dma */
+ mb();
+
+unmap:
+ if (err) {
+ dma_unmap_sg(mmc_dev(host->mmc), host->dma.sg,
+ host->dma.num_ents, host->dma.dir);
+ pr_err("%s: cannot do DMA, fall back to PIO mode err=%d\n",
+ mmc_hostname(host->mmc), err);
+ }
+
+ return err;
}
#ifdef CONFIG_MMC_MSM_SPS_SUPPORT
@@ -825,7 +882,8 @@
struct scatterlist *sg = data->sg;
struct sps_pipe *sps_pipe_handle;
- BUG_ON(data->sg_len > NR_SG); /* Prevent memory corruption */
+ /* Prevent memory corruption */
+ BUG_ON(data->sg_len > msmsdcc_get_nr_sg(host));
host->sps.sg = data->sg;
host->sps.num_ents = data->sg_len;
@@ -3028,17 +3086,10 @@
* transfer. It's ignored for BAM-to-System mode transfer.
*/
sps_config->event_thresh = 0x10;
- /*
- * Max. no of scatter/gather buffers that can
- * be passed by block layer = 32 (NR_SG).
- * Each BAM descritor needs 64 bits (8 bytes).
- * One BAM descriptor is required per buffer transfer.
- * So we would require total 256 (32 * 8) bytes of descriptor FIFO.
- * But due to HW limitation we need to allocate atleast one extra
- * descriptor memory (256 bytes + 8 bytes). But in order to be
- * in power of 2, we are allocating 512 bytes of memory.
- */
- sps_config->desc.size = 512;
+
+ /* Allocate maximum descriptor fifo size */
+ sps_config->desc.size = SPS_MAX_DESC_FIFO_SIZE -
+ (SPS_MAX_DESC_FIFO_SIZE % SPS_MAX_DESC_LENGTH);
sps_config->desc.base = dma_alloc_coherent(mmc_dev(host->mmc),
sps_config->desc.size,
&sps_config->desc.phys_base,
@@ -3921,12 +3972,12 @@
if (plat->is_sdio_al_client)
mmc->pm_flags |= MMC_PM_IGNORE_PM_NOTIFY;
- mmc->max_segs = NR_SG;
- mmc->max_blk_size = 4096; /* MCI_DATA_CTL BLOCKSIZE up to 4096 */
- mmc->max_blk_count = 65535;
+ mmc->max_segs = msmsdcc_get_nr_sg(host);
+ mmc->max_blk_size = MMC_MAX_BLK_SIZE;
+ mmc->max_blk_count = MMC_MAX_BLK_CNT;
- mmc->max_req_size = 33554432; /* MCI_DATA_LENGTH is 25 bits */
- mmc->max_seg_size = mmc->max_req_size;
+ mmc->max_req_size = MMC_MAX_REQ_SIZE;
+ mmc->max_seg_size = msmsdcc_get_max_seg_size(host);
writel_relaxed(0, host->base + MMCIMASK0);
writel_relaxed(MCI_CLEAR_STATIC_MASK, host->base + MMCICLEAR);
diff --git a/drivers/mmc/host/msm_sdcc.h b/drivers/mmc/host/msm_sdcc.h
index 580da82..ca3eed8 100644
--- a/drivers/mmc/host/msm_sdcc.h
+++ b/drivers/mmc/host/msm_sdcc.h
@@ -207,7 +207,7 @@
#define MCI_FIFOHALFSIZE (MCI_FIFOSIZE / 2)
-#define NR_SG 32
+#define NR_SG 128
#define MSM_MMC_IDLE_TIMEOUT 5000 /* msecs */
@@ -217,10 +217,46 @@
*/
#define MSM_MMC_REQ_TIMEOUT 10000 /* msecs */
+/*
+ * Controller HW limitations
+ */
+#define MCI_DATALENGTH_BITS 25
+#define MMC_MAX_REQ_SIZE ((1 << MCI_DATALENGTH_BITS) - 1)
+/* MCI_DATA_CTL BLOCKSIZE up to 4096 */
+#define MMC_MAX_BLK_SIZE 4096
+#define MMC_MIN_BLK_SIZE 512
+#define MMC_MAX_BLK_CNT (MMC_MAX_REQ_SIZE / MMC_MIN_BLK_SIZE)
+
+/* 64KiB */
+#define MAX_SG_SIZE (64 * 1024)
+#define MAX_NR_SG_DMA_PIO (MMC_MAX_REQ_SIZE / MAX_SG_SIZE)
+
+/*
+ * BAM limitations
+ */
+/* upto 16 bits (64K - 1) */
+#define SPS_MAX_DESC_FIFO_SIZE 65535
+/* 16KiB */
+#define SPS_MAX_DESC_SIZE (16 * 1024)
+/* Each descriptor is of length 8 bytes */
+#define SPS_MAX_DESC_LENGTH 8
+#define SPS_MAX_DESCS (SPS_MAX_DESC_FIFO_SIZE / SPS_MAX_DESC_LENGTH)
+#define SPS_MAX_SG_DESCS (MAX_SG_SIZE / SPS_MAX_DESC_SIZE)
+#define MAX_NR_SG_SPS (SPS_MAX_DESCS / SPS_MAX_SG_DESCS)
+
+/*
+ * DMA limitations
+ */
+/* upto 16 bits (64K - 1) */
+#define MMC_MAX_DMA_ROWS (64 * 1024 - 1)
+#define MMC_MAX_DMA_BOX_LENGTH (MMC_MAX_DMA_ROWS * MCI_FIFOSIZE)
+#define MMC_MAX_DMA_CMDS (MAX_NR_SG_DMA_PIO * (MMC_MAX_REQ_SIZE / \
+ MMC_MAX_DMA_BOX_LENGTH))
+
struct clk;
struct msmsdcc_nc_dmadata {
- dmov_box cmd[NR_SG];
+ dmov_box cmd[MMC_MAX_DMA_CMDS];
uint32_t cmdptr;
};
diff --git a/drivers/power/pm8921-bms.c b/drivers/power/pm8921-bms.c
index 143d9b5..6741ef4 100644
--- a/drivers/power/pm8921-bms.c
+++ b/drivers/power/pm8921-bms.c
@@ -18,7 +18,7 @@
#include <linux/errno.h>
#include <linux/mfd/pm8xxx/pm8921-bms.h>
#include <linux/mfd/pm8xxx/core.h>
-#include <linux/mfd/pm8xxx/pm8921-adc.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
#include <linux/mfd/pm8xxx/ccadc.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
@@ -839,9 +839,9 @@
static int get_battery_uvolts(struct pm8921_bms_chip *chip, int *uvolts)
{
int rc;
- struct pm8921_adc_chan_result result;
+ struct pm8xxx_adc_chan_result result;
- rc = pm8921_adc_read(chip->vbat_channel, &result);
+ rc = pm8xxx_adc_read(chip->vbat_channel, &result);
if (rc) {
pr_err("error reading adc channel = %d, rc = %d\n",
chip->vbat_channel, rc);
@@ -1107,9 +1107,9 @@
static void calib_hkadc(struct pm8921_bms_chip *chip)
{
int voltage, rc;
- struct pm8921_adc_chan_result result;
+ struct pm8xxx_adc_chan_result result;
- rc = pm8921_adc_read(the_chip->ref1p25v_channel, &result);
+ rc = pm8xxx_adc_read(the_chip->ref1p25v_channel, &result);
if (rc) {
pr_err("ADC failed for 1.25volts rc = %d\n", rc);
return;
@@ -1126,7 +1126,7 @@
voltage = XOADC_MIN_1P25V;
chip->xoadc_v125 = voltage;
- rc = pm8921_adc_read(the_chip->ref625mv_channel, &result);
+ rc = pm8xxx_adc_read(the_chip->ref625mv_channel, &result);
if (rc) {
pr_err("ADC failed for 1.25volts rc = %d\n", rc);
return;
@@ -1210,14 +1210,14 @@
int pm8921_bms_get_percent_charge(void)
{
int batt_temp, rc;
- struct pm8921_adc_chan_result result;
+ struct pm8xxx_adc_chan_result result;
if (!the_chip) {
pr_err("called before initialization\n");
return -EINVAL;
}
- rc = pm8921_adc_read(the_chip->batt_temp_channel, &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);
@@ -1234,14 +1234,14 @@
int pm8921_bms_get_fcc(void)
{
int batt_temp, rc;
- struct pm8921_adc_chan_result result;
+ struct pm8xxx_adc_chan_result result;
if (!the_chip) {
pr_err("called before initialization\n");
return -EINVAL;
}
- rc = pm8921_adc_read(the_chip->batt_temp_channel, &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);
@@ -1266,9 +1266,9 @@
if (is_battery_full && the_chip != NULL) {
unsigned long flags;
int batt_temp, rc, cc_reading;
- struct pm8921_adc_chan_result result;
+ struct pm8xxx_adc_chan_result result;
- rc = pm8921_adc_read(the_chip->batt_temp_channel, &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);
@@ -1481,9 +1481,9 @@
static int64_t read_battery_id(struct pm8921_bms_chip *chip)
{
int rc;
- struct pm8921_adc_chan_result result;
+ struct pm8xxx_adc_chan_result result;
- rc = pm8921_adc_read(chip->batt_id_channel, &result);
+ rc = pm8xxx_adc_read(chip->batt_id_channel, &result);
if (rc) {
pr_err("error reading batt id channel = %d, rc = %d\n",
chip->vbat_channel, rc);
diff --git a/drivers/power/pm8921-charger.c b/drivers/power/pm8921-charger.c
index d7a7b5a..a91630a 100644
--- a/drivers/power/pm8921-charger.c
+++ b/drivers/power/pm8921-charger.c
@@ -18,7 +18,7 @@
#include <linux/errno.h>
#include <linux/mfd/pm8xxx/pm8921-charger.h>
#include <linux/mfd/pm8xxx/pm8921-bms.h>
-#include <linux/mfd/pm8xxx/pm8921-adc.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
#include <linux/mfd/pm8xxx/ccadc.h>
#include <linux/mfd/pm8xxx/core.h>
#include <linux/interrupt.h>
@@ -264,7 +264,7 @@
static struct pm8921_chg_chip *the_chip;
-static struct pm8921_adc_arb_btm_param btm_config;
+static struct pm8xxx_adc_arb_btm_param btm_config;
static int pm_chg_masked_write(struct pm8921_chg_chip *chip, u16 addr,
u8 mask, u8 val)
@@ -722,9 +722,9 @@
static int64_t read_battery_id(struct pm8921_chg_chip *chip)
{
int rc;
- struct pm8921_adc_chan_result result;
+ struct pm8xxx_adc_chan_result result;
- rc = pm8921_adc_read(chip->batt_id_channel, &result);
+ rc = pm8xxx_adc_read(chip->batt_id_channel, &result);
if (rc) {
pr_err("error reading batt id channel = %d, rc = %d\n",
chip->vbat_channel, rc);
@@ -925,9 +925,9 @@
static int get_prop_battery_mvolts(struct pm8921_chg_chip *chip)
{
int rc;
- struct pm8921_adc_chan_result result;
+ struct pm8xxx_adc_chan_result result;
- rc = pm8921_adc_read(chip->vbat_channel, &result);
+ rc = pm8xxx_adc_read(chip->vbat_channel, &result);
if (rc) {
pr_err("error reading adc channel = %d, rc = %d\n",
chip->vbat_channel, rc);
@@ -1067,9 +1067,9 @@
static int get_prop_batt_temp(struct pm8921_chg_chip *chip)
{
int rc;
- struct pm8921_adc_chan_result result;
+ struct pm8xxx_adc_chan_result result;
- rc = pm8921_adc_read(chip->batt_temp_channel, &result);
+ rc = pm8xxx_adc_read(chip->batt_temp_channel, &result);
if (rc) {
pr_err("error reading adc channel = %d, rc = %d\n",
chip->vbat_channel, rc);
@@ -1935,7 +1935,7 @@
{
int rc;
- rc = pm8921_adc_btm_configure(&btm_config);
+ rc = pm8xxx_adc_btm_configure(&btm_config);
if (rc)
pr_err("failed to configure btm rc=%d", rc);
}
@@ -1996,10 +1996,10 @@
btm_config.low_thr_temp = chip->cool_temp;
btm_config.high_thr_temp = chip->warm_temp;
btm_config.interval = chip->temp_check_period;
- rc = pm8921_adc_btm_configure(&btm_config);
+ rc = pm8xxx_adc_btm_configure(&btm_config);
if (rc)
pr_err("failed to configure btm rc = %d\n", rc);
- rc = pm8921_adc_btm_start();
+ rc = pm8xxx_adc_btm_start();
if (rc)
pr_err("failed to start btm rc = %d\n", rc);
diff --git a/drivers/power/pm8xxx-ccadc.c b/drivers/power/pm8xxx-ccadc.c
index 1245045..5e0f8ec 100644
--- a/drivers/power/pm8xxx-ccadc.c
+++ b/drivers/power/pm8xxx-ccadc.c
@@ -700,6 +700,9 @@
chip->eoc_irq = res->start;
chip->r_sense = pdata->r_sense;
+ calib_ccadc_read_offset_and_gain(chip,
+ &chip->ccadc_gain_uv,
+ &chip->ccadc_offset);
rc = request_irq(chip->eoc_irq,
pm8921_bms_ccadc_eoc_handler, IRQF_TRIGGER_RISING,
"bms_eoc_ccadc", chip);
@@ -707,15 +710,13 @@
pr_err("failed to request %d irq rc= %d\n", chip->eoc_irq, rc);
goto free_chip;
}
+ disable_irq_nosync(chip->eoc_irq);
platform_set_drvdata(pdev, chip);
the_chip = chip;
create_debugfs_entries(chip);
- calib_ccadc_read_offset_and_gain(chip,
- &chip->ccadc_gain_uv,
- &chip->ccadc_offset);
return 0;
free_chip:
diff --git a/drivers/regulator/pm8921-regulator.c b/drivers/regulator/pm8921-regulator.c
index 7e2ac6b..69a1318 100644
--- a/drivers/regulator/pm8921-regulator.c
+++ b/drivers/regulator/pm8921-regulator.c
@@ -85,9 +85,9 @@
#define LDO_CTRL_VPROG_MASK 0x1F
/* TEST register bank 0 */
-#define LDO_TEST_LPM_MASK 0x40
+#define LDO_TEST_LPM_MASK 0x04
#define LDO_TEST_LPM_SEL_CTRL 0x00
-#define LDO_TEST_LPM_SEL_TCXO 0x40
+#define LDO_TEST_LPM_SEL_TCXO 0x04
/* TEST register bank 2 */
#define LDO_TEST_VPROG_UPDATE_MASK 0x08
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 4194e59..01a7df5 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -41,20 +41,41 @@
* system's wall clock; restore it on resume().
*/
-static time_t oldtime;
-static struct timespec oldts;
+static struct timespec old_rtc, old_system, old_delta;
+
static int rtc_suspend(struct device *dev, pm_message_t mesg)
{
struct rtc_device *rtc = to_rtc_device(dev);
struct rtc_time tm;
-
+ struct timespec delta, delta_delta;
if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
return 0;
+ /* snapshot the current RTC and system time at suspend*/
rtc_read_time(rtc, &tm);
- ktime_get_ts(&oldts);
- rtc_tm_to_time(&tm, &oldtime);
+ getnstimeofday(&old_system);
+ rtc_tm_to_time(&tm, &old_rtc.tv_sec);
+
+
+ /*
+ * To avoid drift caused by repeated suspend/resumes,
+ * which each can add ~1 second drift error,
+ * try to compensate so the difference in system time
+ * and rtc time stays close to constant.
+ */
+ delta = timespec_sub(old_system, old_rtc);
+ delta_delta = timespec_sub(delta, old_delta);
+ if (abs(delta_delta.tv_sec) >= 2) {
+ /*
+ * if delta_delta is too large, assume time correction
+ * has occured and set old_delta to the current delta.
+ */
+ old_delta = delta;
+ } else {
+ /* Otherwise try to adjust old_system to compensate */
+ old_system = timespec_sub(old_system, delta_delta);
+ }
return 0;
}
@@ -63,32 +84,42 @@
{
struct rtc_device *rtc = to_rtc_device(dev);
struct rtc_time tm;
- time_t newtime;
- struct timespec time;
- struct timespec newts;
+ struct timespec new_system, new_rtc;
+ struct timespec sleep_time;
if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
return 0;
- ktime_get_ts(&newts);
+ /* snapshot the current rtc and system time at resume */
+ getnstimeofday(&new_system);
rtc_read_time(rtc, &tm);
if (rtc_valid_tm(&tm) != 0) {
pr_debug("%s: bogus resume time\n", dev_name(&rtc->dev));
return 0;
}
- rtc_tm_to_time(&tm, &newtime);
- if (newtime <= oldtime) {
- if (newtime < oldtime)
+ rtc_tm_to_time(&tm, &new_rtc.tv_sec);
+ new_rtc.tv_nsec = 0;
+
+ if (new_rtc.tv_sec <= old_rtc.tv_sec) {
+ if (new_rtc.tv_sec < old_rtc.tv_sec)
pr_debug("%s: time travel!\n", dev_name(&rtc->dev));
return 0;
}
- /* calculate the RTC time delta */
- set_normalized_timespec(&time, newtime - oldtime, 0);
- /* subtract kernel time between rtc_suspend to rtc_resume */
- time = timespec_sub(time, timespec_sub(newts, oldts));
+ /* calculate the RTC time delta (sleep time)*/
+ sleep_time = timespec_sub(new_rtc, old_rtc);
- timekeeping_inject_sleeptime(&time);
+ /*
+ * Since these RTC suspend/resume handlers are not called
+ * at the very end of suspend or the start of resume,
+ * some run-time may pass on either sides of the sleep time
+ * so subtract kernel run-time between rtc_suspend to rtc_resume
+ * to keep things accurate.
+ */
+ sleep_time = timespec_sub(sleep_time,
+ timespec_sub(new_system, old_system));
+
+ timekeeping_inject_sleeptime(&sleep_time);
return 0;
}
diff --git a/drivers/staging/android/lowmemorykiller.c b/drivers/staging/android/lowmemorykiller.c
index 86d5195..70131fa 100644
--- a/drivers/staging/android/lowmemorykiller.c
+++ b/drivers/staging/android/lowmemorykiller.c
@@ -35,6 +35,8 @@
#include <linux/oom.h>
#include <linux/sched.h>
#include <linux/notifier.h>
+#include <linux/memory.h>
+#include <linux/memory_hotplug.h>
static uint32_t lowmem_debug_level = 2;
static int lowmem_adj[6] = {
@@ -52,6 +54,7 @@
};
static int lowmem_minfree_size = 4;
+static unsigned int offlining;
static struct task_struct *lowmem_deathpending;
static unsigned long lowmem_deathpending_timeout;
@@ -79,6 +82,32 @@
return NOTIFY_OK;
}
+#ifdef CONFIG_MEMORY_HOTPLUG
+static int lmk_hotplug_callback(struct notifier_block *self,
+ unsigned long cmd, void *data)
+{
+ switch (cmd) {
+ /* Don't care LMK cases */
+ case MEM_ONLINE:
+ case MEM_OFFLINE:
+ case MEM_CANCEL_ONLINE:
+ case MEM_CANCEL_OFFLINE:
+ case MEM_GOING_ONLINE:
+ offlining = 0;
+ lowmem_print(4, "lmk in normal mode\n");
+ break;
+ /* LMK should account for movable zone */
+ case MEM_GOING_OFFLINE:
+ offlining = 1;
+ lowmem_print(4, "lmk in hotplug mode\n");
+ break;
+ }
+ return NOTIFY_DONE;
+}
+#endif
+
+
+
static int lowmem_shrink(struct shrinker *s, struct shrink_control *sc)
{
struct task_struct *p;
@@ -93,7 +122,20 @@
int other_free = global_page_state(NR_FREE_PAGES);
int other_file = global_page_state(NR_FILE_PAGES) -
global_page_state(NR_SHMEM);
+ struct zone *zone;
+ if (offlining) {
+ /* Discount all free space in the section being offlined */
+ for_each_zone(zone) {
+ if (zone_idx(zone) == ZONE_MOVABLE) {
+ other_free -= zone_page_state(zone,
+ NR_FREE_PAGES);
+ lowmem_print(4, "lowmem_shrink discounted "
+ "%lu pages in movable zone\n",
+ zone_page_state(zone, NR_FREE_PAGES));
+ }
+ }
+ }
/*
* If we already have a death outstanding, then
* bail out right away; indicating to vmscan
@@ -190,6 +232,9 @@
{
task_free_register(&task_nb);
register_shrinker(&lowmem_shrinker);
+#ifdef CONFIG_MEMORY_HOTPLUG
+ hotplug_memory_notifier(lmk_hotplug_callback, 0);
+#endif
return 0;
}
diff --git a/drivers/thermal/msm8960_tsens.c b/drivers/thermal/msm8960_tsens.c
index 0accb32..63d8666 100644
--- a/drivers/thermal/msm8960_tsens.c
+++ b/drivers/thermal/msm8960_tsens.c
@@ -99,6 +99,7 @@
#define TSENS_SENSOR_SHIFT 16
#define TSENS_RED_SHIFT 8
#define TSENS_8960_QFPROM_SHIFT 4
+#define TSENS_SENSOR_QFPROM_SHIFT 2
#define TSENS_SENSOR0_SHIFT 3
#define TSENS_MASK1 1
@@ -680,12 +681,12 @@
for (i = 0; i < tmdev->tsens_num_sensor; i++) {
main_sensor_addr = TSENS_8960_QFPROM_ADDR0 +
(TSENS_8960_QFPROM_SHIFT *
- (i & TSENS_8960_QFPROM_SHIFT >> TSENS_SENSOR0_SHIFT));
+ ((i & TSENS_8960_QFPROM_SHIFT) >> TSENS_SENSOR_QFPROM_SHIFT));
sensor_shift = (i % TSENS_8960_QFPROM_SHIFT) * TSENS_RED_SHIFT;
sensor_mask = TSENS_THRESHOLD_MAX_CODE << sensor_shift;
backup_sensor_addr = TSENS_8960_QFPROM_SPARE_ADDR0 +
(TSENS_8960_QFPROM_SHIFT *
- (i & TSENS_8960_QFPROM_SHIFT >> TSENS_SENSOR0_SHIFT));
+ ((i & TSENS_8960_QFPROM_SHIFT) >> TSENS_SENSOR_QFPROM_SHIFT));
tmdev->sensor[i].calib_data = (readl_relaxed(main_sensor_addr)
& sensor_mask) >> sensor_shift;
diff --git a/drivers/thermal/pm8xxx-tm.c b/drivers/thermal/pm8xxx-tm.c
index 1d518e3..50238f3 100644
--- a/drivers/thermal/pm8xxx-tm.c
+++ b/drivers/thermal/pm8xxx-tm.c
@@ -29,7 +29,7 @@
#include <linux/mfd/pm8xxx/core.h>
#include <linux/mfd/pm8xxx/tm.h>
#include <linux/completion.h>
-#include <linux/mfd/pm8xxx/pm8921-adc.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
#include <linux/msm_adc.h>
/* Register TEMP_ALARM_CTRL bits */
@@ -264,11 +264,11 @@
return 0;
}
-static int pm8xxx_tz_get_temp_pm8921_adc(struct thermal_zone_device *thermal,
+static int pm8xxx_tz_get_temp_pm8xxx_adc(struct thermal_zone_device *thermal,
unsigned long *temp)
{
struct pm8xxx_tm_chip *chip = thermal->devdata;
- struct pm8921_adc_chan_result result = {
+ struct pm8xxx_adc_chan_result result = {
.physical = 0lu,
};
int rc;
@@ -278,7 +278,7 @@
*temp = chip->temp;
- rc = pm8921_adc_read(chip->cdata.adc_channel, &result);
+ rc = pm8xxx_adc_read(chip->cdata.adc_channel, &result);
if (rc < 0) {
pr_err("%s: adc_channel_read_result() failed, rc = %d\n",
chip->cdata.tm_name, rc);
@@ -401,8 +401,8 @@
.get_crit_temp = pm8xxx_tz_get_crit_temp,
};
-static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_pm8921_adc = {
- .get_temp = pm8xxx_tz_get_temp_pm8921_adc,
+static struct thermal_zone_device_ops pm8xxx_thermal_zone_ops_pm8xxx_adc = {
+ .get_temp = pm8xxx_tz_get_temp_pm8xxx_adc,
.get_mode = pm8xxx_tz_get_mode,
.set_mode = pm8xxx_tz_set_mode,
.get_trip_type = pm8xxx_tz_get_trip_type,
@@ -577,8 +577,8 @@
}
/* Select proper thermal zone ops functions based on ADC type. */
- if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8921_ADC)
- tz_ops = &pm8xxx_thermal_zone_ops_pm8921_adc;
+ if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8XXX_ADC)
+ tz_ops = &pm8xxx_thermal_zone_ops_pm8xxx_adc;
else if (chip->cdata.adc_type == PM8XXX_TM_ADC_PM8058_ADC)
tz_ops = &pm8xxx_thermal_zone_ops_pm8058_adc;
else
diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c
index a834128..84cb3f5 100644
--- a/drivers/tty/serial/msm_serial_hs.c
+++ b/drivers/tty/serial/msm_serial_hs.c
@@ -405,9 +405,9 @@
msm_uport->rx.rbuffer);
dma_pool_destroy(msm_uport->rx.pool);
- dma_unmap_single(dev, msm_uport->rx.cmdptr_dmaaddr, sizeof(u32 *),
+ dma_unmap_single(dev, msm_uport->rx.cmdptr_dmaaddr, sizeof(u32),
DMA_TO_DEVICE);
- dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr_ptr, sizeof(u32 *),
+ dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr_ptr, sizeof(u32),
DMA_TO_DEVICE);
dma_unmap_single(dev, msm_uport->tx.mapped_cmd_ptr, sizeof(dmov_box),
DMA_TO_DEVICE);
@@ -897,7 +897,7 @@
mb();
dma_sync_single_for_device(uport->dev, tx->mapped_cmd_ptr_ptr,
- sizeof(u32 *), DMA_TO_DEVICE);
+ sizeof(u32), DMA_TO_DEVICE);
msm_dmov_enqueue_cmd(msm_uport->dma_tx_channel, &tx->xfer);
}
@@ -1726,7 +1726,7 @@
if (!tx->command_ptr)
return -ENOMEM;
- tx->command_ptr_ptr = kmalloc(sizeof(u32 *), GFP_KERNEL | __GFP_DMA);
+ tx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
if (!tx->command_ptr_ptr) {
ret = -ENOMEM;
goto free_tx_command_ptr;
@@ -1736,7 +1736,7 @@
sizeof(dmov_box), DMA_TO_DEVICE);
tx->mapped_cmd_ptr_ptr = dma_map_single(uport->dev,
tx->command_ptr_ptr,
- sizeof(u32 *), DMA_TO_DEVICE);
+ sizeof(u32), DMA_TO_DEVICE);
tx->xfer.cmdptr = DMOV_CMD_ADDR(tx->mapped_cmd_ptr_ptr);
init_waitqueue_head(&rx->wait);
@@ -1772,7 +1772,7 @@
goto free_rx_buffer;
}
- rx->command_ptr_ptr = kmalloc(sizeof(u32 *), GFP_KERNEL | __GFP_DMA);
+ rx->command_ptr_ptr = kmalloc(sizeof(u32), GFP_KERNEL | __GFP_DMA);
if (!rx->command_ptr_ptr) {
pr_err("%s(): cannot allocate rx->command_ptr_ptr", __func__);
ret = -ENOMEM;
@@ -1803,7 +1803,7 @@
*rx->command_ptr_ptr = CMD_PTR_LP | DMOV_CMD_ADDR(rx->mapped_cmd_ptr);
rx->cmdptr_dmaaddr = dma_map_single(uport->dev, rx->command_ptr_ptr,
- sizeof(u32 *), DMA_TO_DEVICE);
+ sizeof(u32), DMA_TO_DEVICE);
rx->xfer.cmdptr = DMOV_CMD_ADDR(rx->cmdptr_dmaaddr);
INIT_DELAYED_WORK(&rx->flip_insert_work, flip_insert_work);
@@ -1826,7 +1826,7 @@
tasklet_kill(&msm_uport->tx.tlet);
tasklet_kill(&msm_uport->rx.tlet);
dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr_ptr,
- sizeof(u32 *), DMA_TO_DEVICE);
+ sizeof(u32), DMA_TO_DEVICE);
dma_unmap_single(uport->dev, msm_uport->tx.mapped_cmd_ptr,
sizeof(dmov_box), DMA_TO_DEVICE);
kfree(msm_uport->tx.command_ptr_ptr);
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 56bf2b5..1e3beac 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -57,6 +57,8 @@
#include "u_smd.c"
#include "u_bam.c"
#include "u_rmnet_ctrl_smd.c"
+#include "u_ctrl_hsic.c"
+#include "u_data_hsic.c"
#include "f_serial.c"
//#include "f_acm.c"
#include "f_adb.c"
@@ -252,14 +254,9 @@
.attributes = rmnet_smd_sdio_attributes,
};
-/* RMNET - used with BAM */
-#define MAX_RMNET_INSTANCES 1
-static int rmnet_instances;
-static int rmnet_function_init(struct android_usb_function *f,
- struct usb_composite_dev *cdev)
-{
- return frmnet_init_port(MAX_RMNET_INSTANCES);
-}
+/*rmnet transport string format(per port):"ctrl0,data0,ctrl1,data1..." */
+#define MAX_XPORT_STR_LEN 50
+static char rmnet_transports[MAX_XPORT_STR_LEN];
static void rmnet_function_cleanup(struct android_usb_function *f)
{
@@ -270,45 +267,74 @@
struct usb_configuration *c)
{
int i;
- int ret = 0;
+ int err = 0;
+ char *ctrl_name;
+ char *data_name;
+ char buf[MAX_XPORT_STR_LEN], *b;
+ static int rmnet_initialized, ports;
- for (i = 0; i < rmnet_instances; i++) {
- ret = frmnet_bind_config(c, i);
- if (ret) {
+ if (!rmnet_initialized) {
+ rmnet_initialized = 1;
+ strlcpy(buf, rmnet_transports, sizeof(buf));
+ b = strim(buf);
+ while (b) {
+ ctrl_name = strsep(&b, ",");
+ data_name = strsep(&b, ",");
+ if (ctrl_name && data_name) {
+ err = frmnet_init_port(ctrl_name, data_name);
+ if (err) {
+ pr_err("rmnet: Cannot open ctrl port:"
+ "'%s' data port:'%s'\n",
+ ctrl_name, data_name);
+ goto out;
+ }
+ ports++;
+ }
+ }
+
+ err = rmnet_gport_setup();
+ if (err) {
+ pr_err("rmnet: Cannot setup transports");
+ goto out;
+ }
+ }
+
+ for (i = 0; i < ports; i++) {
+ err = frmnet_bind_config(c, i);
+ if (err) {
pr_err("Could not bind rmnet%u config\n", i);
break;
}
}
-
- return ret;
+out:
+ return err;
}
-static ssize_t rmnet_instances_show(struct device *dev,
+static ssize_t rmnet_transports_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return snprintf(buf, PAGE_SIZE, "%d\n", rmnet_instances);
+ return snprintf(buf, PAGE_SIZE, "%s\n", rmnet_transports);
}
-static ssize_t rmnet_instances_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t size)
+static ssize_t rmnet_transports_store(
+ struct device *device, struct device_attribute *attr,
+ const char *buff, size_t size)
{
- int value;
+ strlcpy(rmnet_transports, buff, sizeof(rmnet_transports));
- sscanf(buf, "%d", &value);
- if (value > MAX_RMNET_INSTANCES)
- value = MAX_RMNET_INSTANCES;
- rmnet_instances = value;
return size;
}
-static DEVICE_ATTR(instances, S_IRUGO | S_IWUSR, rmnet_instances_show,
- rmnet_instances_store);
+static struct device_attribute dev_attr_rmnet_transports =
+ __ATTR(transports, S_IRUGO | S_IWUSR,
+ rmnet_transports_show,
+ rmnet_transports_store);
static struct device_attribute *rmnet_function_attributes[] = {
- &dev_attr_instances, NULL };
+ &dev_attr_rmnet_transports,
+ NULL };
static struct android_usb_function rmnet_function = {
.name = "rmnet",
- .init = rmnet_function_init,
.cleanup = rmnet_function_cleanup,
.bind_config = rmnet_function_bind_config,
.attributes = rmnet_function_attributes,
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
index 3fd12b1..7299dff 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/f_acm.c
@@ -107,20 +107,6 @@
return container_of(p, struct f_acm, port);
}
-static char *transport_to_str(enum transport_type t)
-{
- switch (t) {
- case USB_GADGET_FSERIAL_TRANSPORT_TTY:
- return "TTY";
- case USB_GADGET_FSERIAL_TRANSPORT_SDIO:
- return "SDIO";
- case USB_GADGET_FSERIAL_TRANSPORT_SMD:
- return "SMD";
- }
-
- return "NONE";
-}
-
static int gport_setup(struct usb_configuration *c)
{
int ret = 0;
@@ -146,7 +132,7 @@
pr_debug("%s: transport:%s f_acm:%p gserial:%p port_num:%d cl_port_no:%d\n",
- __func__, transport_to_str(acm->transport),
+ __func__, xport_to_str(acm->transport),
acm, &acm->port, acm->port_num, port_num);
switch (acm->transport) {
@@ -161,7 +147,7 @@
break;
default:
pr_err("%s: Un-supported transport: %s\n", __func__,
- transport_to_str(acm->transport));
+ xport_to_str(acm->transport));
return -ENODEV;
}
@@ -175,7 +161,7 @@
port_num = gacm_ports[acm->port_num].client_port_num;
pr_debug("%s: transport:%s f_acm:%p gserial:%p port_num:%d cl_pno:%d\n",
- __func__, transport_to_str(acm->transport),
+ __func__, xport_to_str(acm->transport),
acm, &acm->port, acm->port_num, port_num);
switch (acm->transport) {
@@ -190,7 +176,7 @@
break;
default:
pr_err("%s: Un-supported transport:%s\n", __func__,
- transport_to_str(acm->transport));
+ xport_to_str(acm->transport));
return -ENODEV;
}
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index 7686bf2..32791d9 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -17,7 +17,7 @@
#include <linux/usb/android_composite.h>
#include <linux/spinlock.h>
-#include <linux/platform_data/usb_rmnet.h>
+#include <mach/usb_gadget_xport.h>
#include "u_rmnet.h"
#include "gadget_chips.h"
@@ -63,7 +63,15 @@
#define NR_RMNET_PORTS 1
static unsigned int nr_rmnet_ports;
+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_hsic_ports;
static struct rmnet_ports {
+ enum transport_type data_xport;
+ enum transport_type ctrl_xport;
+ unsigned data_xport_num;
+ unsigned ctrl_xport_num;
unsigned port_num;
struct f_rmnet *port;
} rmnet_ports[NR_RMNET_PORTS];
@@ -227,42 +235,123 @@
/* -------------------------------------------*/
-static int rmnet_gport_setup(int no_rmnet_ports)
+static int rmnet_gport_setup(void)
{
- int ret;
+ int ret;
+ int port_idx;
+ int i;
- pr_debug("%s: no_rmnet_ports:%d\n", __func__, no_rmnet_ports);
+ 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);
- ret = gbam_setup(no_rmnet_ports);
- if (ret)
- return ret;
+ if (no_data_bam_ports) {
+ ret = gbam_setup(no_data_bam_ports);
+ if (ret)
+ return ret;
+ }
- ret = gsmd_ctrl_setup(no_rmnet_ports);
- if (ret)
- return ret;
+ if (no_ctrl_smd_ports) {
+ ret = gsmd_ctrl_setup(no_ctrl_smd_ports);
+ if (ret)
+ return ret;
+ }
+
+ if (no_data_hsic_ports) {
+ port_idx = ghsic_data_setup(no_data_hsic_ports,
+ USB_GADGET_RMNET);
+ if (port_idx < 0)
+ return port_idx;
+ for (i = 0; i < nr_rmnet_ports; i++) {
+ if (rmnet_ports[i].data_xport ==
+ USB_GADGET_XPORT_HSIC) {
+ rmnet_ports[i].data_xport_num = port_idx;
+ port_idx++;
+ }
+ }
+ }
+
+ if (no_ctrl_hsic_ports) {
+ port_idx = ghsic_ctrl_setup(no_ctrl_hsic_ports,
+ USB_GADGET_RMNET);
+ if (port_idx < 0)
+ return port_idx;
+ for (i = 0; i < nr_rmnet_ports; i++) {
+ if (rmnet_ports[i].ctrl_xport ==
+ USB_GADGET_XPORT_HSIC) {
+ rmnet_ports[i].ctrl_xport_num = port_idx;
+ port_idx++;
+ }
+ }
+ }
return 0;
}
static int gport_rmnet_connect(struct f_rmnet *dev)
{
- int ret;
+ int ret;
+ unsigned port_num;
+ enum transport_type cxport = rmnet_ports[dev->port_num].ctrl_xport;
+ enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
- pr_debug("%s:dev:%p portno:%d\n",
- __func__, dev, dev->port_num);
+ pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
+ __func__, xport_to_str(cxport), xport_to_str(dxport),
+ dev, dev->port_num);
- ret = gsmd_ctrl_connect(&dev->port, dev->port_num);
- if (ret) {
- pr_err("%s: gsmd_ctrl_connect failed: err:%d\n",
- __func__, ret);
- return ret;
+ port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+ switch (cxport) {
+ case USB_GADGET_XPORT_SMD:
+ ret = gsmd_ctrl_connect(&dev->port, port_num);
+ if (ret) {
+ pr_err("%s: gsmd_ctrl_connect failed: err:%d\n",
+ __func__, ret);
+ return ret;
+ }
+ break;
+ case USB_GADGET_XPORT_HSIC:
+ ret = ghsic_ctrl_connect(&dev->port, port_num);
+ if (ret) {
+ pr_err("%s: ghsic_ctrl_connect failed: err:%d\n",
+ __func__, ret);
+ return ret;
+ }
+ break;
+ case USB_GADGET_XPORT_NONE:
+ break;
+ default:
+ pr_err("%s: Un-supported transport: %s\n", __func__,
+ xport_to_str(cxport));
+ return -ENODEV;
}
- ret = gbam_connect(&dev->port, dev->port_num);
- if (ret) {
- pr_err("%s: gbam_connect failed: err:%d\n",
- __func__, ret);
- return ret;
+ port_num = rmnet_ports[dev->port_num].data_xport_num;
+ switch (dxport) {
+ case USB_GADGET_XPORT_BAM:
+ ret = gbam_connect(&dev->port, port_num);
+ if (ret) {
+ pr_err("%s: gbam_connect failed: err:%d\n",
+ __func__, ret);
+ gsmd_ctrl_disconnect(&dev->port, port_num);
+ return ret;
+ }
+ break;
+ case USB_GADGET_XPORT_HSIC:
+ ret = ghsic_data_connect(&dev->port, port_num);
+ if (ret) {
+ pr_err("%s: ghsic_data_connect failed: err:%d\n",
+ __func__, ret);
+ ghsic_ctrl_disconnect(&dev->port, port_num);
+ return ret;
+ }
+ break;
+ case USB_GADGET_XPORT_NONE:
+ break;
+ default:
+ pr_err("%s: Un-supported transport: %s\n", __func__,
+ xport_to_str(dxport));
+ return -ENODEV;
}
return 0;
@@ -270,12 +359,45 @@
static int gport_rmnet_disconnect(struct f_rmnet *dev)
{
- pr_debug("%s:dev:%p portno:%d\n",
- __func__, dev, dev->port_num);
+ unsigned port_num;
+ enum transport_type cxport = rmnet_ports[dev->port_num].ctrl_xport;
+ enum transport_type dxport = rmnet_ports[dev->port_num].data_xport;
- gbam_disconnect(&dev->port, dev->port_num);
+ pr_debug("%s: ctrl xport: %s data xport: %s dev: %p portno: %d\n",
+ __func__, xport_to_str(cxport), xport_to_str(dxport),
+ dev, dev->port_num);
- gsmd_ctrl_disconnect(&dev->port, dev->port_num);
+ port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+ switch (cxport) {
+ case USB_GADGET_XPORT_SMD:
+ gsmd_ctrl_disconnect(&dev->port, port_num);
+ break;
+ case USB_GADGET_XPORT_HSIC:
+ ghsic_ctrl_disconnect(&dev->port, port_num);
+ break;
+ case USB_GADGET_XPORT_NONE:
+ break;
+ default:
+ pr_err("%s: Un-supported transport: %s\n", __func__,
+ xport_to_str(cxport));
+ return -ENODEV;
+ }
+
+ port_num = rmnet_ports[dev->port_num].data_xport_num;
+ switch (dxport) {
+ case USB_GADGET_XPORT_BAM:
+ gbam_disconnect(&dev->port, port_num);
+ break;
+ case USB_GADGET_XPORT_HSIC:
+ ghsic_data_disconnect(&dev->port, port_num);
+ break;
+ case USB_GADGET_XPORT_NONE:
+ break;
+ default:
+ pr_err("%s: Un-supported transport: %s\n", __func__,
+ xport_to_str(dxport));
+ return -ENODEV;
+ }
return 0;
}
@@ -466,16 +588,24 @@
}
static int
-frmnet_send_cpkt_response(struct grmnet *gr, struct rmnet_ctrl_pkt *cpkt)
+frmnet_send_cpkt_response(void *gr, void *buf, size_t len)
{
struct f_rmnet *dev;
+ struct rmnet_ctrl_pkt *cpkt;
unsigned long flags;
- if (!gr || !cpkt) {
- pr_err("%s: Invalid grmnet/cpkt, grmnet:%p cpkt:%p\n",
- __func__, gr, cpkt);
+ if (!gr || !buf) {
+ pr_err("%s: Invalid grmnet/buf, grmnet:%p buf:%p\n",
+ __func__, gr, buf);
return -ENODEV;
}
+ cpkt = rmnet_alloc_ctrl_pkt(len, GFP_ATOMIC);
+ if (IS_ERR(cpkt)) {
+ pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
+ return -ENOMEM;
+ }
+ memcpy(cpkt->buf, buf, len);
+ cpkt->len = len;
dev = port_to_rmnet(gr);
@@ -500,7 +630,7 @@
{
struct f_rmnet *dev = req->context;
struct usb_composite_dev *cdev;
- struct rmnet_ctrl_pkt *cpkt;
+ unsigned port_num;
if (!dev) {
pr_err("%s: rmnet dev is null\n", __func__);
@@ -511,16 +641,10 @@
cdev = dev->cdev;
- cpkt = rmnet_alloc_ctrl_pkt(req->actual, GFP_ATOMIC);
- if (IS_ERR(cpkt)) {
- pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
- return;
+ if (dev->port.send_encap_cmd) {
+ port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+ dev->port.send_encap_cmd(port_num, req->buf, req->actual);
}
-
- memcpy(cpkt->buf, req->buf, req->actual);
-
- if (dev->port.send_cpkt_request)
- dev->port.send_cpkt_request(&dev->port, dev->port_num, cpkt);
}
static void frmnet_notify_complete(struct usb_ep *ep, struct usb_request *req)
@@ -561,6 +685,7 @@
struct f_rmnet *dev = func_to_rmnet(f);
struct usb_composite_dev *cdev = dev->cdev;
struct usb_request *req = cdev->req;
+ unsigned port_num;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
@@ -615,10 +740,10 @@
break;
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
- if (dev->port.send_cbits_tomodem)
- dev->port.send_cbits_tomodem(&dev->port,
- dev->port_num,
- w_value);
+ if (dev->port.notify_modem) {
+ port_num = rmnet_ports[dev->port_num].ctrl_xport_num;
+ dev->port.notify_modem(&dev->port, port_num, w_value);
+ }
ret = 0;
break;
@@ -827,42 +952,80 @@
kfree(rmnet_ports[i].port);
nr_rmnet_ports = 0;
+ no_ctrl_smd_ports = 0;
+ no_data_bam_ports = 0;
+ no_ctrl_hsic_ports = 0;
+ no_data_hsic_ports = 0;
}
-static int frmnet_init_port(int instances)
+static int frmnet_init_port(const char *ctrl_name, const char *data_name)
{
- int i;
- struct f_rmnet *dev;
- int ret;
+ struct f_rmnet *dev;
+ struct rmnet_ports *rmnet_port;
+ int ret;
+ int i;
- pr_debug("%s: instances :%d\n", __func__, instances);
-
- if (instances > NR_RMNET_PORTS) {
- pr_err("%s: Max-%d instances supported\n", __func__,
- NR_RMNET_PORTS);
+ if (nr_rmnet_ports >= NR_RMNET_PORTS) {
+ pr_err("%s: Max-%d instances supported\n",
+ __func__, NR_RMNET_PORTS);
return -EINVAL;
}
- for (i = 0; i < instances; i++) {
- dev = kzalloc(sizeof(struct f_rmnet), GFP_KERNEL);
- if (!dev) {
- pr_err("%s: Unable to allocate rmnet device\n",
- __func__);
- ret = -ENOMEM;
- goto fail_probe;
- }
+ pr_debug("%s: port#:%d, ctrl port: %s data port: %s\n",
+ __func__, nr_rmnet_ports, ctrl_name, data_name);
- dev->port_num = i;
- spin_lock_init(&dev->lock);
- INIT_LIST_HEAD(&dev->cpkt_resp_q);
-
- rmnet_ports[i].port = dev;
- rmnet_ports[i].port_num = i;
-
- nr_rmnet_ports++;
+ dev = kzalloc(sizeof(struct f_rmnet), GFP_KERNEL);
+ if (!dev) {
+ pr_err("%s: Unable to allocate rmnet device\n", __func__);
+ return -ENOMEM;
}
- rmnet_gport_setup(nr_rmnet_ports);
+ dev->port_num = nr_rmnet_ports;
+ spin_lock_init(&dev->lock);
+ INIT_LIST_HEAD(&dev->cpkt_resp_q);
+
+ rmnet_port = &rmnet_ports[nr_rmnet_ports];
+ rmnet_port->port = dev;
+ rmnet_port->port_num = nr_rmnet_ports;
+ rmnet_port->ctrl_xport = str_to_xport(ctrl_name);
+ rmnet_port->data_xport = str_to_xport(data_name);
+
+ switch (rmnet_port->ctrl_xport) {
+ case USB_GADGET_XPORT_SMD:
+ rmnet_port->ctrl_xport_num = no_ctrl_smd_ports;
+ no_ctrl_smd_ports++;
+ break;
+ case USB_GADGET_XPORT_HSIC:
+ rmnet_port->ctrl_xport_num = no_ctrl_hsic_ports;
+ no_ctrl_hsic_ports++;
+ break;
+ case USB_GADGET_XPORT_NONE:
+ break;
+ default:
+ pr_err("%s: Un-supported transport: %u\n", __func__,
+ rmnet_port->ctrl_xport);
+ ret = -ENODEV;
+ goto fail_probe;
+ }
+
+ switch (rmnet_port->data_xport) {
+ case USB_GADGET_XPORT_BAM:
+ rmnet_port->data_xport_num = no_data_bam_ports;
+ no_data_bam_ports++;
+ break;
+ case USB_GADGET_XPORT_HSIC:
+ rmnet_port->data_xport_num = no_data_hsic_ports;
+ no_data_hsic_ports++;
+ break;
+ case USB_GADGET_XPORT_NONE:
+ break;
+ default:
+ pr_err("%s: Un-supported transport: %u\n", __func__,
+ rmnet_port->data_xport);
+ ret = -ENODEV;
+ goto fail_probe;
+ }
+ nr_rmnet_ports++;
return 0;
@@ -870,5 +1033,11 @@
for (i = 0; i < nr_rmnet_ports; i++)
kfree(rmnet_ports[i].port);
+ nr_rmnet_ports = 0;
+ no_ctrl_smd_ports = 0;
+ no_data_bam_ports = 0;
+ no_ctrl_hsic_ports = 0;
+ no_data_hsic_ports = 0;
+
return ret;
}
diff --git a/drivers/usb/gadget/f_rmnet_smd_sdio.c b/drivers/usb/gadget/f_rmnet_smd_sdio.c
index 2ddbd7c..f974b8a 100644
--- a/drivers/usb/gadget/f_rmnet_smd_sdio.c
+++ b/drivers/usb/gadget/f_rmnet_smd_sdio.c
@@ -42,6 +42,7 @@
#include <mach/msm_smd.h>
#include <mach/sdio_cmux.h>
#include <mach/sdio_dmux.h>
+#include <mach/usb_gadget_xport.h>
#ifdef CONFIG_RMNET_SMD_SDIO_CTL_CHANNEL
static uint32_t rmnet_mux_sdio_ctl_ch = CONFIG_RMNET_SMD_SDIO_CTL_CHANNEL;
@@ -104,12 +105,6 @@
struct list_head list;
};
-enum usb_rmnet_mux_xport_type {
- USB_RMNET_MUX_XPORT_UNDEFINED,
- USB_RMNET_MUX_XPORT_SDIO,
- USB_RMNET_MUX_XPORT_SMD,
-};
-
struct rmnet_mux_ctrl_dev {
struct list_head tx_q;
wait_queue_head_t tx_wait_q;
@@ -176,7 +171,7 @@
struct rmnet_mux_ctrl_dev ctrl_dev;
u8 ifc_id;
- enum usb_rmnet_mux_xport_type xport;
+ enum transport_type xport;
spinlock_t lock;
atomic_t online;
atomic_t notify_count;
@@ -291,18 +286,6 @@
NULL,
};
-static char *xport_to_str(enum usb_rmnet_mux_xport_type t)
-{
- switch (t) {
- case USB_RMNET_MUX_XPORT_SDIO:
- return "SDIO";
- case USB_RMNET_MUX_XPORT_SMD:
- return "SMD";
- default:
- return "UNDEFINED";
- }
-}
-
static struct rmnet_mux_ctrl_pkt *rmnet_mux_alloc_ctrl_pkt(unsigned len,
gfp_t flags)
{
@@ -556,7 +539,7 @@
int status = req->status;
int queue = 0;
- if (dev->xport == USB_RMNET_MUX_XPORT_UNDEFINED) {
+ if (dev->xport == USB_GADGET_XPORT_UNDEF) {
dev_kfree_skb_any(skb);
req->buf = 0;
rmnet_mux_free_req(ep, req);
@@ -614,7 +597,7 @@
struct usb_composite_dev *cdev = dev->cdev;
int status = req->status;
- if (dev->xport == USB_RMNET_MUX_XPORT_UNDEFINED) {
+ if (dev->xport == USB_GADGET_XPORT_UNDEF) {
dev_kfree_skb_any(skb);
req->buf = 0;
rmnet_mux_free_req(ep, req);
@@ -797,7 +780,7 @@
int status = req->status;
int ret;
- if (dev->xport == USB_RMNET_MUX_XPORT_UNDEFINED) {
+ if (dev->xport == USB_GADGET_XPORT_UNDEF) {
rmnet_mux_free_req(ep, req);
return;
}
@@ -857,7 +840,7 @@
int status = req->status;
int schedule = 0;
- if (dev->xport == USB_RMNET_MUX_XPORT_UNDEFINED) {
+ if (dev->xport == USB_GADGET_XPORT_UNDEF) {
rmnet_mux_free_req(ep, req);
return;
}
@@ -1276,7 +1259,7 @@
struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
struct rmnet_mux_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
- if (dev->xport == USB_RMNET_MUX_XPORT_SMD) {
+ if (dev->xport == USB_GADGET_XPORT_SMD) {
tasklet_kill(&smd_dev->smd_data.rx_tlet);
tasklet_kill(&smd_dev->smd_data.tx_tlet);
}
@@ -1416,8 +1399,8 @@
struct rmnet_mux_dev *dev = container_of(f, struct rmnet_mux_dev,
function);
int value;
- enum usb_rmnet_mux_xport_type given_xport;
- enum usb_rmnet_mux_xport_type t;
+ enum transport_type given_xport;
+ enum transport_type t;
struct rmnet_mux_smd_dev *smd_dev = &dev->smd_dev;
struct rmnet_mux_sdio_dev *sdio_dev = &dev->sdio_dev;
struct list_head *pool;
@@ -1433,9 +1416,9 @@
sscanf(buf, "%d", &value);
if (value)
- given_xport = USB_RMNET_MUX_XPORT_SDIO;
+ given_xport = USB_GADGET_XPORT_SDIO;
else
- given_xport = USB_RMNET_MUX_XPORT_SMD;
+ given_xport = USB_GADGET_XPORT_SMD;
if (given_xport == dev->xport) {
pr_err("%s: given_xport:%s cur_xport:%s doing nothing\n",
@@ -1449,14 +1432,14 @@
/* prevent any other pkts to/from usb */
t = dev->xport;
- dev->xport = USB_RMNET_MUX_XPORT_UNDEFINED;
- if (t != USB_RMNET_MUX_XPORT_UNDEFINED) {
+ dev->xport = USB_GADGET_XPORT_UNDEF;
+ if (t != USB_GADGET_XPORT_UNDEF) {
usb_ep_fifo_flush(dev->epin);
usb_ep_fifo_flush(dev->epout);
}
switch (t) {
- case USB_RMNET_MUX_XPORT_SDIO:
+ case USB_GADGET_XPORT_SDIO:
spin_lock_irqsave(&dev->lock, flags);
/* tx_idle */
@@ -1491,7 +1474,7 @@
spin_unlock_irqrestore(&dev->lock, flags);
break;
- case USB_RMNET_MUX_XPORT_SMD:
+ case USB_GADGET_XPORT_SMD:
/* close smd xport */
tasklet_kill(&smd_dev->smd_data.rx_tlet);
tasklet_kill(&smd_dev->smd_data.tx_tlet);
@@ -1530,10 +1513,10 @@
dev->xport = given_xport;
switch (dev->xport) {
- case USB_RMNET_MUX_XPORT_SDIO:
+ case USB_GADGET_XPORT_SDIO:
rmnet_mux_sdio_enable(dev);
break;
- case USB_RMNET_MUX_XPORT_SMD:
+ case USB_GADGET_XPORT_SMD:
rmnet_mux_smd_enable(dev);
break;
default:
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 6cf148a..de8c8ed 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -13,7 +13,7 @@
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/device.h>
-#include <mach/usb_gadget_fserial.h>
+#include <mach/usb_gadget_xport.h>
#include "u_serial.h"
#include "gadget_chips.h"
@@ -27,6 +27,7 @@
* CDC ACM driver. However, for many purposes it's just as functional
* if you can arrange appropriate host side drivers.
*/
+#define GSERIAL_NO_PORTS 2
struct gser_descs {
struct usb_endpoint_descriptor *in;
@@ -75,6 +76,7 @@
static unsigned int no_tty_ports;
static unsigned int no_sdio_ports;
static unsigned int no_smd_ports;
+static unsigned int no_hsic_sports;
static unsigned int nr_ports;
static struct port_info {
@@ -85,7 +87,7 @@
static inline bool is_transport_sdio(enum transport_type t)
{
- if (t == USB_GADGET_FSERIAL_TRANSPORT_SDIO)
+ if (t == USB_GADGET_XPORT_SDIO)
return 1;
return 0;
}
@@ -250,37 +252,16 @@
NULL,
};
-static char *transport_to_str(enum transport_type t)
-{
- switch (t) {
- case USB_GADGET_FSERIAL_TRANSPORT_TTY:
- return "TTY";
- case USB_GADGET_FSERIAL_TRANSPORT_SDIO:
- return "SDIO";
- case USB_GADGET_FSERIAL_TRANSPORT_SMD:
- return "SMD";
- }
-
- return "NONE";
-}
-
-static enum transport_type serial_str_to_transport(const char *name)
-{
- if (!strcasecmp("SDIO", name))
- return USB_GADGET_FSERIAL_TRANSPORT_SDIO;
- if (!strcasecmp("SMD", name))
- return USB_GADGET_FSERIAL_TRANSPORT_SMD;
-
- return USB_GADGET_FSERIAL_TRANSPORT_TTY;
-}
-
-
static int gport_setup(struct usb_configuration *c)
{
int ret = 0;
+ int port_idx;
+ int i;
- 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_tty_ports: %u no_sdio_ports: %u"
+ " no_smd_ports: %u no_hsic_sports: %u nr_ports: %u\n",
+ __func__, no_tty_ports, no_sdio_ports, no_smd_ports,
+ no_hsic_sports, nr_ports);
if (no_tty_ports)
ret = gserial_setup(c->cdev->gadget, no_tty_ports);
@@ -288,33 +269,67 @@
ret = gsdio_setup(c->cdev->gadget, no_sdio_ports);
if (no_smd_ports)
ret = gsmd_setup(c->cdev->gadget, no_smd_ports);
+ if (no_hsic_sports) {
+ port_idx = ghsic_data_setup(no_hsic_sports, USB_GADGET_SERIAL);
+ if (port_idx < 0)
+ return port_idx;
+ for (i = 0; i < nr_ports; i++) {
+ if (gserial_ports[i].transport ==
+ USB_GADGET_XPORT_HSIC) {
+ gserial_ports[i].client_port_num = port_idx;
+ port_idx++;
+ }
+ }
+
+ /*clinet port num is same for data setup and ctrl setup*/
+ ret = ghsic_ctrl_setup(no_hsic_sports, USB_GADGET_SERIAL);
+ if (ret < 0)
+ return ret;
+ return 0;
+ }
return ret;
}
static int gport_connect(struct f_gser *gser)
{
- unsigned port_num;
+ unsigned port_num;
+ int ret;
- pr_debug("%s: transport:%s f_gser:%p gserial:%p port_num:%d\n",
- __func__, transport_to_str(gser->transport),
+ pr_debug("%s: transport: %s f_gser: %p gserial: %p port_num: %d\n",
+ __func__, xport_to_str(gser->transport),
gser, &gser->port, gser->port_num);
port_num = gserial_ports[gser->port_num].client_port_num;
switch (gser->transport) {
- case USB_GADGET_FSERIAL_TRANSPORT_TTY:
+ case USB_GADGET_XPORT_TTY:
gserial_connect(&gser->port, port_num);
break;
- case USB_GADGET_FSERIAL_TRANSPORT_SDIO:
+ case USB_GADGET_XPORT_SDIO:
gsdio_connect(&gser->port, port_num);
break;
- case USB_GADGET_FSERIAL_TRANSPORT_SMD:
+ case USB_GADGET_XPORT_SMD:
gsmd_connect(&gser->port, port_num);
break;
+ case USB_GADGET_XPORT_HSIC:
+ ret = ghsic_ctrl_connect(&gser->port, port_num);
+ if (ret) {
+ pr_err("%s: ghsic_ctrl_connect failed: err:%d\n",
+ __func__, ret);
+ return ret;
+ }
+ ret = ghsic_data_connect(&gser->port, port_num);
+ if (ret) {
+ pr_err("%s: ghsic_data_connect failed: err:%d\n",
+ __func__, ret);
+ ghsic_ctrl_disconnect(&gser->port, port_num);
+ return ret;
+ }
+ break;
default:
pr_err("%s: Un-supported transport: %s\n", __func__,
- transport_to_str(gser->transport));
+ xport_to_str(gser->transport));
return -ENODEV;
}
@@ -325,25 +340,29 @@
{
unsigned port_num;
- pr_debug("%s: transport:%s f_gser:%p gserial:%p port_num:%d\n",
- __func__, transport_to_str(gser->transport),
+ pr_debug("%s: transport: %s f_gser: %p gserial: %p port_num: %d\n",
+ __func__, xport_to_str(gser->transport),
gser, &gser->port, gser->port_num);
port_num = gserial_ports[gser->port_num].client_port_num;
switch (gser->transport) {
- case USB_GADGET_FSERIAL_TRANSPORT_TTY:
+ case USB_GADGET_XPORT_TTY:
gserial_disconnect(&gser->port);
break;
- case USB_GADGET_FSERIAL_TRANSPORT_SDIO:
+ case USB_GADGET_XPORT_SDIO:
gsdio_disconnect(&gser->port, port_num);
break;
- case USB_GADGET_FSERIAL_TRANSPORT_SMD:
+ case USB_GADGET_XPORT_SMD:
gsmd_disconnect(&gser->port, port_num);
break;
+ case USB_GADGET_XPORT_HSIC:
+ ghsic_ctrl_disconnect(&gser->port, port_num);
+ ghsic_data_disconnect(&gser->port, port_num);
+ break;
default:
pr_err("%s: Un-supported transport:%s\n", __func__,
- transport_to_str(gser->transport));
+ xport_to_str(gser->transport));
return -ENODEV;
}
@@ -887,26 +906,30 @@
if (port_num >= GSERIAL_NO_PORTS)
return -ENODEV;
- transport = serial_str_to_transport(name);
+ transport = str_to_xport(name);
pr_debug("%s, port:%d, transport:%s\n", __func__,
- port_num, transport_to_str(transport));
+ port_num, xport_to_str(transport));
gserial_ports[port_num].transport = transport;
gserial_ports[port_num].port_num = port_num;
switch (transport) {
- case USB_GADGET_FSERIAL_TRANSPORT_TTY:
+ case USB_GADGET_XPORT_TTY:
gserial_ports[port_num].client_port_num = no_tty_ports;
no_tty_ports++;
break;
- case USB_GADGET_FSERIAL_TRANSPORT_SDIO:
+ case USB_GADGET_XPORT_SDIO:
gserial_ports[port_num].client_port_num = no_sdio_ports;
no_sdio_ports++;
break;
- case USB_GADGET_FSERIAL_TRANSPORT_SMD:
+ case USB_GADGET_XPORT_SMD:
gserial_ports[port_num].client_port_num = no_smd_ports;
no_smd_ports++;
break;
+ case USB_GADGET_XPORT_HSIC:
+ /*client port number will be updated in gport_setup*/
+ no_hsic_sports++;
+ break;
default:
pr_err("%s: Un-supported transport transport: %u\n",
__func__, gserial_ports[port_num].transport);
diff --git a/drivers/usb/gadget/u_ctrl_hsic.c b/drivers/usb/gadget/u_ctrl_hsic.c
new file mode 100644
index 0000000..fdfab96
--- /dev/null
+++ b/drivers/usb/gadget/u_ctrl_hsic.c
@@ -0,0 +1,617 @@
+/* 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/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+#include <mach/usb_bridge.h>
+#include <mach/usb_gadget_xport.h>
+
+/* from cdc-acm.h */
+#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */
+#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */
+#define ACM_CTRL_OVERRUN (1 << 6)
+#define ACM_CTRL_PARITY (1 << 5)
+#define ACM_CTRL_FRAMING (1 << 4)
+#define ACM_CTRL_RI (1 << 3)
+#define ACM_CTRL_BRK (1 << 2)
+#define ACM_CTRL_DSR (1 << 1)
+#define ACM_CTRL_DCD (1 << 0)
+
+
+static unsigned int no_ctrl_ports;
+
+static const char *ctrl_bridge_names[] = {
+ "dun_ctrl_hsic0",
+ "rmnet_ctrl_hsic0"
+};
+
+#define CTRL_BRIDGE_NAME_MAX_LEN 20
+#define READ_BUF_LEN 1024
+
+#define CH_OPENED 0
+#define CH_READY 1
+
+struct gctrl_port {
+ /* port */
+ unsigned port_num;
+
+ /* gadget */
+ spinlock_t port_lock;
+ void *port_usb;
+
+ /* work queue*/
+ struct workqueue_struct *wq;
+ struct work_struct connect_w;
+ struct work_struct disconnect_w;
+
+ enum gadget_type gtype;
+
+ /*ctrl pkt response cb*/
+ int (*send_cpkt_response)(void *g, void *buf, size_t len);
+
+ struct bridge brdg;
+
+ /* bridge status */
+ unsigned long bridge_sts;
+
+ /* control bits */
+ unsigned cbits_tomodem;
+ unsigned cbits_tohost;
+
+ /* counters */
+ unsigned long to_modem;
+ unsigned long to_host;
+ unsigned long drp_cpkt_cnt;
+};
+
+static struct {
+ struct gctrl_port *port;
+ struct platform_driver pdrv;
+} gctrl_ports[NUM_PORTS];
+
+static int ghsic_ctrl_receive(void *dev, void *buf, size_t actual)
+{
+ struct gctrl_port *port = dev;
+ int retval = 0;
+
+ pr_debug_ratelimited("%s: read complete bytes read: %d\n",
+ __func__, actual);
+
+ /* send it to USB here */
+ if (port && port->send_cpkt_response) {
+ retval = port->send_cpkt_response(port->port_usb, buf, actual);
+ port->to_host++;
+ }
+
+ return retval;
+}
+
+static int
+ghsic_send_cpkt_tomodem(u8 portno, void *buf, size_t len)
+{
+ void *cbuf;
+ struct gctrl_port *port;
+
+ if (portno >= no_ctrl_ports) {
+ pr_err("%s: Invalid portno#%d\n", __func__, portno);
+ return -ENODEV;
+ }
+
+ port = gctrl_ports[portno].port;
+ if (!port) {
+ pr_err("%s: port is null\n", __func__);
+ return -ENODEV;
+ }
+
+ cbuf = kmalloc(len, GFP_ATOMIC);
+ if (!cbuf)
+ return -ENOMEM;
+
+ memcpy(cbuf, buf, len);
+
+ /* drop cpkt if ch is not open */
+ if (!test_bit(CH_OPENED, &port->bridge_sts)) {
+ port->drp_cpkt_cnt++;
+ kfree(cbuf);
+ return 0;
+ }
+
+ pr_debug("%s: ctrl_pkt:%d bytes\n", __func__, len);
+
+ ctrl_bridge_write(port->brdg.ch_id, cbuf, len);
+
+ port->to_modem++;
+
+ return 0;
+}
+
+static void
+ghsic_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
+{
+ struct gctrl_port *port;
+
+ if (portno >= no_ctrl_ports || !gptr) {
+ pr_err("%s: Invalid portno#%d\n", __func__, portno);
+ return;
+ }
+
+ port = gctrl_ports[portno].port;
+ if (!port) {
+ pr_err("%s: port is null\n", __func__);
+ return;
+ }
+
+ if (cbits == port->cbits_tomodem)
+ return;
+
+ port->cbits_tomodem = cbits;
+
+ if (!test_bit(CH_OPENED, &port->bridge_sts))
+ return;
+
+ pr_debug("%s: ctrl_tomodem:%d\n", __func__, cbits);
+
+ ctrl_bridge_set_cbits(port->brdg.ch_id, cbits);
+}
+
+static void ghsic_ctrl_connect_w(struct work_struct *w)
+{
+ struct gserial *gser = NULL;
+ struct grmnet *gr = NULL;
+ struct gctrl_port *port =
+ container_of(w, struct gctrl_port, connect_w);
+ unsigned long flags;
+ int retval;
+ unsigned cbits;
+
+ if (!port || !test_bit(CH_READY, &port->bridge_sts))
+ return;
+
+ pr_debug("%s: port:%p\n", __func__, port);
+
+ retval = ctrl_bridge_open(&port->brdg);
+ if (retval) {
+ pr_err("%s: ctrl bridge open failed :%d\n", __func__, retval);
+ return;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->port_usb) {
+ ctrl_bridge_close(port->brdg.ch_id);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+ set_bit(CH_OPENED, &port->bridge_sts);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ cbits = ctrl_bridge_get_cbits_tohost(port->brdg.ch_id);
+
+ if (port->gtype == USB_GADGET_SERIAL && (cbits & ACM_CTRL_DCD)) {
+ gser = port->port_usb;
+ if (gser && gser->connect)
+ gser->connect(gser);
+ return;
+ }
+
+ if (port->gtype == USB_GADGET_RMNET) {
+ gr = port->port_usb;
+ if (gr && gr->connect)
+ gr->connect(gr);
+ }
+}
+
+int ghsic_ctrl_connect(void *gptr, int port_num)
+{
+ struct gctrl_port *port;
+ struct gserial *gser;
+ struct grmnet *gr;
+ unsigned long flags;
+
+ pr_debug("%s: port#%d\n", __func__, port_num);
+
+ if (port_num > no_ctrl_ports || !gptr) {
+ pr_err("%s: invalid portno#%d\n", __func__, port_num);
+ return -ENODEV;
+ }
+
+ port = gctrl_ports[port_num].port;
+ if (!port) {
+ pr_err("%s: port is null\n", __func__);
+ return -ENODEV;
+ }
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->gtype == USB_GADGET_SERIAL) {
+ gser = gptr;
+ gser->notify_modem = ghsic_send_cbits_tomodem;
+ }
+
+ if (port->gtype == USB_GADGET_RMNET) {
+ gr = gptr;
+ port->send_cpkt_response = gr->send_cpkt_response;
+ gr->send_encap_cmd = ghsic_send_cpkt_tomodem;
+ gr->notify_modem = ghsic_send_cbits_tomodem;
+ }
+
+ port->port_usb = gptr;
+ port->to_host = 0;
+ port->to_modem = 0;
+ port->drp_cpkt_cnt = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ queue_work(port->wq, &port->connect_w);
+
+ return 0;
+}
+
+static void gctrl_disconnect_w(struct work_struct *w)
+{
+ struct gctrl_port *port =
+ container_of(w, struct gctrl_port, disconnect_w);
+
+ if (!test_bit(CH_OPENED, &port->bridge_sts))
+ return;
+
+ /* send the dtr zero */
+ ctrl_bridge_close(port->brdg.ch_id);
+ clear_bit(CH_OPENED, &port->bridge_sts);
+}
+
+void ghsic_ctrl_disconnect(void *gptr, int port_num)
+{
+ struct gctrl_port *port;
+ struct gserial *gser = NULL;
+ struct grmnet *gr = NULL;
+ unsigned long flags;
+
+ pr_debug("%s: port#%d\n", __func__, port_num);
+
+ port = gctrl_ports[port_num].port;
+
+ if (port_num > no_ctrl_ports) {
+ pr_err("%s: invalid portno#%d\n", __func__, port_num);
+ return;
+ }
+
+ if (!gptr || !port) {
+ pr_err("%s: grmnet port is null\n", __func__);
+ return;
+ }
+
+ if (port->gtype == USB_GADGET_SERIAL)
+ gser = gptr;
+ else
+ gr = gptr;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (gr) {
+ gr->send_encap_cmd = 0;
+ gr->notify_modem = 0;
+ }
+
+ if (gser)
+ gser->notify_modem = 0;
+ port->cbits_tomodem = 0;
+ port->port_usb = 0;
+ port->send_cpkt_response = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ queue_work(port->wq, &port->disconnect_w);
+}
+
+static void ghsic_ctrl_status(void *ctxt, unsigned int ctrl_bits)
+{
+ struct gctrl_port *port = ctxt;
+ struct gserial *gser;
+
+ pr_debug("%s - input control lines: dcd%c dsr%c break%c "
+ "ring%c framing%c parity%c overrun%c\n", __func__,
+ ctrl_bits & ACM_CTRL_DCD ? '+' : '-',
+ ctrl_bits & ACM_CTRL_DSR ? '+' : '-',
+ ctrl_bits & ACM_CTRL_BRK ? '+' : '-',
+ ctrl_bits & ACM_CTRL_RI ? '+' : '-',
+ ctrl_bits & ACM_CTRL_FRAMING ? '+' : '-',
+ ctrl_bits & ACM_CTRL_PARITY ? '+' : '-',
+ ctrl_bits & ACM_CTRL_OVERRUN ? '+' : '-');
+
+ port->cbits_tohost = ctrl_bits;
+ gser = port->port_usb;
+ if (gser && gser->send_modem_ctrl_bits)
+ gser->send_modem_ctrl_bits(gser, ctrl_bits);
+}
+
+static int ghsic_ctrl_probe(struct platform_device *pdev)
+{
+ struct gctrl_port *port;
+ unsigned long flags;
+
+ pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+ if (pdev->id >= no_ctrl_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ return -EINVAL;
+ }
+
+ port = gctrl_ports[pdev->id].port;
+ set_bit(CH_READY, &port->bridge_sts);
+
+ /* if usb is online, start read */
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb)
+ queue_work(port->wq, &port->connect_w);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return 0;
+}
+
+static int ghsic_ctrl_remove(struct platform_device *pdev)
+{
+ struct gctrl_port *port;
+ struct gserial *gser = NULL;
+ struct grmnet *gr = NULL;
+ unsigned long flags;
+
+ pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+ if (pdev->id >= no_ctrl_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ return -EINVAL;
+ }
+
+ port = gctrl_ports[pdev->id].port;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ goto not_ready;
+ }
+
+ if (port->gtype == USB_GADGET_SERIAL)
+ gser = port->port_usb;
+ else
+ gr = port->port_usb;
+
+ port->cbits_tohost = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ if (gr && gr->disconnect)
+ gr->disconnect(gr);
+
+ if (gser && gser->disconnect)
+ gser->disconnect(gser);
+
+ ctrl_bridge_close(port->brdg.ch_id);
+
+ clear_bit(CH_OPENED, &port->bridge_sts);
+not_ready:
+ clear_bit(CH_READY, &port->bridge_sts);
+
+ return 0;
+}
+
+static void ghsic_ctrl_port_free(int portno)
+{
+ struct gctrl_port *port = gctrl_ports[portno].port;
+ struct platform_driver *pdrv = &gctrl_ports[portno].pdrv;
+
+ destroy_workqueue(port->wq);
+ kfree(port);
+
+ if (pdrv)
+ platform_driver_unregister(pdrv);
+}
+
+static int gctrl_port_alloc(int portno, enum gadget_type gtype)
+{
+ struct gctrl_port *port;
+ struct platform_driver *pdrv;
+
+ port = kzalloc(sizeof(struct gctrl_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->wq = create_singlethread_workqueue(ctrl_bridge_names[portno]);
+ if (!port->wq) {
+ pr_err("%s: Unable to create workqueue:%s\n",
+ __func__, ctrl_bridge_names[portno]);
+ return -ENOMEM;
+ }
+
+ port->port_num = portno;
+ port->gtype = gtype;
+
+ spin_lock_init(&port->port_lock);
+
+ INIT_WORK(&port->connect_w, ghsic_ctrl_connect_w);
+ INIT_WORK(&port->disconnect_w, gctrl_disconnect_w);
+
+ port->brdg.ch_id = portno;
+ port->brdg.ctx = port;
+ port->brdg.ops.send_pkt = ghsic_ctrl_receive;
+ if (port->gtype == USB_GADGET_SERIAL)
+ port->brdg.ops.send_cbits = ghsic_ctrl_status;
+ gctrl_ports[portno].port = port;
+
+ pdrv = &gctrl_ports[portno].pdrv;
+ pdrv->probe = ghsic_ctrl_probe;
+ pdrv->remove = ghsic_ctrl_remove;
+ pdrv->driver.name = ctrl_bridge_names[portno];
+ pdrv->driver.owner = THIS_MODULE;
+
+ platform_driver_register(pdrv);
+
+ pr_debug("%s: port:%p portno:%d\n", __func__, port, portno);
+
+ return 0;
+}
+
+int ghsic_ctrl_setup(unsigned int num_ports, enum gadget_type gtype)
+{
+ int first_port_id = no_ctrl_ports;
+ int total_num_ports = num_ports + no_ctrl_ports;
+ int i;
+ int ret = 0;
+
+ if (!num_ports || total_num_ports > NUM_PORTS) {
+ pr_err("%s: Invalid num of ports count:%d\n",
+ __func__, num_ports);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: requested ports:%d\n", __func__, num_ports);
+
+ for (i = first_port_id; i < (first_port_id + num_ports); i++) {
+
+ /*probe can be called while port_alloc,so update no_ctrl_ports*/
+ no_ctrl_ports++;
+ ret = gctrl_port_alloc(i, gtype);
+ if (ret) {
+ no_ctrl_ports--;
+ pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+ goto free_ports;
+ }
+ }
+
+ return first_port_id;
+
+free_ports:
+ for (i = first_port_id; i < no_ctrl_ports; i++)
+ ghsic_ctrl_port_free(i);
+ no_ctrl_ports = first_port_id;
+ return ret;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE 1024
+static ssize_t gctrl_read_stats(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct gctrl_port *port;
+ struct platform_driver *pdrv;
+ char *buf;
+ unsigned long flags;
+ int ret;
+ int i;
+ int temp = 0;
+
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < no_ctrl_ports; i++) {
+ port = gctrl_ports[i].port;
+ if (!port)
+ continue;
+ pdrv = &gctrl_ports[i].pdrv;
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ "\nName: %s\n"
+ "#PORT:%d port: %p\n"
+ "to_usbhost: %lu\n"
+ "to_modem: %lu\n"
+ "cpkt_drp_cnt: %lu\n"
+ "DTR: %s\n"
+ "ch_open: %d\n"
+ "ch_ready: %d\n",
+ pdrv->driver.name,
+ i, port,
+ port->to_host, port->to_modem,
+ port->drp_cpkt_cnt,
+ port->cbits_tomodem ? "HIGH" : "LOW",
+ test_bit(CH_OPENED, &port->bridge_sts),
+ test_bit(CH_READY, &port->bridge_sts));
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ }
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static ssize_t gctrl_reset_stats(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct gctrl_port *port;
+ int i;
+ unsigned long flags;
+
+ for (i = 0; i < no_ctrl_ports; i++) {
+ port = gctrl_ports[i].port;
+ if (!port)
+ continue;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ port->to_host = 0;
+ port->to_modem = 0;
+ port->drp_cpkt_cnt = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ }
+ return count;
+}
+
+const struct file_operations gctrl_stats_ops = {
+ .read = gctrl_read_stats,
+ .write = gctrl_reset_stats,
+};
+
+struct dentry *gctrl_dent;
+struct dentry *gctrl_dfile;
+static void gctrl_debugfs_init(void)
+{
+ gctrl_dent = debugfs_create_dir("ghsic_ctrl_xport", 0);
+ if (IS_ERR(gctrl_dent))
+ return;
+
+ gctrl_dfile =
+ debugfs_create_file("status", 0444, gctrl_dent, 0,
+ &gctrl_stats_ops);
+ if (!gctrl_dfile || IS_ERR(gctrl_dfile))
+ debugfs_remove(gctrl_dent);
+}
+
+static void gctrl_debugfs_exit(void)
+{
+ debugfs_remove(gctrl_dfile);
+ debugfs_remove(gctrl_dent);
+}
+
+#else
+static void gctrl_debugfs_init(void) { }
+static void gctrl_debugfs_exit(void) { }
+#endif
+
+static int __init gctrl_init(void)
+{
+ gctrl_debugfs_init();
+
+ return 0;
+}
+module_init(gctrl_init);
+
+static void __exit gctrl_exit(void)
+{
+ gctrl_debugfs_exit();
+}
+module_exit(gctrl_exit);
+MODULE_DESCRIPTION("hsic control xport driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_data_hsic.c b/drivers/usb/gadget/u_data_hsic.c
new file mode 100644
index 0000000..61458ea
--- /dev/null
+++ b/drivers/usb/gadget/u_data_hsic.c
@@ -0,0 +1,961 @@
+/* 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/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/termios.h>
+#include <linux/netdevice.h>
+#include <linux/debugfs.h>
+#include <linux/bitops.h>
+#include <linux/termios.h>
+#include <mach/usb_bridge.h>
+#include <mach/usb_gadget_xport.h>
+
+static unsigned int no_data_ports;
+
+static const char *data_bridge_names[] = {
+ "dun_data_hsic0",
+ "rmnet_data_hsic0"
+};
+
+#define DATA_BRIDGE_NAME_MAX_LEN 20
+
+#define GHSIC_DATA_RMNET_RX_Q_SIZE 50
+#define GHSIC_DATA_RMNET_TX_Q_SIZE 300
+#define GHSIC_DATA_SERIAL_RX_Q_SIZE 2
+#define GHSIC_DATA_SERIAL_TX_Q_SIZE 2
+#define GHSIC_DATA_RX_REQ_SIZE 2048
+
+static unsigned int ghsic_data_rmnet_tx_q_size = GHSIC_DATA_RMNET_TX_Q_SIZE;
+module_param(ghsic_data_rmnet_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_rmnet_rx_q_size = GHSIC_DATA_RMNET_RX_Q_SIZE;
+module_param(ghsic_data_rmnet_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_serial_tx_q_size = GHSIC_DATA_SERIAL_TX_Q_SIZE;
+module_param(ghsic_data_serial_tx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_serial_rx_q_size = GHSIC_DATA_SERIAL_RX_Q_SIZE;
+module_param(ghsic_data_serial_rx_q_size, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_rx_req_size = GHSIC_DATA_RX_REQ_SIZE;
+module_param(ghsic_data_rx_req_size, uint, S_IRUGO | S_IWUSR);
+
+/*flow ctrl*/
+#define GHSIC_DATA_FLOW_CTRL_EN_THRESHOLD 500
+#define GHSIC_DATA_FLOW_CTRL_DISABLE 300
+#define GHSIC_DATA_FLOW_CTRL_SUPPORT 1
+#define GHSIC_DATA_PENDLIMIT_WITH_BRIDGE 500
+
+static unsigned int ghsic_data_fctrl_support = GHSIC_DATA_FLOW_CTRL_SUPPORT;
+module_param(ghsic_data_fctrl_support, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_fctrl_en_thld =
+ GHSIC_DATA_FLOW_CTRL_EN_THRESHOLD;
+module_param(ghsic_data_fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_fctrl_dis_thld = GHSIC_DATA_FLOW_CTRL_DISABLE;
+module_param(ghsic_data_fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int ghsic_data_pend_limit_with_bridge =
+ GHSIC_DATA_PENDLIMIT_WITH_BRIDGE;
+module_param(ghsic_data_pend_limit_with_bridge, uint, S_IRUGO | S_IWUSR);
+
+#define CH_OPENED 0
+#define CH_READY 1
+
+struct gdata_port {
+ /* port */
+ unsigned port_num;
+
+ /* gadget */
+ spinlock_t port_lock;
+ void *port_usb;
+ struct usb_ep *in;
+ struct usb_ep *out;
+
+ enum gadget_type gtype;
+
+ /* data transfer queues */
+ unsigned int tx_q_size;
+ struct list_head tx_idle;
+ struct sk_buff_head tx_skb_q;
+
+ unsigned int rx_q_size;
+ struct list_head rx_idle;
+ struct sk_buff_head rx_skb_q;
+
+ /* work */
+ struct workqueue_struct *wq;
+ struct work_struct connect_w;
+ struct work_struct disconnect_w;
+ struct work_struct write_tomdm_w;
+ struct work_struct write_tohost_w;
+
+ struct bridge brdg;
+
+ /*bridge status*/
+ unsigned long bridge_sts;
+
+ /*counters*/
+ unsigned long to_modem;
+ unsigned long to_host;
+ unsigned int rx_throttled_cnt;
+ unsigned int rx_unthrottled_cnt;
+ unsigned int tx_throttled_cnt;
+ unsigned int tx_unthrottled_cnt;
+ unsigned int tomodem_drp_cnt;
+ unsigned int unthrottled_pnd_skbs;
+};
+
+static struct {
+ struct gdata_port *port;
+ struct platform_driver pdrv;
+} gdata_ports[NUM_PORTS];
+
+static void ghsic_data_start_rx(struct gdata_port *port);
+
+static void ghsic_data_free_requests(struct usb_ep *ep, struct list_head *head)
+{
+ struct usb_request *req;
+
+ while (!list_empty(head)) {
+ req = list_entry(head->next, struct usb_request, list);
+ list_del(&req->list);
+ usb_ep_free_request(ep, req);
+ }
+}
+
+static int ghsic_data_alloc_requests(struct usb_ep *ep, struct list_head *head,
+ int num,
+ void (*cb)(struct usb_ep *ep, struct usb_request *),
+ gfp_t flags)
+{
+ int i;
+ struct usb_request *req;
+
+ pr_debug("%s: ep:%s head:%p num:%d cb:%p", __func__,
+ ep->name, head, num, cb);
+
+ for (i = 0; i < num; i++) {
+ req = usb_ep_alloc_request(ep, flags);
+ if (!req) {
+ pr_debug("%s: req allocated:%d\n", __func__, i);
+ return list_empty(head) ? -ENOMEM : 0;
+ }
+ req->complete = cb;
+ list_add(&req->list, head);
+ }
+
+ return 0;
+}
+
+static void ghsic_data_unthrottle_tx(void *ctx)
+{
+ struct gdata_port *port = ctx;
+ unsigned long flags;
+
+ if (!port)
+ return;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb) {
+ port->tx_unthrottled_cnt++;
+ queue_work(port->wq, &port->write_tomdm_w);
+ pr_debug("%s: port num =%d unthrottled\n", __func__,
+ port->port_num);
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static void ghsic_data_write_tohost(struct work_struct *w)
+{
+ unsigned long flags;
+ struct sk_buff *skb;
+ int ret;
+ struct usb_request *req;
+ struct usb_ep *ep;
+ struct gdata_port *port;
+
+ port = container_of(w, struct gdata_port, write_tohost_w);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ ep = port->in;
+
+ while (!list_empty(&port->tx_idle)) {
+ skb = __skb_dequeue(&port->tx_skb_q);
+ if (!skb)
+ break;
+
+ req = list_first_entry(&port->tx_idle, struct usb_request,
+ list);
+ req->context = skb;
+ req->buf = skb->data;
+ req->length = skb->len;
+
+ list_del(&req->list);
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ ret = usb_ep_queue(ep, req, GFP_KERNEL);
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (ret) {
+ pr_err("%s: usb epIn failed\n", __func__);
+ list_add(&req->list, &port->tx_idle);
+ dev_kfree_skb_any(skb);
+ break;
+ }
+ port->to_host++;
+ if (ghsic_data_fctrl_support &&
+ port->tx_skb_q.qlen <= ghsic_data_fctrl_dis_thld &&
+ test_and_clear_bit(RX_THROTTLED, &port->brdg.flags)) {
+ port->rx_unthrottled_cnt++;
+ port->unthrottled_pnd_skbs = port->tx_skb_q.qlen;
+ pr_debug_ratelimited("%s: disable flow ctrl:"
+ " tx skbq len: %u\n",
+ __func__, port->tx_skb_q.qlen);
+ data_bridge_unthrottle_rx(port->brdg.ch_id);
+ }
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int ghsic_data_receive(void *p, void *data, size_t len)
+{
+ struct gdata_port *port = p;
+ unsigned long flags;
+ struct sk_buff *skb = data;
+
+ if (!port) {
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: p:%p#%d skb_len:%d\n", __func__,
+ port, port->port_num, skb->len);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ dev_kfree_skb_any(skb);
+ return -ENOTCONN;
+ }
+
+ __skb_queue_tail(&port->tx_skb_q, skb);
+
+ if (ghsic_data_fctrl_support &&
+ port->tx_skb_q.qlen >= ghsic_data_fctrl_en_thld) {
+ set_bit(RX_THROTTLED, &port->brdg.flags);
+ port->rx_throttled_cnt++;
+ pr_debug_ratelimited("%s: flow ctrl enabled: tx skbq len: %u\n",
+ __func__, port->tx_skb_q.qlen);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ queue_work(port->wq, &port->write_tohost_w);
+ return -EBUSY;
+ }
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ queue_work(port->wq, &port->write_tohost_w);
+
+ return 0;
+}
+
+static void ghsic_data_write_tomdm(struct work_struct *w)
+{
+ struct gdata_port *port;
+ struct sk_buff *skb;
+ unsigned long flags;
+ int ret;
+
+ port = container_of(w, struct gdata_port, write_tomdm_w);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ if (test_bit(TX_THROTTLED, &port->brdg.flags)) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ goto start_rx;
+ }
+
+ while ((skb = __skb_dequeue(&port->rx_skb_q))) {
+ pr_debug("%s: port:%p tom:%lu pno:%d\n", __func__,
+ port, port->to_modem, port->port_num);
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ ret = data_bridge_write(port->brdg.ch_id, skb);
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (ret < 0) {
+ if (ret == -EBUSY) {
+ /*flow control*/
+ port->tx_throttled_cnt++;
+ break;
+ }
+ pr_err_ratelimited("%s: write error:%d\n",
+ __func__, ret);
+ port->tomodem_drp_cnt++;
+ dev_kfree_skb_any(skb);
+ break;
+ }
+ port->to_modem++;
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+start_rx:
+ ghsic_data_start_rx(port);
+}
+
+static void ghsic_data_epin_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct gdata_port *port = ep->driver_data;
+ struct sk_buff *skb = req->context;
+ int status = req->status;
+
+ switch (status) {
+ case 0:
+ /* successful completion */
+ break;
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ /* connection gone */
+ dev_kfree_skb_any(skb);
+ req->buf = 0;
+ usb_ep_free_request(ep, req);
+ return;
+ default:
+ pr_err("%s: data tx ep error %d\n", __func__, status);
+ break;
+ }
+
+ dev_kfree_skb_any(skb);
+
+ spin_lock(&port->port_lock);
+ list_add_tail(&req->list, &port->tx_idle);
+ spin_unlock(&port->port_lock);
+
+ queue_work(port->wq, &port->write_tohost_w);
+}
+
+static void
+ghsic_data_epout_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct gdata_port *port = ep->driver_data;
+ struct sk_buff *skb = req->context;
+ int status = req->status;
+ int queue = 0;
+
+ switch (status) {
+ case 0:
+ skb_put(skb, req->actual);
+ queue = 1;
+ break;
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ /* cable disconnection */
+ dev_kfree_skb_any(skb);
+ req->buf = 0;
+ usb_ep_free_request(ep, req);
+ return;
+ default:
+ pr_err_ratelimited("%s: %s response error %d, %d/%d\n",
+ __func__, ep->name, status,
+ req->actual, req->length);
+ dev_kfree_skb_any(skb);
+ break;
+ }
+
+ spin_lock(&port->port_lock);
+ if (queue) {
+ __skb_queue_tail(&port->rx_skb_q, skb);
+ list_add_tail(&req->list, &port->rx_idle);
+ queue_work(port->wq, &port->write_tomdm_w);
+ }
+ spin_unlock(&port->port_lock);
+}
+
+static void ghsic_data_start_rx(struct gdata_port *port)
+{
+ struct usb_request *req;
+ struct usb_ep *ep;
+ unsigned long flags;
+ int ret;
+ struct sk_buff *skb;
+
+ pr_debug("%s: port:%p\n", __func__, port);
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ ep = port->out;
+
+ while (port->port_usb && !list_empty(&port->rx_idle)) {
+ if (port->rx_skb_q.qlen > ghsic_data_pend_limit_with_bridge)
+ break;
+
+ req = list_first_entry(&port->rx_idle,
+ struct usb_request, list);
+
+ skb = alloc_skb(ghsic_data_rx_req_size, GFP_ATOMIC);
+ if (!skb)
+ break;
+
+ list_del(&req->list);
+ req->buf = skb->data;
+ req->length = ghsic_data_rx_req_size;
+ req->context = skb;
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ ret = usb_ep_queue(ep, req, GFP_KERNEL);
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (ret) {
+ dev_kfree_skb_any(skb);
+
+ pr_err_ratelimited("%s: rx queue failed\n", __func__);
+
+ if (port->port_usb)
+ list_add(&req->list, &port->rx_idle);
+ else
+ usb_ep_free_request(ep, req);
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static void ghsic_data_start_io(struct gdata_port *port)
+{
+ unsigned long flags;
+ struct usb_ep *ep;
+ int ret;
+
+ pr_debug("%s: port:%p\n", __func__, port);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (!port->port_usb) {
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ ep = port->out;
+ ret = ghsic_data_alloc_requests(ep, &port->rx_idle,
+ port->rx_q_size, ghsic_data_epout_complete, GFP_ATOMIC);
+ if (ret) {
+ pr_err("%s: rx req allocation failed\n", __func__);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ ep = port->in;
+ ret = ghsic_data_alloc_requests(ep, &port->tx_idle,
+ port->tx_q_size, ghsic_data_epin_complete, GFP_ATOMIC);
+ if (ret) {
+ pr_err("%s: tx req allocation failed\n", __func__);
+ ghsic_data_free_requests(ep, &port->rx_idle);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ return;
+ }
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ /* queue out requests */
+ ghsic_data_start_rx(port);
+}
+
+static void ghsic_data_connect_w(struct work_struct *w)
+{
+ struct gdata_port *port =
+ container_of(w, struct gdata_port, connect_w);
+ int ret;
+
+ if (!port || !test_bit(CH_READY, &port->bridge_sts))
+ return;
+
+ pr_debug("%s: port:%p\n", __func__, port);
+
+ ret = data_bridge_open(&port->brdg);
+ if (ret) {
+ pr_err("%s: unable open bridge ch:%d err:%d\n",
+ __func__, port->brdg.ch_id, ret);
+ return;
+ }
+
+ set_bit(CH_OPENED, &port->bridge_sts);
+
+ ghsic_data_start_io(port);
+}
+
+static void ghsic_data_disconnect_w(struct work_struct *w)
+{
+ struct gdata_port *port =
+ container_of(w, struct gdata_port, disconnect_w);
+
+ if (!test_bit(CH_OPENED, &port->bridge_sts))
+ return;
+
+ data_bridge_close(port->brdg.ch_id);
+ clear_bit(CH_OPENED, &port->bridge_sts);
+}
+
+static void ghsic_data_free_buffers(struct gdata_port *port)
+{
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ if (!port || !port->port_usb)
+ goto free_buf_out;
+
+ ghsic_data_free_requests(port->in, &port->tx_idle);
+ ghsic_data_free_requests(port->out, &port->rx_idle);
+
+ while ((skb = __skb_dequeue(&port->tx_skb_q)))
+ dev_kfree_skb_any(skb);
+
+ while ((skb = __skb_dequeue(&port->rx_skb_q)))
+ dev_kfree_skb_any(skb);
+
+free_buf_out:
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int ghsic_data_probe(struct platform_device *pdev)
+{
+ struct gdata_port *port;
+ unsigned long flags;
+
+ pr_debug("%s: name:%s no_data_ports= %d\n",
+ __func__, pdev->name, no_data_ports);
+
+ if (pdev->id >= no_data_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ return -EINVAL;
+ }
+
+ port = gdata_ports[pdev->id].port;
+ set_bit(CH_READY, &port->bridge_sts);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ /* if usb is online, try opening bridge */
+ if (port->port_usb)
+ queue_work(port->wq, &port->connect_w);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return 0;
+}
+
+/* mdm disconnect */
+static int ghsic_data_remove(struct platform_device *pdev)
+{
+ struct gdata_port *port;
+ struct usb_ep *ep_in = NULL;
+ struct usb_ep *ep_out = NULL;
+ unsigned long flags;
+
+ pr_debug("%s: name:%s\n", __func__, pdev->name);
+
+ if (pdev->id >= no_data_ports) {
+ pr_err("%s: invalid port: %d\n", __func__, pdev->id);
+ return -EINVAL;
+ }
+
+ port = gdata_ports[pdev->id].port;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb) {
+ ep_in = port->in;
+ ep_out = port->out;
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ if (ep_in)
+ usb_ep_fifo_flush(ep_in);
+ if (ep_out)
+ usb_ep_fifo_flush(ep_out);
+
+ ghsic_data_free_buffers(port);
+
+ data_bridge_close(port->brdg.ch_id);
+
+ clear_bit(CH_READY, &port->bridge_sts);
+ clear_bit(CH_OPENED, &port->bridge_sts);
+
+ return 0;
+}
+
+static void ghsic_data_port_free(int portno)
+{
+ struct gdata_port *port = gdata_ports[portno].port;
+ struct platform_driver *pdrv = &gdata_ports[portno].pdrv;
+
+ destroy_workqueue(port->wq);
+ kfree(port);
+
+ if (pdrv)
+ platform_driver_unregister(pdrv);
+}
+
+static int ghsic_data_port_alloc(unsigned port_num, enum gadget_type gtype)
+{
+ struct gdata_port *port;
+ struct platform_driver *pdrv;
+
+ port = kzalloc(sizeof(struct gdata_port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ port->wq = create_singlethread_workqueue(data_bridge_names[port_num]);
+ if (!port->wq) {
+ pr_err("%s: Unable to create workqueue:%s\n",
+ __func__, data_bridge_names[port_num]);
+ return -ENOMEM;
+ }
+ port->port_num = port_num;
+
+ /* port initialization */
+ spin_lock_init(&port->port_lock);
+
+ INIT_WORK(&port->connect_w, ghsic_data_connect_w);
+ INIT_WORK(&port->disconnect_w, ghsic_data_disconnect_w);
+ INIT_WORK(&port->write_tohost_w, ghsic_data_write_tohost);
+ INIT_WORK(&port->write_tomdm_w, ghsic_data_write_tomdm);
+
+ INIT_LIST_HEAD(&port->tx_idle);
+ INIT_LIST_HEAD(&port->rx_idle);
+
+ skb_queue_head_init(&port->tx_skb_q);
+ skb_queue_head_init(&port->rx_skb_q);
+
+ port->gtype = gtype;
+ port->brdg.ch_id = port_num;
+ port->brdg.ctx = port;
+ port->brdg.ops.send_pkt = ghsic_data_receive;
+ port->brdg.ops.unthrottle_tx = ghsic_data_unthrottle_tx;
+ gdata_ports[port_num].port = port;
+
+ pdrv = &gdata_ports[port_num].pdrv;
+ pdrv->probe = ghsic_data_probe;
+ pdrv->remove = ghsic_data_remove;
+ pdrv->driver.name = data_bridge_names[port_num];
+ pdrv->driver.owner = THIS_MODULE;
+
+ platform_driver_register(pdrv);
+
+ pr_debug("%s: port:%p portno:%d\n", __func__, port, port_num);
+
+ return 0;
+}
+
+void ghsic_data_disconnect(void *gptr, int port_num)
+{
+ struct gdata_port *port;
+ unsigned long flags;
+
+ pr_debug("%s: port#%d\n", __func__, port_num);
+
+ port = gdata_ports[port_num].port;
+
+ if (port_num > no_data_ports) {
+ pr_err("%s: invalid portno#%d\n", __func__, port_num);
+ return;
+ }
+
+ if (!gptr || !port) {
+ pr_err("%s: port is null\n", __func__);
+ return;
+ }
+
+ ghsic_data_free_buffers(port);
+
+ /* disable endpoints */
+ if (port->in)
+ usb_ep_disable(port->out);
+
+ if (port->out)
+ usb_ep_disable(port->in);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ port->port_usb = 0;
+ port->in = NULL;
+ port->out = NULL;
+ clear_bit(TX_THROTTLED, &port->brdg.flags);
+ clear_bit(RX_THROTTLED, &port->brdg.flags);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ queue_work(port->wq, &port->disconnect_w);
+}
+
+int ghsic_data_connect(void *gptr, int port_num)
+{
+ struct gdata_port *port;
+ struct gserial *gser;
+ struct grmnet *gr;
+ struct usb_endpoint_descriptor *in_desc;
+ struct usb_endpoint_descriptor *out_desc;
+ unsigned long flags;
+ int ret = 0;
+
+ pr_debug("%s: port#%d\n", __func__, port_num);
+
+ port = gdata_ports[port_num].port;
+
+ if (port_num > no_data_ports) {
+ pr_err("%s: invalid portno#%d\n", __func__, port_num);
+ return -ENODEV;
+ }
+
+ if (!gptr || !port) {
+ pr_err("%s: port is null\n", __func__);
+ return -ENODEV;
+ }
+
+ if (port->gtype == USB_GADGET_SERIAL) {
+ gser = gptr;
+ port->in = gser->in;
+ port->out = gser->out;
+ port->tx_q_size = ghsic_data_serial_tx_q_size;
+ port->rx_q_size = ghsic_data_serial_rx_q_size;
+ gser->in->driver_data = port;
+ gser->out->driver_data = port;
+ in_desc = gser->in_desc;
+ out_desc = gser->out_desc;
+ } else {
+ gr = gptr;
+ port->in = gr->in;
+ port->out = gr->out;
+ port->tx_q_size = ghsic_data_rmnet_tx_q_size;
+ port->rx_q_size = ghsic_data_rmnet_rx_q_size;
+ gr->in->driver_data = port;
+ gr->out->driver_data = port;
+ in_desc = gr->in_desc;
+ out_desc = gr->out_desc;
+ }
+
+ ret = usb_ep_enable(port->in, in_desc);
+ if (ret) {
+ pr_err("%s: usb_ep_enable failed eptype:IN ep:%p",
+ __func__, port->in);
+ goto fail;
+ }
+
+ ret = usb_ep_enable(port->out, out_desc);
+ if (ret) {
+ pr_err("%s: usb_ep_enable failed eptype:OUT ep:%p",
+ __func__, port->out);
+ usb_ep_disable(port->in);
+ goto fail;
+ }
+ spin_lock_irqsave(&port->port_lock, flags);
+ port->port_usb = gptr;
+ port->to_host = 0;
+ port->to_modem = 0;
+ port->tomodem_drp_cnt = 0;
+ port->rx_throttled_cnt = 0;
+ port->rx_unthrottled_cnt = 0;
+ port->tx_throttled_cnt = 0;
+ port->tx_unthrottled_cnt = 0;
+ port->unthrottled_pnd_skbs = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ queue_work(port->wq, &port->connect_w);
+fail:
+ return ret;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE 1024
+static ssize_t ghsic_data_read_stats(struct file *file,
+ char __user *ubuf, size_t count, loff_t *ppos)
+{
+ struct gdata_port *port;
+ struct platform_driver *pdrv;
+ char *buf;
+ unsigned long flags;
+ int ret;
+ int i;
+ int temp = 0;
+
+ buf = kzalloc(sizeof(char) * DEBUG_BUF_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < no_data_ports; i++) {
+ port = gdata_ports[i].port;
+ if (!port)
+ continue;
+ pdrv = &gdata_ports[i].pdrv;
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ "\nName: %s\n"
+ "#PORT:%d port#: %p\n"
+ "dpkts_to_usbhost: %lu\n"
+ "dpkts_to_modem: %lu\n"
+ "tomodem_drp_cnt: %u\n"
+ "tx_buf_len: %u\n"
+ "rx_buf_len: %u\n"
+ "rx thld cnt %u\n"
+ "rx unthld cnt %u\n"
+ "tx thld cnt %u\n"
+ "tx unthld cnt %u\n"
+ "uthld pnd skbs %u\n"
+ "RX_THROTTLED %d\n"
+ "TX_THROTTLED %d\n"
+ "data_ch_open: %d\n"
+ "data_ch_ready: %d\n",
+ pdrv->driver.name,
+ i, port,
+ port->to_host, port->to_modem,
+ port->tomodem_drp_cnt,
+ port->tx_skb_q.qlen,
+ port->rx_skb_q.qlen,
+ port->rx_throttled_cnt,
+ port->rx_unthrottled_cnt,
+ port->tx_throttled_cnt,
+ port->tx_unthrottled_cnt,
+ port->unthrottled_pnd_skbs,
+ test_bit(RX_THROTTLED, &port->brdg.flags),
+ test_bit(TX_THROTTLED, &port->brdg.flags),
+ test_bit(CH_OPENED, &port->bridge_sts),
+ test_bit(CH_READY, &port->bridge_sts));
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ }
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static ssize_t ghsic_data_reset_stats(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct gdata_port *port;
+ int i;
+ unsigned long flags;
+
+ for (i = 0; i < no_data_ports; i++) {
+ port = gdata_ports[i].port;
+ if (!port)
+ continue;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ port->to_host = 0;
+ port->to_modem = 0;
+ port->tomodem_drp_cnt = 0;
+ port->rx_throttled_cnt = 0;
+ port->rx_unthrottled_cnt = 0;
+ port->tx_throttled_cnt = 0;
+ port->tx_unthrottled_cnt = 0;
+ port->unthrottled_pnd_skbs = 0;
+ spin_unlock_irqrestore(&port->port_lock, flags);
+ }
+ return count;
+}
+
+const struct file_operations ghsic_stats_ops = {
+ .read = ghsic_data_read_stats,
+ .write = ghsic_data_reset_stats,
+};
+
+static struct dentry *gdata_dent;
+static struct dentry *gdata_dfile;
+
+static void ghsic_data_debugfs_init(void)
+{
+ gdata_dent = debugfs_create_dir("ghsic_data_xport", 0);
+ if (IS_ERR(gdata_dent))
+ return;
+
+ gdata_dfile = debugfs_create_file("status", 0444, gdata_dent, 0,
+ &ghsic_stats_ops);
+ if (!gdata_dfile || IS_ERR(gdata_dfile))
+ debugfs_remove(gdata_dent);
+}
+
+static void ghsic_data_debugfs_exit(void)
+{
+ debugfs_remove(gdata_dfile);
+ debugfs_remove(gdata_dent);
+}
+
+#else
+static void ghsic_data_debugfs_init(void) { }
+static void ghsic_data_debugfs_exit(void) { }
+
+#endif
+
+int ghsic_data_setup(unsigned num_ports, enum gadget_type gtype)
+{
+ int first_port_id = no_data_ports;
+ int total_num_ports = num_ports + no_data_ports;
+ int ret = 0;
+ int i;
+
+ if (!num_ports || total_num_ports > NUM_PORTS) {
+ pr_err("%s: Invalid num of ports count:%d\n",
+ __func__, num_ports);
+ return -EINVAL;
+ }
+ pr_debug("%s: count: %d\n", __func__, num_ports);
+
+ for (i = first_port_id; i < (num_ports + first_port_id); i++) {
+
+ /*probe can be called while port_alloc,so update no_data_ports*/
+ no_data_ports++;
+ ret = ghsic_data_port_alloc(i, gtype);
+ if (ret) {
+ no_data_ports--;
+ pr_err("%s: Unable to alloc port:%d\n", __func__, i);
+ goto free_ports;
+ }
+ }
+
+ /*return the starting index*/
+ return first_port_id;
+
+free_ports:
+ for (i = first_port_id; i < no_data_ports; i++)
+ ghsic_data_port_free(i);
+ no_data_ports = first_port_id;
+
+ return ret;
+}
+
+static int __init ghsic_data_init(void)
+{
+ ghsic_data_debugfs_init();
+
+ return 0;
+}
+module_init(ghsic_data_init);
+
+static void __exit ghsic_data_exit(void)
+{
+ ghsic_data_debugfs_exit();
+}
+module_exit(ghsic_data_exit);
+MODULE_DESCRIPTION("hsic data xport driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/u_rmnet.h b/drivers/usb/gadget/u_rmnet.h
index 3c21316..d8de31e 100644
--- a/drivers/usb/gadget/u_rmnet.h
+++ b/drivers/usb/gadget/u_rmnet.h
@@ -35,19 +35,14 @@
/* to usb host, aka laptop, windows pc etc. Will
* be filled by usb driver of rmnet functionality
*/
- int (*send_cpkt_response)(struct grmnet *g,
- struct rmnet_ctrl_pkt *pkt);
+ int (*send_cpkt_response)(void *g, void *buf, size_t len);
/* to modem, and to be filled by driver implementing
* control function
*/
- int (*send_cpkt_request)(struct grmnet *g,
- u8 port_num,
- struct rmnet_ctrl_pkt *pkt);
+ int (*send_encap_cmd)(u8 port_num, void *buf, size_t len);
- void (*send_cbits_tomodem)(struct grmnet *g,
- u8 port_num,
- int cbits);
+ void (*notify_modem)(void *g, u8 port_num, int cbits);
void (*disconnect)(struct grmnet *g);
void (*connect)(struct grmnet *g);
diff --git a/drivers/usb/gadget/u_rmnet_ctrl_smd.c b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
index fc159cc..8b08b7a 100644
--- a/drivers/usb/gadget/u_rmnet_ctrl_smd.c
+++ b/drivers/usb/gadget/u_rmnet_ctrl_smd.c
@@ -83,6 +83,7 @@
kfree(pkt);
return ERR_PTR(-ENOMEM);
}
+
pkt->len = len;
return pkt;
@@ -103,7 +104,8 @@
struct smd_ch_info *c = container_of(w, struct smd_ch_info, read_w);
struct rmnet_ctrl_port *port = c->port;
int sz;
- struct rmnet_ctrl_pkt *cpkt;
+ size_t len;
+ void *buf;
unsigned long flags;
while (1) {
@@ -114,22 +116,20 @@
if (smd_read_avail(c->ch) < sz)
break;
- cpkt = alloc_rmnet_ctrl_pkt(sz, GFP_KERNEL);
- if (IS_ERR(cpkt)) {
- pr_err("%s: unable to allocate rmnet control pkt\n",
- __func__);
+ buf = kmalloc(sz, GFP_KERNEL);
+ if (!buf)
return;
- }
- cpkt->len = smd_read(c->ch, cpkt->buf, sz);
+
+ len = smd_read(c->ch, buf, sz);
/* send it to USB here */
spin_lock_irqsave(&port->port_lock, flags);
if (port->port_usb && port->port_usb->send_cpkt_response) {
- port->port_usb->send_cpkt_response(
- port->port_usb,
- cpkt);
+ port->port_usb->send_cpkt_response(port->port_usb,
+ buf, len);
c->to_host++;
}
+ kfree(buf);
spin_unlock_irqrestore(&port->port_lock, flags);
}
}
@@ -157,8 +157,7 @@
ret = smd_write(c->ch, cpkt->buf, cpkt->len);
spin_lock_irqsave(&port->port_lock, flags);
if (ret != cpkt->len) {
- pr_err("%s: smd_write failed err:%d\n",
- __func__, ret);
+ pr_err("%s: smd_write failed err:%d\n", __func__, ret);
free_rmnet_ctrl_pkt(cpkt);
break;
}
@@ -169,24 +168,29 @@
}
static int
-grmnet_ctrl_smd_send_cpkt_tomodem(struct grmnet *gr, u8 portno,
- struct rmnet_ctrl_pkt *cpkt)
+grmnet_ctrl_smd_send_cpkt_tomodem(u8 portno,
+ void *buf, size_t len)
{
unsigned long flags;
struct rmnet_ctrl_port *port;
struct smd_ch_info *c;
+ struct rmnet_ctrl_pkt *cpkt;
if (portno >= n_rmnet_ctrl_ports) {
pr_err("%s: Invalid portno#%d\n", __func__, portno);
return -ENODEV;
}
- if (!gr) {
- pr_err("%s: grmnet is null\n", __func__);
- return -ENODEV;
+ port = ctrl_smd_ports[portno].port;
+
+ cpkt = alloc_rmnet_ctrl_pkt(len, GFP_ATOMIC);
+ if (IS_ERR(cpkt)) {
+ pr_err("%s: Unable to allocate ctrl pkt\n", __func__);
+ return -ENOMEM;
}
- port = ctrl_smd_ports[portno].port;
+ memcpy(cpkt->buf, buf, len);
+ cpkt->len = len;
spin_lock_irqsave(&port->port_lock, flags);
c = &port->ctrl_ch;
@@ -207,7 +211,7 @@
#define RMNET_CTRL_DTR 0x01
static void
-gsmd_ctrl_send_cbits_tomodem(struct grmnet *gr, u8 portno, int cbits)
+gsmd_ctrl_send_cbits_tomodem(void *gptr, u8 portno, int cbits)
{
struct rmnet_ctrl_port *port;
struct smd_ch_info *c;
@@ -220,7 +224,7 @@
return;
}
- if (!gr) {
+ if (!gptr) {
pr_err("%s: grmnet is null\n", __func__);
return;
}
@@ -362,8 +366,8 @@
spin_lock_irqsave(&port->port_lock, flags);
port->port_usb = gr;
- gr->send_cpkt_request = grmnet_ctrl_smd_send_cpkt_tomodem;
- gr->send_cbits_tomodem = gsmd_ctrl_send_cbits_tomodem;
+ gr->send_encap_cmd = grmnet_ctrl_smd_send_cpkt_tomodem;
+ gr->notify_modem = gsmd_ctrl_send_cbits_tomodem;
spin_unlock_irqrestore(&port->port_lock, flags);
queue_work(grmnet_ctrl_wq, &port->connect_w);
@@ -395,8 +399,8 @@
spin_lock_irqsave(&port->port_lock, flags);
port->port_usb = 0;
- gr->send_cpkt_request = 0;
- gr->send_cbits_tomodem = 0;
+ gr->send_encap_cmd = 0;
+ gr->notify_modem = 0;
c->cbits_tomodem = 0;
while (!list_empty(&c->tx_q)) {
diff --git a/drivers/usb/gadget/u_sdio.c b/drivers/usb/gadget/u_sdio.c
index 6ba7543..9bd4370 100644
--- a/drivers/usb/gadget/u_sdio.c
+++ b/drivers/usb/gadget/u_sdio.c
@@ -639,10 +639,11 @@
port->cbits_to_modem, ~(port->cbits_to_modem));
}
-void gsdio_ctrl_notify_modem(struct gserial *gser, u8 portno, int ctrl_bits)
+void gsdio_ctrl_notify_modem(void *gptr, u8 portno, int ctrl_bits)
{
struct gsdio_port *port;
int temp;
+ struct gserial *gser = gptr;
if (portno >= n_sdio_ports) {
pr_err("%s: invalid portno#%d\n", __func__, portno);
diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h
index fea53d8..c937006 100644
--- a/drivers/usb/gadget/u_serial.h
+++ b/drivers/usb/gadget/u_serial.h
@@ -55,7 +55,7 @@
int (*send_modem_ctrl_bits)(struct gserial *p, int ctrl_bits);
/* notification changes to modem */
- void (*notify_modem)(struct gserial *gser, u8 portno, int ctrl_bits);
+ void (*notify_modem)(void *gser, u8 portno, int ctrl_bits);
};
/* utilities to allocate/free request and buffer */
diff --git a/drivers/usb/gadget/u_smd.c b/drivers/usb/gadget/u_smd.c
index 60826b9..caccade 100644
--- a/drivers/usb/gadget/u_smd.c
+++ b/drivers/usb/gadget/u_smd.c
@@ -579,10 +579,11 @@
}
}
-static void gsmd_notify_modem(struct gserial *gser, u8 portno, int ctrl_bits)
+static void gsmd_notify_modem(void *gptr, u8 portno, int ctrl_bits)
{
struct gsmd_port *port;
int temp;
+ struct gserial *gser = gptr;
if (portno >= n_smd_ports) {
pr_err("%s: invalid portno#%d\n", __func__, portno);
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index c5dcdbf..13828e0 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -283,27 +283,13 @@
To compile this driver as a module, choose M here: the
module will be called diag_bridge_test. If unsure, choose N.
-config USB_QCOM_DUN_BRIDGE
- tristate "USB Qualcomm modem DUN bridge driver"
+config USB_QCOM_MDM_BRIDGE
+ tristate "USB Qualcomm modem bridge driver for DUN and RMNET"
depends on USB
help
Say Y here if you have a Qualcomm modem device connected via USB that
- will be bridged in kernel space. This driver will enable bridging
- with the gadget serial driver for use in dial-up networking. This is
- not the same as the qcserial driver that exposes a TTY interface to
- userspace.
-
+ will be bridged in kernel space. This driver works as a bridge to pass
+ control and data packets between the modem and peripheral usb gadget
+ driver for dial up network and RMNET.
To compile this driver as a module, choose M here: the module
- will be called dun_bridge.
-
-config USB_QCOM_DUN_BRIDGE_TEST
- tristate "USB Qualcomm modem DUN bridge driver test"
- depends on USB && USB_QCOM_DUN_BRIDGE && !USB_SERIAL_QUALCOMM
- help
- Say Y here if you want to enable the test hook for the
- Qualcomm modem bridge driver. When enabled, this will create
- a debugfs file entry named "dun_bridge_test" which can be used
- to read and write directly to the modem.
-
- To compile this driver as a module, choose M here: the module
- will be called dun_bridge_test.
+ will be called mdm_bridge. If unsure, choose N.
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index bb69a02..b4aee65 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -31,5 +31,5 @@
obj-$(CONFIG_USB_QCOM_DIAG_BRIDGE) += diag_bridge.o
obj-$(CONFIG_USB_QCOM_DIAG_BRIDGE_TEST) += diag_bridge_test.o
-obj-$(CONFIG_USB_QCOM_DUN_BRIDGE) += dun_bridge.o
-obj-$(CONFIG_USB_QCOM_DUN_BRIDGE_TEST) += dun_bridge_test.o
+mdm_bridge-y := mdm_ctrl_bridge.o mdm_data_bridge.o
+obj-$(CONFIG_USB_QCOM_MDM_BRIDGE) += mdm_bridge.o
diff --git a/drivers/usb/misc/dun_bridge.c b/drivers/usb/misc/dun_bridge.c
deleted file mode 100644
index aca7714..0000000
--- a/drivers/usb/misc/dun_bridge.c
+++ /dev/null
@@ -1,520 +0,0 @@
-/* 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/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/kref.h>
-#include <linux/platform_device.h>
-#include <linux/usb.h>
-#include <linux/usb/cdc.h>
-#include <linux/usb/ch9.h>
-#include <asm/unaligned.h>
-#include <mach/usb_dun_bridge.h>
-
-#define DRIVER_DESC "Qualcomm USB DUN bridge driver"
-#define DRIVER_VERSION "1.0"
-
-struct dun_bridge {
- struct usb_device *udev;
- struct usb_interface *intf;
- struct usb_anchor submitted;
- u8 int_in_epaddr;
- unsigned in, out; /* bulk in/out pipes */
-
- struct urb *inturb;
- struct usb_ctrlrequest cmd;
- u8 *ctrl_buf;
-
- struct kref kref;
- struct platform_device *pdev;
-
- struct dun_bridge_ops *ops;
-};
-
-static struct dun_bridge *__dev;
-
-/* This assumes that __dev has already been initialized by probe(). */
-int dun_bridge_open(struct dun_bridge_ops *ops)
-{
- struct dun_bridge *dev = __dev;
- int ret = 0;
-
- if (!dev) {
- err("%s: dev is null", __func__);
- return -ENODEV;
- }
-
- if (!ops || !ops->read_complete || !ops->write_complete)
- return -EINVAL;
-
- dev->ops = ops;
- if (ops->ctrl_status) {
- ret = usb_submit_urb(dev->inturb, GFP_KERNEL);
- if (ret)
- pr_err("%s: submitting int urb failed: %d\n",
- __func__, ret);
- }
-
- return ret;
-}
-EXPORT_SYMBOL(dun_bridge_open);
-
-int dun_bridge_close(void)
-{
- struct dun_bridge *dev = __dev;
- if (!dev)
- return -ENODEV;
-
- dev_dbg(&dev->udev->dev, "%s:", __func__);
- usb_unlink_anchored_urbs(&dev->submitted);
- usb_unlink_urb(dev->inturb);
- dev->ops = NULL;
-
- return 0;
-}
-EXPORT_SYMBOL(dun_bridge_close);
-
-static void read_cb(struct urb *urb)
-{
- struct dun_bridge *dev = urb->context;
- struct dun_bridge_ops *ops;
-
- if (!dev || !dev->intf) {
- pr_err("%s: device is disconnected\n", __func__);
- kfree(urb->transfer_buffer);
- return;
- }
-
- dev_dbg(&dev->udev->dev, "%s: status:%d actual:%d\n", __func__,
- urb->status, urb->actual_length);
-
- usb_autopm_put_interface(dev->intf);
- ops = dev->ops;
- if (ops)
- ops->read_complete(ops->ctxt,
- urb->transfer_buffer,
- urb->transfer_buffer_length,
- /* callback must check this value for error */
- urb->status < 0 ?
- urb->status : urb->actual_length);
- else {
- /* can't call back, free buffer on caller's behalf */
- dev_err(&dev->udev->dev, "cannot complete read callback\n");
- kfree(urb->transfer_buffer);
- }
-}
-
-int dun_bridge_read(void *data, int len)
-{
- struct dun_bridge *dev = __dev;
- struct urb *urb;
- int ret;
-
- if (!dev || !dev->ops)
- return -ENODEV;
-
- if (!dev->intf) {
- pr_err("%s: device is disconnected\n", __func__);
- return -ENODEV;
- }
-
- if (!len) {
- dev_err(&dev->udev->dev, "%s: invalid len:%d\n", __func__, len);
- return -EINVAL;
- }
-
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb) {
- dev_err(&dev->udev->dev, "%s: Unable to alloc urb\n", __func__);
- return -ENOMEM;
- }
-
- usb_fill_bulk_urb(urb, dev->udev, dev->in,
- data, len, read_cb, dev);
- usb_anchor_urb(urb, &dev->submitted);
-
- usb_autopm_get_interface(dev->intf);
- ret = usb_submit_urb(urb, GFP_KERNEL);
- if (ret) {
- dev_err(&dev->udev->dev, "%s: submit urb err:%d\n",
- __func__, ret);
- usb_unanchor_urb(urb);
- usb_autopm_put_interface(dev->intf);
- }
-
- usb_free_urb(urb);
- return ret;
-}
-EXPORT_SYMBOL(dun_bridge_read);
-
-static void write_cb(struct urb *urb)
-{
- struct dun_bridge *dev = urb->context;
- struct dun_bridge_ops *ops;
-
- if (!dev || !dev->intf) {
- pr_err("%s: device is disconnected\n", __func__);
- kfree(urb->transfer_buffer);
- return;
- }
-
- dev_dbg(&dev->udev->dev, "%s: status:%d actual:%d\n", __func__,
- urb->status, urb->actual_length);
-
- usb_autopm_put_interface(dev->intf);
- ops = dev->ops;
- if (ops)
- ops->write_complete(ops->ctxt,
- urb->transfer_buffer,
- urb->transfer_buffer_length,
- /* callback must check this value for error */
- urb->status < 0 ?
- urb->status : urb->actual_length);
- else {
- /* can't call back, free buffer on caller's behalf */
- dev_err(&dev->udev->dev, "cannot complete write callback\n");
- kfree(urb->transfer_buffer);
- }
-}
-
-int dun_bridge_write(void *data, int len)
-{
- struct dun_bridge *dev = __dev;
- struct urb *urb;
- int ret;
-
- if (!dev || !dev->ops)
- return -ENODEV;
-
- if (!dev->intf) {
- pr_err("%s: device is disconnected\n", __func__);
- return -ENODEV;
- }
-
- if (!len) {
- dev_err(&dev->udev->dev, "%s: invalid len:%d\n", __func__, len);
- return -EINVAL;
- }
-
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb) {
- dev_err(&dev->udev->dev, "%s: Unable to alloc urb\n", __func__);
- return -ENOMEM;
- }
-
- usb_fill_bulk_urb(urb, dev->udev, dev->out,
- data, len, write_cb, dev);
- usb_anchor_urb(urb, &dev->submitted);
-
- usb_autopm_get_interface(dev->intf);
- ret = usb_submit_urb(urb, GFP_KERNEL);
- if (ret) {
- dev_err(&dev->udev->dev, "%s: submit urb err:%d\n",
- __func__, ret);
- usb_unanchor_urb(urb);
- usb_autopm_put_interface(dev->intf);
- }
-
- usb_free_urb(urb);
- return ret;
-}
-EXPORT_SYMBOL(dun_bridge_write);
-
-static void ctrl_cb(struct urb *urb)
-{
- struct dun_bridge *dev = urb->context;
- usb_autopm_put_interface(dev->intf);
-}
-
-int dun_bridge_send_ctrl_bits(unsigned ctrl_bits)
-{
- struct dun_bridge *dev = __dev;
- struct urb *urb = NULL;
- int ret;
-
- if (!dev || !dev->intf) {
- pr_err("%s: device is disconnected\n", __func__);
- return -ENODEV;
- }
-
- dev_dbg(&dev->udev->dev, "%s: %#x", __func__, ctrl_bits);
-
- dev->cmd.bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- dev->cmd.bRequest = USB_CDC_REQ_SET_CONTROL_LINE_STATE;
- dev->cmd.wValue = cpu_to_le16(ctrl_bits);
- dev->cmd.wIndex = cpu_to_le16(dev->int_in_epaddr);
- dev->cmd.wLength = 0;
-
- urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!urb) {
- dev_err(&dev->udev->dev, "%s: Unable to alloc urb\n", __func__);
- return -ENOMEM;
- }
-
- usb_fill_control_urb(urb, dev->udev, usb_sndctrlpipe(dev->udev, 0),
- (unsigned char *)&dev->cmd, NULL, 0,
- ctrl_cb, dev);
-
- usb_autopm_get_interface(dev->intf);
- ret = usb_submit_urb(urb, GFP_ATOMIC);
- if (ret) {
- dev_err(&dev->udev->dev, "%s: submit urb err:%d\n",
- __func__, ret);
- usb_autopm_put_interface(dev->intf);
- }
-
- usb_free_urb(urb);
- return ret;
-}
-EXPORT_SYMBOL(dun_bridge_send_ctrl_bits);
-
-static void int_cb(struct urb *urb)
-{
- struct dun_bridge *dev = urb->context;
- struct usb_cdc_notification *dr = urb->transfer_buffer;
- unsigned char *data;
- unsigned int ctrl_bits;
- int status = urb->status;
-
- if (!dev || !dev->intf) {
- pr_err("%s: device is disconnected\n", __func__);
- return;
- }
-
- switch (status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dev_err(&dev->udev->dev,
- "%s - urb shutting down with status: %d\n",
- __func__, status);
- return;
- default:
- dev_err(&dev->udev->dev,
- "%s - nonzero urb status received: %d\n",
- __func__, status);
- goto resubmit_urb;
- }
-
- data = (unsigned char *)(dr + 1);
- switch (dr->bNotificationType) {
- case USB_CDC_NOTIFY_NETWORK_CONNECTION:
- dev_dbg(&dev->udev->dev, "%s network\n", dr->wValue ?
- "connected to" : "disconnected from");
- break;
-
- case USB_CDC_NOTIFY_SERIAL_STATE:
- ctrl_bits = get_unaligned_le16(data);
- dev_dbg(&dev->udev->dev, "serial state: %d\n", ctrl_bits);
- if (dev->ops && dev->ops->ctrl_status)
- dev->ops->ctrl_status(dev->ops->ctxt, ctrl_bits);
- break;
-
- default:
- dev_err(&dev->udev->dev, "unknown notification %d received: "
- "index %d len %d data0 %d data1 %d\n",
- dr->bNotificationType, dr->wIndex,
- dr->wLength, data[0], data[1]);
- break;
- }
-resubmit_urb:
- status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
- if (status)
- dev_err(&dev->udev->dev, "%s: submit urb err:%d\n",
- __func__, status);
-}
-
-static void dun_bridge_delete(struct kref *kref)
-{
- struct dun_bridge *dev = container_of(kref, struct dun_bridge, kref);
-
- __dev = NULL;
- usb_put_dev(dev->udev);
- usb_free_urb(dev->inturb);
- kfree(dev->ctrl_buf);
- kfree(dev);
-}
-
-static int
-dun_bridge_probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
- struct dun_bridge *dev;
- struct usb_host_interface *iface_desc;
- struct usb_endpoint_descriptor *epd;
- __u8 iface_num;
- int i;
- int ctrlsize = 0;
- int ret = -ENOMEM;
-
- iface_desc = intf->cur_altsetting;
- iface_num = iface_desc->desc.bInterfaceNumber;
-
- /* is this interface supported? */
- if (iface_num != id->driver_info)
- return -ENODEV;
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev) {
- pr_err("%s: unable to allocate dev\n", __func__);
- goto error;
- }
-
- dev->pdev = platform_device_alloc("dun_bridge", 0);
- if (!dev->pdev) {
- pr_err("%s: unable to allocate platform device\n", __func__);
- kfree(dev);
- return -ENOMEM;
- }
- __dev = dev;
-
- kref_init(&dev->kref);
- dev->udev = usb_get_dev(interface_to_usbdev(intf));
- dev->intf = intf;
-
- init_usb_anchor(&dev->submitted);
- dev->inturb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->inturb) {
- ret = -ENOMEM;
- goto error;
- }
-
- for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
- epd = &iface_desc->endpoint[i].desc;
-
- if (usb_endpoint_is_int_in(epd)) {
- dev->int_in_epaddr = epd->bEndpointAddress;
- ctrlsize = le16_to_cpu(epd->wMaxPacketSize);
-
- dev->ctrl_buf = kzalloc(ctrlsize, GFP_KERNEL);
- if (!dev->ctrl_buf) {
- ret = -ENOMEM;
- goto error;
- }
-
- usb_fill_int_urb(dev->inturb, dev->udev,
- usb_rcvintpipe(dev->udev,
- dev->int_in_epaddr),
- dev->ctrl_buf, ctrlsize,
- int_cb, dev, epd->bInterval);
-
- } else if (usb_endpoint_is_bulk_in(epd))
- dev->in = usb_rcvbulkpipe(dev->udev,
- epd->bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK);
-
- else if (usb_endpoint_is_bulk_out(epd))
- dev->out = usb_sndbulkpipe(dev->udev,
- epd->bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK);
- }
-
- if (!dev->int_in_epaddr && !dev->in && !dev->out) {
- dev_err(&dev->udev->dev, "%s: could not find all endpoints\n",
- __func__);
- ret = -ENODEV;
- goto error;
- }
-
- usb_set_intfdata(intf, dev);
- platform_device_add(dev->pdev);
- return 0;
-error:
- if (dev)
- kref_put(&dev->kref, dun_bridge_delete);
- return ret;
-}
-
-static void dun_bridge_disconnect(struct usb_interface *intf)
-{
- struct dun_bridge *dev = usb_get_intfdata(intf);
-
- platform_device_del(dev->pdev);
- usb_set_intfdata(intf, NULL);
- dev->intf = NULL;
-
- kref_put(&dev->kref, dun_bridge_delete);
-
- pr_debug("%s: DUN Bridge now disconnected\n", __func__);
-}
-
-static int dun_bridge_suspend(struct usb_interface *intf, pm_message_t message)
-{
- struct dun_bridge *dev = usb_get_intfdata(intf);
-
- dev_dbg(&dev->udev->dev, "%s:", __func__);
- usb_unlink_anchored_urbs(&dev->submitted);
- usb_unlink_urb(dev->inturb);
-
- return 0;
-}
-
-static int dun_bridge_resume(struct usb_interface *intf)
-{
- struct dun_bridge *dev = usb_get_intfdata(intf);
- int ret = 0;
-
- if (dev->ops && dev->ops->ctrl_status) {
- ret = usb_submit_urb(dev->inturb, GFP_KERNEL);
- if (ret)
- dev_err(&dev->udev->dev, "%s: submit int urb err: %d\n",
- __func__, ret);
- }
-
- return ret;
-}
-
-#define VALID_INTERFACE_NUM 2
-static const struct usb_device_id id_table[] = {
- { USB_DEVICE(0x05c6, 0x9001), /* Generic QC Modem device */
- .driver_info = VALID_INTERFACE_NUM },
- { } /* Terminating entry */
-};
-MODULE_DEVICE_TABLE(usb, id_table);
-
-static struct usb_driver dun_bridge_driver = {
- .name = "dun_usb_bridge",
- .probe = dun_bridge_probe,
- .disconnect = dun_bridge_disconnect,
- .id_table = id_table,
- .suspend = dun_bridge_suspend,
- .resume = dun_bridge_resume,
- .supports_autosuspend = true,
-};
-
-static int __init dun_bridge_init(void)
-{
- int ret;
-
- ret = usb_register(&dun_bridge_driver);
- if (ret)
- pr_err("%s: unable to register dun_bridge_driver\n", __func__);
-
- return ret;
-}
-
-static void __exit dun_bridge_exit(void)
-{
- usb_deregister(&dun_bridge_driver);
-}
-
-module_init(dun_bridge_init);
-module_exit(dun_bridge_exit);
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL V2");
diff --git a/drivers/usb/misc/dun_bridge_test.c b/drivers/usb/misc/dun_bridge_test.c
deleted file mode 100644
index d545e13..0000000
--- a/drivers/usb/misc/dun_bridge_test.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/*
- * 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/debugfs.h>
-#include <linux/usb/cdc.h>
-#include <linux/uaccess.h>
-#include <mach/usb_dun_bridge.h>
-
-#define RD_BUF_SIZE 2048
-#define DUN_TEST_CONNECTED 0
-
-
-struct dun_bridge_test_dev {
- char *read_buf;
- size_t buflen;
- struct work_struct read_w;
- unsigned long flags;
-
- struct dun_bridge_ops ops;
-};
-static struct dun_bridge_test_dev *__dev;
-
-static struct dentry *dfile;
-
-static void
-dun_bridge_test_read_complete(void *d, char *buf, size_t size, size_t actual)
-{
- if (actual < 0) {
- pr_err("%s: read complete err\n", __func__);
- return;
- }
-
- __dev->buflen = actual;
- buf[actual] = 0;
-
- pr_info("%s: %s\n", __func__, buf);
-
- if (test_bit(DUN_TEST_CONNECTED, &__dev->flags))
- schedule_work(&__dev->read_w);
-}
-
-static void dun_bridge_test_read_work(struct work_struct *w)
-{
- struct dun_bridge_test_dev *dev =
- container_of(w, struct dun_bridge_test_dev, read_w);
-
- dun_bridge_read(dev->read_buf, RD_BUF_SIZE);
-}
-
-static void
-dun_bridge_test_write_complete(void *d, char *buf, size_t size, size_t actual)
-{
- struct dun_bridge_test_dev *dev = d;
-
- if (actual > 0)
- schedule_work(&dev->read_w);
-
- kfree(buf);
-}
-
-#if defined(CONFIG_DEBUG_FS)
-#define DEBUG_BUF_SIZE 1024
-
-#define ACM_CTRL_DTR 0x01
-#define ACM_CTRL_RTS 0x02
-
-static int debug_open(struct inode *inode, struct file *file)
-{
- struct dun_bridge_test_dev *dev = __dev;
- int ret = 0;
-
- if (!dev)
- return -ENODEV;
-
- if (!test_bit(DUN_TEST_CONNECTED, &dev->flags)) {
- ret = dun_bridge_open(&dev->ops);
- if (ret)
- return ret;
- set_bit(DUN_TEST_CONNECTED, &dev->flags);
- dun_bridge_send_ctrl_bits(ACM_CTRL_DTR | ACM_CTRL_RTS);
- }
-
- return ret;
-}
-
-static ssize_t debug_read(struct file *file, char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct dun_bridge_test_dev *dev = __dev;
- return simple_read_from_buffer(ubuf, count, ppos,
- dev->read_buf, dev->buflen);
-}
-
-static ssize_t debug_write(struct file *file, const char __user *ubuf,
- size_t count, loff_t *ppos)
-{
- struct dun_bridge_test_dev *dev = __dev;
- unsigned char *buf;
- int ret;
-
- if (!dev)
- return -ENODEV;
-
- buf = kmalloc(count, GFP_KERNEL);
- if (!buf) {
- pr_err("%s: unable to allocate mem for writing\n", __func__);
- return -ENOMEM;
- }
-
- if (!copy_from_user(buf, ubuf, count)) {
- ret = dun_bridge_write(buf, count);
- if (ret < 0) {
- pr_err("%s: error writing to dun_bridge\n", __func__);
- kfree(buf);
- return ret;
- }
- } else {
- pr_err("%s: error copying for writing\n", __func__);
- kfree(buf);
- }
-
- return count;
-}
-
-const struct file_operations dun_bridge_test_debug_ops = {
- .open = debug_open,
- .read = debug_read,
- .write = debug_write,
-};
-
-static void dun_bridge_test_debug_init(void)
-{
- dfile = debugfs_create_file("dun_bridge_test", 0555, NULL,
- NULL, &dun_bridge_test_debug_ops);
-}
-#else
-static void dun_bridge_test_debug_init(void) { }
-#endif
-
-static int __init dun_bridge_test_init(void)
-{
- struct dun_bridge_test_dev *dev;
-
- pr_info("%s\n", __func__);
-
- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
- return -ENOMEM;
-
- __dev = dev;
-
- dev->ops.read_complete = dun_bridge_test_read_complete;
- dev->ops.write_complete = dun_bridge_test_write_complete;
- dev->read_buf = kmalloc(RD_BUF_SIZE, GFP_KERNEL);
- if (!dev->read_buf) {
- pr_err("%s: unable to allocate read buffer\n", __func__);
- kfree(dev);
- return -ENOMEM;
- }
-
- dev->ops.ctxt = dev;
- INIT_WORK(&dev->read_w, dun_bridge_test_read_work);
-
- dun_bridge_test_debug_init();
-
- return 0;
-}
-
-static void __exit dun_bridge_test_exit(void)
-{
- struct dun_bridge_test_dev *dev = __dev;
-
- pr_info("%s:\n", __func__);
-
- if (test_bit(DUN_TEST_CONNECTED, &dev->flags))
- dun_bridge_close();
-
- debugfs_remove(dfile);
-
- kfree(dev->read_buf);
- kfree(dev);
-}
-
-module_init(dun_bridge_test_init);
-module_exit(dun_bridge_test_exit);
-
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE("GPL V2");
diff --git a/drivers/usb/misc/mdm_ctrl_bridge.c b/drivers/usb/misc/mdm_ctrl_bridge.c
new file mode 100644
index 0000000..87adf2e
--- /dev/null
+++ b/drivers/usb/misc/mdm_ctrl_bridge.c
@@ -0,0 +1,729 @@
+/* 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/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/ratelimit.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/cdc.h>
+#include <linux/termios.h>
+#include <asm/unaligned.h>
+#include <mach/usb_bridge.h>
+
+static const char *ctrl_bridge_names[] = {
+ "dun_ctrl_hsic0",
+ "rmnet_ctrl_hsic0"
+};
+
+/* polling interval for Interrupt ep */
+#define HS_INTERVAL 7
+#define FS_LS_INTERVAL 3
+
+#define ACM_CTRL_DTR (1 << 0)
+#define DEFAULT_READ_URB_LENGTH 4096
+
+struct ctrl_bridge {
+
+ struct usb_device *udev;
+ struct usb_interface *intf;
+
+ unsigned int int_pipe;
+ struct urb *inturb;
+ void *intbuf;
+
+ struct urb *readurb;
+ void *readbuf;
+
+ struct usb_anchor tx_submitted;
+ struct usb_ctrlrequest *in_ctlreq;
+
+ struct bridge *brdg;
+ struct platform_device *pdev;
+
+ /* input control lines (DSR, CTS, CD, RI) */
+ unsigned int cbits_tohost;
+
+ /* output control lines (DTR, RTS) */
+ unsigned int cbits_tomdm;
+
+ /* counters */
+ unsigned int snd_encap_cmd;
+ unsigned int get_encap_res;
+ unsigned int resp_avail;
+ unsigned int set_ctrl_line_sts;
+ unsigned int notify_ser_state;
+
+};
+
+static struct ctrl_bridge *__dev[MAX_BRIDGE_DEVICES];
+
+/* counter used for indexing ctrl bridge devices */
+static int ch_id;
+
+unsigned int ctrl_bridge_get_cbits_tohost(unsigned int id)
+{
+ struct ctrl_bridge *dev;
+
+ if (id >= MAX_BRIDGE_DEVICES)
+ return -EINVAL;
+
+ dev = __dev[id];
+ if (!dev)
+ return -ENODEV;
+
+ return dev->cbits_tohost;
+}
+EXPORT_SYMBOL(ctrl_bridge_get_cbits_tohost);
+
+int ctrl_bridge_set_cbits(unsigned int id, unsigned int cbits)
+{
+ struct ctrl_bridge *dev;
+ struct bridge *brdg;
+ int retval;
+
+ if (id >= MAX_BRIDGE_DEVICES)
+ return -EINVAL;
+
+ dev = __dev[id];
+ if (!dev)
+ return -ENODEV;
+
+ pr_debug("%s: dev[id] =%u cbits : %u\n", __func__, id, cbits);
+
+ brdg = dev->brdg;
+ if (!brdg)
+ return -ENODEV;
+
+ dev->cbits_tomdm = cbits;
+
+ retval = ctrl_bridge_write(id, NULL, 0);
+
+ /* if DTR is high, update latest modem info to host */
+ if (brdg && (cbits & ACM_CTRL_DTR) && brdg->ops.send_cbits)
+ brdg->ops.send_cbits(brdg->ctx, dev->cbits_tohost);
+
+ return retval;
+}
+EXPORT_SYMBOL(ctrl_bridge_set_cbits);
+
+static void resp_avail_cb(struct urb *urb)
+{
+ struct ctrl_bridge *dev = urb->context;
+ struct usb_device *udev;
+ int status = 0;
+ int resubmit_urb = 1;
+ struct bridge *brdg = dev->brdg;
+
+ udev = interface_to_usbdev(dev->intf);
+ switch (urb->status) {
+ case 0:
+ /*success*/
+ dev->get_encap_res++;
+ if (brdg && brdg->ops.send_pkt)
+ brdg->ops.send_pkt(brdg->ctx, urb->transfer_buffer,
+ urb->actual_length);
+ break;
+
+ /*do not resubmit*/
+ case -ESHUTDOWN:
+ case -ENOENT:
+ case -ECONNRESET:
+ /* unplug */
+ case -EPROTO:
+ /*babble error*/
+ resubmit_urb = 0;
+ /*resubmit*/
+ case -EOVERFLOW:
+ default:
+ dev_dbg(&udev->dev, "%s: non zero urb status = %d\n",
+ __func__, urb->status);
+ }
+
+ if (resubmit_urb) {
+ /*re- submit int urb to check response available*/
+ status = usb_submit_urb(dev->inturb, GFP_ATOMIC);
+ if (status)
+ dev_err(&udev->dev,
+ "%s: Error re-submitting Int URB %d\n",
+ __func__, status);
+ }
+}
+
+static void notification_available_cb(struct urb *urb)
+{
+ int status;
+ struct usb_cdc_notification *ctrl;
+ struct usb_device *udev;
+ struct ctrl_bridge *dev = urb->context;
+ struct bridge *brdg = dev->brdg;
+ unsigned int ctrl_bits;
+ unsigned char *data;
+
+ udev = interface_to_usbdev(dev->intf);
+
+ switch (urb->status) {
+ case 0:
+ /*success*/
+ break;
+ case -ESHUTDOWN:
+ case -ENOENT:
+ case -ECONNRESET:
+ case -EPROTO:
+ /* unplug */
+ return;
+ case -EPIPE:
+ dev_err(&udev->dev, "%s: stall on int endpoint\n", __func__);
+ /* TBD : halt to be cleared in work */
+ case -EOVERFLOW:
+ default:
+ pr_debug_ratelimited("%s: non zero urb status = %d\n",
+ __func__, urb->status);
+ goto resubmit_int_urb;
+ }
+
+ ctrl = (struct usb_cdc_notification *)urb->transfer_buffer;
+ data = (unsigned char *)(ctrl + 1);
+
+ switch (ctrl->bNotificationType) {
+ case USB_CDC_NOTIFY_RESPONSE_AVAILABLE:
+ dev->resp_avail++;
+ usb_fill_control_urb(dev->readurb, udev,
+ usb_rcvctrlpipe(udev, 0),
+ (unsigned char *)dev->in_ctlreq,
+ dev->readbuf,
+ DEFAULT_READ_URB_LENGTH,
+ resp_avail_cb, dev);
+
+ status = usb_submit_urb(dev->readurb, GFP_ATOMIC);
+ if (status) {
+ dev_err(&udev->dev,
+ "%s: Error submitting Read URB %d\n",
+ __func__, status);
+ goto resubmit_int_urb;
+ }
+ return;
+ case USB_CDC_NOTIFY_NETWORK_CONNECTION:
+ dev_dbg(&udev->dev, "%s network\n", ctrl->wValue ?
+ "connected to" : "disconnected from");
+ break;
+ case USB_CDC_NOTIFY_SERIAL_STATE:
+ dev->notify_ser_state++;
+ ctrl_bits = get_unaligned_le16(data);
+ dev_dbg(&udev->dev, "serial state: %d\n", ctrl_bits);
+ dev->cbits_tohost = ctrl_bits;
+ if (brdg && brdg->ops.send_cbits)
+ brdg->ops.send_cbits(brdg->ctx, ctrl_bits);
+ break;
+ default:
+ dev_err(&udev->dev, "%s: unknown notification %d received:"
+ "index %d len %d data0 %d data1 %d",
+ __func__, ctrl->bNotificationType, ctrl->wIndex,
+ ctrl->wLength, data[0], data[1]);
+ }
+
+resubmit_int_urb:
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status)
+ dev_err(&udev->dev, "%s: Error re-submitting Int URB %d\n",
+ __func__, status);
+}
+
+int ctrl_bridge_start_read(struct ctrl_bridge *dev)
+{
+ int retval = 0;
+ struct usb_device *udev;
+
+ udev = interface_to_usbdev(dev->intf);
+
+ retval = usb_autopm_get_interface_async(dev->intf);
+ if (retval < 0) {
+ dev_err(&udev->dev, "%s resumption fail\n", __func__);
+ goto done_nopm;
+ }
+
+ retval = usb_submit_urb(dev->inturb, GFP_KERNEL);
+ if (retval < 0)
+ dev_err(&udev->dev, "%s intr submit %d\n", __func__, retval);
+
+ usb_autopm_put_interface_async(dev->intf);
+done_nopm:
+ return retval;
+}
+
+static int ctrl_bridge_stop_read(struct ctrl_bridge *dev)
+{
+ if (dev->readurb) {
+ dev_dbg(&dev->udev->dev, "killing rcv urb\n");
+ usb_unlink_urb(dev->readurb);
+ }
+
+ if (dev->inturb) {
+ dev_dbg(&dev->udev->dev, "killing int urb\n");
+ usb_unlink_urb(dev->inturb);
+ }
+
+ return 0;
+}
+
+int ctrl_bridge_open(struct bridge *brdg)
+{
+ struct ctrl_bridge *dev;
+
+ if (!brdg) {
+ err("bridge is null\n");
+ return -EINVAL;
+ }
+
+ if (brdg->ch_id >= MAX_BRIDGE_DEVICES)
+ return -EINVAL;
+
+ dev = __dev[brdg->ch_id];
+ if (!dev) {
+ err("dev is null\n");
+ return -ENODEV;
+ }
+
+ dev->brdg = brdg;
+ dev->snd_encap_cmd = 0;
+ dev->get_encap_res = 0;
+ dev->resp_avail = 0;
+ dev->set_ctrl_line_sts = 0;
+ dev->notify_ser_state = 0;
+
+ return ctrl_bridge_start_read(dev);
+}
+EXPORT_SYMBOL(ctrl_bridge_open);
+
+void ctrl_bridge_close(unsigned int id)
+{
+ struct ctrl_bridge *dev;
+
+ if (id >= MAX_BRIDGE_DEVICES)
+ return;
+
+ dev = __dev[id];
+ if (!dev && !dev->brdg)
+ return;
+
+ dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+ ctrl_bridge_set_cbits(dev->brdg->ch_id, 0);
+ usb_unlink_anchored_urbs(&dev->tx_submitted);
+ ctrl_bridge_stop_read(dev);
+
+ dev->brdg = NULL;
+}
+EXPORT_SYMBOL(ctrl_bridge_close);
+
+static void ctrl_write_callback(struct urb *urb)
+{
+
+ if (urb->status) {
+ pr_debug("Write status/size %d/%d\n",
+ urb->status, urb->actual_length);
+ }
+
+ kfree(urb->transfer_buffer);
+ kfree(urb->setup_packet);
+ usb_free_urb(urb);
+}
+
+int ctrl_bridge_write(unsigned int id, char *data, size_t size)
+{
+ int result;
+ struct urb *writeurb;
+ struct usb_ctrlrequest *out_ctlreq;
+ struct usb_device *udev;
+ struct ctrl_bridge *dev;
+
+ if (id >= MAX_BRIDGE_DEVICES) {
+ result = -EINVAL;
+ goto free_data;
+ }
+
+ dev = __dev[id];
+
+ if (!dev) {
+ result = -ENODEV;
+ goto free_data;
+ }
+
+ udev = interface_to_usbdev(dev->intf);
+
+ dev_dbg(&udev->dev, "%s:[id]:%u: write (%d bytes)\n",
+ __func__, id, size);
+
+ writeurb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!writeurb) {
+ dev_err(&udev->dev, "%s: error allocating read urb\n",
+ __func__);
+ result = -ENOMEM;
+ goto free_data;
+ }
+
+ out_ctlreq = kmalloc(sizeof(*out_ctlreq), GFP_ATOMIC);
+ if (!out_ctlreq) {
+ dev_err(&udev->dev,
+ "%s: error allocating setup packet buffer\n",
+ __func__);
+ result = -ENOMEM;
+ goto free_urb;
+ }
+
+ /* CDC Send Encapsulated Request packet */
+ out_ctlreq->bRequestType = (USB_DIR_OUT | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE);
+ if (!data && !size) {
+ out_ctlreq->bRequest = USB_CDC_REQ_SET_CONTROL_LINE_STATE;
+ out_ctlreq->wValue = dev->cbits_tomdm;
+ dev->set_ctrl_line_sts++;
+ } else {
+ out_ctlreq->bRequest = USB_CDC_SEND_ENCAPSULATED_COMMAND;
+ out_ctlreq->wValue = 0;
+ dev->snd_encap_cmd++;
+ }
+ out_ctlreq->wIndex =
+ dev->intf->cur_altsetting->desc.bInterfaceNumber;
+ out_ctlreq->wLength = cpu_to_le16(size);
+
+ usb_fill_control_urb(writeurb, udev,
+ usb_sndctrlpipe(udev, 0),
+ (unsigned char *)out_ctlreq,
+ (void *)data, size,
+ ctrl_write_callback, NULL);
+
+ result = usb_autopm_get_interface_async(dev->intf);
+ if (result < 0) {
+ dev_err(&udev->dev, "%s: unable to resume interface: %d\n",
+ __func__, result);
+
+ /*
+ * Revisit: if (result == -EPERM)
+ * bridge_suspend(dev->intf, PMSG_SUSPEND);
+ */
+
+ goto free_ctrlreq;
+ }
+
+ usb_anchor_urb(writeurb, &dev->tx_submitted);
+ result = usb_submit_urb(writeurb, GFP_ATOMIC);
+ if (result < 0) {
+ dev_err(&udev->dev, "%s: submit URB error %d\n",
+ __func__, result);
+ usb_autopm_put_interface_async(dev->intf);
+ goto unanchor_urb;
+ }
+
+ return size;
+
+unanchor_urb:
+ usb_unanchor_urb(writeurb);
+free_ctrlreq:
+ kfree(out_ctlreq);
+free_urb:
+ usb_free_urb(writeurb);
+free_data:
+ kfree(data);
+
+ return result;
+}
+EXPORT_SYMBOL(ctrl_bridge_write);
+
+int ctrl_bridge_suspend(unsigned int id)
+{
+ struct ctrl_bridge *dev;
+
+ if (id >= MAX_BRIDGE_DEVICES)
+ return -EINVAL;
+
+ dev = __dev[id];
+ if (!dev)
+ return -ENODEV;
+
+ usb_kill_anchored_urbs(&dev->tx_submitted);
+
+ return ctrl_bridge_stop_read(dev);
+}
+
+int ctrl_bridge_resume(unsigned int id)
+{
+ struct ctrl_bridge *dev;
+
+ if (id >= MAX_BRIDGE_DEVICES)
+ return -EINVAL;
+
+ dev = __dev[id];
+ if (!dev)
+ return -ENODEV;
+
+ return ctrl_bridge_start_read(dev);
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE 1024
+static ssize_t ctrl_bridge_read_stats(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct ctrl_bridge *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 < ch_id; i++) {
+ dev = __dev[i];
+ if (!dev)
+ continue;
+
+ temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ "\nName#%s dev %p\n"
+ "snd encap cmd cnt: %u\n"
+ "get encap res cnt: %u\n"
+ "res available cnt: %u\n"
+ "set ctrlline sts cnt: %u\n"
+ "notify ser state cnt: %u\n"
+ "cbits_tomdm: %d\n"
+ "cbits_tohost: %d\n",
+ dev->pdev->name, dev,
+ dev->snd_encap_cmd,
+ dev->get_encap_res,
+ dev->resp_avail,
+ dev->set_ctrl_line_sts,
+ dev->notify_ser_state,
+ dev->cbits_tomdm,
+ dev->cbits_tohost);
+
+ }
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static ssize_t ctrl_bridge_reset_stats(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct ctrl_bridge *dev;
+ int i;
+
+ for (i = 0; i < ch_id; i++) {
+ dev = __dev[i];
+ if (!dev)
+ continue;
+
+ dev->snd_encap_cmd = 0;
+ dev->get_encap_res = 0;
+ dev->resp_avail = 0;
+ dev->set_ctrl_line_sts = 0;
+ dev->notify_ser_state = 0;
+ }
+ return count;
+}
+
+const struct file_operations ctrl_stats_ops = {
+ .read = ctrl_bridge_read_stats,
+ .write = ctrl_bridge_reset_stats,
+};
+
+struct dentry *ctrl_dent;
+struct dentry *ctrl_dfile;
+static void ctrl_bridge_debugfs_init(void)
+{
+ ctrl_dent = debugfs_create_dir("ctrl_hsic_bridge", 0);
+ if (IS_ERR(ctrl_dent))
+ return;
+
+ ctrl_dfile =
+ debugfs_create_file("status", 0644, ctrl_dent, 0,
+ &ctrl_stats_ops);
+ if (!ctrl_dfile || IS_ERR(ctrl_dfile))
+ debugfs_remove(ctrl_dent);
+}
+
+static void ctrl_bridge_debugfs_exit(void)
+{
+ debugfs_remove(ctrl_dfile);
+ debugfs_remove(ctrl_dent);
+}
+
+#else
+static void ctrl_bridge_debugfs_init(void) { }
+static void ctrl_bridge_debugfs_exit(void) { }
+#endif
+
+int
+ctrl_bridge_probe(struct usb_interface *ifc, struct usb_host_endpoint *int_in,
+ int id)
+{
+ struct ctrl_bridge *dev;
+ struct usb_device *udev;
+ struct usb_endpoint_descriptor *ep;
+ u16 wMaxPacketSize;
+ int retval = 0;
+ int interval;
+
+ udev = interface_to_usbdev(ifc);
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ dev_err(&udev->dev, "%s: unable to allocate dev\n",
+ __func__);
+ return -ENOMEM;
+ }
+ dev->pdev = platform_device_alloc(ctrl_bridge_names[id], id);
+ if (!dev->pdev) {
+ dev_err(&dev->udev->dev,
+ "%s: unable to allocate platform device\n", __func__);
+ retval = -ENOMEM;
+ goto nomem;
+ }
+
+ dev->udev = udev;
+ dev->int_pipe = usb_rcvintpipe(udev,
+ int_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ dev->intf = ifc;
+
+ init_usb_anchor(&dev->tx_submitted);
+
+ /*use max pkt size from ep desc*/
+ ep = &dev->intf->cur_altsetting->endpoint[0].desc;
+
+ dev->inturb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->inturb) {
+ dev_err(&udev->dev, "%s: error allocating int urb\n", __func__);
+ retval = -ENOMEM;
+ goto pdev_del;
+ }
+
+ wMaxPacketSize = le16_to_cpu(ep->wMaxPacketSize);
+
+ dev->intbuf = kmalloc(wMaxPacketSize, GFP_KERNEL);
+ if (!dev->intbuf) {
+ dev_err(&udev->dev, "%s: error allocating int buffer\n",
+ __func__);
+ retval = -ENOMEM;
+ goto free_inturb;
+ }
+
+ interval =
+ (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);
+
+ dev->readurb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->readurb) {
+ dev_err(&udev->dev, "%s: error allocating read urb\n",
+ __func__);
+ retval = -ENOMEM;
+ goto free_intbuf;
+ }
+
+ dev->readbuf = kmalloc(DEFAULT_READ_URB_LENGTH, GFP_KERNEL);
+ if (!dev->readbuf) {
+ dev_err(&udev->dev, "%s: error allocating read buffer\n",
+ __func__);
+ retval = -ENOMEM;
+ goto free_rurb;
+ }
+
+ dev->in_ctlreq = kmalloc(sizeof(*dev->in_ctlreq), GFP_KERNEL);
+ if (!dev->in_ctlreq) {
+ dev_err(&udev->dev,
+ "%s:error allocating setup packet buffer\n",
+ __func__);
+ retval = -ENOMEM;
+ goto free_rbuf;
+ }
+
+ 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);
+
+ __dev[id] = dev;
+
+ platform_device_add(dev->pdev);
+
+ ch_id++;
+
+ return retval;
+
+free_rbuf:
+ kfree(dev->readbuf);
+free_rurb:
+ usb_free_urb(dev->readurb);
+free_intbuf:
+ kfree(dev->intbuf);
+free_inturb:
+ usb_free_urb(dev->inturb);
+pdev_del:
+ platform_device_del(dev->pdev);
+nomem:
+ kfree(dev);
+
+ return retval;
+}
+
+void ctrl_bridge_disconnect(unsigned int id)
+{
+ struct ctrl_bridge *dev = __dev[id];
+
+ dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+ kfree(dev->in_ctlreq);
+ kfree(dev->readbuf);
+ kfree(dev->intbuf);
+
+ usb_free_urb(dev->readurb);
+ usb_free_urb(dev->inturb);
+
+ platform_device_del(dev->pdev);
+ __dev[id] = NULL;
+ ch_id--;
+
+ kfree(dev);
+}
+
+static int __init ctrl_bridge_init(void)
+{
+ ctrl_bridge_debugfs_init();
+
+ return 0;
+}
+module_init(ctrl_bridge_init);
+
+static void __exit ctrl_bridge_exit(void)
+{
+ ctrl_bridge_debugfs_exit();
+}
+module_exit(ctrl_bridge_exit);
+
+MODULE_DESCRIPTION("Qualcomm modem control bridge driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/misc/mdm_data_bridge.c b/drivers/usb/misc/mdm_data_bridge.c
new file mode 100644
index 0000000..c41fcfb
--- /dev/null
+++ b/drivers/usb/misc/mdm_data_bridge.c
@@ -0,0 +1,923 @@
+/* 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/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/ratelimit.h>
+#include <mach/usb_bridge.h>
+
+#define MAX_RX_URBS 50
+#define RMNET_RX_BUFSIZE 2048
+
+#define STOP_SUBMIT_URB_LIMIT 400
+#define FLOW_CTRL_EN_THRESHOLD 500
+#define FLOW_CTRL_DISABLE 300
+#define FLOW_CTRL_SUPPORT 1
+
+static const char *data_bridge_names[] = {
+ "dun_data_hsic0",
+ "rmnet_data_hsic0"
+};
+
+static struct workqueue_struct *bridge_wq;
+
+static unsigned int fctrl_support = FLOW_CTRL_SUPPORT;
+module_param(fctrl_support, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int fctrl_en_thld = FLOW_CTRL_EN_THRESHOLD;
+module_param(fctrl_en_thld, uint, S_IRUGO | S_IWUSR);
+
+static unsigned int fctrl_dis_thld = FLOW_CTRL_DISABLE;
+module_param(fctrl_dis_thld, uint, S_IRUGO | S_IWUSR);
+
+unsigned int max_rx_urbs = MAX_RX_URBS;
+module_param(max_rx_urbs, uint, S_IRUGO | S_IWUSR);
+
+unsigned int stop_submit_urb_limit = STOP_SUBMIT_URB_LIMIT;
+module_param(stop_submit_urb_limit, uint, S_IRUGO | S_IWUSR);
+
+#define TX_HALT BIT(0)
+#define RX_HALT BIT(1)
+#define SUSPENDED BIT(2)
+
+struct data_bridge {
+ struct usb_interface *intf;
+ struct usb_device *udev;
+ unsigned int bulk_in;
+ unsigned int bulk_out;
+
+ /* keep track of in-flight URBs */
+ struct usb_anchor tx_active;
+ struct usb_anchor rx_active;
+
+ /* keep track of outgoing URBs during suspend */
+ struct usb_anchor delayed;
+
+ struct list_head rx_idle;
+ struct sk_buff_head rx_done;
+
+ struct workqueue_struct *wq;
+ struct work_struct process_rx_w;
+
+ struct bridge *brdg;
+
+ /* work queue function for handling halt conditions */
+ struct work_struct kevent;
+
+ unsigned long flags;
+
+ struct platform_device *pdev;
+
+ /* counters */
+ atomic_t pending_txurbs;
+ unsigned int txurb_drp_cnt;
+ unsigned long to_host;
+ unsigned long to_modem;
+ unsigned int tx_throttled_cnt;
+ unsigned int tx_unthrottled_cnt;
+ unsigned int rx_throttled_cnt;
+ unsigned int rx_unthrottled_cnt;
+};
+
+static struct data_bridge *__dev[MAX_BRIDGE_DEVICES];
+
+/* counter used for indexing data bridge devices */
+static int ch_id;
+
+static int submit_rx_urb(struct data_bridge *dev, struct urb *urb,
+ gfp_t flags);
+
+static inline bool rx_halted(struct data_bridge *dev)
+{
+ return test_bit(RX_HALT, &dev->flags);
+}
+
+static inline bool rx_throttled(struct bridge *brdg)
+{
+ return test_bit(RX_THROTTLED, &brdg->flags);
+}
+
+int data_bridge_unthrottle_rx(unsigned int id)
+{
+ struct data_bridge *dev;
+
+ if (id >= MAX_BRIDGE_DEVICES)
+ return -EINVAL;
+
+ dev = __dev[id];
+ if (!dev && !dev->brdg)
+ return -ENODEV;
+
+ dev->rx_unthrottled_cnt++;
+ queue_work(dev->wq, &dev->process_rx_w);
+
+ return 0;
+}
+EXPORT_SYMBOL(data_bridge_unthrottle_rx);
+
+static void data_bridge_process_rx(struct work_struct *work)
+{
+ int retval;
+ unsigned long flags;
+ struct urb *rx_idle;
+ struct sk_buff *skb;
+ struct data_bridge *dev =
+ container_of(work, struct data_bridge, process_rx_w);
+
+ struct bridge *brdg = dev->brdg;
+
+ if (!brdg || !brdg->ops.send_pkt || rx_halted(dev))
+ return;
+
+ while (!rx_throttled(brdg) && (skb = skb_dequeue(&dev->rx_done))) {
+ dev->to_host++;
+ /* hand off sk_buff to client,they'll need to free it */
+ retval = brdg->ops.send_pkt(brdg->ctx, skb, skb->len);
+ if (retval == -ENOTCONN || retval == -EINVAL) {
+ return;
+ } else if (retval == -EBUSY) {
+ dev->rx_throttled_cnt++;
+ break;
+ }
+ }
+
+ spin_lock_irqsave(&dev->rx_done.lock, flags);
+ if (dev->rx_done.qlen > stop_submit_urb_limit && rx_throttled(brdg)) {
+ spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+ return;
+ }
+
+ while (!list_empty(&dev->rx_idle)) {
+
+ rx_idle = list_first_entry(&dev->rx_idle, struct urb, urb_list);
+ list_del(&rx_idle->urb_list);
+ spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+ retval = submit_rx_urb(dev, rx_idle, GFP_KERNEL);
+ spin_lock_irqsave(&dev->rx_done.lock, flags);
+ if (retval)
+ break;
+ }
+ spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+}
+
+static void data_bridge_read_cb(struct urb *urb)
+{
+ struct bridge *brdg;
+ struct sk_buff *skb = urb->context;
+ struct data_bridge *dev = *(struct data_bridge **)skb->cb;
+ bool queue = 0;
+
+ brdg = dev->brdg;
+
+ skb_put(skb, urb->actual_length);
+
+ switch (urb->status) {
+ case 0: /* success */
+ queue = 1;
+ spin_lock(&dev->rx_done.lock);
+ __skb_queue_tail(&dev->rx_done, skb);
+ spin_unlock(&dev->rx_done.lock);
+ break;
+
+ /*do not resubmit*/
+ case -EPIPE:
+ set_bit(RX_HALT, &dev->flags);
+ dev_err(&dev->udev->dev, "%s: epout halted\n", __func__);
+ schedule_work(&dev->kevent);
+ /* FALLTHROUGH */
+ case -ESHUTDOWN:
+ case -ENOENT: /* suspended */
+ case -ECONNRESET: /* unplug */
+ case -EPROTO:
+ dev_kfree_skb_any(skb);
+ break;
+
+ /*resubmit */
+ case -EOVERFLOW: /*babble error*/
+ default:
+ queue = 1;
+ dev_kfree_skb_any(skb);
+ pr_debug_ratelimited("%s: non zero urb status = %d\n",
+ __func__, urb->status);
+ break;
+ }
+
+ spin_lock(&dev->rx_done.lock);
+ list_add_tail(&urb->urb_list, &dev->rx_idle);
+ spin_unlock(&dev->rx_done.lock);
+
+ if (queue)
+ queue_work(dev->wq, &dev->process_rx_w);
+}
+
+static int submit_rx_urb(struct data_bridge *dev, struct urb *rx_urb,
+ gfp_t flags)
+{
+ struct sk_buff *skb;
+ int retval = -EINVAL;
+
+ skb = alloc_skb(RMNET_RX_BUFSIZE, flags);
+ if (!skb) {
+ usb_free_urb(rx_urb);
+ return -ENOMEM;
+ }
+
+ *((struct data_bridge **)skb->cb) = dev;
+
+ usb_fill_bulk_urb(rx_urb, dev->udev, dev->bulk_in,
+ skb->data, RMNET_RX_BUFSIZE,
+ data_bridge_read_cb, skb);
+
+ if (test_bit(SUSPENDED, &dev->flags))
+ goto suspended;
+
+ usb_anchor_urb(rx_urb, &dev->rx_active);
+ retval = usb_submit_urb(rx_urb, flags);
+ if (retval)
+ goto fail;
+
+ return 0;
+fail:
+ usb_unanchor_urb(rx_urb);
+suspended:
+ dev_kfree_skb_any(skb);
+ usb_free_urb(rx_urb);
+ return retval;
+}
+
+static int data_bridge_prepare_rx(struct data_bridge *dev)
+{
+ int i;
+ struct urb *rx_urb;
+
+ for (i = 0; i < max_rx_urbs; i++) {
+ rx_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!rx_urb)
+ return -ENOMEM;
+
+ list_add_tail(&rx_urb->urb_list, &dev->rx_idle);
+ }
+ return 0;
+}
+
+int data_bridge_open(struct bridge *brdg)
+{
+ struct data_bridge *dev;
+
+ if (!brdg) {
+ err("bridge is null\n");
+ return -EINVAL;
+ }
+
+ if (brdg->ch_id >= MAX_BRIDGE_DEVICES)
+ return -EINVAL;
+
+ dev = __dev[brdg->ch_id];
+ if (!dev) {
+ err("dev is null\n");
+ return -ENODEV;
+ }
+
+ dev_dbg(&dev->udev->dev, "%s: dev:%p\n", __func__, dev);
+
+ dev->brdg = brdg;
+ atomic_set(&dev->pending_txurbs, 0);
+ dev->to_host = 0;
+ dev->to_modem = 0;
+ dev->txurb_drp_cnt = 0;
+ dev->tx_throttled_cnt = 0;
+ dev->tx_unthrottled_cnt = 0;
+ dev->rx_throttled_cnt = 0;
+ dev->rx_unthrottled_cnt = 0;
+
+ queue_work(dev->wq, &dev->process_rx_w);
+
+ return 0;
+}
+EXPORT_SYMBOL(data_bridge_open);
+
+void data_bridge_close(unsigned int id)
+{
+ struct data_bridge *dev;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+ if (id >= MAX_BRIDGE_DEVICES)
+ return;
+
+ dev = __dev[id];
+ if (!dev && !dev->brdg)
+ return;
+
+ dev_dbg(&dev->udev->dev, "%s:\n", __func__);
+
+ usb_unlink_anchored_urbs(&dev->tx_active);
+ usb_unlink_anchored_urbs(&dev->rx_active);
+ usb_unlink_anchored_urbs(&dev->delayed);
+
+ spin_lock_irqsave(&dev->rx_done.lock, flags);
+ while ((skb = __skb_dequeue(&dev->rx_done)))
+ dev_kfree_skb_any(skb);
+ spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+
+ dev->brdg = NULL;
+}
+EXPORT_SYMBOL(data_bridge_close);
+
+static void defer_kevent(struct work_struct *work)
+{
+ int status;
+ struct data_bridge *dev =
+ container_of(work, struct data_bridge, kevent);
+
+ if (!dev)
+ return;
+
+ if (test_bit(TX_HALT, &dev->flags)) {
+ usb_unlink_anchored_urbs(&dev->tx_active);
+
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0) {
+ dev_err(&dev->udev->dev,
+ "can't acquire interface, status %d\n", status);
+ return;
+ }
+
+ status = usb_clear_halt(dev->udev, dev->bulk_out);
+ usb_autopm_put_interface(dev->intf);
+ if (status < 0 && status != -EPIPE && status != -ESHUTDOWN)
+ dev_err(&dev->udev->dev,
+ "can't clear tx halt, status %d\n", status);
+ else
+ clear_bit(TX_HALT, &dev->flags);
+ }
+
+ if (test_bit(RX_HALT, &dev->flags)) {
+ usb_unlink_anchored_urbs(&dev->rx_active);
+
+ status = usb_autopm_get_interface(dev->intf);
+ if (status < 0) {
+ dev_err(&dev->udev->dev,
+ "can't acquire interface, status %d\n", status);
+ return;
+ }
+
+ status = usb_clear_halt(dev->udev, dev->bulk_in);
+ usb_autopm_put_interface(dev->intf);
+ if (status < 0 && status != -EPIPE && status != -ESHUTDOWN)
+ dev_err(&dev->udev->dev,
+ "can't clear rx halt, status %d\n", status);
+ else {
+ clear_bit(RX_HALT, &dev->flags);
+ if (dev->brdg)
+ queue_work(dev->wq, &dev->process_rx_w);
+ }
+ }
+}
+
+static void data_bridge_write_cb(struct urb *urb)
+{
+ struct sk_buff *skb = urb->context;
+ struct data_bridge *dev = *(struct data_bridge **)skb->cb;
+ struct bridge *brdg = dev->brdg;
+ int pending;
+
+ pr_debug("%s: dev:%p\n", __func__, dev);
+
+ switch (urb->status) {
+ case 0: /*success*/
+ break;
+ case -EPIPE:
+ set_bit(TX_HALT, &dev->flags);
+ dev_err(&dev->udev->dev, "%s: epout halted\n", __func__);
+ schedule_work(&dev->kevent);
+ /* FALLTHROUGH */
+ case -ESHUTDOWN:
+ case -ENOENT: /* suspended */
+ case -ECONNRESET: /* unplug */
+ case -EOVERFLOW: /*babble error*/
+ /* FALLTHROUGH */
+ default:
+ pr_debug_ratelimited("%s: non zero urb status = %d\n",
+ __func__, urb->status);
+ }
+
+ usb_free_urb(urb);
+ dev_kfree_skb_any(skb);
+
+ pending = atomic_dec_return(&dev->pending_txurbs);
+
+ /*flow ctrl*/
+ if (brdg && fctrl_support && pending <= fctrl_dis_thld &&
+ test_and_clear_bit(TX_THROTTLED, &brdg->flags)) {
+ pr_debug_ratelimited("%s: disable flow ctrl: pend urbs:%u\n",
+ __func__, pending);
+ dev->tx_unthrottled_cnt++;
+ if (brdg->ops.unthrottle_tx)
+ brdg->ops.unthrottle_tx(brdg->ctx);
+ }
+
+ usb_autopm_put_interface_async(dev->intf);
+}
+
+int data_bridge_write(unsigned int id, struct sk_buff *skb)
+{
+ int result;
+ int size = skb->len;
+ int pending;
+ struct urb *txurb;
+ struct data_bridge *dev = __dev[id];
+ struct bridge *brdg;
+
+ if (!dev || !dev->brdg || !usb_get_intfdata(dev->intf))
+ return -ENODEV;
+
+ brdg = dev->brdg;
+
+ dev_dbg(&dev->udev->dev, "%s: write (%d bytes)\n", __func__, skb->len);
+
+ result = usb_autopm_get_interface(dev->intf);
+ if (result < 0) {
+ dev_err(&dev->udev->dev, "%s: resume failure\n", __func__);
+ goto error;
+ }
+
+ txurb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!txurb) {
+ dev_err(&dev->udev->dev, "%s: error allocating read urb\n",
+ __func__);
+ result = -ENOMEM;
+ goto error;
+ }
+
+ /* store dev pointer in skb */
+ *((struct data_bridge **)skb->cb) = dev;
+
+ usb_fill_bulk_urb(txurb, dev->udev, dev->bulk_out,
+ skb->data, skb->len, data_bridge_write_cb, skb);
+
+ if (test_bit(SUSPENDED, &dev->flags)) {
+ usb_anchor_urb(txurb, &dev->delayed);
+ goto free_urb;
+ }
+
+ pending = atomic_inc_return(&dev->pending_txurbs);
+ usb_anchor_urb(txurb, &dev->tx_active);
+
+ result = usb_submit_urb(txurb, GFP_KERNEL);
+ if (result < 0) {
+ usb_unanchor_urb(txurb);
+ atomic_dec(&dev->pending_txurbs);
+ dev_err(&dev->udev->dev, "%s: submit URB error %d\n",
+ __func__, result);
+ goto free_urb;
+ }
+
+ dev->to_modem++;
+ dev_dbg(&dev->udev->dev, "%s: pending_txurbs: %u\n", __func__, pending);
+
+ /* flow control: last urb submitted but return -EBUSY */
+ if (fctrl_support && pending > fctrl_en_thld) {
+ set_bit(TX_THROTTLED, &brdg->flags);
+ dev->tx_throttled_cnt++;
+ pr_debug_ratelimited("%s: enable flow ctrl pend txurbs:%u\n",
+ __func__, pending);
+ return -EBUSY;
+ }
+
+ return size;
+
+free_urb:
+ usb_free_urb(txurb);
+error:
+ dev->txurb_drp_cnt++;
+ usb_autopm_put_interface(dev->intf);
+
+ return result;
+}
+EXPORT_SYMBOL(data_bridge_write);
+
+static int data_bridge_resume(struct data_bridge *dev)
+{
+ struct urb *urb;
+ int retval;
+
+ while ((urb = usb_get_from_anchor(&dev->delayed))) {
+ usb_anchor_urb(urb, &dev->tx_active);
+ atomic_inc(&dev->pending_txurbs);
+ retval = usb_submit_urb(urb, GFP_ATOMIC);
+ if (retval < 0) {
+ atomic_dec(&dev->pending_txurbs);
+ usb_unanchor_urb(urb);
+
+ /* TODO: need to free urb data */
+ usb_scuttle_anchored_urbs(&dev->delayed);
+ break;
+ }
+ dev->to_modem++;
+ dev->txurb_drp_cnt--;
+ }
+
+ clear_bit(SUSPENDED, &dev->flags);
+
+ if (dev->brdg)
+ queue_work(dev->wq, &dev->process_rx_w);
+
+ return 0;
+}
+
+static int bridge_resume(struct usb_interface *iface)
+{
+ int retval = 0;
+ int oldstate;
+ struct data_bridge *dev = usb_get_intfdata(iface);
+ struct bridge *brdg = dev->brdg;
+
+ oldstate = iface->dev.power.power_state.event;
+ iface->dev.power.power_state.event = PM_EVENT_ON;
+
+ retval = data_bridge_resume(dev);
+ if (!retval) {
+ if (oldstate & PM_EVENT_SUSPEND && brdg)
+ retval = ctrl_bridge_resume(brdg->ch_id);
+ }
+ return retval;
+}
+
+static int data_bridge_suspend(struct data_bridge *dev, pm_message_t message)
+{
+ if (atomic_read(&dev->pending_txurbs) &&
+ (message.event & PM_EVENT_AUTO))
+ return -EBUSY;
+
+ set_bit(SUSPENDED, &dev->flags);
+
+ usb_kill_anchored_urbs(&dev->tx_active);
+ usb_kill_anchored_urbs(&dev->rx_active);
+
+ return 0;
+}
+
+static int bridge_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ int retval;
+ struct data_bridge *dev = usb_get_intfdata(intf);
+ struct bridge *brdg = dev->brdg;
+
+ retval = data_bridge_suspend(dev, message);
+ if (!retval) {
+ if (message.event & PM_EVENT_SUSPEND) {
+ if (brdg)
+ retval = ctrl_bridge_suspend(brdg->ch_id);
+ intf->dev.power.power_state.event = message.event;
+ }
+ } else {
+ dev_dbg(&dev->udev->dev, "%s: device is busy,cannot suspend\n",
+ __func__);
+ }
+ return retval;
+}
+
+static int data_bridge_probe(struct usb_interface *iface,
+ struct usb_host_endpoint *bulk_in,
+ struct usb_host_endpoint *bulk_out, int id)
+{
+ struct data_bridge *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ err("%s: unable to allocate dev\n", __func__);
+ return -ENOMEM;
+ }
+
+ dev->pdev = platform_device_alloc(data_bridge_names[id], id);
+ if (!dev->pdev) {
+ err("%s: unable to allocate platform device\n", __func__);
+ kfree(dev);
+ return -ENOMEM;
+ }
+
+ init_usb_anchor(&dev->tx_active);
+ init_usb_anchor(&dev->rx_active);
+ init_usb_anchor(&dev->delayed);
+
+ INIT_LIST_HEAD(&dev->rx_idle);
+ skb_queue_head_init(&dev->rx_done);
+
+ dev->wq = bridge_wq;
+
+ dev->udev = interface_to_usbdev(iface);
+ dev->intf = iface;
+
+ dev->bulk_in = usb_rcvbulkpipe(dev->udev,
+ bulk_in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+ dev->bulk_out = usb_sndbulkpipe(dev->udev,
+ bulk_out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+
+ usb_set_intfdata(iface, dev);
+
+ INIT_WORK(&dev->kevent, defer_kevent);
+ INIT_WORK(&dev->process_rx_w, data_bridge_process_rx);
+
+ __dev[id] = dev;
+
+ /*allocate list of rx urbs*/
+ data_bridge_prepare_rx(dev);
+
+ platform_device_add(dev->pdev);
+
+ return 0;
+}
+
+#if defined(CONFIG_DEBUG_FS)
+#define DEBUG_BUF_SIZE 1024
+static ssize_t data_bridge_read_stats(struct file *file, char __user *ubuf,
+ size_t count, loff_t *ppos)
+{
+ struct data_bridge *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 < ch_id; i++) {
+ dev = __dev[i];
+ if (!dev)
+ continue;
+
+ temp += scnprintf(buf + temp, DEBUG_BUF_SIZE - temp,
+ "\nName#%s dev %p\n"
+ "pending tx urbs: %u\n"
+ "tx urb drp cnt: %u\n"
+ "to host: %lu\n"
+ "to mdm: %lu\n"
+ "tx throttled cnt: %u\n"
+ "tx unthrottled cnt: %u\n"
+ "rx throttled cnt: %u\n"
+ "rx unthrottled cnt: %u\n"
+ "rx done skb qlen: %u\n"
+ "suspended: %d\n"
+ "TX_HALT: %d\n"
+ "RX_HALT: %d\n",
+ dev->pdev->name, dev,
+ atomic_read(&dev->pending_txurbs),
+ dev->txurb_drp_cnt,
+ dev->to_host,
+ dev->to_modem,
+ dev->tx_throttled_cnt,
+ dev->tx_unthrottled_cnt,
+ dev->rx_throttled_cnt,
+ dev->rx_unthrottled_cnt,
+ dev->rx_done.qlen,
+ test_bit(SUSPENDED, &dev->flags),
+ test_bit(TX_HALT, &dev->flags),
+ test_bit(RX_HALT, &dev->flags));
+
+ }
+
+ ret = simple_read_from_buffer(ubuf, count, ppos, buf, temp);
+
+ kfree(buf);
+
+ return ret;
+}
+
+static ssize_t data_bridge_reset_stats(struct file *file,
+ const char __user *buf, size_t count, loff_t *ppos)
+{
+ struct data_bridge *dev;
+ int i;
+
+ for (i = 0; i < ch_id; i++) {
+ dev = __dev[i];
+ if (!dev)
+ continue;
+
+ dev->to_host = 0;
+ dev->to_modem = 0;
+ dev->txurb_drp_cnt = 0;
+ dev->tx_throttled_cnt = 0;
+ dev->tx_unthrottled_cnt = 0;
+ dev->rx_throttled_cnt = 0;
+ dev->rx_unthrottled_cnt = 0;
+ }
+ return count;
+}
+
+const struct file_operations data_stats_ops = {
+ .read = data_bridge_read_stats,
+ .write = data_bridge_reset_stats,
+};
+
+struct dentry *data_dent;
+struct dentry *data_dfile;
+static void data_bridge_debugfs_init(void)
+{
+ data_dent = debugfs_create_dir("data_hsic_bridge", 0);
+ if (IS_ERR(data_dent))
+ return;
+
+ data_dfile = debugfs_create_file("status", 0644, data_dent, 0,
+ &data_stats_ops);
+ if (!data_dfile || IS_ERR(data_dfile))
+ debugfs_remove(data_dent);
+}
+
+static void data_bridge_debugfs_exit(void)
+{
+ debugfs_remove(data_dfile);
+ debugfs_remove(data_dent);
+}
+
+#else
+static void data_bridge_debugfs_init(void) { }
+static void data_bridge_debugfs_exit(void) { }
+#endif
+
+static int __devinit
+bridge_probe(struct usb_interface *iface, const struct usb_device_id *id)
+{
+ 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 i;
+ int status = 0;
+ int numends;
+ int iface_num;
+
+ iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
+
+ if (iface->num_altsetting != 1) {
+ err("%s invalid num_altsetting %u\n",
+ __func__, iface->num_altsetting);
+ return -EINVAL;
+ }
+
+ udev = interface_to_usbdev(iface);
+ usb_get_dev(udev);
+
+ if (iface_num != DUN_IFACE_NUM && iface_num != TETHERED_RMNET_IFACE_NUM)
+ return 0;
+
+ 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;
+ }
+
+ status = data_bridge_probe(iface, bulk_in, bulk_out, ch_id);
+ if (status < 0) {
+ dev_err(&udev->dev, "data_bridge_probe failed %d\n", status);
+ goto out;
+ }
+
+ status = ctrl_bridge_probe(iface, int_in, ch_id);
+ if (status < 0) {
+ dev_err(&udev->dev, "ctrl_bridge_probe failed %d\n", status);
+ goto free_data_bridge;
+ }
+ ch_id++;
+
+ return 0;
+
+free_data_bridge:
+ platform_device_del(__dev[ch_id]->pdev);
+ usb_set_intfdata(iface, NULL);
+ kfree(__dev[ch_id]);
+ __dev[ch_id] = NULL;
+out:
+ usb_put_dev(udev);
+
+ return status;
+}
+
+static void bridge_disconnect(struct usb_interface *intf)
+{
+ struct data_bridge *dev = usb_get_intfdata(intf);
+ struct list_head *head;
+ struct urb *rx_urb;
+ unsigned long flags;
+ int iface_num;
+
+ if (!dev) {
+ err("%s: data device not found\n", __func__);
+ return;
+ }
+
+ iface_num = intf->cur_altsetting->desc.bInterfaceNumber;
+ if (iface_num != DUN_IFACE_NUM && iface_num != TETHERED_RMNET_IFACE_NUM)
+ return;
+
+ ch_id--;
+ ctrl_bridge_disconnect(ch_id);
+ platform_device_del(dev->pdev);
+ usb_set_intfdata(intf, NULL);
+ __dev[ch_id] = NULL;
+
+ cancel_work_sync(&dev->process_rx_w);
+ cancel_work_sync(&dev->kevent);
+
+ /*free rx urbs*/
+ head = &dev->rx_idle;
+ spin_lock_irqsave(&dev->rx_done.lock, flags);
+ while (!list_empty(head)) {
+ rx_urb = list_entry(head->next, struct urb, urb_list);
+ list_del(&rx_urb->urb_list);
+ usb_free_urb(rx_urb);
+ }
+ spin_unlock_irqrestore(&dev->rx_done.lock, flags);
+
+ usb_put_dev(dev->udev);
+ kfree(dev);
+}
+
+static const struct usb_device_id bridge_ids[] = {
+ { USB_DEVICE(0x5c6, 0x9001) },
+};
+
+MODULE_DEVICE_TABLE(usb, bridge_ids);
+
+static struct usb_driver bridge_driver = {
+ .name = "mdm_bridge",
+ .probe = bridge_probe,
+ .disconnect = bridge_disconnect,
+ .id_table = bridge_ids,
+ .suspend = bridge_suspend,
+ .resume = bridge_resume,
+ .supports_autosuspend = 1,
+};
+
+static int __init bridge_init(void)
+{
+ int ret;
+
+ ret = usb_register(&bridge_driver);
+ if (ret) {
+ err("%s: unable to register mdm_bridge driver", __func__);
+ return ret;
+ }
+
+ bridge_wq = create_singlethread_workqueue("mdm_bridge");
+ if (!bridge_wq) {
+ usb_deregister(&bridge_driver);
+ pr_err("%s: Unable to create workqueue:bridge\n", __func__);
+ return -ENOMEM;
+ }
+
+ data_bridge_debugfs_init();
+
+ return 0;
+}
+
+static void __exit bridge_exit(void)
+{
+ data_bridge_debugfs_exit();
+ destroy_workqueue(bridge_wq);
+ usb_deregister(&bridge_driver);
+}
+
+module_init(bridge_init);
+module_exit(bridge_exit);
+
+MODULE_DESCRIPTION("Qualcomm modem data bridge driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/otg/msm72k_otg.c b/drivers/usb/otg/msm72k_otg.c
index 714099a..be3a279d 100644
--- a/drivers/usb/otg/msm72k_otg.c
+++ b/drivers/usb/otg/msm72k_otg.c
@@ -1226,11 +1226,17 @@
{
struct msm_otg *dev = the_msm_otg;
- if (!atomic_read(&dev->in_lpm) || !online)
- return;
+ /*
+ * Process disconnect only for wallcharger
+ * during fast plug-out plug-in at the
+ * AC source side.
+ */
+ if (online)
+ set_bit(B_SESS_VLD, &dev->inputs);
+ else
+ clear_bit(B_SESS_VLD, &dev->inputs);
wake_lock(&dev->wlock);
- set_bit(B_SESS_VLD, &dev->inputs);
queue_work(dev->wq, &dev->sm_work);
}
diff --git a/drivers/video/msm/hdmi_msm.c b/drivers/video/msm/hdmi_msm.c
index 712b724..8a1d56e 100644
--- a/drivers/video/msm/hdmi_msm.c
+++ b/drivers/video/msm/hdmi_msm.c
@@ -1975,13 +1975,14 @@
* Read HDMI_I2C_DATA with the following fields set
* RW = 0x1 (read)
* DATA = BCAPS (this is field where data is pulled from)
- * INDEX = 0x3 (where the data has been placed in buffer by hardware)
+ * INDEX = 0x5 (where the data has been placed in buffer by hardware)
* INDEX_WRITE = 0x1 (explicitly define offset) */
/* Write this data to DDC buffer */
- HDMI_OUTP_ND(0x0238, 0x1 | (3 << 16) | (1 << 31));
+ HDMI_OUTP_ND(0x0238, 0x1 | (5 << 16) | (1 << 31));
/* Discard first byte */
HDMI_INP_ND(0x0238);
+
for (ndx = 0; ndx < data_len; ++ndx) {
reg_val = HDMI_INP_ND(0x0238);
data_buf[ndx] = (uint8) ((reg_val & 0x0000FF00) >> 8);
@@ -2672,7 +2673,30 @@
for (i = 0; i < ksv_bytes - 1; i++) {
/* Write KSV byte and do not set DONE bit[0] */
HDMI_OUTP_ND(0x0244, kvs_fifo[i] << 16);
+
+ /* Once 64 bytes have been written, we need to poll for
+ * HDCP_SHA_BLOCK_DONE before writing any further
+ */
+ if (i && !((i+1)%64)) {
+ timeout_count = 100;
+ while (!(HDMI_INP_ND(0x0240) & 0x1)
+ && (--timeout_count)) {
+ DEV_DBG("HDCP Auth Part II: Waiting for the "
+ "computation of the current 64 byte to "
+ "complete. HDCP_SHA_STATUS=%08x. "
+ "timeout_count=%d\n",
+ HDMI_INP_ND(0x0240), timeout_count);
+ msleep(20);
+ }
+ if (!timeout_count) {
+ ret = -ETIMEDOUT;
+ DEV_ERR("%s(%d): timedout", __func__, __LINE__);
+ goto error;
+ }
+ }
+
}
+
/* Write l to DONE bit[0] */
HDMI_OUTP_ND(0x0244, (kvs_fifo[ksv_bytes - 1] << 16) | 0x1);
@@ -2680,8 +2704,9 @@
[4] COMP_DONE */
/* Now wait for HDCP_SHA_COMP_DONE */
timeout_count = 100;
- while ((0x10 != (HDMI_INP_ND(0x0240) & 0x10)) && timeout_count--)
+ while ((0x10 != (HDMI_INP_ND(0x0240) & 0xFFFFFF10)) && --timeout_count)
msleep(20);
+
if (!timeout_count) {
ret = -ETIMEDOUT;
DEV_ERR("%s(%d): timedout", __func__, __LINE__);
@@ -2692,8 +2717,10 @@
[20] V_MATCHES */
timeout_count = 100;
while (((HDMI_INP_ND(0x011C) & (1 << 20)) != (1 << 20))
- && timeout_count--)
+ && --timeout_count) {
msleep(20);
+ }
+
if (!timeout_count) {
ret = -ETIMEDOUT;
DEV_ERR("%s(%d): timedout", __func__, __LINE__);
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index 1a606b5..e1b9e73 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -423,6 +423,7 @@
header-y += msm_audio_qcp.h
header-y += msm_audio_amrnb.h
header-y += msm_audio_voicememo.h
+header-y += msm_audio_sbc.h
header-y += msm_ipc.h
header-y += msm_charm.h
header-y += tzcom.h
diff --git a/include/linux/ion.h b/include/linux/ion.h
index 7de40d4..89ac992 100644
--- a/include/linux/ion.h
+++ b/include/linux/ion.h
@@ -438,6 +438,7 @@
/* struct ion_flush_data - data passed to ion for flushing caches
*
* @handle: handle with data to flush
+ * @fd: fd to flush
* @vaddr: userspace virtual address mapped with mmap
* @offset: offset into the handle to flush
* @length: length of handle to flush
@@ -448,6 +449,7 @@
*/
struct ion_flush_data {
struct ion_handle *handle;
+ int fd;
void *vaddr;
unsigned int offset;
unsigned int length;
diff --git a/include/linux/mfd/pm8xxx/core.h b/include/linux/mfd/pm8xxx/core.h
index a4c23b0..f0a3278 100644
--- a/include/linux/mfd/pm8xxx/core.h
+++ b/include/linux/mfd/pm8xxx/core.h
@@ -44,6 +44,7 @@
#define PM8XXX_REVISION_8921_1p0 1
#define PM8XXX_REVISION_8921_1p1 2
#define PM8XXX_REVISION_8921_2p0 3
+#define PM8XXX_REVISION_8921_3p0 4
#define PM8XXX_REVISION_8821_TEST 0
#define PM8XXX_REVISION_8821_1p0 1
diff --git a/include/linux/mfd/pm8xxx/pm8921-adc.h b/include/linux/mfd/pm8xxx/pm8921-adc.h
deleted file mode 100644
index c66ae84..0000000
--- a/include/linux/mfd/pm8xxx/pm8921-adc.h
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * 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.
- */
-/*
- * Qualcomm PMIC 8921 ADC driver header file
- *
- */
-
-#ifndef __PM8921_ADC_H
-#define __PM8921_ADC_H
-
-#include <linux/kernel.h>
-#include <linux/list.h>
-
-/**
- * enum pm8921_adc_channels - PM8921 AMUX arbiter channels
- * %CHANNEL_VCOIN: Backup voltage for certain register set
- * %CHANNEL_VBAT: Battery voltage
- * %CHANNEL_DCIN: Charger input voltage without internal OVP
- * %CHANNEL_ICHG: Charge-current monitor
- * %CHANNEL_VPH_PWR: Main system power
- * %CHANNEL_IBAT: Battery charge current
- * %CHANNEL_MPP_1: 16:1 pre-mux unity scale MPP input
- * %CHANNEL_MPP_2: 16:1 pre-mux 1/3 scale MPP input
- * %CHANNEL_BATT_THERM: Battery temperature
- * %CHANNEL_BATT_ID: Battery detection
- * %CHANNEL_USBIN: Charger input voltage with internal OVP
- * %CHANNEL_DIE_TEMP: Pmic_die temperature
- * %CHANNEL_625MV: 625mv reference channel
- * %CHANNEL_125V: 1.25v reference channel
- * %CHANNEL_CHG_TEMP: Charger temperature
- * %CHANNEL_MUXOFF: Channel to reduce input load on the mux
- * %CHANNEL_NONE: Do not use this channel
- */
-enum pm8921_adc_channels {
- CHANNEL_VCOIN = 0,
- CHANNEL_VBAT,
- CHANNEL_DCIN,
- CHANNEL_ICHG,
- CHANNEL_VPH_PWR,
- CHANNEL_IBAT,
- CHANNEL_MPP_1,
- CHANNEL_MPP_2,
- CHANNEL_BATT_THERM,
- CHANNEL_BATT_ID,
- CHANNEL_USBIN,
- CHANNEL_DIE_TEMP,
- CHANNEL_625MV,
- CHANNEL_125V,
- CHANNEL_CHG_TEMP,
- CHANNEL_MUXOFF,
- CHANNEL_NONE,
- ADC_MPP_1_ATEST_8 = 20,
- ADC_MPP_1_USB_SNS_DIV20,
- ADC_MPP_1_DCIN_SNS_DIV20,
- ADC_MPP_1_AMUX3,
- ADC_MPP_1_AMUX4,
- ADC_MPP_1_AMUX5,
- ADC_MPP_1_AMUX6,
- ADC_MPP_1_AMUX7,
- ADC_MPP_1_AMUX8,
- ADC_MPP_1_ATEST_1,
- ADC_MPP_1_ATEST_2,
- ADC_MPP_1_ATEST_3,
- ADC_MPP_1_ATEST_4,
- ADC_MPP_1_ATEST_5,
- ADC_MPP_1_ATEST_6,
- ADC_MPP_1_ATEST_7,
- ADC_MPP_1_CHANNEL_NONE,
- ADC_MPP_2_ATEST_8 = 40,
- ADC_MPP_2_USB_SNS_DIV20,
- ADC_MPP_2_DCIN_SNS_DIV20,
- ADC_MPP_2_AMUX3,
- ADC_MPP_2_AMUX4,
- ADC_MPP_2_AMUX5,
- ADC_MPP_2_AMUX6,
- ADC_MPP_2_AMUX7,
- ADC_MPP_2_AMUX8,
- ADC_MPP_2_ATEST_1,
- ADC_MPP_2_ATEST_2,
- ADC_MPP_2_ATEST_3,
- ADC_MPP_2_ATEST_4,
- ADC_MPP_2_ATEST_5,
- ADC_MPP_2_ATEST_6,
- ADC_MPP_2_ATEST_7,
- ADC_MPP_2_CHANNEL_NONE,
-};
-
-#define PM8921_ADC_PMIC_0 0x0
-
-#define PM8921_CHANNEL_ADC_625_MV 625
-#define PM8921_CHANNEL_MPP_SCALE1_IDX 20
-#define PM8921_CHANNEL_MPP_SCALE3_IDX 40
-
-#define PM8921_AMUX_MPP_3 0x3
-#define PM8921_AMUX_MPP_4 0x4
-#define PM8921_AMUX_MPP_5 0x5
-#define PM8921_AMUX_MPP_6 0x6
-#define PM8921_AMUX_MPP_7 0x7
-#define PM8921_AMUX_MPP_8 0x8
-
-#define PM8921_ADC_DEV_NAME "pm8921-adc"
-
-/**
- * enum pm8921_adc_decimation_type - Sampling rate supported
- * %ADC_DECIMATION_TYPE1: 512
- * %ADC_DECIMATION_TYPE2: 1K
- * %ADC_DECIMATION_TYPE3: 2K
- * %ADC_DECIMATION_TYPE4: 4k
- * %ADC_DECIMATION_NONE: Do not use this Sampling type
- *
- * The Sampling rate is specific to each channel of the PM8921 ADC arbiter.
- */
-enum pm8921_adc_decimation_type {
- ADC_DECIMATION_TYPE1 = 0,
- ADC_DECIMATION_TYPE2,
- ADC_DECIMATION_TYPE3,
- ADC_DECIMATION_TYPE4,
- ADC_DECIMATION_NONE,
-};
-
-/**
- * enum pm8921_adc_calib_type - PM8921 ADC Calibration type
- * %ADC_CALIB_ABSOLUTE: Use 625mV and 1.25V reference channels
- * %ADC_CALIB_RATIOMETRIC: Use reference Voltage/GND
- * %ADC_CALIB_CONFIG_NONE: Do not use this calibration type
- *
- * Use the input reference voltage depending on the calibration type
- * to calcluate the offset and gain parameters. The calibration is
- * specific to each channel of the PM8921 ADC.
- */
-enum pm8921_adc_calib_type {
- ADC_CALIB_ABSOLUTE = 0,
- ADC_CALIB_RATIOMETRIC,
- ADC_CALIB_NONE,
-};
-
-/**
- * enum pm8921_adc_channel_scaling_param - pre-scaling AMUX ratio
- * %CHAN_PATH_SCALING1: ratio of {1, 1}
- * %CHAN_PATH_SCALING2: ratio of {1, 3}
- * %CHAN_PATH_SCALING3: ratio of {1, 4}
- * %CHAN_PATH_SCALING4: ratio of {1, 6}
- * %CHAN_PATH_NONE: Do not use this pre-scaling ratio type
- *
- * The pre-scaling is applied for signals to be within the voltage range
- * of the ADC.
- */
-enum pm8921_adc_channel_scaling_param {
- CHAN_PATH_SCALING1 = 0,
- CHAN_PATH_SCALING2,
- CHAN_PATH_SCALING3,
- CHAN_PATH_SCALING4,
- CHAN_PATH_SCALING_NONE,
-};
-
-/**
- * enum pm8921_adc_amux_input_rsv - HK/XOADC reference voltage
- * %AMUX_RSV0: XO_IN/XOADC_GND
- * %AMUX_RSV1: PMIC_IN/XOADC_GND
- * %AMUX_RSV2: PMIC_IN/BMS_CSP
- * %AMUX_RSV3: not used
- * %AMUX_RSV4: XOADC_GND/XOADC_GND
- * %AMUX_RSV5: XOADC_VREF/XOADC_GND
- * %AMUX_NONE: Do not use this input reference voltage selection
- */
-enum pm8921_adc_amux_input_rsv {
- AMUX_RSV0 = 0,
- AMUX_RSV1,
- AMUX_RSV2,
- AMUX_RSV3,
- AMUX_RSV4,
- AMUX_RSV5,
- AMUX_NONE,
-};
-
-/**
- * enum pm8921_adc_premux_mpp_scale_type - 16:1 pre-mux scale ratio
- * %PREMUX_MPP_SCALE_0: No scaling to the input signal
- * %PREMUX_MPP_SCALE_1: Unity scaling selected by the user for MPP input
- * %PREMUX_MPP_SCALE_1_DIV3: 1/3 pre-scale to the input MPP signal
- * %PREMUX_MPP_NONE: Do not use this pre-scale mpp type
- */
-enum pm8921_adc_premux_mpp_scale_type {
- PREMUX_MPP_SCALE_0 = 0,
- PREMUX_MPP_SCALE_1,
- PREMUX_MPP_SCALE_1_DIV3,
- PREMUX_MPP_NONE,
-};
-
-/**
- * enum pm8921_adc_scale_fn_type - Scaling function for pm8921 pre calibrated
- * digital data relative to ADC reference
- * %ADC_SCALE_DEFAULT: Default scaling to convert raw adc code to voltage
- * %ADC_SCALE_BATT_THERM: Conversion to temperature based on btm parameters
- * %ADC_SCALE_PMIC_THERM: Returns result in milli degree's Centigrade
- * %ADC_SCALE_XTERN_CHGR_CUR: Returns current across 0.1 ohm resistor
- * %ADC_SCALE_XOTHERM: Returns XO thermistor voltage in degree's Centigrade
- * %ADC_SCALE_NONE: Do not use this scaling type
- */
-enum pm8921_adc_scale_fn_type {
- ADC_SCALE_DEFAULT = 0,
- ADC_SCALE_BATT_THERM,
- ADC_SCALE_PA_THERM,
- ADC_SCALE_PMIC_THERM,
- ADC_SCALE_XOTHERM,
- ADC_SCALE_NONE,
-};
-
-/**
- * struct pm8921_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
- * @adc_gnd: A/D word of the Ground reference used for the channel
- *
- * Each ADC device has different offset and gain parameters which are computed
- * to calibrate the device.
- */
-struct pm8921_adc_linear_graph {
- int32_t offset;
- int32_t dy;
- int32_t dx;
- int32_t adc_vref;
- int32_t adc_gnd;
-};
-
-/**
- * struct pm8921_adc_map_pt - Map the graph representation for ADC channel
- * @x: Represent the ADC digitized code
- * @y: Represent the physical data which can be temperature, voltage,
- * resistance
- */
-struct pm8921_adc_map_pt {
- int32_t x;
- int32_t y;
-};
-
-/**
- * struct pm8921_adc_scaling_ratio - Represent scaling ratio for adc input
- * @num: Numerator scaling parameter
- * @den: Denominator scaling parameter
- */
-struct pm8921_adc_scaling_ratio {
- int32_t num;
- int32_t den;
-};
-
-/**
- * struct pm8921_adc_properties - Represent the ADC properties
- * @adc_reference: Reference voltage for PM8921 ADC
- * @bitresolution: ADC bit resolution for PM8921 ADC
- * @biploar: Polarity for PM8921 ADC
- */
-struct pm8921_adc_properties {
- uint32_t adc_vdd_reference;
- uint32_t bitresolution;
- bool bipolar;
-};
-
-/**
- * struct pm8921_adc_chan_properties - Represent channel properties of the ADC
- * @offset_gain_numerator: The inverse numerator of the gain applied to the
- * input channel
- * @offset_gain_denominator: The inverse denominator of the gain applied to the
- * input channel
- * @adc_graph: ADC graph for the channel of struct type pm8921_adc_linear_graph
- */
-struct pm8921_adc_chan_properties {
- uint32_t offset_gain_numerator;
- uint32_t offset_gain_denominator;
- struct pm8921_adc_linear_graph adc_graph[2];
-};
-
-/**
- * struct pm8921_adc_chan_result - Represent the result of the PM8921 ADC
- * @chan: The channel number of the requested conversion
- * @adc_code: The pre-calibrated digital output of a given ADC relative to the
- * the ADC reference
- * @measurement: In units specific for a given ADC; most ADC uses reference
- * voltage but some ADC uses reference current. This measurement
- * 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.
- */
-struct pm8921_adc_chan_result {
- uint32_t chan;
- int32_t adc_code;
- int64_t measurement;
- int64_t physical;
-};
-
-#if defined(CONFIG_SENSORS_PM8921_ADC) \
- || defined(CONFIG_SENSORS_PM8921_ADC_MODULE)
-/**
- * pm8921_adc_scale_default() - 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 pm8921 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 pm8921_adc_scale_default(int32_t adc_code,
- const struct pm8921_adc_properties *adc_prop,
- const struct pm8921_adc_chan_properties *chan_prop,
- struct pm8921_adc_chan_result *chan_rslt);
-/**
- * pm8921_adc_scale_tdkntcg_therm() - Scales the pre-calibrated digital output
- * of an ADC to the ADC reference and compensates for the
- * gain and offset. Returns the temperature of the xo therm in mili
- degC.
- * @adc_code: pre-calibrated digital ouput of the ADC.
- * @adc_prop: adc properties of the pm8921 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 pm8921_adc_tdkntcg_therm(int32_t adc_code,
- const struct pm8921_adc_properties *adc_prop,
- const struct pm8921_adc_chan_properties *chan_prop,
- struct pm8921_adc_chan_result *chan_rslt);
-/**
- * pm8921_adc_scale_batt_therm() - Scales the pre-calibrated digital output
- * of an ADC to the ADC reference and compensates for the
- * gain and offset. Returns the temperature in degC.
- * @adc_code: pre-calibrated digital ouput of the ADC.
- * @adc_prop: adc properties of the pm8921 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 pm8921_adc_scale_batt_therm(int32_t adc_code,
- const struct pm8921_adc_properties *adc_prop,
- const struct pm8921_adc_chan_properties *chan_prop,
- struct pm8921_adc_chan_result *chan_rslt);
-/**
- * pm8921_adc_scale_pa_therm() - Scales the pre-calibrated digital output
- * of an ADC to the ADC reference and compensates for the
- * gain and offset. Returns the temperature in degC.
- * @adc_code: pre-calibrated digital ouput of the ADC.
- * @adc_prop: adc properties of the pm8921 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 pm8921_adc_scale_pa_therm(int32_t adc_code,
- const struct pm8921_adc_properties *adc_prop,
- const struct pm8921_adc_chan_properties *chan_prop,
- struct pm8921_adc_chan_result *chan_rslt);
-/**
- * pm8921_adc_scale_pmic_therm() - Scales the pre-calibrated digital output
- * of an ADC to the ADC reference and compensates for the
- * gain and offset. Performs the AMUX out as 2mv/K and returns
- * the temperature in mili degC.
- * @adc_code: pre-calibrated digital ouput of the ADC.
- * @adc_prop: adc properties of the pm8921 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 pm8921_adc_scale_pmic_therm(int32_t adc_code,
- const struct pm8921_adc_properties *adc_prop,
- const struct pm8921_adc_chan_properties *chan_prop,
- struct pm8921_adc_chan_result *chan_rslt);
-#else
-static inline int32_t pm8921_adc_scale_default(int32_t adc_code,
- const struct pm8921_adc_properties *adc_prop,
- const struct pm8921_adc_chan_properties *chan_prop,
- struct pm8921_adc_chan_result *chan_rslt)
-{ return -ENXIO; }
-static inline int32_t pm8921_adc_tdkntcg_therm(int32_t adc_code,
- const struct pm8921_adc_properties *adc_prop,
- const struct pm8921_adc_chan_properties *chan_prop,
- struct pm8921_adc_chan_result *chan_rslt)
-{ return -ENXIO; }
-static inline int32_t pm8921_adc_scale_batt_therm(int32_t adc_code,
- const struct pm8921_adc_properties *adc_prop,
- const struct pm8921_adc_chan_properties *chan_prop,
- struct pm8921_adc_chan_result *chan_rslt)
-{ return -ENXIO; }
-static inline int32_t pm8921_adc_scale_pa_therm(int32_t adc_code,
- const struct pm8921_adc_properties *adc_prop,
- const struct pm8921_adc_chan_properties *chan_prop,
- struct pm8921_adc_chan_result *chan_rslt)
-{ return -ENXIO; }
-static inline int32_t pm8921_adc_scale_pmic_therm(int32_t adc_code,
- const struct pm8921_adc_properties *adc_prop,
- const struct pm8921_adc_chan_properties *chan_prop,
- struct pm8921_adc_chan_result *chan_rslt)
-{ return -ENXIO; }
-#endif
-
-/**
- * struct pm8921_adc_scale_fn - Scaling function prototype
- * @chan: Function pointer to one of the scaling functions
- * which takes the adc properties, channel properties,
- * and returns the physical result
- */
-struct pm8921_adc_scale_fn {
- int32_t (*chan) (int32_t,
- const struct pm8921_adc_properties *,
- const struct pm8921_adc_chan_properties *,
- struct pm8921_adc_chan_result *);
-};
-
-/**
- * struct pm8921_adc_amux - AMUX properties for individual channel
- * @name: Channel name
- * @channel_name: Channel in integer used from pm8921_adc_channels
- * @chan_path_prescaling: Channel scaling performed on the input signal
- * @adc_rsv: Input reference Voltage/GND selection to the ADC
- * @adc_decimation: Sampling rate desired for the channel
- * adc_scale_fn: Scaling function to convert to the data meaningful for
- * each individual channel whether it is voltage, current,
- * temperature, etc and compensates the channel properties
- */
-struct pm8921_adc_amux {
- char *name;
- enum pm8921_adc_channels channel_name;
- enum pm8921_adc_channel_scaling_param chan_path_prescaling;
- enum pm8921_adc_amux_input_rsv adc_rsv;
- enum pm8921_adc_decimation_type adc_decimation;
- enum pm8921_adc_scale_fn_type adc_scale_fn;
-};
-
-/**
- * struct pm8921_adc_arb_btm_param - PM8921 ADC BTM parameters to set threshold
- * temperature for client notification
- * @low_thr_temp: low temperature threshold request for notification
- * @high_thr_temp: high temperature threshold request for notification
- * @low_thr_voltage: low temperature converted to voltage by arbiter driver
- * @high_thr_voltage: high temperature converted to voltage by arbiter driver
- * @interval: Interval period to check for temperature notification
- * @btm_warm_fn: Remote function call for warm threshold.
- * @btm_cool_fn: Remote function call for cold threshold.
- *
- * BTM client passes the parameters to be set for the
- * temperature threshold notifications. The client is
- * responsible for setting the new threshold
- * levels once the thresholds are reached
- */
-struct pm8921_adc_arb_btm_param {
- int32_t low_thr_temp;
- int32_t high_thr_temp;
- uint64_t low_thr_voltage;
- uint64_t high_thr_voltage;
- int32_t interval;
- void (*btm_warm_fn) (bool);
- void (*btm_cool_fn) (bool);
-};
-
-int32_t pm8921_adc_batt_scaler(struct pm8921_adc_arb_btm_param *,
- const struct pm8921_adc_properties *adc_prop,
- const struct pm8921_adc_chan_properties *chan_prop);
-/**
- * struct pm8921_adc_platform_data - PM8921 ADC platform data
- * @adc_prop: ADC specific parameters, voltage and channel setup
- * @adc_channel: Channel properties of the ADC arbiter
- * @adc_num_board_channel: Number of channels added in the board file
- * @adc_mpp_base: PM8921 MPP0 base passed from board file. This is used
- * to offset the PM8921 MPP passed to configure the
- * the MPP to AMUX mapping.
- */
-struct pm8921_adc_platform_data {
- struct pm8921_adc_properties *adc_prop;
- struct pm8921_adc_amux *adc_channel;
- uint32_t adc_num_board_channel;
- uint32_t adc_mpp_base;
-};
-
-/* Public API */
-#if defined(CONFIG_SENSORS_PM8921_ADC) \
- || defined(CONFIG_SENSORS_PM8921_ADC_MODULE)
-/**
- * pm8921_adc_read() - Performs ADC read on the channel.
- * @channel: Input channel to perform the ADC read.
- * @result: Structure pointer of type adc_chan_result
- * in which the ADC read results are stored.
- */
-uint32_t pm8921_adc_read(enum pm8921_adc_channels channel,
- struct pm8921_adc_chan_result *result);
-/**
- * pm8921_adc_mpp_config_read() - Configure's the PM8921 MPP
- * to AMUX6 and performs an ADC read.
- * @mpp_num PM8921 MPP number to configure to AMUX6.
- * @channel: Input channel to perform the ADC read.
- * a) 'ADC_MPP_1_AMUX6' if the input voltage is less than 1.8V
- * b) 'ADC_MPP_2_AMUX6' if the input voltage is greater then 1.8V
- * the input voltage is pre-divided by 3 and passed to the ADC.
- * The appropriate scaling function needs to be selected to let
- * the driver know a post scaling is required before returning
- * the result.
- * @result: Structure pointer of type adc_chan_result
- * in which the ADC read results are stored.
- */
-uint32_t pm8921_adc_mpp_config_read(uint32_t mpp_num,
- enum pm8921_adc_channels channel,
- struct pm8921_adc_chan_result *result);
-/**
- * pm8921_adc_btm_start() - Configure the BTM registers and start
- monitoring the BATT_THERM channel for
- threshold warm/cold temperature set
- by the Battery client. The btm_start
- api is to be used after calling the
- pm8921_btm_configure() api which sets
- the temperature thresholds, interval
- and functions to call when warm/cold
- events are triggered.
- * @param: none.
- */
-uint32_t pm8921_adc_btm_start(void);
-
-/**
- * pm8921_adc_btm_end() - Configures the BTM registers to stop
- * monitoring the BATT_THERM channel for
- * warm/cold events and disables the
- * interval timer.
- * @param: none.
- */
-uint32_t pm8921_adc_btm_end(void);
-
-/**
- * pm8921_adc_btm_configure() - Configures the BATT_THERM channel
- * parameters for warm/cold thresholds.
- * Sets the interval timer for perfoming
- * reading the temperature done by the HW.
- * @btm_param: Structure pointer of type adc_arb_btm_param *
- * which client provides for threshold warm/cold,
- * interval and functions to call when warm/cold
- * events are triggered.
- */
-uint32_t pm8921_adc_btm_configure(struct pm8921_adc_arb_btm_param *);
-#else
-static inline uint32_t pm8921_adc_read(uint32_t channel,
- struct pm8921_adc_chan_result *result)
-{ return -ENXIO; }
-static inline uint32_t pm8921_adc_mpp_config_read(uint32_t mpp_num,
- enum pm8921_adc_channels channel,
- struct pm8921_adc_chan_result *result)
-{ return -ENXIO; }
-static inline uint32_t pm8921_adc_btm_start(void)
-{ return -ENXIO; }
-static inline uint32_t pm8921_adc_btm_end(void)
-{ return -ENXIO; }
-static inline uint32_t pm8921_adc_btm_configure(
- struct pm8921_adc_arb_btm_param *param)
-{ return -ENXIO; }
-#endif
-
-#endif /* PM8921_ADC_H */
diff --git a/include/linux/mfd/pm8xxx/pm8921.h b/include/linux/mfd/pm8xxx/pm8921.h
index 2c931b6..d4cdc3c 100644
--- a/include/linux/mfd/pm8xxx/pm8921.h
+++ b/include/linux/mfd/pm8xxx/pm8921.h
@@ -31,7 +31,7 @@
#include <linux/input/pmic8xxx-keypad.h>
#include <linux/regulator/pm8921-regulator.h>
#include <linux/mfd/pm8xxx/pm8921-charger.h>
-#include <linux/mfd/pm8xxx/pm8921-adc.h>
+#include <linux/mfd/pm8xxx/pm8xxx-adc.h>
#include <linux/mfd/pm8xxx/pm8921-bms.h>
#include <linux/leds-pm8xxx.h>
#include <linux/mfd/pm8xxx/vibrator.h>
@@ -126,7 +126,7 @@
struct pm8xxx_misc_platform_data *misc_pdata;
struct pm8921_regulator_platform_data *regulator_pdatas;
int num_regulators;
- struct pm8921_adc_platform_data *adc_pdata;
+ struct pm8xxx_adc_platform_data *adc_pdata;
struct pm8xxx_led_platform_data *leds_pdata;
struct pm8xxx_vibrator_platform_data *vibrator_pdata;
struct pm8xxx_ccadc_platform_data *ccadc_pdata;
diff --git a/include/linux/mfd/pm8xxx/tm.h b/include/linux/mfd/pm8xxx/tm.h
index 5eeefd9..6974754 100644
--- a/include/linux/mfd/pm8xxx/tm.h
+++ b/include/linux/mfd/pm8xxx/tm.h
@@ -25,7 +25,7 @@
enum pm8xxx_tm_adc_type {
PM8XXX_TM_ADC_NONE, /* Estimates temp based on overload level. */
PM8XXX_TM_ADC_PM8058_ADC,
- PM8XXX_TM_ADC_PM8921_ADC,
+ PM8XXX_TM_ADC_PM8XXX_ADC,
};
struct pm8xxx_tm_core_data {
diff --git a/include/linux/msm_audio_sbc.h b/include/linux/msm_audio_sbc.h
index 0a7602a..c1de751 100644
--- a/include/linux/msm_audio_sbc.h
+++ b/include/linux/msm_audio_sbc.h
@@ -1,16 +1,3 @@
-/* Copyright (c) 2010, 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_AUDIO_SBC_H
#define __MSM_AUDIO_SBC_H
diff --git a/include/linux/msm_kgsl.h b/include/linux/msm_kgsl.h
index 273850f..1f898b0 100644
--- a/include/linux/msm_kgsl.h
+++ b/include/linux/msm_kgsl.h
@@ -25,6 +25,14 @@
#define KGSL_FLAGS_RESERVED2 0x00000080
#define KGSL_FLAGS_SOFT_RESET 0x00000100
+/* Clock flags to show which clocks should be controled by a given platform */
+#define KGSL_CLK_SRC 0x00000001
+#define KGSL_CLK_CORE 0x00000002
+#define KGSL_CLK_IFACE 0x00000004
+#define KGSL_CLK_MEM 0x00000008
+#define KGSL_CLK_MEM_IFACE 0x00000010
+#define KGSL_CLK_AXI 0x00000020
+
#define KGSL_MAX_PWRLEVELS 5
#define KGSL_CONVERT_TO_MBPS(val) \
@@ -130,30 +138,15 @@
#define KGSL_2D1_REG_MEMORY "kgsl_2d1_reg_memory"
#define KGSL_2D1_IRQ "kgsl_2d1_irq"
-struct kgsl_grp_clk_name {
- const char *clk;
- const char *pclk;
-};
-
-struct kgsl_device_pwr_data {
+struct kgsl_device_platform_data {
struct kgsl_pwrlevel pwrlevel[KGSL_MAX_PWRLEVELS];
int init_level;
int num_levels;
int (*set_grp_async)(void);
unsigned int idle_timeout;
unsigned int nap_allowed;
-};
-
-struct kgsl_clk_data {
- struct kgsl_grp_clk_name name;
+ unsigned int clk_map;
struct msm_bus_scale_pdata *bus_scale_table;
-};
-
-struct kgsl_device_platform_data {
- struct kgsl_device_pwr_data pwr_data;
- struct kgsl_clk_data clk;
- /* imem_clk_name is for 3d only, not used in 2d devices */
- struct kgsl_grp_clk_name imem_clk_name;
const char *iommu_user_ctx_name;
const char *iommu_priv_ctx_name;
};
diff --git a/include/linux/platform_data/usb_rmnet.h b/include/linux/platform_data/usb_rmnet.h
deleted file mode 100644
index 68c0db7..0000000
--- a/include/linux/platform_data/usb_rmnet.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/* 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 __LINUX_USB_GADGET_RMNET_H__
-#define __LINUX_USB_GADGET_RMNET_H__
-
-#include <linux/platform_device.h>
-
-struct usb_rmnet_pdata {
- unsigned num_instances;
-};
-
-#endif
diff --git a/include/media/radio-iris.h b/include/media/radio-iris.h
index d9687ba..dbdb651 100644
--- a/include/media/radio-iris.h
+++ b/include/media/radio-iris.h
@@ -727,13 +727,19 @@
#define RSB_CALIB_SIZE 4
#define CALIB_DATA_OFSET 2
#define CALIB_MODE_OFSET 1
-
#define MAX_CALIB_SIZE 75
-struct hci_fm_set_cal_req {
+struct hci_fm_set_cal_req_proc {
__u8 mode;
- /*Max calibration data size*/
- __u8 data[MAX_CALIB_SIZE];
+ /*Max process calibration data size*/
+ __u8 data[PROCS_CALIB_SIZE];
} __packed;
+
+struct hci_fm_set_cal_req_dc {
+ __u8 mode;
+ /*Max DC calibration data size*/
+ __u8 data[DC_CALIB_SIZE];
+} __packed;
+
struct hci_cc_do_calibration_rsp {
__u8 status;
__u8 mode;
diff --git a/include/net/bluetooth/amp.h b/include/net/bluetooth/amp.h
index 53fea37..0a2849a 100644
--- a/include/net/bluetooth/amp.h
+++ b/include/net/bluetooth/amp.h
@@ -45,9 +45,6 @@
__le16 reason;
} __packed;
-#define HCI_A2MP_ID(id) ((id)+0x10) /* convert HCI dev index to AMP ID */
-#define A2MP_HCI_ID(id) ((id)-0x10) /* convert AMP ID to HCI dev index */
-
struct a2mp_discover_req {
__le16 mtu;
__le16 ext_feat;
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 8487c49..709dc6d 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -753,7 +753,8 @@
hotremove_migrate_alloc(struct page *page, unsigned long private, int **x)
{
/* This should be improooooved!! */
- return alloc_page(GFP_HIGHUSER_MOVABLE);
+ return alloc_page(GFP_HIGHUSER_MOVABLE | __GFP_NORETRY | __GFP_NOWARN |
+ __GFP_NOMEMALLOC);
}
#define NR_OFFLINE_AT_ONCE_PAGES (256)
diff --git a/net/bluetooth/amp.c b/net/bluetooth/amp.c
index af8ee26..4918caa 100644
--- a/net/bluetooth/amp.c
+++ b/net/bluetooth/amp.c
@@ -255,7 +255,7 @@
read_lock(&mgr->ctx_list_lock);
list_for_each_entry(ctx, &mgr->ctx_list, list) {
struct hci_dev *ctx_hdev;
- ctx_hdev = hci_dev_get(A2MP_HCI_ID(ctx->id));
+ ctx_hdev = hci_dev_get(ctx->id);
if ((ctx_hdev == hdev) && (ctx->evt_type & evt_type)) {
switch (evt_type) {
case AMP_HCI_CMD_STATUS:
@@ -356,7 +356,7 @@
if (hdev) {
if ((hdev->amp_type != HCI_BREDR) &&
test_bit(HCI_UP, &hdev->flags)) {
- (cl + num_ctrls)->id = HCI_A2MP_ID(hdev->id);
+ (cl + num_ctrls)->id = hdev->id;
(cl + num_ctrls)->type = hdev->amp_type;
(cl + num_ctrls)->status = hdev->amp_status;
++num_ctrls;
@@ -459,7 +459,7 @@
rsp.status = 1;
BT_DBG("id %d", id);
- hdev = hci_dev_get(A2MP_HCI_ID(id));
+ hdev = hci_dev_get(id);
if (hdev && hdev->amp_type != HCI_BREDR) {
rsp.status = 0;
@@ -510,14 +510,14 @@
int result = -EINVAL;
BT_DBG("lcon %p", lcon);
+ hdev = hci_dev_get(id);
+ if (!hdev)
+ goto ap_finished;
+ BT_DBG("hdev %p", hdev);
mgr = get_create_amp_mgr(lcon, NULL);
if (!mgr)
goto ap_finished;
BT_DBG("mgr %p", mgr);
- hdev = hci_dev_get(A2MP_HCI_ID(id));
- if (!hdev)
- goto ap_finished;
- BT_DBG("hdev %p", hdev);
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK,
&mgr->l2cap_conn->hcon->dst);
if (conn) {
@@ -534,6 +534,8 @@
return;
ap_finished:
+ if (hdev)
+ hci_dev_put(hdev);
l2cap_amp_physical_complete(result, id, remote_id, sk);
}
@@ -553,7 +555,7 @@
return -ENOMEM;
ctx->id = req->id;
ctx->d.gaa.req_ident = hdr->ident;
- ctx->hdev = hci_dev_get(A2MP_HCI_ID(ctx->id));
+ ctx->hdev = hci_dev_get(ctx->id);
if (ctx->hdev)
ctx->d.gaa.assoc = kmalloc(ctx->hdev->amp_assoc_size,
GFP_ATOMIC);
@@ -826,7 +828,7 @@
ctx->d.apl.len_so_far = 0;
ctx->d.apl.rem_len = skb->len;
skb_pull(skb, skb->len);
- ctx->hdev = hci_dev_get(A2MP_HCI_ID(ctx->id));
+ ctx->hdev = hci_dev_get(ctx->id);
start_ctx(mgr, ctx);
return 0;
}
@@ -1122,7 +1124,7 @@
if (hdev) {
struct hci_conn *conn;
ctx->hdev = hdev;
- ctx->id = HCI_A2MP_ID(hdev->id);
+ ctx->id = hdev->id;
ctx->d.cpl.remote_id = cl->id;
conn = hci_conn_hash_lookup_ba(hdev,
ACL_LINK,
@@ -1425,7 +1427,7 @@
rsp.status = 0;
BT_DBG("local_id %d remote_id %d",
(int) rsp.local_id, (int) rsp.remote_id);
- hdev = hci_dev_get(A2MP_HCI_ID(rsp.local_id));
+ hdev = hci_dev_get(rsp.local_id);
if (!hdev) {
rsp.status = 1; /* Invalid Controller ID */
goto dpl_finished;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index abc3ef7..d63dd1a 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -3158,21 +3158,23 @@
struct hci_conn *hcon;
struct hci_chan *chan;
- hdev = hci_dev_get(A2MP_HCI_ID(amp_id));
+ hdev = hci_dev_get(amp_id);
if (!hdev)
return NULL;
BT_DBG("hdev %s", hdev->name);
hcon = hci_conn_hash_lookup_ba(hdev, ACL_LINK, pi->conn->dst);
- if (!hcon)
- return NULL;
+ if (!hcon) {
+ chan = NULL;
+ goto done;
+ }
chan = hci_chan_list_lookup_id(hdev, hcon->handle);
if (chan) {
l2cap_aggregate(chan, pi);
hci_chan_hold(chan);
- return chan;
+ goto done;
}
if (bt_sk(pi)->parent) {
@@ -3186,6 +3188,8 @@
(struct hci_ext_fs *) &pi->local_fs,
(struct hci_ext_fs *) &pi->remote_fs);
}
+done:
+ hci_dev_put(hdev);
return chan;
}
@@ -3743,6 +3747,12 @@
BT_DBG("sk %p, rsp %p, len %d, req %p", sk, rsp, len, data);
+ /* Initialize rfc in case no rfc option is received */
+ rfc.mode = pi->mode;
+ rfc.retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+ rfc.monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+ rfc.max_pdu_size = L2CAP_DEFAULT_MAX_PDU_SIZE;
+
while (len >= L2CAP_CONF_OPT_SIZE) {
len -= l2cap_get_conf_opt(&rsp, &type, &olen, &val);
@@ -3837,6 +3847,12 @@
BT_DBG("sk %p, rsp %p, len %d", sk, rsp, len);
+ /* Initialize rfc in case no rfc option is received */
+ rfc.mode = pi->mode;
+ rfc.retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
+ rfc.monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
+ rfc.max_pdu_size = L2CAP_DEFAULT_MAX_PDU_SIZE;
+
if ((pi->mode != L2CAP_MODE_ERTM) && (pi->mode != L2CAP_MODE_STREAMING))
return;
@@ -4727,7 +4743,7 @@
struct hci_dev *hdev;
/* Validate AMP controller id */
- hdev = hci_dev_get(A2MP_HCI_ID(req->amp_id));
+ hdev = hci_dev_get(req->amp_id);
if (!hdev || !test_bit(HCI_UP, &hdev->flags)) {
struct l2cap_create_chan_rsp rsp;
@@ -4805,7 +4821,7 @@
if (req->dest_amp_id) {
struct hci_dev *hdev;
- hdev = hci_dev_get(A2MP_HCI_ID(req->dest_amp_id));
+ hdev = hci_dev_get(req->dest_amp_id);
if (!hdev || !test_bit(HCI_UP, &hdev->flags)) {
if (hdev)
hci_dev_put(hdev);
@@ -4813,6 +4829,7 @@
result = L2CAP_MOVE_CHAN_REFUSED_CONTROLLER;
goto send_move_response;
}
+ hci_dev_put(hdev);
}
if (((pi->amp_move_state != L2CAP_AMP_STATE_STABLE &&
diff --git a/sound/soc/codecs/wcd9310.c b/sound/soc/codecs/wcd9310.c
index cab5ec5..3b83532 100644
--- a/sound/soc/codecs/wcd9310.c
+++ b/sound/soc/codecs/wcd9310.c
@@ -62,6 +62,22 @@
u16 ctl_reg;
};
+/* Codec supports 2 IIR filters */
+enum {
+ IIR1 = 0,
+ IIR2,
+ IIR_MAX,
+};
+/* Codec supports 5 bands */
+enum {
+ BAND1 = 0,
+ BAND2,
+ BAND3,
+ BAND4,
+ BAND5,
+ BAND_MAX,
+};
+
struct tabla_priv {
struct snd_soc_codec *codec;
u32 adc_count;
@@ -204,6 +220,173 @@
return 0;
}
+static int tabla_get_iir_enable_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] =
+ snd_soc_read(codec, (TABLA_A_CDC_IIR1_CTL + 16 * iir_idx)) &
+ (1 << band_idx);
+
+ pr_debug("%s: IIR #%d band #%d enable %d\n", __func__,
+ iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int tabla_put_iir_enable_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+ int value = ucontrol->value.integer.value[0];
+
+ /* Mask first 5 bits, 6-8 are reserved */
+ snd_soc_update_bits(codec, (TABLA_A_CDC_IIR1_CTL + 16 * iir_idx),
+ (1 << band_idx), (value << band_idx));
+
+ pr_debug("%s: IIR #%d band #%d enable %d\n", __func__,
+ iir_idx, band_idx, value);
+ return 0;
+}
+static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec,
+ int iir_idx, int band_idx,
+ int coeff_idx)
+{
+ /* Address does not automatically update if reading */
+ snd_soc_update_bits(codec,
+ (TABLA_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+ 0x1F, band_idx * BAND_MAX + coeff_idx);
+
+ /* Mask bits top 2 bits since they are reserved */
+ return ((snd_soc_read(codec,
+ (TABLA_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) << 24) |
+ (snd_soc_read(codec,
+ (TABLA_A_CDC_IIR1_COEF_B3_CTL + 16 * iir_idx)) << 16) |
+ (snd_soc_read(codec,
+ (TABLA_A_CDC_IIR1_COEF_B4_CTL + 16 * iir_idx)) << 8) |
+ (snd_soc_read(codec,
+ (TABLA_A_CDC_IIR1_COEF_B5_CTL + 16 * iir_idx)))) &
+ 0x3FFFFFFF;
+}
+
+static int tabla_get_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ ucontrol->value.integer.value[0] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 0);
+ ucontrol->value.integer.value[1] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 1);
+ ucontrol->value.integer.value[2] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 2);
+ ucontrol->value.integer.value[3] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 3);
+ ucontrol->value.integer.value[4] =
+ get_iir_band_coeff(codec, iir_idx, band_idx, 4);
+
+ pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+ "%s: IIR #%d band #%d b1 = 0x%x\n"
+ "%s: IIR #%d band #%d b2 = 0x%x\n"
+ "%s: IIR #%d band #%d a1 = 0x%x\n"
+ "%s: IIR #%d band #%d a2 = 0x%x\n",
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[0],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[1],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[2],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[3],
+ __func__, iir_idx, band_idx,
+ (uint32_t)ucontrol->value.integer.value[4]);
+ return 0;
+}
+
+static void set_iir_band_coeff(struct snd_soc_codec *codec,
+ int iir_idx, int band_idx,
+ int coeff_idx, uint32_t value)
+{
+ /* Mask top 3 bits, 6-8 are reserved */
+ /* Update address manually each time */
+ snd_soc_update_bits(codec,
+ (TABLA_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+ 0x1F, band_idx * BAND_MAX + coeff_idx);
+
+ /* Mask top 2 bits, 7-8 are reserved */
+ snd_soc_update_bits(codec,
+ (TABLA_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx),
+ 0x3F, (value >> 24) & 0x3F);
+
+ /* Isolate 8bits at a time */
+ snd_soc_update_bits(codec,
+ (TABLA_A_CDC_IIR1_COEF_B3_CTL + 16 * iir_idx),
+ 0xFF, (value >> 16) & 0xFF);
+
+ snd_soc_update_bits(codec,
+ (TABLA_A_CDC_IIR1_COEF_B4_CTL + 16 * iir_idx),
+ 0xFF, (value >> 8) & 0xFF);
+
+ snd_soc_update_bits(codec,
+ (TABLA_A_CDC_IIR1_COEF_B5_CTL + 16 * iir_idx),
+ 0xFF, value & 0xFF);
+}
+
+static int tabla_put_iir_band_audio_mixer(
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ int iir_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->reg;
+ int band_idx = ((struct soc_multi_mixer_control *)
+ kcontrol->private_value)->shift;
+
+ set_iir_band_coeff(codec, iir_idx, band_idx, 0,
+ ucontrol->value.integer.value[0]);
+ set_iir_band_coeff(codec, iir_idx, band_idx, 1,
+ ucontrol->value.integer.value[1]);
+ set_iir_band_coeff(codec, iir_idx, band_idx, 2,
+ ucontrol->value.integer.value[2]);
+ set_iir_band_coeff(codec, iir_idx, band_idx, 3,
+ ucontrol->value.integer.value[3]);
+ set_iir_band_coeff(codec, iir_idx, band_idx, 4,
+ ucontrol->value.integer.value[4]);
+
+ pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+ "%s: IIR #%d band #%d b1 = 0x%x\n"
+ "%s: IIR #%d band #%d b2 = 0x%x\n"
+ "%s: IIR #%d band #%d a1 = 0x%x\n"
+ "%s: IIR #%d band #%d a2 = 0x%x\n",
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 0),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 1),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 2),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 3),
+ __func__, iir_idx, band_idx,
+ get_iir_band_coeff(codec, iir_idx, band_idx, 4));
+ return 0;
+}
+
static const char *tabla_ear_pa_gain_text[] = {"POS_6_DB", "POS_2_DB"};
static const struct soc_enum tabla_ear_pa_gain_enum[] = {
SOC_ENUM_SINGLE_EXT(2, tabla_ear_pa_gain_text),
@@ -342,6 +525,7 @@
SOC_SINGLE_TLV("ADC6 Volume", TABLA_A_TX_5_6_EN, 1, 3, 0, analog_gain),
SOC_SINGLE("MICBIAS1 CAPLESS Switch", TABLA_A_MICB_1_CTL, 4, 1, 1),
+ SOC_SINGLE("MICBIAS2 CAPLESS Switch", TABLA_A_MICB_2_CTL, 4, 1, 1),
SOC_SINGLE("MICBIAS3 CAPLESS Switch", TABLA_A_MICB_3_CTL, 4, 1, 1),
SOC_SINGLE("MICBIAS4 CAPLESS Switch", TABLA_A_MICB_4_CTL, 4, 1, 1),
@@ -384,6 +568,48 @@
SOC_ENUM("RX5 HPF cut off", cf_rxmix5_enum),
SOC_ENUM("RX6 HPF cut off", cf_rxmix6_enum),
SOC_ENUM("RX7 HPF cut off", cf_rxmix7_enum),
+
+ SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0,
+ tabla_get_iir_enable_audio_mixer, tabla_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0,
+ tabla_get_iir_enable_audio_mixer, tabla_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0,
+ tabla_get_iir_enable_audio_mixer, tabla_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0,
+ tabla_get_iir_enable_audio_mixer, tabla_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0,
+ tabla_get_iir_enable_audio_mixer, tabla_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band1", IIR2, BAND1, 1, 0,
+ tabla_get_iir_enable_audio_mixer, tabla_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band2", IIR2, BAND2, 1, 0,
+ tabla_get_iir_enable_audio_mixer, tabla_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band3", IIR2, BAND3, 1, 0,
+ tabla_get_iir_enable_audio_mixer, tabla_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band4", IIR2, BAND4, 1, 0,
+ tabla_get_iir_enable_audio_mixer, tabla_put_iir_enable_audio_mixer),
+ SOC_SINGLE_EXT("IIR2 Enable Band5", IIR2, BAND5, 1, 0,
+ tabla_get_iir_enable_audio_mixer, tabla_put_iir_enable_audio_mixer),
+
+ SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5,
+ tabla_get_iir_band_audio_mixer, tabla_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5,
+ tabla_get_iir_band_audio_mixer, tabla_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5,
+ tabla_get_iir_band_audio_mixer, tabla_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5,
+ tabla_get_iir_band_audio_mixer, tabla_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5,
+ tabla_get_iir_band_audio_mixer, tabla_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band1", IIR2, BAND1, 255, 0, 5,
+ tabla_get_iir_band_audio_mixer, tabla_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band2", IIR2, BAND2, 255, 0, 5,
+ tabla_get_iir_band_audio_mixer, tabla_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band3", IIR2, BAND3, 255, 0, 5,
+ tabla_get_iir_band_audio_mixer, tabla_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band4", IIR2, BAND4, 255, 0, 5,
+ tabla_get_iir_band_audio_mixer, tabla_put_iir_band_audio_mixer),
+ SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5,
+ tabla_get_iir_band_audio_mixer, tabla_put_iir_band_audio_mixer),
};
static const char *rx_mix1_text[] = {
@@ -1988,6 +2214,11 @@
if ((reg >= TABLA_A_CDC_MBHC_EN_CTL) || (reg < 0x100))
return 1;
+ /* IIR Coeff registers are not cacheable */
+ if ((reg >= TABLA_A_CDC_IIR1_COEF_B1_CTL) &&
+ (reg <= TABLA_A_CDC_IIR2_COEF_B5_CTL))
+ return 1;
+
return 0;
}