Merge changes I29417600,Ic3b53ed5 into msm-3.0

* changes:
  msm: acpuclock-8960: Use VDD dig corner regulator for 8930 and 8627
  msm: board-8930: Add new VDD_dig voltage corner regulator
diff --git a/Documentation/devicetree/bindings/resource-names.txt b/Documentation/devicetree/bindings/resource-names.txt
new file mode 100644
index 0000000..e280fef
--- /dev/null
+++ b/Documentation/devicetree/bindings/resource-names.txt
@@ -0,0 +1,54 @@
+Some properties contain an ordered list of 1 or more datum which are
+normally accessed by index.  However, some devices will have multiple
+values which are more naturally accessed by name.  Device nodes can
+include a supplemental property for assigning names to each of the list
+items.  The names property consists of a list of strings in the same
+order as the data in the resource property.
+
+The following supplemental names properties are defined.
+
+Resource Property	Supplemental Names Property
+-----------------	---------------------------
+reg			reg-names
+clocks			clock-names
+interrupts		interrupt-names
+
+Usage:
+
+The -names property must be used in conjunction with the normal resource
+property. If not it will be ignored.
+
+Examples:
+
+l4-abe {
+	compatible = "simple-bus";
+	#address-cells = <2>;
+	#size-cells = <1>;
+	ranges = <0 0 0x48000000 0x00001000>, /* MPU path */
+		 <1 0 0x49000000 0x00001000>; /* L3 path */
+	mcasp {
+		compatible = "ti,mcasp";
+		reg = <0 0x10 0x10>, <0 0x20 0x10>,
+		      <1 0x10 0x10>, <1 0x20 0x10>;
+		reg-names = "mpu", "dat",
+			    "dma", "dma_dat";
+		interrupts = <11>, <12>;
+		interrupt-names = "rx", "tx";
+	};
+
+	timer {
+		compatible = "ti,timer";
+		reg = <0 0x40 0x10>, <1 0x40 0x10>;
+		reg-names = "mpu", "dma";
+	};
+};
+
+
+usb {
+	compatible = "ti,usb-host";
+	reg = <0x4a064000 0x800>, <0x4a064800 0x200>,
+	      <0x4a064c00 0x200>;
+	reg-names = "config", "ohci", "ehci";
+	interrupts = <14>, <15>;
+	interrupt-names = "ohci", "ehci";
+};
diff --git a/arch/arm/configs/msm-copper_defconfig b/arch/arm/configs/msm-copper_defconfig
index e9479c6..f1fe039 100644
--- a/arch/arm/configs/msm-copper_defconfig
+++ b/arch/arm/configs/msm-copper_defconfig
@@ -94,6 +94,7 @@
 CONFIG_INPUT_UINPUT=y
 CONFIG_SERIAL_MSM_HSL=y
 CONFIG_SERIAL_MSM_HSL_CONSOLE=y
+CONFIG_DIAG_CHAR=y
 CONFIG_HW_RANDOM=y
 CONFIG_DCC_TTY=y
 CONFIG_SPMI=y
diff --git a/arch/arm/configs/msm7630-perf_defconfig b/arch/arm/configs/msm7630-perf_defconfig
index b6c56b6..2a9f4a0 100644
--- a/arch/arm/configs/msm7630-perf_defconfig
+++ b/arch/arm/configs/msm7630-perf_defconfig
@@ -293,6 +293,9 @@
 CONFIG_SND_SOC=y
 CONFIG_SND_MSM7KV2_SOC=y
 CONFIG_SND_MVS_SOC=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
 CONFIG_USB=y
 CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
 CONFIG_USB_SUSPEND=y
diff --git a/arch/arm/configs/msm8960_defconfig b/arch/arm/configs/msm8960_defconfig
index d12df73..ebbfb0b 100644
--- a/arch/arm/configs/msm8960_defconfig
+++ b/arch/arm/configs/msm8960_defconfig
@@ -299,6 +299,8 @@
 CONFIG_SENSORS_PM8XXX_ADC=y
 CONFIG_THERMAL=y
 CONFIG_THERMAL_TSENS8960=y
+CONFIG_THERMAL_PM8XXX=y
+CONFIG_THERMAL_MONITOR=y
 CONFIG_MFD_PM8921_CORE=y
 CONFIG_MFD_PM8821_CORE=y
 CONFIG_MFD_PM8038_CORE=y
