Merge "defconfig: 9615: Enable SPS for MMC" into msm-3.0
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 2f092c8..3265ffa 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -5447,13 +5447,19 @@
 	/* Deassert MM SW_RESET_ALL signal. */
 	writel_relaxed(0, SW_RESET_ALL_REG);
 
+	/*
+	 * Some bits are only used on either 8960 or 8064 and are marked as
+	 * reserved bits on the other SoC. Writing to these reserved bits
+	 * should have no effect.
+	 */
 	/* Initialize MM AHB registers: Enable the FPB clock and disable HW
 	 * gating for all clocks. Also set VFE_AHB's FORCE_CORE_ON bit to
 	 * prevent its memory from being collapsed when the clock is halted.
 	 * The sleep and wake-up delays are set to safe values. */
 	rmwreg(0x00000003, AHB_EN_REG,  0x6C000103);
 	writel_relaxed(0x000007F9, AHB_EN2_REG);
-	rmwreg(0x00000000, AHB_EN3_REG, 0x00000001);
+	if (cpu_is_apq8064())
+		rmwreg(0x00000000, AHB_EN3_REG, 0x00000001);
 
 	/* Deassert all locally-owned MM AHB resets. */
 	rmwreg(0, SW_RESET_AHB_REG, 0xFFF7DFFF);
@@ -5467,7 +5473,8 @@
 	rmwreg(0x3027FCFF, MAXI_EN2_REG, 0x3A3FFFFF);
 	rmwreg(0x0027FCFF, MAXI_EN3_REG, 0x003FFFFF);
 	rmwreg(0x0027FCFF, MAXI_EN4_REG, 0x017FFFFF);
-	rmwreg(0x009FE4FF, MAXI_EN5_REG, 0x01FFEFFF);
+	if (cpu_is_apq8064())
+		rmwreg(0x009FE4FF, MAXI_EN5_REG, 0x01FFEFFF);
 	rmwreg(0x000003C7, SAXI_EN_REG,  0x00003FFF);
 
 	/* Initialize MM CC registers: Set MM FORCE_CORE_ON bits so that core
@@ -5479,22 +5486,26 @@
 	rmwreg(0x80FF0000, DSI2_BYTE_CC_REG,  0xE0FF0010);
 	rmwreg(0x80FF0000, DSI_PIXEL_CC_REG,  0xE0FF0010);
 	rmwreg(0x80FF0000, DSI2_PIXEL_CC_REG, 0xE0FF0010);
-	rmwreg(0x80FF0000, GFX2D0_CC_REG,     0xE0FF0010);
-	rmwreg(0x80FF0000, GFX2D1_CC_REG,     0xE0FF0010);
 	rmwreg(0x80FF0000, GFX3D_CC_REG,      0xE0FF0010);
 	rmwreg(0x80FF0000, IJPEG_CC_REG,      0xE0FF0010);
 	rmwreg(0x80FF0000, JPEGD_CC_REG,      0xE0FF0010);
 	rmwreg(0x80FF0000, MDP_CC_REG,        0xE1FF0010);
 	rmwreg(0x80FF0000, MDP_LUT_CC_REG,    0xE0FF0010);
 	rmwreg(0x80FF0000, ROT_CC_REG,        0xE0FF0010);
-	rmwreg(0x80FF0000, TV_CC_REG,         0xE1FFC010);
 	rmwreg(0x000004FF, TV_CC2_REG,        0x000007FF);
 	rmwreg(0xC0FF0000, VCODEC_CC_REG,     0xE0FF0010);
 	rmwreg(0x80FF0000, VFE_CC_REG,        0xE0FF4010);
 	rmwreg(0x800000FF, VFE_CC2_REG,       0xE00000FF);
 	rmwreg(0x80FF0000, VPE_CC_REG,        0xE0FF0010);
-	if (cpu_is_apq8064())
+	if (cpu_is_msm8960()) {
+		rmwreg(0x80FF0000, GFX2D0_CC_REG,     0xE0FF0010);
+		rmwreg(0x80FF0000, GFX2D1_CC_REG,     0xE0FF0010);
+		rmwreg(0x80FF0000, TV_CC_REG,         0xE1FFC010);
+	}
+	if (cpu_is_apq8064()) {
+		rmwreg(0x00000000, TV_CC_REG,         0x00004010);
 		rmwreg(0x80FF0000, VCAP_CC_REG,       0xE0FF1010);
+	}
 
 	/*
 	 * Initialize USB_HS_HCLK_FS registers: Set FORCE_C_ON bits so that
@@ -5502,8 +5513,10 @@
 	 * and wake-up value to max.
 	 */
 	rmwreg(0x0000004F, USB_HS1_HCLK_FS_REG, 0x0000007F);
