Merge "msm: mdm: add board support for the sglte modem" into msm-3.0
diff --git a/arch/arm/configs/msm8960-perf_defconfig b/arch/arm/configs/msm8960-perf_defconfig
index 620c8c0..4cbd1d1 100644
--- a/arch/arm/configs/msm8960-perf_defconfig
+++ b/arch/arm/configs/msm8960-perf_defconfig
@@ -63,6 +63,7 @@
CONFIG_MSM_SMD_PKG4=y
CONFIG_MSM_PCIE=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_RMNET_SMUX=y
CONFIG_MSM_DSPS=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
@@ -282,6 +283,9 @@
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_PMIC8XXX_PWRKEY=y
# CONFIG_LEGACY_PTYS is not set
+CONFIG_N_SMUX=y
+CONFIG_N_SMUX_LOOPBACK=y
+CONFIG_SMUX_CTL=y
CONFIG_SERIAL_MSM_HS=y
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index 6f7102c..a901684 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -62,6 +62,7 @@
CONFIG_MSM_SMD_PKG4=y
CONFIG_MSM_PCIE=y
CONFIG_MSM_BAM_DMUX=y
+CONFIG_MSM_RMNET_SMUX=y
CONFIG_MSM_DSPS=y
CONFIG_MSM_IPC_ROUTER=y
CONFIG_MSM_IPC_ROUTER_SMD_XPRT=y
@@ -291,6 +292,9 @@
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_PMIC8XXX_PWRKEY=y
# CONFIG_LEGACY_PTYS is not set
+CONFIG_N_SMUX=y
+CONFIG_N_SMUX_LOOPBACK=y
+CONFIG_SMUX_CTL=y
CONFIG_SERIAL_MSM_HS=y
CONFIG_SERIAL_MSM_HSL=y
CONFIG_SERIAL_MSM_HSL_CONSOLE=y
diff --git a/arch/arm/mach-msm/board-9615.c b/arch/arm/mach-msm/board-9615.c
index 67697d2..9ad2c5e 100644
--- a/arch/arm/mach-msm/board-9615.c
+++ b/arch/arm/mach-msm/board-9615.c
@@ -851,9 +851,7 @@
#ifdef CONFIG_LTC4088_CHARGER
&msm_device_charger,
#endif
-#ifndef CONFIG_USB_CI13XXX_MSM_HSIC
&msm_device_otg,
-#endif
&msm_device_hsic_peripheral,
&msm_device_gadget_peripheral,
&msm_device_hsusb_host,
diff --git a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
index d8a3e60..be11989 100644
--- a/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
+++ b/arch/arm/mach-msm/include/mach/usb_gadget_xport.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+/* Copyright (c) 2011-2012, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -22,6 +22,7 @@
USB_GADGET_XPORT_BAM,
USB_GADGET_XPORT_BAM2BAM,
USB_GADGET_XPORT_HSIC,
+ USB_GADGET_XPORT_HSUART,
USB_GADGET_XPORT_NONE,
};
@@ -42,6 +43,8 @@
return "BAM2BAM";
case USB_GADGET_XPORT_HSIC:
return "HSIC";
+ case USB_GADGET_XPORT_HSUART:
+ return "HSUART";
case USB_GADGET_XPORT_NONE:
return "NONE";
default:
@@ -63,6 +66,8 @@
return USB_GADGET_XPORT_BAM2BAM;
if (!strncasecmp("HSIC", name, XPORT_STR_LEN))
return USB_GADGET_XPORT_HSIC;
+ if (!strncasecmp("HSUART", name, XPORT_STR_LEN))
+ return USB_GADGET_XPORT_HSUART;
if (!strncasecmp("", name, XPORT_STR_LEN))
return USB_GADGET_XPORT_NONE;
@@ -79,6 +84,11 @@
#define NUM_PORTS (NUM_RMNET_HSIC_PORTS \
+ NUM_DUN_HSIC_PORTS)
+#define NUM_RMNET_HSUART_PORTS 1
+#define NUM_DUN_HSUART_PORTS 1
+#define NUM_HSUART_PORTS (NUM_RMNET_HSUART_PORTS \
+ + NUM_DUN_HSUART_PORTS)
+
int ghsic_ctrl_connect(void *, int);
void ghsic_ctrl_disconnect(void *, int);
int ghsic_ctrl_setup(unsigned int, enum gadget_type);
@@ -86,4 +96,10 @@
void ghsic_data_disconnect(void *, int);
int ghsic_data_setup(unsigned int, enum gadget_type);
+int ghsuart_ctrl_connect(void *, int);
+void ghsuart_ctrl_disconnect(void *, int);
+int ghsuart_ctrl_setup(unsigned int, enum gadget_type);
+int ghsuart_data_connect(void *, int);
+void ghsuart_data_disconnect(void *, int);
+int ghsuart_data_setup(unsigned int, enum gadget_type);
#endif
diff --git a/arch/arm/mach-msm/sdio_al_dloader.c b/arch/arm/mach-msm/sdio_al_dloader.c
index 74b8478..f48c32b 100644
--- a/arch/arm/mach-msm/sdio_al_dloader.c
+++ b/arch/arm/mach-msm/sdio_al_dloader.c
@@ -1140,14 +1140,6 @@
return status;
}
- status = sdio_dld_create_thread();
- if (status) {
- sdio_dld_dealloc_local_buffers();
- pr_err(MODULE_NAME ": %s, failed in sdio_dld_create_thread()."
- "status=%d\n", __func__, status);
- return status;
- }
-
/* init waiting event of the write callback */
init_waitqueue_head(&sdio_dld->write_callback_event.wait_event);
@@ -1168,6 +1160,15 @@
sdio_dld->push_timer.data = (unsigned long) sdio_dld;
sdio_dld->push_timer.function = sdio_dld_push_timer_handler;
+ status = sdio_dld_create_thread();
+ if (status) {
+ del_timer_sync(&sdio_dld->timer);
+ del_timer_sync(&sdio_dld->push_timer);
+ sdio_dld_dealloc_local_buffers();
+ pr_err(MODULE_NAME ": %s, failed in sdio_dld_create_thread()."
+ "status=%d\n", __func__, status);
+ return status;
+ }
return 0;
}
diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c
index 6ce7cbb..f917c98 100644
--- a/drivers/mmc/card/block.c
+++ b/drivers/mmc/card/block.c
@@ -59,6 +59,8 @@
#define INAND_CMD38_ARG_SECTRIM1 0x81
#define INAND_CMD38_ARG_SECTRIM2 0x88
+#define MMC_SANITIZE_REQ_TIMEOUT 240000 /* msec */
+
static DEFINE_MUTEX(block_mutex);
/*
@@ -871,7 +873,8 @@
mmc_hostname(card->host), __func__);
err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
- EXT_CSD_SANITIZE_START, 1, 0);
+ EXT_CSD_SANITIZE_START, 1,
+ MMC_SANITIZE_REQ_TIMEOUT);
if (err)
pr_err("%s: %s - mmc_switch() with "
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 8aab269..44041b8 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -3466,6 +3466,15 @@
for routing IP packets within the MSM using
BAM as a physical transport.
+config MSM_RMNET_SMUX
+ bool "RMNET SMUX Driver"
+ depends on N_SMUX
+ help
+ Implements RMNET over SMUX interface.
+ RMNET provides a virtual ethernet interface
+ for routing IP packets within the MSM using
+ HSUART as a physical transport.
+
config MSM_RMNET_DEBUG
bool "MSM RMNET debug interface"
depends on MSM_RMNET
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 7b3cd59..7373a61 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -304,6 +304,7 @@
obj-$(CONFIG_MSM_RMNET) += msm_rmnet.o
obj-$(CONFIG_MSM_RMNET_SDIO) += msm_rmnet_sdio.o
obj-$(CONFIG_MSM_RMNET_BAM) += msm_rmnet_bam.o
+obj-$(CONFIG_MSM_RMNET_SMUX) += msm_rmnet_smux.o
obj-$(CONFIG_NIU) += niu.o
obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
diff --git a/drivers/net/msm_rmnet_smux.c b/drivers/net/msm_rmnet_smux.c
new file mode 100644
index 0000000..70e7182
--- /dev/null
+++ b/drivers/net/msm_rmnet_smux.c
@@ -0,0 +1,944 @@
+/* Copyright (c) 2012, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+/*
+ * RMNET SMUX Module.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/wakelock.h>
+#include <linux/if_arp.h>
+#include <linux/msm_rmnet.h>
+#include <linux/platform_device.h>
+#include <linux/smux.h>
+#include <linux/ip.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+
+/* Debug message support */
+static int msm_rmnet_smux_debug_mask;
+module_param_named(debug_enable, msm_rmnet_smux_debug_mask,
+ int, S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define DEBUG_MASK_LVL0 (1U << 0)
+#define DEBUG_MASK_LVL1 (1U << 1)
+#define DEBUG_MASK_LVL2 (1U << 2)
+
+#define DBG(m, x...) do { \
+ if (msm_rmnet_smux_debug_mask & m) \
+ pr_info(x); \
+} while (0)
+
+#define DBG0(x...) DBG(DEBUG_MASK_LVL0, x)
+#define DBG1(x...) DBG(DEBUG_MASK_LVL1, x)
+#define DBG2(x...) DBG(DEBUG_MASK_LVL2, x)
+
+/* Configure device instances */
+#define RMNET_SMUX_DEVICE_COUNT (1)
+
+/* allow larger frames */
+#define RMNET_DATA_LEN 2000
+
+#define DEVICE_ID_INVALID -1
+
+#define DEVICE_INACTIVE 0x00
+#define DEVICE_ACTIVE 0x01
+
+#define HEADROOM_FOR_SMUX 8 /* for mux header */
+#define HEADROOM_FOR_QOS 8
+#define TAILROOM 8 /* for padding by mux layer */
+
+struct rmnet_private {
+ struct net_device_stats stats;
+ uint32_t ch_id;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ ktime_t last_packet;
+ unsigned long wakeups_xmit;
+ unsigned long wakeups_rcv;
+ unsigned long timeout_us;
+#endif
+ spinlock_t lock;
+ struct tasklet_struct tsklt;
+ /* IOCTL specified mode (protocol, QoS header) */
+ u32 operation_mode;
+ uint8_t device_state;
+ uint8_t in_reset;
+};
+
+static struct net_device *netdevs[RMNET_SMUX_DEVICE_COUNT];
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+static unsigned long timeout_us;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/*
+ * If early suspend is enabled then we specify two timeout values,
+ * screen on (default), and screen is off.
+ */
+static unsigned long timeout_suspend_us;
+static struct device *rmnet0;
+
+/* Set timeout in us when the screen is off. */
+static ssize_t timeout_suspend_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+ timeout_suspend_us = strict_strtoul(buf, NULL, 10);
+ return n;
+}
+
+static ssize_t timeout_suspend_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%lu\n",
+ (unsigned long) timeout_suspend_us);
+}
+
+static DEVICE_ATTR(timeout_suspend, 0664, timeout_suspend_show,
+ timeout_suspend_store);
+
+static void rmnet_early_suspend(struct early_suspend *handler)
+{
+ if (rmnet0) {
+ struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
+ p->timeout_us = timeout_suspend_us;
+ }
+}
+
+static void rmnet_late_resume(struct early_suspend *handler)
+{
+ if (rmnet0) {
+ struct rmnet_private *p = netdev_priv(to_net_dev(rmnet0));
+ p->timeout_us = timeout_us;
+ }
+}
+
+static struct early_suspend rmnet_power_suspend = {
+ .suspend = rmnet_early_suspend,
+ .resume = rmnet_late_resume,
+};
+
+static int __init rmnet_late_init(void)
+{
+ register_early_suspend(&rmnet_power_suspend);
+ return 0;
+}
+
+late_initcall(rmnet_late_init);
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+
+/* Returns 1 if packet caused rmnet to wakeup, 0 otherwise. */
+static int rmnet_cause_wakeup(struct rmnet_private *p)
+{
+ int ret = 0;
+ ktime_t now;
+ if (p->timeout_us == 0) /* Check if disabled */
+ return 0;
+
+ /* Use real (wall) time. */
+ now = ktime_get_real();
+
+ if (ktime_us_delta(now, p->last_packet) > p->timeout_us)
+ ret = 1;
+
+ p->last_packet = now;
+ return ret;
+}
+
+static ssize_t wakeups_xmit_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmnet_private *p = netdev_priv(to_net_dev(d));
+ return snprintf(buf, PAGE_SIZE, "%lu\n", p->wakeups_xmit);
+}
+
+DEVICE_ATTR(wakeups_xmit, 0444, wakeups_xmit_show, NULL);
+
+static ssize_t wakeups_rcv_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmnet_private *p = netdev_priv(to_net_dev(d));
+ return snprintf(buf, PAGE_SIZE, "%lu\n", p->wakeups_rcv);
+}
+
+DEVICE_ATTR(wakeups_rcv, 0444, wakeups_rcv_show, NULL);
+
+/* Set timeout in us. */
+static ssize_t timeout_store(struct device *d,
+ struct device_attribute *attr,
+ const char *buf, size_t n)
+{
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ struct rmnet_private *p = netdev_priv(to_net_dev(d));
+ p->timeout_us = timeout_us = strict_strtoul(buf, NULL, 10);
+#else
+/* If using early suspend/resume hooks do not write the value on store. */
+ timeout_us = strict_strtoul(buf, NULL, 10);
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+ return n;
+}
+
+static ssize_t timeout_show(struct device *d,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmnet_private *p = netdev_priv(to_net_dev(d));
+ p = netdev_priv(to_net_dev(d));
+ return snprintf(buf, PAGE_SIZE, "%lu\n", timeout_us);
+}
+
+DEVICE_ATTR(timeout, 0664, timeout_show, timeout_store);
+#endif /* CONFIG_MSM_RMNET_DEBUG */
+
+/* Forward declaration */
+static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+
+
+
+static int count_this_packet(void *_hdr, int len)
+{
+ struct ethhdr *hdr = _hdr;
+
+ if (len >= ETH_HLEN && hdr->h_proto == htons(ETH_P_ARP))
+ return 0;
+
+ return 1;
+}
+
+static __be16 rmnet_ip_type_trans(struct sk_buff *skb, struct net_device *dev)
+{
+ __be16 protocol = 0;
+
+ skb->dev = dev;
+
+ /* Determine L3 protocol */
+ switch (skb->data[0] & 0xf0) {
+ case 0x40:
+ protocol = htons(ETH_P_IP);
+ break;
+ case 0x60:
+ protocol = htons(ETH_P_IPV6);
+ break;
+ default:
+ pr_err("[%s] rmnet_recv() L3 protocol decode error: 0x%02x",
+ dev->name, skb->data[0] & 0xf0);
+ /* skb will be dropped in upper layer for unknown protocol */
+ }
+ return protocol;
+}
+
+static void smux_read_done(void *rcv_dev, const void *meta_data)
+{
+ struct rmnet_private *p;
+ struct net_device *dev = rcv_dev;
+ u32 opmode;
+ unsigned long flags;
+ struct sk_buff *skb = NULL;
+ const struct smux_meta_read *read_meta_info = meta_data;
+
+ if (!dev || !read_meta_info) {
+ DBG1("%s:invalid read_done callback recieved", __func__);
+ return;
+ }
+
+ p = netdev_priv(dev);
+
+ skb = (struct sk_buff *) read_meta_info->pkt_priv;
+
+ if (!skb || skb->dev != dev) {
+ DBG1("%s: ERR:skb pointer NULL in READ_DONE CALLBACK",
+ __func__);
+ return;
+ }
+
+ /* Handle Rx frame format */
+ spin_lock_irqsave(&p->lock, flags);
+ opmode = p->operation_mode;
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ if (RMNET_IS_MODE_IP(opmode)) {
+ /* Driver in IP mode */
+ skb->protocol =
+ rmnet_ip_type_trans(skb, dev);
+ } else {
+ /* Driver in Ethernet mode */
+ skb->protocol =
+ eth_type_trans(skb, dev);
+ }
+ if (RMNET_IS_MODE_IP(opmode) ||
+ count_this_packet(skb->data, skb->len)) {
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ p->wakeups_rcv +=
+ rmnet_cause_wakeup(p);
+#endif
+ p->stats.rx_packets++;
+ p->stats.rx_bytes += skb->len;
+ }
+ DBG2("[%s] Rx packet #%lu len=%d\n",
+ dev->name, p->stats.rx_packets,
+ skb->len);
+ /* Deliver to network stack */
+ netif_rx(skb);
+
+ return;
+}
+
+static void smux_write_done(void *dev, const void *meta_data)
+{
+ struct rmnet_private *p = netdev_priv(dev);
+ u32 opmode;
+ struct sk_buff *skb = NULL;
+ const struct smux_meta_write *write_meta_info = meta_data;
+ unsigned long flags;
+
+ if (!dev || !write_meta_info) {
+ DBG1("%s: ERR:invalid WRITE_DONE callback recieved", __func__);
+ return;
+ }
+
+ skb = (struct sk_buff *) write_meta_info->pkt_priv;
+
+ if (!skb) {
+ DBG1("%s: ERR:skb pointer NULL in WRITE_DONE"
+ " CALLBACK", __func__);
+ return;
+ }
+
+ spin_lock_irqsave(&p->lock, flags);
+ opmode = p->operation_mode;
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ DBG1("%s: write complete\n", __func__);
+ if (RMNET_IS_MODE_IP(opmode) ||
+ count_this_packet(skb->data, skb->len)) {
+ p->stats.tx_packets++;
+ p->stats.tx_bytes += skb->len;
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ p->wakeups_xmit += rmnet_cause_wakeup(p);
+#endif
+ }
+ DBG1("[%s] Tx packet #%lu len=%d mark=0x%x\n",
+ ((struct net_device *)(dev))->name, p->stats.tx_packets,
+ skb->len, skb->mark);
+ dev_kfree_skb_any(skb);
+ if (netif_queue_stopped(dev) &&
+ msm_smux_is_ch_low(p->ch_id)) {
+ DBG0("%s: Low WM hit, waking queue=%p\n",
+ __func__, skb);
+ netif_wake_queue(dev);
+ }
+}
+
+void rmnet_smux_notify(void *priv, int event_type, const void *metadata)
+{
+ struct rmnet_private *p;
+ struct net_device *dev;
+ unsigned long flags;
+ struct sk_buff *skb = NULL;
+ u32 opmode;
+ const struct smux_meta_disconnected *ssr_info;
+ const struct smux_meta_read *read_meta_info;
+ const struct smux_meta_write *write_meta_info = metadata;
+
+
+ if (!priv)
+ DBG0("%s: priv(cookie) NULL, ignoring notification:"
+ " %d\n", __func__, event_type);
+
+ switch (event_type) {
+ case SMUX_CONNECTED:
+ p = netdev_priv(priv);
+ dev = priv;
+
+ DBG0("[%s] SMUX_CONNECTED event dev:%s\n", __func__, dev->name);
+
+ netif_carrier_on(dev);
+ netif_start_queue(dev);
+
+ spin_lock_irqsave(&p->lock, flags);
+ p->device_state = DEVICE_ACTIVE;
+ spin_unlock_irqrestore(&p->lock, flags);
+ break;
+
+ case SMUX_DISCONNECTED:
+ p = netdev_priv(priv);
+ dev = priv;
+ ssr_info = metadata;
+
+ DBG0("[%s] SMUX_DISCONNECTED event dev:%s\n",
+ __func__, dev->name);
+
+ if (ssr_info && ssr_info->is_ssr == 1)
+ DBG0("SSR detected on :%s\n", dev->name);
+
+ netif_carrier_off(dev);
+ netif_stop_queue(dev);
+
+ spin_lock_irqsave(&p->lock, flags);
+ p->device_state = DEVICE_INACTIVE;
+ spin_unlock_irqrestore(&p->lock, flags);
+ break;
+
+ case SMUX_READ_DONE:
+ smux_read_done(priv, metadata);
+ break;
+
+ case SMUX_READ_FAIL:
+ p = netdev_priv(priv);
+ dev = priv;
+ read_meta_info = metadata;
+
+ if (!dev || !read_meta_info) {
+ DBG1("%s: ERR:invalid read failed callback"
+ " recieved", __func__);
+ return;
+ }
+
+ skb = (struct sk_buff *) read_meta_info->pkt_priv;
+
+ if (!skb) {
+ DBG1("%s: ERR:skb pointer NULL in read fail"
+ " CALLBACK", __func__);
+ return;
+ }
+
+ DBG0("%s: read failed\n", __func__);
+
+ opmode = p->operation_mode;
+
+ if (RMNET_IS_MODE_IP(opmode) ||
+ count_this_packet(skb->data, skb->len))
+ p->stats.rx_dropped++;
+
+ dev_kfree_skb_any(skb);
+ break;
+
+ case SMUX_WRITE_DONE:
+ smux_write_done(priv, metadata);
+ break;
+
+ case SMUX_WRITE_FAIL:
+ p = netdev_priv(priv);
+ dev = priv;
+ write_meta_info = metadata;
+
+ if (!dev || !write_meta_info) {
+ DBG1("%s: ERR:invalid WRITE_DONE"
+ "callback recieved", __func__);
+ return;
+ }
+
+ skb = (struct sk_buff *) write_meta_info->pkt_priv;
+
+ if (!skb) {
+ DBG1("%s: ERR:skb pointer NULL in"
+ " WRITE_DONE CALLBACK", __func__);
+ return;
+ }
+
+ DBG0("%s: write failed\n", __func__);
+
+ opmode = p->operation_mode;
+
+ if (RMNET_IS_MODE_IP(opmode) ||
+ count_this_packet(skb->data, skb->len)) {
+ p->stats.tx_dropped++;
+ }
+
+ dev_kfree_skb_any(skb);
+ break;
+
+ case SMUX_LOW_WM_HIT:
+ dev = priv;
+ DBG0("[%s] Low WM hit dev:%s\n", __func__, dev->name);
+ netif_start_queue(dev);
+ break;
+
+ case SMUX_HIGH_WM_HIT:
+ dev = priv;
+ DBG0("[%s] Low WM hit dev:%s\n", __func__, dev->name);
+ netif_stop_queue(dev);
+ break;
+
+ default:
+ dev = priv;
+ DBG0("[%s] Invalid event:%d received on"
+ " dev: %s\n", __func__, event_type, dev->name);
+ break;
+ }
+
+ return;
+}
+
+int get_rx_buffers(void *priv, void **pkt_priv, void **buffer, int size)
+{
+ struct net_device *dev = (struct net_device *) priv;
+ struct sk_buff *skb = NULL;
+ void *ptr = NULL;
+
+ DBG0("[%s] dev:%s\n", __func__, dev->name);
+ skb = __dev_alloc_skb(size, GFP_ATOMIC);
+ if (skb == NULL) {
+ DBG0("%s: unable to alloc skb\n", __func__);
+ return -ENOMEM;
+ }
+
+ /* TODO skb_reserve(skb, NET_IP_ALIGN); for ethernet mode */
+ /* Populate some params now. */
+ skb->dev = dev;
+ ptr = skb_put(skb, size);
+
+ skb_set_network_header(skb, 0);
+
+ /* done with skb setup, return the buffer pointer. */
+ *pkt_priv = skb;
+ *buffer = ptr;
+
+ return 0;
+}
+
+static int __rmnet_open(struct net_device *dev)
+{
+ struct rmnet_private *p = netdev_priv(dev);
+
+ DBG0("[%s] __rmnet_open()\n", dev->name);
+
+ if (p->device_state == DEVICE_ACTIVE) {
+ return 0;
+ } else {
+ DBG0("[%s] Platform inactive\n", dev->name);
+ return -ENODEV;
+ }
+}
+
+static int rmnet_open(struct net_device *dev)
+{
+ int rc = 0;
+
+ DBG0("[%s] rmnet_open()\n", dev->name);
+
+ rc = __rmnet_open(dev);
+
+ if (rc == 0)
+ netif_start_queue(dev);
+
+ return rc;
+}
+
+static int rmnet_stop(struct net_device *dev)
+{
+ DBG0("[%s] rmnet_stop()\n", dev->name);
+
+ netif_stop_queue(dev);
+ return 0;
+}
+
+static int rmnet_change_mtu(struct net_device *dev, int new_mtu)
+{
+ if (0 > new_mtu || RMNET_DATA_LEN < new_mtu)
+ return -EINVAL;
+
+ DBG0("[%s] MTU change: old=%d new=%d\n",
+ dev->name, dev->mtu, new_mtu);
+ dev->mtu = new_mtu;
+
+ return 0;
+}
+
+static int _rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct rmnet_private *p = netdev_priv(dev);
+ int smux_ret;
+ struct QMI_QOS_HDR_S *qmih;
+ u32 opmode;
+ unsigned long flags;
+
+ /* For QoS mode, prepend QMI header and assign flow ID from skb->mark */
+ spin_lock_irqsave(&p->lock, flags);
+ opmode = p->operation_mode;
+ spin_unlock_irqrestore(&p->lock, flags);
+
+ if (RMNET_IS_MODE_QOS(opmode)) {
+ qmih = (struct QMI_QOS_HDR_S *)
+ skb_push(skb, sizeof(struct QMI_QOS_HDR_S));
+ qmih->version = 1;
+ qmih->flags = 0;
+ qmih->flow_id = skb->mark;
+ }
+
+ dev->trans_start = jiffies;
+
+ /* if write() succeeds, skb access is unsafe in this process */
+ smux_ret = msm_smux_write(p->ch_id, skb, skb->data, skb->len);
+
+ if (smux_ret != 0 && smux_ret != -EAGAIN) {
+ pr_err("[%s] %s: write returned error %d",
+ dev->name, __func__, smux_ret);
+ return -EPERM;
+ }
+
+ return smux_ret;
+}
+
+static int rmnet_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct rmnet_private *p = netdev_priv(dev);
+ int ret = 0;
+
+ if (netif_queue_stopped(dev) || (p->device_state == DEVICE_INACTIVE)) {
+ pr_err("[%s]fatal: rmnet_xmit called when "
+ "netif_queue is stopped", dev->name);
+ return 0;
+ }
+
+ ret = _rmnet_xmit(skb, dev);
+
+ if (ret == -EPERM) {
+ /* Do not stop the queue here.
+ * It will lead to ir-recoverable state.
+ */
+ ret = NETDEV_TX_BUSY;
+ goto exit;
+ }
+
+ if (msm_smux_is_ch_full(p->ch_id) || (ret == -EAGAIN)) {
+ /*
+ * EAGAIN means we attempted to overflow the high watermark
+ * Clearly the queue is not stopped like it should be, so
+ * stop it and return BUSY to the TCP/IP framework. It will
+ * retry this packet with the queue is restarted which happens
+ * low watermark is called.
+ */
+ netif_stop_queue(dev);
+ ret = NETDEV_TX_BUSY;
+ goto exit;
+ }
+exit:
+ return ret;
+}
+
+static struct net_device_stats *rmnet_get_stats(struct net_device *dev)
+{
+ struct rmnet_private *p = netdev_priv(dev);
+ return &p->stats;
+}
+
+static void rmnet_set_multicast_list(struct net_device *dev)
+{
+}
+
+static void rmnet_tx_timeout(struct net_device *dev)
+{
+ pr_warning("[%s] rmnet_tx_timeout()\n", dev->name);
+}
+
+static const struct net_device_ops rmnet_ops_ether = {
+ .ndo_open = rmnet_open,
+ .ndo_stop = rmnet_stop,
+ .ndo_start_xmit = rmnet_xmit,
+ .ndo_get_stats = rmnet_get_stats,
+ .ndo_set_multicast_list = rmnet_set_multicast_list,
+ .ndo_tx_timeout = rmnet_tx_timeout,
+ .ndo_do_ioctl = rmnet_ioctl,
+ .ndo_change_mtu = rmnet_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static const struct net_device_ops rmnet_ops_ip = {
+ .ndo_open = rmnet_open,
+ .ndo_stop = rmnet_stop,
+ .ndo_start_xmit = rmnet_xmit,
+ .ndo_get_stats = rmnet_get_stats,
+ .ndo_set_multicast_list = rmnet_set_multicast_list,
+ .ndo_tx_timeout = rmnet_tx_timeout,
+ .ndo_do_ioctl = rmnet_ioctl,
+ .ndo_change_mtu = rmnet_change_mtu,
+ .ndo_set_mac_address = 0,
+ .ndo_validate_addr = 0,
+};
+
+static int rmnet_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+ struct rmnet_private *p = netdev_priv(dev);
+ u32 old_opmode = p->operation_mode;
+ unsigned long flags;
+ int prev_mtu = dev->mtu;
+ int rc = 0;
+
+ /* Process IOCTL command */
+ switch (cmd) {
+ case RMNET_IOCTL_SET_LLP_ETHERNET: /* Set Ethernet protocol */
+ /* Perform Ethernet config only if in IP mode currently*/
+ if (p->operation_mode & RMNET_MODE_LLP_IP) {
+ ether_setup(dev);
+ random_ether_addr(dev->dev_addr);
+ dev->mtu = prev_mtu;
+
+ dev->netdev_ops = &rmnet_ops_ether;
+ spin_lock_irqsave(&p->lock, flags);
+ p->operation_mode &= ~RMNET_MODE_LLP_IP;
+ p->operation_mode |= RMNET_MODE_LLP_ETH;
+ spin_unlock_irqrestore(&p->lock, flags);
+ DBG0("[%s] rmnet_ioctl(): "
+ "set Ethernet protocol mode\n",
+ dev->name);
+ }
+ break;
+
+ case RMNET_IOCTL_SET_LLP_IP: /* Set RAWIP protocol */
+ /* Perform IP config only if in Ethernet mode currently*/
+ if (p->operation_mode & RMNET_MODE_LLP_ETH) {
+
+ /* Undo config done in ether_setup() */
+ dev->header_ops = 0; /* No header */
+ dev->type = ARPHRD_RAWIP;
+ dev->hard_header_len = 0;
+ dev->mtu = prev_mtu;
+ dev->addr_len = 0;
+ dev->flags &= ~(IFF_BROADCAST |
+ IFF_MULTICAST);
+
+ dev->needed_headroom = HEADROOM_FOR_SMUX +
+ HEADROOM_FOR_QOS;
+ dev->needed_tailroom = TAILROOM;
+ dev->netdev_ops = &rmnet_ops_ip;
+ spin_lock_irqsave(&p->lock, flags);
+ p->operation_mode &= ~RMNET_MODE_LLP_ETH;
+ p->operation_mode |= RMNET_MODE_LLP_IP;
+ spin_unlock_irqrestore(&p->lock, flags);
+ DBG0("[%s] rmnet_ioctl(): "
+ "set IP protocol mode\n",
+ dev->name);
+ }
+ break;
+
+ case RMNET_IOCTL_GET_LLP: /* Get link protocol state */
+ ifr->ifr_ifru.ifru_data =
+ (void *)(p->operation_mode &
+ (RMNET_MODE_LLP_ETH|RMNET_MODE_LLP_IP));
+ break;
+
+ case RMNET_IOCTL_SET_QOS_ENABLE: /* Set QoS header enabled */
+ spin_lock_irqsave(&p->lock, flags);
+ p->operation_mode |= RMNET_MODE_QOS;
+ spin_unlock_irqrestore(&p->lock, flags);
+ DBG0("[%s] rmnet_ioctl(): set QMI QOS header enable\n",
+ dev->name);
+ break;
+
+ case RMNET_IOCTL_SET_QOS_DISABLE: /* Set QoS header disabled */
+ spin_lock_irqsave(&p->lock, flags);
+ p->operation_mode &= ~RMNET_MODE_QOS;
+ spin_unlock_irqrestore(&p->lock, flags);
+ DBG0("[%s] rmnet_ioctl(): set QMI QOS header disable\n",
+ dev->name);
+ break;
+
+ case RMNET_IOCTL_GET_QOS: /* Get QoS header state */
+ ifr->ifr_ifru.ifru_data =
+ (void *)(p->operation_mode & RMNET_MODE_QOS);
+ break;
+
+ case RMNET_IOCTL_GET_OPMODE: /* Get operation mode */
+ ifr->ifr_ifru.ifru_data = (void *)p->operation_mode;
+ break;
+
+ case RMNET_IOCTL_OPEN: /* Open transport port */
+ rc = __rmnet_open(dev);
+ DBG0("[%s] rmnet_ioctl(): open transport port\n",
+ dev->name);
+ break;
+
+ case RMNET_IOCTL_CLOSE: /* Close transport port */
+ DBG0("[%s] rmnet_ioctl(): close transport port\n",
+ dev->name);
+ break;
+
+ default:
+ pr_err("[%s] error: rmnet_ioct called for unsupported cmd[%d]",
+ dev->name, cmd);
+ return -EINVAL;
+ }
+
+ DBG2("[%s] %s: cmd=0x%x opmode old=0x%08x new=0x%08x\n",
+ dev->name, __func__, cmd, old_opmode, p->operation_mode);
+ return rc;
+}
+
+static void __init rmnet_setup(struct net_device *dev)
+{
+ /* Using Ethernet mode by default */
+ dev->netdev_ops = &rmnet_ops_ether;
+ ether_setup(dev);
+
+ /* set this after calling ether_setup */
+ dev->mtu = RMNET_DATA_LEN;
+ dev->needed_headroom = HEADROOM_FOR_SMUX + HEADROOM_FOR_QOS ;
+ dev->needed_tailroom = TAILROOM;
+ random_ether_addr(dev->dev_addr);
+
+ dev->watchdog_timeo = 1000; /* 10 seconds? */
+}
+
+
+static int smux_rmnet_probe(struct platform_device *pdev)
+{
+ int i;
+ int r;
+ struct rmnet_private *p;
+
+ for (i = 0; i < RMNET_SMUX_DEVICE_COUNT; i++) {
+ p = netdev_priv(netdevs[i]);
+
+ if ((p != NULL) && (p->device_state == DEVICE_INACTIVE)) {
+ r = msm_smux_open(p->ch_id,
+ netdevs[i],
+ rmnet_smux_notify,
+ get_rx_buffers);
+
+ if (r < 0) {
+ DBG0("%s: ch=%d open failed with rc %d\n",
+ __func__, p->ch_id, r);
+ }
+ }
+ }
+ return 0;
+}
+
+static int smux_rmnet_remove(struct platform_device *pdev)
+{
+ int i;
+ int r;
+ struct rmnet_private *p;
+
+ for (i = 0; i < RMNET_SMUX_DEVICE_COUNT; i++) {
+ p = netdev_priv(netdevs[i]);
+
+ if ((p != NULL) && (p->device_state == DEVICE_ACTIVE)) {
+ r = msm_smux_close(p->ch_id);
+
+ if (r < 0) {
+ DBG0("%s: ch=%d close failed with rc %d\n",
+ __func__, p->ch_id, r);
+ continue;
+ }
+ netif_carrier_off(netdevs[i]);
+ netif_stop_queue(netdevs[i]);
+ }
+ }
+ return 0;
+}
+
+
+static struct platform_driver smux_rmnet_driver = {
+ .probe = smux_rmnet_probe,
+ .remove = smux_rmnet_remove,
+ .driver = {
+ .name = "SMUX_RMNET",
+ .owner = THIS_MODULE,
+ },
+};
+
+
+static int __init rmnet_init(void)
+{
+ int ret;
+ struct device *d;
+ struct net_device *dev;
+ struct rmnet_private *p;
+ unsigned n;
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ timeout_us = 0;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ timeout_suspend_us = 0;
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+#endif /* CONFIG_MSM_RMNET_DEBUG */
+
+ for (n = 0; n < RMNET_SMUX_DEVICE_COUNT; n++) {
+ dev = alloc_netdev(sizeof(struct rmnet_private),
+ "rmnet_smux%d", rmnet_setup);
+
+ if (!dev) {
+ pr_err("%s: no memory for netdev %d\n", __func__, n);
+ return -ENOMEM;
+ }
+
+ netdevs[n] = dev;
+ d = &(dev->dev);
+ p = netdev_priv(dev);
+ /* Initial config uses Ethernet */
+ p->operation_mode = RMNET_MODE_LLP_ETH;
+ p->ch_id = n;
+ p->in_reset = 0;
+ spin_lock_init(&p->lock);
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ p->timeout_us = timeout_us;
+ p->wakeups_xmit = p->wakeups_rcv = 0;
+#endif
+
+ ret = register_netdev(dev);
+ if (ret) {
+ pr_err("%s: unable to register netdev"
+ " %d rc=%d\n", __func__, n, ret);
+ free_netdev(dev);
+ return ret;
+ }
+
+#ifdef CONFIG_MSM_RMNET_DEBUG
+ if (device_create_file(d, &dev_attr_timeout))
+ continue;
+ if (device_create_file(d, &dev_attr_wakeups_xmit))
+ continue;
+ if (device_create_file(d, &dev_attr_wakeups_rcv))
+ continue;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ if (device_create_file(d, &dev_attr_timeout_suspend))
+ continue;
+
+ /* Only care about rmnet0 for suspend/resume tiemout hooks. */
+ if (n == 0)
+ rmnet0 = d;
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+#endif /* CONFIG_MSM_RMNET_DEBUG */
+
+ }
+
+ ret = platform_driver_register(&smux_rmnet_driver);
+ if (ret) {
+ pr_err("%s: registration failed n=%d rc=%d\n",
+ __func__, n, ret);
+ return ret;
+ }
+ return 0;
+}
+
+module_init(rmnet_init);
+MODULE_DESCRIPTION("MSM RMNET SMUX TRANSPORT");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
index 0fe8f2a..6d61bb6 100644
--- a/drivers/platform/msm/sps/sps.c
+++ b/drivers/platform/msm/sps/sps.c
@@ -662,6 +662,11 @@
{
struct sps_bam *bam;
+ if (handle == NULL) {
+ SPS_ERR("sps:%s:handle is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
list_for_each_entry(bam, &sps->bams_q, list) {
if (bam->props.phys_addr == phys_addr) {
*handle = (u32) bam;
@@ -816,6 +821,14 @@
struct sps_bam *bam;
int result;
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (connect == NULL) {
+ SPS_ERR("sps:%s:connection is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
if (sps == NULL)
return -ENODEV;
@@ -959,6 +972,14 @@
SPS_DBG2("sps:%s.", __func__);
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (reg == NULL) {
+ SPS_ERR("sps:%s:registered event is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
if (sps == NULL)
return -ENODEV;
@@ -993,6 +1014,11 @@
SPS_DBG2("sps:%s.", __func__);
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
@@ -1017,6 +1043,11 @@
SPS_DBG2("sps:%s.", __func__);
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
@@ -1041,6 +1072,14 @@
SPS_DBG("sps:%s.", __func__);
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (transfer == NULL) {
+ SPS_ERR("sps:%s:transfer is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
@@ -1066,6 +1105,11 @@
SPS_DBG("sps:%s.", __func__);
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
if ((flags & SPS_IOVEC_FLAG_NWD) &&
!(flags & (SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_CMD))) {
SPS_ERR("sps:NWD is only valid with EOT or CMD.\n");
@@ -1119,6 +1163,14 @@
SPS_DBG("sps:%s.", __func__);
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (notify == NULL) {
+ SPS_ERR("sps:%s:event_notify is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
@@ -1142,6 +1194,14 @@
SPS_DBG("sps:%s.", __func__);
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (empty == NULL) {
+ SPS_ERR("sps:%s:result pointer is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
@@ -1165,6 +1225,14 @@
SPS_DBG("sps:%s.", __func__);
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (count == NULL) {
+ SPS_ERR("sps:%s:result pointer is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
@@ -1187,6 +1255,11 @@
SPS_DBG2("sps:%s: dev = 0x%x", __func__, dev);
+ if (dev == 0) {
+ SPS_ERR("sps:%s:device handle should not be 0.\n", __func__);
+ return SPS_ERROR;
+ }
+
mutex_lock(&sps->lock);
/* Search for the target BAM device */
bam = sps_h2bam(dev);
@@ -1219,8 +1292,11 @@
{
struct sps_pipe *pipe = h;
- if (config == NULL) {
- SPS_ERR("sps:Config pointer is NULL");
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (config == NULL) {
+ SPS_ERR("sps:%s:config pointer is NULL.\n", __func__);
return SPS_ERROR;
}
@@ -1243,6 +1319,14 @@
SPS_DBG("sps:%s.", __func__);
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (config == NULL) {
+ SPS_ERR("sps:%s:config pointer is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
@@ -1268,6 +1352,14 @@
struct sps_bam *bam;
int result;
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (connect == NULL) {
+ SPS_ERR("sps:%s:connection is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
if (owner != SPS_OWNER_REMOTE) {
SPS_ERR("sps:Unsupported ownership state: %d", owner);
return SPS_ERROR;
@@ -1309,6 +1401,11 @@
int sps_alloc_mem(struct sps_pipe *h, enum sps_mem mem,
struct sps_mem_buffer *mem_buffer)
{
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
if (sps == NULL)
return -ENODEV;
@@ -1340,6 +1437,11 @@
*/
int sps_free_mem(struct sps_pipe *h, struct sps_mem_buffer *mem_buffer)
{
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
if (mem_buffer == NULL || mem_buffer->phys_base == SPS_ADDR_INVALID) {
SPS_ERR("sps:invalid memory to free");
return SPS_ERROR;
@@ -1364,6 +1466,14 @@
SPS_DBG("sps:%s.", __func__);
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (desc_num == NULL) {
+ SPS_ERR("sps:%s:result pointer is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
@@ -1390,6 +1500,14 @@
int ok;
int result;
+ if (bam_props == NULL) {
+ SPS_ERR("sps:%s:bam_props is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (dev_handle == NULL) {
+ SPS_ERR("sps:%s:device handle is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
if (sps == NULL)
return SPS_ERROR;
@@ -1399,9 +1517,6 @@
return -EAGAIN;
}
- if (bam_props == NULL || dev_handle == NULL)
- return SPS_ERROR;
-
/* Check BAM parameters */
manage = bam_props->manage & SPS_BAM_MGR_ACCESS_MASK;
if (manage != SPS_BAM_MGR_NONE) {
@@ -1532,6 +1647,11 @@
{
struct sps_bam *bam;
+ if (dev_handle == 0) {
+ SPS_ERR("sps:%s:device handle should not be 0.\n", __func__);
+ return SPS_ERROR;
+ }
+
bam = sps_h2bam(dev_handle);
if (bam == NULL) {
SPS_ERR("sps:did not find a BAM for this handle");
@@ -1578,13 +1698,16 @@
struct sps_bam *bam;
int result;
- if (h == NULL || iovec == NULL) {
- SPS_ERR("sps:invalid pipe or iovec");
+ SPS_DBG("sps:%s.", __func__);
+
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (iovec == NULL) {
+ SPS_ERR("sps:%s:iovec pointer is NULL.\n", __func__);
return SPS_ERROR;
}
- SPS_DBG("sps:%s.", __func__);
-
bam = sps_bam_lock(pipe);
if (bam == NULL)
return SPS_ERROR;
@@ -1611,8 +1734,14 @@
SPS_DBG("sps:%s.", __func__);
- if (h == NULL || timer_ctrl == NULL) {
- SPS_ERR("sps:invalid pipe or timer ctrl");
+ if (h == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (timer_ctrl == NULL) {
+ SPS_ERR("sps:%s:timer_ctrl pointer is NULL.\n", __func__);
+ return SPS_ERROR;
+ } else if (timer_result == NULL) {
+ SPS_ERR("sps:%s:result pointer is NULL.\n", __func__);
return SPS_ERROR;
}
@@ -1657,6 +1786,11 @@
{
int res;
+ if (ctx == NULL) {
+ SPS_ERR("sps:%s:pipe is NULL.\n", __func__);
+ return SPS_ERROR;
+ }
+
res = sps_client_de_init(ctx);
if (res == 0)
diff --git a/drivers/tty/n_smux.c b/drivers/tty/n_smux.c
index 7ba54fe..8d2a16e 100644
--- a/drivers/tty/n_smux.c
+++ b/drivers/tty/n_smux.c
@@ -2707,7 +2707,6 @@
int i;
int tmp;
unsigned long flags;
- int ret = 0;
if (!smux.is_initialized)
return -ENODEV;
@@ -2716,14 +2715,14 @@
if (smux.ld_open_count) {
pr_err("%s: %p multiple instances not supported\n",
__func__, tty);
- ret = -EEXIST;
- goto out;
+ spin_unlock_irqrestore(&smux.lock_lha0, flags);
+ return -EEXIST;
}
++smux.ld_open_count;
if (tty->ops->write == NULL) {
- ret = -EINVAL;
- goto out;
+ spin_unlock_irqrestore(&smux.lock_lha0, flags);
+ return -EINVAL;
}
/* connect to TTY */
@@ -2742,6 +2741,7 @@
} else {
spin_unlock(&smux.tx_lock_lha2);
}
+ spin_unlock_irqrestore(&smux.lock_lha0, flags);
/* register platform devices */
for (i = 0; i < ARRAY_SIZE(smux_devs); ++i) {
@@ -2750,10 +2750,7 @@
pr_err("%s: error %d registering device %s\n",
__func__, tmp, smux_devs[i].name);
}
-
-out:
- spin_unlock_irqrestore(&smux.lock_lha0, flags);
- return ret;
+ return 0;
}
static void smuxld_close(struct tty_struct *tty)
@@ -2765,16 +2762,15 @@
if (smux.ld_open_count <= 0) {
pr_err("%s: invalid ld count %d\n", __func__,
smux.ld_open_count);
- goto out;
+ spin_unlock_irqrestore(&smux.lock_lha0, flags);
+ return;
}
+ spin_unlock_irqrestore(&smux.lock_lha0, flags);
for (i = 0; i < ARRAY_SIZE(smux_devs); ++i)
platform_device_unregister(&smux_devs[i]);
--smux.ld_open_count;
-
-out:
- spin_unlock_irqrestore(&smux.lock_lha0, flags);
}
/**
diff --git a/drivers/tty/serial/msm_serial_hs_lite.c b/drivers/tty/serial/msm_serial_hs_lite.c
index 62d25cf..59104ed 100644
--- a/drivers/tty/serial/msm_serial_hs_lite.c
+++ b/drivers/tty/serial/msm_serial_hs_lite.c
@@ -123,7 +123,7 @@
{}
};
static struct dentry *debug_base;
-static inline void wait_for_xmitr(struct uart_port *port, int bits);
+static inline void wait_for_xmitr(struct uart_port *port);
static int get_console_state(struct uart_port *port);
static inline void msm_hsl_write(struct uart_port *port,
unsigned int val, unsigned int off)
@@ -383,15 +383,16 @@
/* Handle x_char */
if (port->x_char) {
- wait_for_xmitr(port, UARTDM_ISR_TX_READY_BMSK);
- msm_hsl_write(port, tx_count + 1,
- regmap[vid][UARTDM_NCF_TX]);
+ wait_for_xmitr(port);
+ msm_hsl_write(port, tx_count + 1, regmap[vid][UARTDM_NCF_TX]);
+ msm_hsl_read(port, regmap[vid][UARTDM_NCF_TX]);
msm_hsl_write(port, port->x_char, regmap[vid][UARTDM_TF]);
port->icount.tx++;
port->x_char = 0;
} else if (tx_count) {
- wait_for_xmitr(port, UARTDM_ISR_TX_READY_BMSK);
+ wait_for_xmitr(port);
msm_hsl_write(port, tx_count, regmap[vid][UARTDM_NCF_TX]);
+ msm_hsl_read(port, regmap[vid][UARTDM_NCF_TX]);
}
if (!tx_count) {
msm_hsl_stop_tx(port);
@@ -1103,7 +1104,7 @@
/*
* Wait for transmitter & holding register to empty
* Derived from wait_for_xmitr in 8250 serial driver by Russell King */
-void wait_for_xmitr(struct uart_port *port, int bits)
+static void wait_for_xmitr(struct uart_port *port)
{
struct msm_hsl_port *msm_hsl_port = UART_TO_MSM(port);
unsigned int vid = msm_hsl_port->ver_id;
@@ -1111,8 +1112,8 @@
if (!(msm_hsl_read(port, regmap[vid][UARTDM_SR]) &
UARTDM_SR_TXEMT_BMSK)) {
- while ((msm_hsl_read(port, regmap[vid][UARTDM_ISR]) &
- bits) != bits) {
+ while (!(msm_hsl_read(port, regmap[vid][UARTDM_ISR]) &
+ UARTDM_ISR_TX_READY_BMSK)) {
udelay(1);
touch_nmi_watchdog();
cpu_relax();
@@ -1130,7 +1131,7 @@
{
unsigned int vid = UART_TO_MSM(port)->ver_id;
- wait_for_xmitr(port, UARTDM_ISR_TX_READY_BMSK);
+ wait_for_xmitr(port);
msm_hsl_write(port, 1, regmap[vid][UARTDM_NCF_TX]);
/*
* Dummy read to add 1 AHB clock delay to fix UART hardware bug.
diff --git a/drivers/tty/smux_ctl.c b/drivers/tty/smux_ctl.c
index 26a49a0..69adbf3 100644
--- a/drivers/tty/smux_ctl.c
+++ b/drivers/tty/smux_ctl.c
@@ -42,7 +42,6 @@
#define SMUX_CTL_MAX_BUF_SIZE 2048
#define SMUX_CTL_MODULE_NAME "smux_ctl"
#define DEBUG
-#define DEBUG_LOOPBACK
static int msm_smux_ctl_debug_mask;
module_param_named(debug_mask, msm_smux_ctl_debug_mask,
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index f4ca8e1..92e95a6 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -61,6 +61,8 @@
#include "u_rmnet_ctrl_smd.c"
#include "u_ctrl_hsic.c"
#include "u_data_hsic.c"
+#include "u_ctrl_hsuart.c"
+#include "u_data_hsuart.c"
#include "f_serial.c"
#include "f_acm.c"
#include "f_adb.c"
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index b29ef82..a86fc22 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -3306,9 +3306,8 @@
udc->gadget.dev.parent = dev;
udc->gadget.dev.release = udc_release;
- udc->transceiver = otg_get_transceiver();
-
if (udc->udc_driver->flags & CI13XXX_REQUIRE_TRANSCEIVER) {
+ udc->transceiver = otg_get_transceiver();
if (udc->transceiver == NULL) {
retval = -ENODEV;
goto free_udc;
diff --git a/drivers/usb/gadget/f_rmnet.c b/drivers/usb/gadget/f_rmnet.c
index f7230fe..53a6398 100644
--- a/drivers/usb/gadget/f_rmnet.c
+++ b/drivers/usb/gadget/f_rmnet.c
@@ -55,9 +55,11 @@
static unsigned int nr_rmnet_ports;
static unsigned int no_ctrl_smd_ports;
static unsigned int no_ctrl_hsic_ports;
+static unsigned int no_ctrl_hsuart_ports;
static unsigned int no_data_bam_ports;
static unsigned int no_data_bam2bam_ports;
static unsigned int no_data_hsic_ports;
+static unsigned int no_data_hsuart_ports;
static struct rmnet_ports {
enum transport_type data_xport;
enum transport_type ctrl_xport;
@@ -232,12 +234,12 @@
int port_idx;
int i;
- pr_debug("%s: bam ports: %u bam2bam ports: %u data hsic ports: %u"
- " smd ports: %u ctrl hsic ports: %u"
+ pr_debug("%s: bam ports: %u bam2bam ports: %u data hsic ports: %u data hsuart ports: %u"
+ " smd ports: %u ctrl hsic ports: %u ctrl hsuart ports: %u"
" nr_rmnet_ports: %u\n",
__func__, no_data_bam_ports, no_data_bam2bam_ports,
- no_data_hsic_ports, no_ctrl_smd_ports,
- no_ctrl_hsic_ports, nr_rmnet_ports);
+ no_data_hsic_ports, no_data_hsuart_ports, no_ctrl_smd_ports,
+ no_ctrl_hsic_ports, no_ctrl_hsuart_ports, nr_rmnet_ports);
if (no_data_bam_ports || no_data_bam2bam_ports) {
ret = gbam_setup(no_data_bam_ports,
@@ -280,6 +282,34 @@
}
}
+ if (no_data_hsuart_ports) {
+ port_idx = ghsuart_data_setup(no_data_hsuart_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_HSUART) {
+ rmnet_ports[i].data_xport_num = port_idx;
+ port_idx++;
+ }
+ }
+ }
+
+ if (no_ctrl_hsuart_ports) {
+ port_idx = ghsuart_ctrl_setup(no_ctrl_hsuart_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_HSUART) {
+ rmnet_ports[i].ctrl_xport_num = port_idx;
+ port_idx++;
+ }
+ }
+ }
+
return 0;
}
@@ -312,6 +342,14 @@
return ret;
}
break;
+ case USB_GADGET_XPORT_HSUART:
+ ret = ghsuart_ctrl_connect(&dev->port, port_num);
+ if (ret) {
+ pr_err("%s: ghsuart_ctrl_connect failed: err:%d\n",
+ __func__, ret);
+ return ret;
+ }
+ break;
case USB_GADGET_XPORT_NONE:
break;
default:
@@ -342,6 +380,15 @@
return ret;
}
break;
+ case USB_GADGET_XPORT_HSUART:
+ ret = ghsuart_data_connect(&dev->port, port_num);
+ if (ret) {
+ pr_err("%s: ghsuart_data_connect failed: err:%d\n",
+ __func__, ret);
+ ghsuart_ctrl_disconnect(&dev->port, port_num);
+ return ret;
+ }
+ break;
case USB_GADGET_XPORT_NONE:
break;
default:
@@ -371,6 +418,9 @@
case USB_GADGET_XPORT_HSIC:
ghsic_ctrl_disconnect(&dev->port, port_num);
break;
+ case USB_GADGET_XPORT_HSUART:
+ ghsuart_ctrl_disconnect(&dev->port, port_num);
+ break;
case USB_GADGET_XPORT_NONE:
break;
default:
@@ -388,6 +438,9 @@
case USB_GADGET_XPORT_HSIC:
ghsic_data_disconnect(&dev->port, port_num);
break;
+ case USB_GADGET_XPORT_HSUART:
+ ghsuart_data_disconnect(&dev->port, port_num);
+ break;
case USB_GADGET_XPORT_NONE:
break;
default:
@@ -998,6 +1051,8 @@
no_data_bam2bam_ports = 0;
no_ctrl_hsic_ports = 0;
no_data_hsic_ports = 0;
+ no_ctrl_hsuart_ports = 0;
+ no_data_hsuart_ports = 0;
}
static int frmnet_init_port(const char *ctrl_name, const char *data_name)
@@ -1041,6 +1096,10 @@
rmnet_port->ctrl_xport_num = no_ctrl_hsic_ports;
no_ctrl_hsic_ports++;
break;
+ case USB_GADGET_XPORT_HSUART:
+ rmnet_port->ctrl_xport_num = no_ctrl_hsuart_ports;
+ no_ctrl_hsuart_ports++;
+ break;
case USB_GADGET_XPORT_NONE:
break;
default:
@@ -1063,6 +1122,10 @@
rmnet_port->data_xport_num = no_data_hsic_ports;
no_data_hsic_ports++;
break;
+ case USB_GADGET_XPORT_HSUART:
+ rmnet_port->data_xport_num = no_data_hsuart_ports;
+ no_data_hsuart_ports++;
+ break;
case USB_GADGET_XPORT_NONE:
break;
default:
@@ -1084,6 +1147,8 @@
no_data_bam_ports = 0;
no_ctrl_hsic_ports = 0;
no_data_hsic_ports = 0;
+ no_ctrl_hsuart_ports = 0;
+ no_data_hsuart_ports = 0;
return ret;
}
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 8d9f090..08a1712 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -27,7 +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
+#define GSERIAL_NO_PORTS 3
struct f_gser {
@@ -67,6 +67,7 @@
static unsigned int no_sdio_ports;
static unsigned int no_smd_ports;
static unsigned int no_hsic_sports;
+static unsigned int no_hsuart_sports;
static unsigned int nr_ports;
static struct port_info {
@@ -249,9 +250,9 @@
int i;
pr_debug("%s: no_tty_ports: %u no_sdio_ports: %u"
- " no_smd_ports: %u no_hsic_sports: %u nr_ports: %u\n",
+ " no_smd_ports: %u no_hsic_sports: %u no_hsuart_ports: %u nr_ports: %u\n",
__func__, no_tty_ports, no_sdio_ports, no_smd_ports,
- no_hsic_sports, nr_ports);
+ no_hsic_sports, no_hsuart_sports, nr_ports);
if (no_tty_ports)
ret = gserial_setup(c->cdev->gadget, no_tty_ports);
@@ -278,6 +279,22 @@
return ret;
return 0;
}
+ if (no_hsuart_sports) {
+ port_idx = ghsuart_data_setup(no_hsuart_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_HSUART) {
+ gserial_ports[i].client_port_num = port_idx;
+ port_idx++;
+ }
+ }
+
+ return 0;
+ }
return ret;
}
@@ -317,6 +334,14 @@
return ret;
}
break;
+ case USB_GADGET_XPORT_HSUART:
+ ret = ghsuart_data_connect(&gser->port, port_num);
+ if (ret) {
+ pr_err("%s: ghsuart_data_connect failed: err:%d\n",
+ __func__, ret);
+ return ret;
+ }
+ break;
default:
pr_err("%s: Un-supported transport: %s\n", __func__,
xport_to_str(gser->transport));
@@ -350,6 +375,9 @@
ghsic_ctrl_disconnect(&gser->port, port_num);
ghsic_data_disconnect(&gser->port, port_num);
break;
+ case USB_GADGET_XPORT_HSUART:
+ ghsuart_data_disconnect(&gser->port, port_num);
+ break;
default:
pr_err("%s: Un-supported transport:%s\n", __func__,
xport_to_str(gser->transport));
@@ -854,11 +882,13 @@
gser->port.func.disable = gser_disable;
gser->transport = gserial_ports[port_num].transport;
#ifdef CONFIG_MODEM_SUPPORT
- /* We support only two ports for now */
+ /* We support only three ports for now */
if (port_num == 0)
gser->port.func.name = "modem";
- else
+ else if (port_num == 1)
gser->port.func.name = "nmea";
+ else
+ gser->port.func.name = "modem2";
gser->port.func.setup = gser_setup;
gser->port.connect = gser_connect;
gser->port.get_dtr = gser_get_dtr;
@@ -910,6 +940,10 @@
/*client port number will be updated in gport_setup*/
no_hsic_sports++;
break;
+ case USB_GADGET_XPORT_HSUART:
+ /*client port number will be updated in gport_setup*/
+ no_hsuart_sports++;
+ break;
default:
pr_err("%s: Un-supported transport transport: %u\n",
__func__, gserial_ports[port_num].transport);
diff --git a/drivers/usb/gadget/msm72k_udc.c b/drivers/usb/gadget/msm72k_udc.c
index 863ddcd..0d53da0 100644
--- a/drivers/usb/gadget/msm72k_udc.c
+++ b/drivers/usb/gadget/msm72k_udc.c
@@ -2018,10 +2018,14 @@
static int
msm72k_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
{
- struct msm_endpoint *ept = to_msm_endpoint(_ep);
- unsigned char ep_type =
- desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ struct msm_endpoint *ept;
+ unsigned char ep_type;
+ if (_ep == NULL || desc == NULL)
+ return -EINVAL;
+
+ ept = to_msm_endpoint(_ep);
+ ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
_ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize);
config_ept(ept);
ept->wedged = 0;
diff --git a/drivers/video/msm/mdp4_overlay_dsi_video.c b/drivers/video/msm/mdp4_overlay_dsi_video.c
index 8b2edd9..0db4841 100644
--- a/drivers/video/msm/mdp4_overlay_dsi_video.c
+++ b/drivers/video/msm/mdp4_overlay_dsi_video.c
@@ -24,6 +24,9 @@
#include <linux/spinlock.h>
#include <linux/fb.h>
#include <linux/msm_mdp.h>
+#include <linux/ktime.h>
+#include <linux/wakelock.h>
+#include <linux/time.h>
#include <asm/system.h>
#include <asm/mach-types.h>
#include <mach/hardware.h>
@@ -450,6 +453,94 @@
}
/*
+ * MDP Timer/Functions
+ * Set up an HR timer to wake up the CPU's just before the return of a VSync
+ * This reduces any latencies that may arise if the CPU's were power collapsed
+ * in between.
+ */
+#define VSYNC_INTERVAL 16
+#define WAKE_DELAY 3 /* 3 ioctls * 600 us = 2ms + 1ms buffer */
+#define MAX_VSYNC_GAP 4 /* Marker to detect whether to skip timer */
+
+/* Move the globals into context data structure for 3.4 upgrade */
+static int first_time = 1;
+static ktime_t last_vsync_time_ns;
+struct hrtimer hr_mdp_timer_pc;
+
+static unsigned long compute_vsync_interval(void)
+{
+ ktime_t currtime_us;
+ unsigned long diff_from_vsync, vsync_interval;
+ /*
+ * Get interval beween last vsync and current time
+ * Current time = CPU programming MDP for next Vsync
+ */
+ currtime_us = ktime_get();
+ diff_from_vsync =
+ (ktime_to_us(ktime_sub(currtime_us, last_vsync_time_ns)));
+ diff_from_vsync /= USEC_PER_MSEC;
+ /*
+ * If the last Vsync occurred more than 64 ms ago, skip programming
+ * the timer
+ */
+ if (diff_from_vsync < (VSYNC_INTERVAL*MAX_VSYNC_GAP)) {
+ vsync_interval =
+ (VSYNC_INTERVAL-diff_from_vsync)%VSYNC_INTERVAL;
+ } else
+ vsync_interval = VSYNC_INTERVAL+1;
+
+ return vsync_interval;
+}
+
+enum hrtimer_restart mdp_pc_hrtimer_callback(struct hrtimer *timer)
+{
+ if (!wake_lock_active(&mdp_idle_wakelock)) {
+ /* Hold Wakelock if no locks held */
+ wake_lock(&mdp_idle_wakelock);
+ }
+ return HRTIMER_NORESTART;
+}
+
+void init_pc_timer(void)
+{
+ /*
+ * Initialize hr timer which fires a few ms before Vsync - this
+ * gets rid of any latencies that may arise due to
+ * wake up from PC
+ */
+ hrtimer_init(&hr_mdp_timer_pc, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hr_mdp_timer_pc.function = &mdp_pc_hrtimer_callback;
+}
+
+void program_pc_timer(unsigned long diff_interval)
+{
+ ktime_t ktime_pc;
+ unsigned long delay_in_ns = 0;
+
+ /* Skip programming timer due to invalid delay */
+ if (diff_interval > VSYNC_INTERVAL)
+ return;
+
+ if (diff_interval < WAKE_DELAY) {
+ /*
+ * Difference from last vsync was a multiple of refresh rate
+ * (16*x)%16). Reset it to actual time to next vsync
+ */
+ if (diff_interval == 0)
+ delay_in_ns = VSYNC_INTERVAL-WAKE_DELAY;
+ else
+ return; /* too close to vsync to fire timer - skip */
+ } else if (diff_interval == WAKE_DELAY) {
+ delay_in_ns = 1; /* diff_interval/WAKE_DELAY */
+ } else {
+ delay_in_ns = diff_interval-WAKE_DELAY;
+ }
+ delay_in_ns *= NSEC_PER_MSEC;
+ ktime_pc = ktime_set(0, delay_in_ns);
+ hrtimer_start(&hr_mdp_timer_pc, ktime_pc, HRTIMER_MODE_REL);
+}
+
+/*
* mdp4_overlay_dsi_video_wait4event:
* INTR_DMA_P_DONE and INTR_PRIMARY_VSYNC event only
* no INTR_OVERLAY0_DONE event allowed.
@@ -459,12 +550,21 @@
{
unsigned long flag;
unsigned int data;
+ unsigned long vsync_interval;
data = inpdw(MDP_BASE + DSI_VIDEO_BASE);
data &= 0x01;
if (data == 0) /* timing generator disabled */
return;
-
+ if ((intr_done == INTR_PRIMARY_VSYNC) ||
+ (intr_done == INTR_DMA_P_DONE)) {
+ if (first_time) {
+ init_pc_timer();
+ first_time = 0;
+ }
+ vsync_interval = compute_vsync_interval();
+ program_pc_timer(vsync_interval);
+ }
spin_lock_irqsave(&mdp_spin_lock, flag);
INIT_COMPLETION(dsi_video_comp);
mfd->dma->waiting = TRUE;
@@ -551,6 +651,10 @@
void mdp4_primary_vsync_dsi_video(void)
{
complete_all(&dsi_video_comp);
+ last_vsync_time_ns = ktime_get();
+ /* Release Wakelock */
+ if (wake_lock_active(&mdp_idle_wakelock))
+ wake_unlock(&mdp_idle_wakelock);
}
/*
@@ -577,6 +681,10 @@
blt_cfg_changed = 0;
}
complete_all(&dsi_video_comp);
+ last_vsync_time_ns = ktime_get();
+ /* Release Wakelock */
+ if (wake_lock_active(&mdp_idle_wakelock))
+ wake_unlock(&mdp_idle_wakelock);
}
/*
diff --git a/drivers/video/msm/msm_dss_io_7x27a.c b/drivers/video/msm/msm_dss_io_7x27a.c
index 28204a5..f4f8375 100644
--- a/drivers/video/msm/msm_dss_io_7x27a.c
+++ b/drivers/video/msm/msm_dss_io_7x27a.c
@@ -256,8 +256,11 @@
int i, off;
MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0001);/* start phy sw reset */
- msleep(100);
+ wmb();
+ usleep(10);
MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0000);/* end phy w reset */
+ wmb();
+ usleep(10);
MIPI_OUTP(MIPI_DSI_BASE + 0x2cc, 0x0003);/* regulator_ctrl_0 */
MIPI_OUTP(MIPI_DSI_BASE + 0x2d0, 0x0001);/* regulator_ctrl_1 */
MIPI_OUTP(MIPI_DSI_BASE + 0x2d4, 0x0001);/* regulator_ctrl_2 */
diff --git a/drivers/video/msm/msm_dss_io_8960.c b/drivers/video/msm/msm_dss_io_8960.c
index 30edbd1..d1ddafb 100644
--- a/drivers/video/msm/msm_dss_io_8960.c
+++ b/drivers/video/msm/msm_dss_io_8960.c
@@ -487,8 +487,11 @@
int i, off;
MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0001);/* start phy sw reset */
- msleep(100);
+ wmb();
+ usleep(1);
MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0000);/* end phy w reset */
+ wmb();
+ usleep(1);
MIPI_OUTP(MIPI_DSI_BASE + 0x500, 0x0003);/* regulator_ctrl_0 */
MIPI_OUTP(MIPI_DSI_BASE + 0x504, 0x0001);/* regulator_ctrl_1 */
MIPI_OUTP(MIPI_DSI_BASE + 0x508, 0x0001);/* regulator_ctrl_2 */
diff --git a/drivers/video/msm/msm_dss_io_8x60.c b/drivers/video/msm/msm_dss_io_8x60.c
index bbee726..a1897e3 100644
--- a/drivers/video/msm/msm_dss_io_8x60.c
+++ b/drivers/video/msm/msm_dss_io_8x60.c
@@ -346,8 +346,11 @@
int i, off;
MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0001);/* start phy sw reset */
- msleep(100);
+ wmb();
+ usleep(1);
MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0000);/* end phy w reset */
+ wmb();
+ usleep(1);
MIPI_OUTP(MIPI_DSI_BASE + 0x2cc, 0x0003);/* regulator_ctrl_0 */
MIPI_OUTP(MIPI_DSI_BASE + 0x2d0, 0x0001);/* regulator_ctrl_1 */
MIPI_OUTP(MIPI_DSI_BASE + 0x2d4, 0x0001);/* regulator_ctrl_2 */
diff --git a/drivers/video/msm/msm_fb.c b/drivers/video/msm/msm_fb.c
index ff08548..d3126df 100644
--- a/drivers/video/msm/msm_fb.c
+++ b/drivers/video/msm/msm_fb.c
@@ -52,6 +52,9 @@
#define MSM_FB_NUM 3
#endif
+/* Idle wakelock to prevent PC between wake up and Vsync */
+struct wake_lock mdp_idle_wakelock;
+
static unsigned char *fbram;
static unsigned char *fbram_phys;
static int fbram_size;
@@ -439,6 +442,10 @@
complete(&mfd->msmfb_no_update_notify);
complete(&mfd->msmfb_update_notify);
+ /* Do this only for the primary panel */
+ if (mfd->fbi->node == 0)
+ wake_lock_destroy(&mdp_idle_wakelock);
+
/* remove /dev/fb* */
unregister_framebuffer(mfd->fbi);
@@ -1356,6 +1363,9 @@
return -EPERM;
}
+ if (fbi->node == 0)
+ wake_lock_init(&mdp_idle_wakelock, WAKE_LOCK_IDLE, "mdp");
+
fbram += fix->smem_len;
fbram_phys += fix->smem_len;
fbram_size -= fix->smem_len;
diff --git a/drivers/video/msm/msm_fb.h b/drivers/video/msm/msm_fb.h
index 87753b2..7b3bca0 100644
--- a/drivers/video/msm/msm_fb.h
+++ b/drivers/video/msm/msm_fb.h
@@ -34,6 +34,7 @@
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/hrtimer.h>
+#include <linux/wakelock.h>
#include <linux/fb.h>
#include <linux/list.h>
@@ -44,6 +45,9 @@
#include <linux/earlysuspend.h>
#endif
+/* Idle wakelock to prevent PC between wake up and Vsync */
+extern struct wake_lock mdp_idle_wakelock;
+
#include "msm_fb_panel.h"
#include "mdp.h"
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index a7b95d3..3ecc6d4 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -5397,9 +5397,6 @@
BT_DBG("sk %p", sk);
- if (!sk)
- return;
-
lock_sock(sk);
if (sk->sk_state != BT_CONNECTED && !l2cap_pi(sk)->amp_id) {
@@ -5539,8 +5536,11 @@
container_of(work, struct l2cap_logical_link_work, work);
struct sock *sk = log_link_work->chan->l2cap_sk;
- l2cap_logical_link_complete(log_link_work->chan, log_link_work->status);
- sock_put(sk);
+ if (sk) {
+ l2cap_logical_link_complete(log_link_work->chan,
+ log_link_work->status);
+ sock_put(sk);
+ }
hci_chan_put(log_link_work->chan);
kfree(log_link_work);
}