diff --git a/arch/arm/mach-msm/board-8064-camera.c b/arch/arm/mach-msm/board-8064-camera.c
index c714bc8..d335978 100644
--- a/arch/arm/mach-msm/board-8064-camera.c
+++ b/arch/arm/mach-msm/board-8064-camera.c
@@ -530,8 +530,10 @@
 	msm_gpiomux_install(apq8064_cam_common_configs,
 			ARRAY_SIZE(apq8064_cam_common_configs));
 
-	if (machine_is_apq8064_cdp() || machine_is_apq8064_liquid())
+	if (machine_is_apq8064_cdp())
 		sensor_board_info_imx074.mount_angle = 0;
+	else if (machine_is_apq8064_liquid())
+		sensor_board_info_imx074.mount_angle = 180;
 
 	platform_device_register(&msm8960_device_i2c_mux_gsbi4);
 	platform_device_register(&msm8960_device_csiphy0);
diff --git a/arch/arm/mach-msm/board-8064-pmic.c b/arch/arm/mach-msm/board-8064-pmic.c
index 75160ef..4b4f32a 100644
--- a/arch/arm/mach-msm/board-8064-pmic.c
+++ b/arch/arm/mach-msm/board-8064-pmic.c
@@ -125,7 +125,6 @@
 	PM8921_GPIO_INPUT(38, PM_GPIO_PULL_UP_1P5),
 	/* TABLA CODEC RESET */
 	PM8921_GPIO_OUTPUT(34, 1, MED),
-	PM8921_GPIO_INPUT(17, PM_GPIO_PULL_UP_1P5),	/* SD_WP */
 };
 
 static struct pm8xxx_gpio_init pm8921_mtp_kp_gpios[] __initdata = {
@@ -136,6 +135,7 @@
 static struct pm8xxx_gpio_init pm8921_cdp_kp_gpios[] __initdata = {
 	PM8921_GPIO_INPUT(37, PM_GPIO_PULL_UP_1P5),
 	PM8921_GPIO_INPUT(42, PM_GPIO_PULL_UP_1P5),
+	PM8921_GPIO_INPUT(17, PM_GPIO_PULL_UP_1P5),	/* SD_WP */
 };
 
 /* Initial PM8XXX MPP configurations */
diff --git a/arch/arm/mach-msm/board-8064-storage.c b/arch/arm/mach-msm/board-8064-storage.c
index b8cae49..b52004a 100644
--- a/arch/arm/mach-msm/board-8064-storage.c
+++ b/arch/arm/mach-msm/board-8064-storage.c
@@ -273,6 +273,10 @@
 	if (apq8064_sdc3_pdata) {
 		apq8064_sdc3_pdata->swfi_latency =
 				apq8064_rpm_get_swfi_latency();
+		if (!machine_is_apq8064_cdp()) {
+			apq8064_sdc3_pdata->wpswitch_gpio = 0;
+			apq8064_sdc3_pdata->wpswitch_polarity = 0;
+		}
 		apq8064_add_sdcc(3, apq8064_sdc3_pdata);
 	}
 }
diff --git a/drivers/media/radio/radio-iris.c b/drivers/media/radio/radio-iris.c
index 793f063..c3331f8 100644
--- a/drivers/media/radio/radio-iris.c
+++ b/drivers/media/radio/radio-iris.c
@@ -1879,6 +1879,7 @@
 
 	switch (opcode) {
 	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_RECV_REQ):
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_TRANS_REQ):
 		hci_cc_fm_enable_rsp(hdev, skb);
 		break;
 	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_GET_RECV_CONF_REQ):
@@ -1886,6 +1887,7 @@
 		break;
 
 	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_RECV_REQ):
+	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_TRANS_REQ):
 		hci_cc_fm_disable_rsp(hdev, skb);
 		break;
 
@@ -1900,8 +1902,6 @@
 	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_WAN_AVD_CTRL):
 	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_EN_NOTCH_CTRL):
 	case hci_recv_ctrl_cmd_op_pack(HCI_OCF_FM_SET_CH_DET_THRESHOLD):
-	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_ENABLE_TRANS_REQ):
-	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_DISABLE_TRANS_REQ):
 	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_RT_REQ):
 	case hci_trans_ctrl_cmd_op_pack(HCI_OCF_FM_RDS_PS_REQ):
 	case hci_common_cmd_op_pack(HCI_OCF_FM_DEFAULT_DATA_WRITE):