-	rmwreg(0x0000004F, USB_HS3_HCLK_FS_REG, 0x0000007F);
-	rmwreg(0x0000004F, USB_HS4_HCLK_FS_REG, 0x0000007F);
+	if (cpu_is_apq8064()) {
+		rmwreg(0x0000004F, USB_HS3_HCLK_FS_REG, 0x0000007F);
+		rmwreg(0x0000004F, USB_HS4_HCLK_FS_REG, 0x0000007F);
+	}
 
 	/* De-assert MM AXI resets to all hardware blocks. */
 	writel_relaxed(0, SW_RESET_AXI_REG);
@@ -5529,7 +5542,8 @@
 	writel_relaxed(BIT(15), PDM_CLK_NS_REG);
 
 	/* Source SLIMBus xo src from slimbus reference clock */
-	writel_relaxed(0x3, SLIMBUS_XO_SRC_CLK_CTL_REG);
+	if (cpu_is_msm8960())
+		writel_relaxed(0x3, SLIMBUS_XO_SRC_CLK_CTL_REG);
 
 	/* Source the dsi_byte_clks from the DSI PHY PLLs */
 	rmwreg(0x1, DSI1_BYTE_NS_REG, 0x7);
diff --git a/arch/arm/mach-msm/sdio_al.c b/arch/arm/mach-msm/sdio_al.c
index 8267fad..ace437b 100644
--- a/arch/arm/mach-msm/sdio_al.c
+++ b/arch/arm/mach-msm/sdio_al.c
@@ -108,6 +108,7 @@
  *  packet) rx data.
  */
 #define DEFAULT_READ_THRESHOLD  	(1024)
+#define LOW_LATENCY_THRESHOLD		(1)
 
 /* Extra bytes to ensure getting the rx threshold interrupt on stream channels
    when restoring the threshold after sleep */
@@ -119,7 +120,6 @@
 
 #define THRESHOLD_DISABLE_VAL  		(0xFFFFFFFF)
 
-
 /** Mailbox polling time for packet channels */
 #define DEFAULT_POLL_DELAY_MSEC		10
 /** Mailbox polling time for streaming channels */
@@ -986,7 +986,7 @@
 			continue;
 		}
 		if (ch->is_packet_mode == false) {
-			ch->read_threshold = 1;
+			ch->read_threshold = LOW_LATENCY_THRESHOLD;
 			set_pipe_threshold(sdio_al_dev,
 					   ch->rx_pipe_index,
 					   ch->read_threshold);
@@ -1156,13 +1156,22 @@
 					sdio_al_dev->host->index);
 			}
 			ch->read_avail = read_avail;
-			/* Restore default thresh for non packet channels */
+
+			/*
+			 * Restore default thresh for non packet channels.
+			 * in case it IS low latency channel then read_threshold
+			 * and def_read_threshold are both
+			 * LOW_LATENCY_THRESHOLD
+			 */
 			if ((ch->read_threshold != ch->def_read_threshold) &&
 			    (read_avail >= ch->threshold_change_cnt)) {
-				ch->read_threshold = ch->def_read_threshold;
-				set_pipe_threshold(sdio_al_dev,
-						   ch->rx_pipe_index,
-						   ch->read_threshold);
+				if (!ch->is_low_latency_ch) {
+					ch->read_threshold =
+						ch->def_read_threshold;
+					set_pipe_threshold(sdio_al_dev,
+							   ch->rx_pipe_index,
+							   ch->read_threshold);
+				}
 			}
 		}
 
@@ -1968,14 +1977,18 @@
 		goto exit_err;
 	}
 
-	/* Aggregation up to 90% of the maximum size */
-	ch->read_threshold = (ch_config->max_rx_threshold * 9) / 10;
+	ch->read_threshold = LOW_LATENCY_THRESHOLD;
+	ch->is_low_latency_ch = ch_config->is_low_latency_ch;
 	/* Threshold on 50% of the maximum size , sdioc uses double-buffer */
 	ch->write_threshold = (ch_config->max_tx_threshold * 5) / 10;
 	ch->threshold_change_cnt = ch->ch_config.max_rx_threshold -
 			ch->read_threshold + THRESHOLD_CHANGE_EXTRA_BYTES;
 