diff --git a/drivers/media/video/msm/wfd/wfd-ioctl.c b/drivers/media/video/msm/wfd/wfd-ioctl.c
index e38bb80..fd6e6e7 100644
--- a/drivers/media/video/msm/wfd/wfd-ioctl.c
+++ b/drivers/media/video/msm/wfd/wfd-ioctl.c
@@ -388,6 +388,8 @@
 	struct wfd_inst *inst = (struct wfd_inst *)priv_data->private_data;
 	int rc = 0;
 
+	WFD_MSG_ERR("Stream on called\n");
+	WFD_MSG_DBG("enc start\n");
 	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
 			ENCODE_START, (void *)inst->venc_inst);
 	if (rc) {
@@ -395,6 +397,7 @@
 		goto subdev_start_fail;
 	}
 
+	WFD_MSG_DBG("vsg start\n");
 	rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core, ioctl,
 			VSG_START, NULL);
 	if (rc) {
@@ -408,6 +411,7 @@
 		rc = PTR_ERR(inst->mdp_task);
 		goto subdev_start_fail;
 	}
+	WFD_MSG_DBG("mdp start\n");
 	rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl,
 			 MDP_START, (void *)inst->mdp_inst);
 	if (rc)
@@ -423,17 +427,20 @@
 		(struct wfd_device *)video_drvdata(priv_data);
 	struct wfd_inst *inst = (struct wfd_inst *)priv_data->private_data;
 	int rc = 0;
+	WFD_MSG_DBG("mdp stop\n");
 	rc = v4l2_subdev_call(&wfd_dev->mdp_sdev, core, ioctl,
 			 MDP_STOP, (void *)inst->mdp_inst);
 	if (rc)
 		WFD_MSG_ERR("Failed to stop MDP\n");
 
+	WFD_MSG_DBG("vsg stop\n");
 	rc = v4l2_subdev_call(&wfd_dev->vsg_sdev, core, ioctl,
 			 VSG_STOP, NULL);
 	if (rc)
 		WFD_MSG_ERR("Failed to stop VSG\n");
 
 	kthread_stop(inst->mdp_task);
+	WFD_MSG_DBG("enc stop\n");
 	rc = v4l2_subdev_call(&wfd_dev->enc_sdev, core, ioctl,
 			ENCODE_STOP, (void *)inst->venc_inst);
 	if (rc)
diff --git a/drivers/media/video/msm/wfd/wfd-util.h b/drivers/media/video/msm/wfd/wfd-util.h
index 3fe03e6..06dbffa 100644
--- a/drivers/media/video/msm/wfd/wfd-util.h
+++ b/drivers/media/video/msm/wfd/wfd-util.h
@@ -20,12 +20,11 @@
 #ifdef DEBUG_WFD
 	#define WFD_MSG_INFO(fmt...) pr_info(WFD_TAG fmt)
 	#define WFD_MSG_WARN(fmt...) pr_warning(WFD_TAG fmt)
-	#define WFD_MSG_DBG(fmt...) pr_debug(WFD_TAG fmt)
 #else
 	#define WFD_MSG_INFO(fmt...)
 	#define WFD_MSG_WARN(fmt...)
-	#define WFD_MSG_DBG(fmt...)
 #endif
 	#define WFD_MSG_ERR(fmt...) pr_err(KERN_ERR WFD_TAG fmt)
 	#define WFD_MSG_CRIT(fmt...) pr_crit(KERN_CRIT WFD_TAG fmt)
+	#define WFD_MSG_DBG(fmt...) pr_debug(WFD_TAG fmt)
 #endif
diff --git a/drivers/misc/qseecom.c b/drivers/misc/qseecom.c
index 495a986..ba00d5e 100644
--- a/drivers/misc/qseecom.c
+++ b/drivers/misc/qseecom.c
@@ -486,23 +486,6 @@
 	if (__copy_from_user(&req, (void __user *)argp, sizeof(req)))
 		return -EFAULT;
 
-	if (qseecom.qseos_version == QSEOS_VERSION_13) {
-		long pil_error;
-		mutex_lock(&pil_access_lock);
-		if (pil_ref_cnt == 0) {
-			pil = pil_get("tzapps");
-			if (IS_ERR(pil)) {
-				pr_err("Playready PIL image load failed\n");
-				pil_error = PTR_ERR(pil);
-				pil = NULL;
-				pr_debug("tzapps image load FAILED\n");
-				mutex_unlock(&pil_access_lock);
-				return pil_error;
-			}
-		}
-		pil_ref_cnt++;
-		mutex_unlock(&pil_access_lock);
-	}
 	/* Get the handle of the shared fd */
 	data->client.ihandle = ion_import_fd(qseecom.ion_clnt, req.ifd_data_fd);
 	if (IS_ERR_OR_NULL(data->client.ihandle)) {
@@ -789,11 +772,6 @@
 				break;
 			}
 		}
-		mutex_lock(&pil_access_lock);
-		if (pil_ref_cnt == 1)
-			pil_put(pil);
-		pil_ref_cnt--;
-		mutex_unlock(&pil_access_lock);
 	}
 	data->released = true;
 	return ret;
@@ -1330,6 +1308,23 @@
 	data->released = false;
 	init_waitqueue_head(&data->abort_wq);
 	atomic_set(&data->ioctl_count, 0);
+	if (qseecom.qseos_version == QSEOS_VERSION_13) {
+		int pil_error;
+		mutex_lock(&pil_access_lock);
+		if (pil_ref_cnt == 0) {
+			pil = pil_get("tzapps");
+			if (IS_ERR(pil)) {
+				pr_err("Playready PIL image load failed\n");
+				pil_error = PTR_ERR(pil);
+				pil = NULL;
+				pr_debug("tzapps image load FAILED\n");
+				mutex_unlock(&pil_access_lock);
+				return pil_error;
+			}
+		}
+		pil_ref_cnt++;
+		mutex_unlock(&pil_access_lock);
+	}
 	return ret;
 }
 
@@ -1349,6 +1344,13 @@
 			return ret;
 		}
 	}
+	if (qseecom.qseos_version == QSEOS_VERSION_13) {
+		mutex_lock(&pil_access_lock);
+		if (pil_ref_cnt == 1)
+			pil_put(pil);
+		pil_ref_cnt--;
+		mutex_unlock(&pil_access_lock);
+	}
 	kfree(data);
 	qsee_disable_clock_vote();
 
diff --git a/drivers/of/address.c b/drivers/of/address.c
index da1f4b9..e29a5cf 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -14,7 +14,7 @@
 static struct of_bus *of_match_bus(struct device_node *np);
 static int __of_address_to_resource(struct device_node *dev,
 		const __be32 *addrp, u64 size, unsigned int flags,
-				    struct resource *r);
+		const char *name, struct resource *r);
 
 /* Debug utility */
 #ifdef DEBUG
@@ -215,7 +215,7 @@
 	addrp = of_get_pci_address(dev, bar, &size, &flags);
 	if (addrp == NULL)
 		return -EINVAL;
-	return __of_address_to_resource(dev, addrp, size, flags, r);
+	return __of_address_to_resource(dev, addrp, size, flags, NULL, r);
 }
 EXPORT_SYMBOL_GPL(of_pci_address_to_resource);
 #endif /* CONFIG_PCI */
@@ -529,7 +529,7 @@
 
 static int __of_address_to_resource(struct device_node *dev,
 		const __be32 *addrp, u64 size, unsigned int flags,
-				    struct resource *r)
+		const char *name, struct resource *r)
 {
 	u64 taddr;
 
@@ -551,7 +551,8 @@
 		r->end = taddr + size - 1;
 	}
 	r->flags = flags;
-	r->name = dev->full_name;
+	r->name = name ? name : dev->full_name;
+
 	return 0;
 }
 
@@ -569,11 +570,16 @@
 	const __be32	*addrp;
 	u64		size;
 	unsigned int	flags;
+	const char	*name = NULL;
 
 	addrp = of_get_address(dev, index, &size, &flags);
 	if (addrp == NULL)
 		return -EINVAL;
-	return __of_address_to_resource(dev, addrp, size, flags, r);
+
+	/* Get optional "reg-names" property to add a name to a resource */
+	of_property_read_string_index(dev, "reg-names",	index, &name);
+
+	return __of_address_to_resource(dev, addrp, size, flags, name, r);
 }
 EXPORT_SYMBOL_GPL(of_address_to_resource);
 