-	ch->def_read_threshold = ch->read_threshold;
+	if (ch->is_low_latency_ch)
+		ch->def_read_threshold = LOW_LATENCY_THRESHOLD;
+	else /* Aggregation up to 90% of the maximum size */
+		ch->def_read_threshold = (ch_config->max_rx_threshold * 9) / 10;
+
 	ch->is_packet_mode = ch_config->is_packet_mode;
 	if (!ch->is_packet_mode) {
 		ch->poll_delay_msec = DEFAULT_POLL_DELAY_NOPACKET_MSEC;
diff --git a/arch/arm/mach-msm/sdio_al_private.h b/arch/arm/mach-msm/sdio_al_private.h
index f352499..36d9ec1 100644
--- a/arch/arm/mach-msm/sdio_al_private.h
+++ b/arch/arm/mach-msm/sdio_al_private.h
@@ -66,7 +66,8 @@
 	u32 is_host_ok_to_sleep;
 	u32 is_packet_mode;
 	u32 peer_operation;
-	u32 reserved[24];
+	u32 is_low_latency_ch;
+	u32 reserved[23];
 };
 
 
@@ -173,6 +174,7 @@
 	int min_write_avail;
 	int poll_delay_msec;
 	int is_packet_mode;
+	int is_low_latency_ch;
 
 	struct peer_sdioc_channel_config ch_config;
 
diff --git a/drivers/bluetooth/hci_smd.c b/drivers/bluetooth/hci_smd.c
index 31634ff..66bab6b 100644
--- a/drivers/bluetooth/hci_smd.c
+++ b/drivers/bluetooth/hci_smd.c
@@ -302,6 +302,7 @@
 		break;
 	}
 
+	kfree_skb(skb);
 	wake_unlock(&hs.wake_lock_tx);
 	return ret;
 }
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index 165bbbf..0f8d474 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -329,6 +329,12 @@
 			KGSL_PWR_WARN(device, "state -> SUSPEND, device %d\n",
 				device->id);
 			break;
+		case KGSL_STATE_SLUMBER:
+			INIT_COMPLETION(device->hwaccess_gate);
+			device->state = KGSL_STATE_SUSPEND;
+			KGSL_PWR_WARN(device, "state -> SUSPEND, device %d\n",
+				device->id);
+			break;
 		default:
 			KGSL_PWR_ERR(device, "suspend fail, device %d\n",
 					device->id);
@@ -355,28 +361,16 @@
 	KGSL_PWR_WARN(device, "resume start\n");
 	mutex_lock(&device->mutex);
 	if (device->state == KGSL_STATE_SUSPEND) {
-		device->requested_state = KGSL_STATE_ACTIVE;
-		kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL);
-		status = device->ftbl->start(device, 0);
-		if (status == 0) {
-			device->state = KGSL_STATE_ACTIVE;
-			KGSL_PWR_WARN(device,
-					"state -> ACTIVE, device %d\n",
-					device->id);
-		} else {
-			KGSL_PWR_ERR(device,
-					"resume failed, device %d\n",
-					device->id);
-			device->state = KGSL_STATE_INIT;
-			goto end;
-		}
+		device->state = KGSL_STATE_SLUMBER;
+		status = 0;
+		KGSL_PWR_WARN(device,
+				"state -> SLUMBER, device %d\n",
+				device->id);
 		complete_all(&device->hwaccess_gate);
 	}
 	device->requested_state = KGSL_STATE_NONE;
 
-end:
 	mutex_unlock(&device->mutex);
-	kgsl_check_idle(device);
 	KGSL_PWR_WARN(device, "resume end\n");
 	return status;
 }
@@ -417,9 +411,12 @@
 {
 	struct kgsl_device *device = container_of(h,
 					struct kgsl_device, display_off);
+	KGSL_PWR_WARN(device, "early suspend start\n");
 	mutex_lock(&device->mutex);
-	kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL);
+	device->requested_state = KGSL_STATE_SLUMBER;
+	kgsl_pwrctrl_sleep(device);
 	mutex_unlock(&device->mutex);
+	KGSL_PWR_WARN(device, "early suspend end\n");
 }
 EXPORT_SYMBOL(kgsl_early_suspend_driver);
 