diff --git a/drivers/of/base.c b/drivers/of/base.c
index 02ed367..39bcf16 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -661,6 +661,90 @@
 EXPORT_SYMBOL_GPL(of_property_read_string);
 
 /**
+ * of_property_read_string_index - Find and read a string from a multiple
+ * strings property.
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ * @index:	index of the string in the list of strings
+ * @out_string:	pointer to null terminated return string, modified only if
+ *		return value is 0.
+ *
+ * Search for a property in a device tree node and retrieve a null
+ * terminated string value (pointer to data, not a copy) in the list of strings
+ * contained in that property.
+ * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if
+ * property does not have a value, and -EILSEQ if the string is not
+ * null-terminated within the length of the property data.
+ *
+ * The out_string pointer is modified only if a valid string can be decoded.
+ */
+int of_property_read_string_index(struct device_node *np, const char *propname,
+				  int index, const char **output)
+{
+	struct property *prop = of_find_property(np, propname, NULL);
+	int i = 0;
+	size_t l = 0, total = 0;
+	const char *p;
+
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+	if (strnlen(prop->value, prop->length) >= prop->length)
+		return -EILSEQ;
+
+	p = prop->value;
+
+	for (i = 0; total < prop->length; total += l, p += l) {
+		l = strlen(p) + 1;
+		if ((*p != 0) && (i++ == index)) {
+			*output = p;
+			return 0;
+		}
+	}
+	return -ENODATA;
+}
+EXPORT_SYMBOL_GPL(of_property_read_string_index);
+
+
+/**
+ * of_property_count_strings - Find and return the number of strings from a
+ * multiple strings property.
+ * @np:		device node from which the property value is to be read.
+ * @propname:	name of the property to be searched.
+ *
+ * Search for a property in a device tree node and retrieve the number of null
+ * terminated string contain in it. Returns the number of strings on
+ * success, -EINVAL if the property does not exist, -ENODATA if property
+ * does not have a value, and -EILSEQ if the string is not null-terminated
+ * within the length of the property data.
+ */
+int of_property_count_strings(struct device_node *np, const char *propname)
+{
+	struct property *prop = of_find_property(np, propname, NULL);
+	int i = 0;
+	size_t l = 0, total = 0;
+	const char *p;
+
+	if (!prop)
+		return -EINVAL;
+	if (!prop->value)
+		return -ENODATA;
+	if (strnlen(prop->value, prop->length) >= prop->length)
+		return -EILSEQ;
+
+	p = prop->value;
+
+	for (i = 0; total < prop->length; total += l, p += l) {
+		l = strlen(p) + 1;
+		if (*p != 0)
+			i++;
+	}
+	return i;
+}
+EXPORT_SYMBOL_GPL(of_property_count_strings);
+
+/**
  * of_parse_phandle - Resolve a phandle property to a device_node pointer
  * @np: Pointer to device node holding phandle property
  * @phandle_name: Name of property holding a phandle value
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 19c0115..8a31313 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -346,9 +346,18 @@
 	/* Only dereference the resource if both the
 	 * resource and the irq are valid. */
 	if (r && irq != NO_IRQ) {
+		const char *name = NULL;
+
+		/*
+		 * Get optional "interrupts-names" property to add a name
+		 * to the resource.
+		 */
+		of_property_read_string_index(dev, "interrupt-names", index,
+					      &name);
+
 		r->start = r->end = irq;
 		r->flags = IORESOURCE_IRQ;
-		r->name = dev->full_name;
+		r->name = name ? name : dev->full_name;
 	}
 
 	return irq;
diff --git a/include/linux/of.h b/include/linux/of.h
index f904bf4..b904f7e 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -202,6 +202,11 @@
 
 extern int of_property_read_string(struct device_node *np, char *propname,
 					const char **out_string);
+extern int of_property_read_string_index(struct device_node *np,
+					 const char *propname,
+					 int index, const char **output);
+extern int of_property_count_strings(struct device_node *np,
+				     const char *propname);
 extern int of_device_is_compatible(const struct device_node *device,
 				   const char *);
 extern int of_device_is_available(const struct device_node *device);
@@ -263,6 +268,19 @@
 	return -ENOSYS;
 }
 
+static inline int of_property_read_string_index(struct device_node *np,
+						const char *propname, int index,
+						const char **out_string)
+{
+	return -ENOSYS;
+}
+
+static inline int of_property_count_strings(struct device_node *np,
+					    const char *propname)
+{
+	return -ENOSYS;
+}
+
 static inline const void *of_get_property(const struct device_node *node,
 				const char *name,
 				int *lenp)
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index cd85855..eb89f4b 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -869,6 +869,17 @@
 	__le16   num_blocks;
 } __packed;
 
+#define HCI_OP_READ_RSSI	0x1405
+struct hci_cp_read_rssi {
+	__le16   handle;
+} __packed;
+
+struct hci_rp_read_rssi {
+	__u8     status;
+	__le16   handle;
+	__s8     rssi;
+} __packed;
+
 #define HCI_OP_READ_LOCAL_AMP_INFO	0x1409
 struct hci_rp_read_local_amp_info {
 	__u8     status;
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 3ca14ff..4c86f24 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -310,12 +310,17 @@
 	__u8		remote_oob;
 	__u8		remote_auth;
 
+	__s8	rssi_threshold;
+	__u16	rssi_update_interval;
+	__u8	rssi_update_thresh_exceed;
+
 	unsigned int	sent;
 
 	struct sk_buff_head data_q;
 
 	struct timer_list disc_timer;
 	struct timer_list idle_timer;
+	struct delayed_work	rssi_update_work;
 
 	struct work_struct work_add;
 	struct work_struct work_del;
@@ -607,6 +612,10 @@
 void hci_conn_hold_device(struct hci_conn *conn);
 void hci_conn_put_device(struct hci_conn *conn);
 
+void hci_conn_set_rssi_reporter(struct hci_conn *conn,
+		s8 rssi_threshold, u16 interval, u8 updateOnThreshExceed);
+void hci_conn_unset_rssi_reporter(struct hci_conn *conn);
+
 static inline void hci_conn_hold(struct hci_conn *conn)
 {
 	atomic_inc(&conn->refcnt);
@@ -1039,6 +1048,8 @@
 								u8 status);
 int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le,
 				u8 *dev_class, s8 rssi, u8 eir_len, u8 *eir);
+void mgmt_read_rssi_complete(u16 index, s8 rssi, bdaddr_t *bdaddr,
+				u16 handle, u8 status);
 int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 status, u8 *name);
 void mgmt_inquiry_started(u16 index);
 void mgmt_inquiry_complete_evt(u16 index, u8 status);
@@ -1090,4 +1101,6 @@
 void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]);
 void hci_le_ltk_neg_reply(struct hci_conn *conn);
 
+void hci_read_rssi(struct hci_conn *conn);
+
 #endif /* __HCI_CORE_H */
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 9c089e0..e34c425 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -233,6 +233,19 @@
 	__u8 enable;
 } __packed;
 
+#define MGMT_OP_SET_RSSI_REPORTER		0x0022
+struct mgmt_cp_set_rssi_reporter {
+	bdaddr_t	bdaddr;
+	__s8		rssi_threshold;
+	__le16	interval;
+	__u8		updateOnThreshExceed;
+} __packed;
+
+#define MGMT_OP_UNSET_RSSI_REPORTER		0x0023
+struct mgmt_cp_unset_rssi_reporter {
+	bdaddr_t	bdaddr;
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	__le16 opcode;
@@ -360,3 +373,9 @@
 	bdaddr_t bdaddr;
 	uint8_t features[8];
 } __packed;
+
+#define MGMT_EV_RSSI_UPDATE		0x0020
+struct mgmt_ev_rssi_update {
+	bdaddr_t	bdaddr;
+	__s8			rssi;
+} __packed;
diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
index a8e5af9..2854395 100644
--- a/net/bluetooth/hci_conn.c
+++ b/net/bluetooth/hci_conn.c
@@ -225,6 +225,18 @@
 }
 EXPORT_SYMBOL(hci_le_conn_update);
 
+void hci_read_rssi(struct hci_conn *conn)
+{
+	struct hci_cp_read_rssi cp;
+	struct hci_dev *hdev = conn->hdev;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.handle   = cpu_to_le16(conn->handle);
+
+	hci_send_cmd(hdev, HCI_OP_READ_RSSI, sizeof(cp), &cp);
+}
+EXPORT_SYMBOL(hci_read_rssi);
+
 void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8],
 							__u8 ltk[16])
 {
@@ -340,6 +352,18 @@
 	hci_conn_enter_sniff_mode(conn);
 }
 