@@ -442,9 +439,13 @@
 {
 	struct kgsl_device *device = container_of(h,
 					struct kgsl_device, display_off);
+	KGSL_PWR_WARN(device, "late resume start\n");
 	mutex_lock(&device->mutex);
-	kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
+	kgsl_pwrctrl_wake(device);
+	device->pwrctrl.restore_slumber = 0;
 	mutex_unlock(&device->mutex);
+	kgsl_check_idle(device);
+	KGSL_PWR_WARN(device, "late resume end\n");
 }
 EXPORT_SYMBOL(kgsl_late_resume_driver);
 
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index 2f369ed..b6e79b8 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -46,6 +46,7 @@
 #define KGSL_STATE_SUSPEND	0x00000010
 #define KGSL_STATE_HUNG		0x00000020
 #define KGSL_STATE_DUMP_AND_RECOVER	0x00000040
+#define KGSL_STATE_SLUMBER	0x00000080
 
 #define KGSL_GRAPHICS_MEMORY_LOW_WATERMARK  0x1000000
 
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 84f2b33..b4f22ee 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -583,7 +583,8 @@
 
 	mutex_lock(&device->mutex);
 	if (device->ftbl->isidle(device) &&
-		(device->requested_state != KGSL_STATE_SLEEP))
+		(device->requested_state != KGSL_STATE_SLEEP) &&
+		(device->requested_state != KGSL_STATE_SLUMBER))
 		kgsl_pwrscale_idle(device);
 
 	if (device->state & (KGSL_STATE_ACTIVE | KGSL_STATE_NAP)) {
@@ -622,7 +623,8 @@
 void kgsl_pre_hwaccess(struct kgsl_device *device)
 {
 	BUG_ON(!mutex_is_locked(&device->mutex));
-	if (device->state & (KGSL_STATE_SLEEP | KGSL_STATE_NAP))
+	if (device->state & (KGSL_STATE_SLEEP | KGSL_STATE_NAP |
+				KGSL_STATE_SLUMBER))
 		kgsl_pwrctrl_wake(device);
 }
 EXPORT_SYMBOL(kgsl_pre_hwaccess);
@@ -640,8 +642,40 @@
 		wait_for_completion(&device->recovery_gate);
 		mutex_lock(&device->mutex);
 	}
- }
+}
 
+static int
+_slumber(struct kgsl_device *device)
+{
+	int status = -EINVAL;
+	if (!device)
+		return -EINVAL;
+	KGSL_PWR_WARN(device, "Slumber start\n");
+
+	device->requested_state = KGSL_STATE_SLUMBER;
+	del_timer(&device->idle_timer);
+	switch (device->state) {
+	case KGSL_STATE_ACTIVE:
+		/* Wait for the device to become idle */
+		device->ftbl->idle(device, KGSL_TIMEOUT_DEFAULT);
+	case KGSL_STATE_NAP:
+	case KGSL_STATE_SLEEP:
+		device->ftbl->suspend_context(device);
+		device->ftbl->stop(device);
+		device->state = KGSL_STATE_SLUMBER;
+		device->pwrctrl.restore_slumber = 1;
+		KGSL_PWR_WARN(device, "state -> SLUMBER, device %d\n",
+				device->id);
+		break;
+	default:
+		break;
+	}
+	status = 0;
+	/* Don't set requested state to NONE
+	It's done in kgsl_pwrctrl_sleep*/
+	KGSL_PWR_WARN(device, "Done going to slumber\n");
+	return status;
+}
 
 /******************************************************************/
 /* Caller must hold the device mutex. */
@@ -656,13 +690,24 @@
 			goto nap;
 	} else if (device->requested_state == KGSL_STATE_SLEEP) {
 		if (device->state == KGSL_STATE_NAP ||
-			device->ftbl->isidle(device))
-			goto sleep;
+			device->ftbl->isidle(device)) {
+			if (!device->pwrctrl.restore_slumber)
+				goto sleep;
+			else
+				goto slumber;
+			}
+	} else if (device->requested_state == KGSL_STATE_SLUMBER) {
+		if (device->ftbl->isidle(device))
+			goto slumber;
 	}
 
 	device->requested_state = KGSL_STATE_NONE;
 	return -EBUSY;
 
+
+slumber:
+	_slumber(device);
+
 sleep:
 	kgsl_pwrctrl_irq(device, KGSL_PWRFLAGS_OFF);
 	kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_OFF);
@@ -694,6 +739,24 @@
 }
 EXPORT_SYMBOL(kgsl_pwrctrl_sleep);
 