+static void hci_conn_rssi_update(struct work_struct *work)
+{
+	struct delayed_work *delayed =
+		container_of(work, struct delayed_work, work);
+	struct hci_conn *conn =
+		container_of(delayed, struct hci_conn, rssi_update_work);
+
+	BT_DBG("conn %p mode %d", conn, conn->mode);
+
+	hci_read_rssi(conn);
+}
+
 struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type,
 					__u16 pkt_type, bdaddr_t *dst)
 {
@@ -392,6 +416,7 @@
 
 	setup_timer(&conn->disc_timer, hci_conn_timeout, (unsigned long)conn);
 	setup_timer(&conn->idle_timer, hci_conn_idle, (unsigned long)conn);
+	INIT_DELAYED_WORK(&conn->rssi_update_work, hci_conn_rssi_update);
 
 	atomic_set(&conn->refcnt, 0);
 
@@ -434,6 +459,7 @@
 	del_timer(&conn->idle_timer);
 	del_timer(&conn->disc_timer);
 	del_timer(&conn->smp_timer);
+	__cancel_delayed_work(&conn->rssi_update_work);
 
 	if (conn->type == ACL_LINK) {
 		struct hci_conn *sco = conn->link;
@@ -900,6 +926,43 @@
 			jiffies + msecs_to_jiffies(hdev->idle_timeout));
 }
 
+static inline void hci_conn_stop_rssi_timer(struct hci_conn *conn)
+{
+	BT_DBG("conn %p", conn);
+	cancel_delayed_work(&conn->rssi_update_work);
+}
+
+static inline void hci_conn_start_rssi_timer(struct hci_conn *conn,
+	u16 interval)
+{
+	struct hci_dev *hdev = conn->hdev;
+	BT_DBG("conn %p, pending %d", conn,
+			delayed_work_pending(&conn->rssi_update_work));
+	if (!delayed_work_pending(&conn->rssi_update_work)) {
+		queue_delayed_work(hdev->workqueue, &conn->rssi_update_work,
+				msecs_to_jiffies(interval));
+	}
+}
+
+void hci_conn_set_rssi_reporter(struct hci_conn *conn,
+	s8 rssi_threshold, u16 interval, u8 updateOnThreshExceed)
+{
+	if (conn) {
+		conn->rssi_threshold = rssi_threshold;
+		conn->rssi_update_interval = interval;
+		conn->rssi_update_thresh_exceed = updateOnThreshExceed;
+		hci_conn_start_rssi_timer(conn, interval);
+	}
+}
+
+void hci_conn_unset_rssi_reporter(struct hci_conn *conn)
+{
+	if (conn) {
+		BT_DBG("Deleting the rssi_update_timer");
+		hci_conn_stop_rssi_timer(conn);
+	}
+}
+
 /* Enter sniff mode */
 void hci_conn_enter_sniff_mode(struct hci_conn *conn)
 {
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index aac934a..1d76674 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -940,6 +940,21 @@
 	hci_dev_unlock(hdev);
 }
 
+static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct hci_conn *conn;
+	struct hci_rp_read_rssi *rp = (void *) skb->data;
+
+	BT_DBG("%s status 0x%x", hdev->name, rp->status);
+
+	BT_DBG("%s rssi : %d handle : %d", hdev->name, rp->rssi, rp->handle);
+
+	conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle));
+	if (conn)
+		mgmt_read_rssi_complete(hdev->id, rp->rssi, &conn->dst,
+			__le16_to_cpu(rp->handle), rp->status);
+}
+
 static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev,
 							struct sk_buff *skb)
 {
@@ -2177,6 +2192,10 @@
 		hci_cc_le_read_buffer_size(hdev, skb);
 		break;
 
+	case HCI_OP_READ_RSSI:
+		hci_cc_read_rssi(hdev, skb);
+		break;
+
 	case HCI_OP_USER_CONFIRM_REPLY:
 		hci_cc_user_confirm_reply(hdev, skb);
 		break;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index bc68c9f..ddb650b 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -1810,6 +1810,81 @@
 	return err;
 }
 
+static int set_rssi_reporter(struct sock *sk, u16 index,
+				unsigned char *data, u16 len)
+{
+	struct mgmt_cp_set_rssi_reporter *cp = (void *) data;
+	struct hci_dev *hdev;
+	struct hci_conn *conn;
+	int err = 0;
+
+	if (len != sizeof(*cp))
+		return cmd_status(sk, index, MGMT_OP_SET_RSSI_REPORTER,
+								EINVAL);
+
+	hdev = hci_dev_get(index);
+	if (!hdev)
+		return cmd_status(sk, index, MGMT_OP_SET_RSSI_REPORTER,
+							ENODEV);
+
+	hci_dev_lock(hdev);
+
+	conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
+
+	if (!conn) {
+		err = cmd_status(sk, index, MGMT_OP_SET_RSSI_REPORTER,
+						ENOTCONN);
+		goto failed;
+	}
+
+	BT_DBG("updateOnThreshExceed %d ", cp->updateOnThreshExceed);
+	hci_conn_set_rssi_reporter(conn, cp->rssi_threshold,
+			__le16_to_cpu(cp->interval), cp->updateOnThreshExceed);
+
+failed:
+	hci_dev_unlock(hdev);
+	hci_dev_put(hdev);
+
+	return err;
+}
+
+static int unset_rssi_reporter(struct sock *sk, u16 index,
+			unsigned char *data, u16 len)
+{
+	struct mgmt_cp_unset_rssi_reporter *cp = (void *) data;
+	struct hci_dev *hdev;
+	struct hci_conn *conn;
+	int err = 0;
+
+	if (len != sizeof(*cp))
+		return cmd_status(sk, index, MGMT_OP_UNSET_RSSI_REPORTER,
+					EINVAL);
+
+	hdev = hci_dev_get(index);
+
+	if (!hdev)
+		return cmd_status(sk, index, MGMT_OP_UNSET_RSSI_REPORTER,
+					ENODEV);
+
+	hci_dev_lock(hdev);
+
+	conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->bdaddr);
+
+	if (!conn) {
+		err = cmd_status(sk, index, MGMT_OP_UNSET_RSSI_REPORTER,
+					ENOTCONN);
+		goto failed;
+	}
+
+	hci_conn_unset_rssi_reporter(conn);
+
+failed:
+	hci_dev_unlock(hdev);
+	hci_dev_put(hdev);
+
+	return err;
+}
+
 static int set_local_name(struct sock *sk, u16 index, unsigned char *data,
 								u16 len)
 {
@@ -2365,6 +2440,12 @@
 	case MGMT_OP_SET_CONNECTION_PARAMS:
 		err = set_connection_params(sk, index, buf + sizeof(*hdr), len);
 		break;
+	case MGMT_OP_SET_RSSI_REPORTER:
+		err = set_rssi_reporter(sk, index, buf + sizeof(*hdr), len);
+		break;
+	case MGMT_OP_UNSET_RSSI_REPORTER:
+		err = unset_rssi_reporter(sk, index, buf + sizeof(*hdr), len);
+		break;
 	case MGMT_OP_READ_LOCAL_OOB_DATA:
 		err = read_local_oob_data(sk, index);
 		break;
@@ -2817,6 +2898,57 @@
 	return err;
 }
 
+void mgmt_read_rssi_complete(u16 index, s8 rssi, bdaddr_t *bdaddr,
+		u16 handle, u8 status)
+{
+	struct mgmt_ev_rssi_update ev;
+	struct hci_conn *conn;
+	struct hci_dev *hdev;
+
+	if (status)
+		return;
+
+	hdev = hci_dev_get(index);
+	conn = hci_conn_hash_lookup_handle(hdev, handle);
+
+	if (!conn)
+		return;
+
+	BT_DBG("rssi_update_thresh_exceed : %d ",
+		   conn->rssi_update_thresh_exceed);
+	BT_DBG("RSSI Threshold : %d , recvd RSSI : %d ",
+			conn->rssi_threshold, rssi);
+
+	if (conn->rssi_update_thresh_exceed == 1) {
+		BT_DBG("rssi_update_thresh_exceed == 1");
+		if (rssi >= conn->rssi_threshold) {
+			memset(&ev, 0, sizeof(ev));
+			bacpy(&ev.bdaddr, bdaddr);
+			ev.rssi = rssi;
+			mgmt_event(MGMT_EV_RSSI_UPDATE, index, &ev,
+				sizeof(ev), NULL);
+		} else {
+			hci_conn_set_rssi_reporter(conn, conn->rssi_threshold,
+				conn->rssi_update_interval,
+				conn->rssi_update_thresh_exceed);
+		}
+	} else {
+		BT_DBG("rssi_update_thresh_exceed == 0");
+		if (rssi <= conn->rssi_threshold) {
+			memset(&ev, 0, sizeof(ev));
+			bacpy(&ev.bdaddr, bdaddr);
+			ev.rssi = rssi;
+			mgmt_event(MGMT_EV_RSSI_UPDATE, index, &ev,
+				sizeof(ev), NULL);
+		} else {
+			hci_conn_set_rssi_reporter(conn, conn->rssi_threshold,
+				conn->rssi_update_interval,
+				conn->rssi_update_thresh_exceed);
+		}
+	}
+}
+
+
 int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 le,
 			u8 *dev_class, s8 rssi, u8 eir_len, u8 *eir)
 {