+static int
+_wake_from_slumber(struct kgsl_device *device)
+{
+	int status = -EINVAL;
+	if (!device)
+		return -EINVAL;
+
+	KGSL_PWR_WARN(device, "wake from slumber start\n");
+
+	device->requested_state = KGSL_STATE_ACTIVE;
+	kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_NOMINAL);
+	status = device->ftbl->start(device, 0);
+	device->requested_state = KGSL_STATE_NONE;
+
+	KGSL_PWR_WARN(device, "Done waking from slumber\n");
+	return status;
+}
+
 /******************************************************************/
 /* Caller must hold the device mutex. */
 void kgsl_pwrctrl_wake(struct kgsl_device *device)
@@ -701,6 +764,9 @@
 	if (device->state == KGSL_STATE_SUSPEND)
 		return;
 
+	if (device->state == KGSL_STATE_SLUMBER)
+		_wake_from_slumber(device);
+
 	if (device->state != KGSL_STATE_NAP) {
 		kgsl_pwrctrl_axi(device, KGSL_PWRFLAGS_ON);
 		kgsl_pwrscale_wake(device);
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index 127a19b..17dab76 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -56,6 +56,7 @@
 	const char *src_clk_name;
 	s64 time;
 	struct kgsl_busy busy;
+	unsigned int restore_slumber;
 };
 
 void kgsl_pwrctrl_clk(struct kgsl_device *device, int state);
diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c
index 9017706..9d268d1 100644
--- a/drivers/usb/gadget/ci13xxx_udc.c
+++ b/drivers/usb/gadget/ci13xxx_udc.c
@@ -1699,6 +1699,9 @@
 	if (mReq->req.status != -EALREADY)
 		return -EINVAL;
 
+	/* clean speculative fetches on req->ptr->token */
+	mb();
+
 	if ((TD_STATUS_ACTIVE & mReq->ptr->token) != 0)
 		return -EBUSY;
 
@@ -2074,7 +2077,6 @@
 			continue;   /* not configured */
 
 		if (hw_test_and_clear_complete(i)) {
-			udelay(200);
 			err = isr_tr_complete_low(mEp);
 			if (mEp->type == USB_ENDPOINT_XFER_CONTROL) {
 				if (err > 0)   /* needs status phase */
diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index aa6aeb9..b67803a 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -708,6 +708,12 @@
 	__le16   sco_max_pkt;
 } __packed;
 
+#define HCI_OP_WRITE_CURRENT_IAC_LAP	0x0c3a
+struct hci_cp_write_current_iac_lap {
+	__u8     num_current_iac;
+	__u8     lap[6];
+} __packed;
+
 #define HCI_OP_WRITE_INQUIRY_MODE	0x0c45
 
 #define HCI_MAX_EIR_LENGTH		240
diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h
index 5e855dbf..208c157 100644
--- a/include/net/bluetooth/mgmt.h
+++ b/include/net/bluetooth/mgmt.h
@@ -90,6 +90,8 @@
 	__u8 major;
 	__u8 minor;
 } __packed;
+#define MGMT_MAJOR_CLASS_MASK		0x1F
+#define MGMT_MAJOR_CLASS_LIMITED	0x20
 
 #define MGMT_OP_SET_SERVICE_CACHE	0x000C
 struct mgmt_cp_set_service_cache {
@@ -214,6 +216,8 @@
 	bdaddr_t bdaddr;
 } __packed;
 
+#define MGMT_OP_SET_LIMIT_DISCOVERABLE	0x001F
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	__le16 opcode;
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index a328db9..ea72708 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -409,6 +409,124 @@
 	return err;
 }
 
+static u8 get_service_classes(struct hci_dev *hdev)
+{
+	struct list_head *p;
+	u8 val = 0;
+
+	list_for_each(p, &hdev->uuids) {
+		struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
+
+		val |= uuid->svc_hint;
+	}
+
+	return val;
+}
+
+static int update_class(struct hci_dev *hdev)
+{
+	u8 cod[3];
+
+	BT_DBG("%s", hdev->name);
+
+	if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
+		return 0;
+
+	cod[0] = hdev->minor_class;
+	cod[1] = hdev->major_class;
+	cod[2] = get_service_classes(hdev);
+
+	if (memcmp(cod, hdev->dev_class, 3) == 0)
+		return 0;
+
+	return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
+}
+
+static int set_limited_discoverable(struct sock *sk, u16 index,
+						unsigned char *data, u16 len)
+{
+	struct mgmt_mode *cp;
+	struct hci_dev *hdev;
+	struct pending_cmd *cmd;
+	struct hci_cp_write_current_iac_lap dcp;
+	int update_cod;
+	int err = 0;
+	/* General Inquiry LAP: 0x9E8B33, Limited Inquiry LAP: 0x9E8B00 */
+	u8 lap[] = { 0x33, 0x8b, 0x9e, 0x00, 0x8b, 0x9e };
+
+	cp = (void *) data;
+
+	BT_DBG("hci%u discoverable: %d", index, cp->val);
+
+	if (!cp || len != sizeof(*cp))
+		return cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+									EINVAL);
+
+	hdev = hci_dev_get(index);
+	if (!hdev)
+		return cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+									ENODEV);
+
+	hci_dev_lock(hdev);
+
+	if (!test_bit(HCI_UP, &hdev->flags)) {
+		err = cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+								ENETDOWN);
+		goto failed;
+	}
+
+	if (mgmt_pending_find(MGMT_OP_SET_LIMIT_DISCOVERABLE, index)) {
+		err = cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+									EBUSY);
+		goto failed;
+	}
+
+	if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) &&
+					test_bit(HCI_PSCAN, &hdev->flags)) {
+		err = cmd_status(sk, index, MGMT_OP_SET_LIMIT_DISCOVERABLE,
+								EALREADY);
+		goto failed;
+	}
+
+	cmd = mgmt_pending_add(sk, MGMT_OP_SET_LIMIT_DISCOVERABLE, index, data,
+									len);
+	if (!cmd) {
+		err = -ENOMEM;
+		goto failed;
+	}
+
+	memset(&dcp, 0, sizeof(dcp));
+	dcp.num_current_iac = cp->val ? 2 : 1;
+	memcpy(&dcp.lap, lap, dcp.num_current_iac * 3);
+	update_cod = 1;
+
+	if (cp->val) {
+		if (hdev->major_class & MGMT_MAJOR_CLASS_LIMITED)
+			update_cod = 0;
+		hdev->major_class |= MGMT_MAJOR_CLASS_LIMITED;
+	} else {
+		if (!(hdev->major_class & MGMT_MAJOR_CLASS_LIMITED))
+			update_cod = 0;
+		hdev->major_class &= ~MGMT_MAJOR_CLASS_LIMITED;
+	}
+
+	if (update_cod)
+		err = update_class(hdev);
+
+	if (err >= 0)
+		err = hci_send_cmd(hdev, HCI_OP_WRITE_CURRENT_IAC_LAP,
+							sizeof(dcp), &dcp);
+
+	if (err < 0)
+		mgmt_pending_remove(cmd);
+
+failed:
+	hci_dev_unlock(hdev);
+	hci_dev_put(hdev);
+
+	return err;
+}
+
 static int set_discoverable(struct sock *sk, u16 index, unsigned char *data,
 									u16 len)
 {
@@ -751,39 +869,6 @@
 	return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp);
 }
 
-static u8 get_service_classes(struct hci_dev *hdev)
-{
-	struct list_head *p;
-	u8 val = 0;
-
-	list_for_each(p, &hdev->uuids) {
-		struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list);
-
-		val |= uuid->svc_hint;
-	}
-
-	return val;
-}
-
-static int update_class(struct hci_dev *hdev)
-{
-	u8 cod[3];
-
-	BT_DBG("%s", hdev->name);
-
-	if (test_bit(HCI_SERVICE_CACHE, &hdev->flags))
-		return 0;
-
-	cod[0] = hdev->minor_class;
-	cod[1] = hdev->major_class;
-	cod[2] = get_service_classes(hdev);
-
-	if (memcmp(cod, hdev->dev_class, 3) == 0)
-		return 0;
-
-	return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod);
-}
-
 static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len)
 {
 	struct mgmt_cp_add_uuid *cp;
@@ -912,7 +997,8 @@
 
 	hci_dev_lock(hdev);
 
-	hdev->major_class = cp->major;
+	hdev->major_class &= ~MGMT_MAJOR_CLASS_MASK;
+	hdev->major_class |= cp->major & MGMT_MAJOR_CLASS_MASK;
 	hdev->minor_class = cp->minor;
 
 	err = update_class(hdev);
@@ -2111,6 +2197,10 @@
 	case MGMT_OP_SET_DISCOVERABLE:
 		err = set_discoverable(sk, index, buf + sizeof(*hdr), len);
 		break;
+	case MGMT_OP_SET_LIMIT_DISCOVERABLE:
+		err = set_limited_discoverable(sk, index, buf + sizeof(*hdr),
+									len);
+		break;
 	case MGMT_OP_SET_CONNECTABLE:
 		err = set_connectable(sk, index, buf + sizeof(*hdr), len);
 		break;