Merge android-msm-wahoo-4.4-qt-qpr3 into android-msm-wahoo-4.4

SBMerger: 284775313
Change-Id: Id15c79bd513a5fcb898d857b32d8782977b3388a
Signed-off-by: SecurityBot <android-nexus-securitybot@system.gserviceaccount.com>
diff --git a/Makefile b/Makefile
index 49a773f..e6ba668 100644
--- a/Makefile
+++ b/Makefile
@@ -1082,11 +1082,15 @@
 
 uts_len := 64
 define filechk_utsrelease.h
-	if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then \
-	  echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2;    \
-	  exit 1;                                                         \
-	fi;                                                               \
-	(echo \#define UTS_RELEASE \"$(KERNELRELEASE)\";)
+	if [ `echo -n "$(KERNELRELEASE)" | wc -c ` -gt $(uts_len) ]; then   \
+	  echo '"$(KERNELRELEASE)" exceeds $(uts_len) characters' >&2;      \
+	  exit 1;                                                           \
+	fi;                                                                 \
+	if [ -n "$(BUILD_NUMBER)" ]; then                                   \
+	  (echo \#define UTS_RELEASE \"$(KERNELRELEASE)-ab$(BUILD_NUMBER)\";) \
+	else                                                                \
+	  (echo \#define UTS_RELEASE \"$(KERNELRELEASE)\";)                 \
+	fi
 endef
 
 define filechk_version.h
diff --git a/build.config.common b/build.config.common
index 632d09e..f694728 100644
--- a/build.config.common
+++ b/build.config.common
@@ -4,8 +4,8 @@
 CROSS_COMPILE_ARM32=arm-linux-androideabi-
 DEFCONFIG=wahoo_defconfig
 EXTRA_CMDS=''
-CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-r353983c/bin/
-LD_LIBRARY_PATH=${ROOT_DIR}/prebuilts-master/clang/host/linux-x86/clang-r353983c/lib64:$LD_LIBRARY_PATH
+CLANG_PREBUILT_BIN=prebuilts-master/clang/host/linux-x86/clang-r370808/bin/
+LD_LIBRARY_PATH=${ROOT_DIR}/prebuilts-master/clang/host/linux-x86/clang-r370808/lib64:$LD_LIBRARY_PATH
 export LD_LIBRARY_PATH
 LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin
 LINUX_GCC_CROSS_COMPILE_ARM32_PREBUILTS_BIN=prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin
diff --git a/build.config.debug_api b/build.config.debug_api
index 43f6377..d832b95 100644
--- a/build.config.debug_api
+++ b/build.config.debug_api
@@ -1,5 +1,5 @@
 KERNEL_DIR=private/msm-google
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common
+. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common.clang
 POST_DEFCONFIG_CMDS="check_defconfig && update_debug_config"
 
 function update_debug_config() {
@@ -15,5 +15,5 @@
          -e CONFIG_PANIC_ON_WARN_DEFAULT_ENABLE \
          -d CONFIG_KERNEL_LZ4
     (cd ${OUT_DIR} && \
-     make O=${OUT_DIR} $archsubarch CROSS_COMPILE=${CROSS_COMPILE} olddefconfig)
+     make ${CC_LD_ARG} O=${OUT_DIR} olddefconfig)
 }
diff --git a/build.config.debug_hang b/build.config.debug_hang
index 801613a..8585c65 100644
--- a/build.config.debug_hang
+++ b/build.config.debug_hang
@@ -1,5 +1,5 @@
 KERNEL_DIR=private/msm-google
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common
+. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common.clang
 POST_DEFCONFIG_CMDS="check_defconfig && update_debug_config"
 
 function update_debug_config() {
@@ -17,5 +17,5 @@
          -e CONFIG_PANIC_ON_WARN_DEFAULT_ENABLE \
          -d CONFIG_KERNEL_LZ4
     (cd ${OUT_DIR} && \
-     make O=${OUT_DIR} $archsubarch CROSS_COMPILE=${CROSS_COMPILE} olddefconfig)
+     make ${CC_LD_ARG} O=${OUT_DIR} olddefconfig)
 }
diff --git a/build.config.debug_locking b/build.config.debug_locking
index bae928a..e9dccea 100644
--- a/build.config.debug_locking
+++ b/build.config.debug_locking
@@ -1,5 +1,5 @@
 KERNEL_DIR=private/msm-google
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common
+. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common.clang
 POST_DEFCONFIG_CMDS="check_defconfig && update_debug_config"
 
 function update_debug_config() {
@@ -13,5 +13,5 @@
          -e CONFIG_PANIC_ON_WARN_DEFAULT_ENABLE \
          -d CONFIG_KERNEL_LZ4
     (cd ${OUT_DIR} && \
-     make O=${OUT_DIR} $archsubarch CROSS_COMPILE=${CROSS_COMPILE} olddefconfig)
+     make ${CC_LD_ARG} O=${OUT_DIR} olddefconfig)
 }
diff --git a/build.config.debug_memory b/build.config.debug_memory
index 774e79d..49fc594 100644
--- a/build.config.debug_memory
+++ b/build.config.debug_memory
@@ -1,5 +1,5 @@
 KERNEL_DIR=private/msm-google
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common
+. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common.clang
 POST_DEFCONFIG_CMDS="check_defconfig && update_debug_config"
 
 function update_debug_config() {
@@ -37,5 +37,5 @@
          -e CONFIG_PANIC_ON_WARN_DEFAULT_ENABLE \
          -d CONFIG_KERNEL_LZ4
     (cd ${OUT_DIR} && \
-     make O=${OUT_DIR} $archsubarch CROSS_COMPILE=${CROSS_COMPILE} olddefconfig)
+     make ${CC_LD_ARG} O=${OUT_DIR} olddefconfig)
 }
diff --git a/build.config.kasan b/build.config.kasan
index 6505347..df6ead1 100644
--- a/build.config.kasan
+++ b/build.config.kasan
@@ -1,11 +1,11 @@
 KERNEL_DIR=private/msm-google
-. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common
+. ${ROOT_DIR}/${KERNEL_DIR}/build.config.common.clang
 POST_DEFCONFIG_CMDS="check_defconfig && update_kasan_config"
 
 function update_kasan_config() {
     ${KERNEL_DIR}/scripts/config --file ${OUT_DIR}/.config \
          -e CONFIG_KASAN \
-         -e CONFIG_KASAN_INLINE \
+         -e CONFIG_KASAN_OUTLINE \
          -e CONFIG_TEST_KASAN \
          -e CONFIG_KCOV \
          -e CONFIG_SLUB \
@@ -14,7 +14,9 @@
          -d CONFIG_SLUB_DEBUG_PANIC_ON \
          -d CONFIG_KASAN_OUTLINE \
          -d CONFIG_KERNEL_LZ4 \
-	 -d CONFIG_RANDOMIZE_BASE
+         -d CONFIG_RANDOMIZE_BASE \
+         -d CONFIG_CC_WERROR \
+         --set-val CONFIG_FRAME_WARN 0
     (cd ${OUT_DIR} && \
-     make O=${OUT_DIR} $archsubarch CROSS_COMPILE=${CROSS_COMPILE} olddefconfig)
+     make ${CC_LD_ARG} O=${OUT_DIR} olddefconfig)
 }
diff --git a/drivers/char/diag/diag_memorydevice.c b/drivers/char/diag/diag_memorydevice.c
index 2d7bafb..2f68589 100644
--- a/drivers/char/diag/diag_memorydevice.c
+++ b/drivers/char/diag/diag_memorydevice.c
@@ -213,7 +213,10 @@
 			continue;
 
 		found = 1;
-		driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
+		if (!(driver->data_ready[i] & USER_SPACE_DATA_TYPE)) {
+			driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
+			atomic_inc(&driver->data_ready_notif[i]);
+		}
 		pr_debug("diag: wake up logging process\n");
 		wake_up_interruptible(&driver->wait_q);
 	}
diff --git a/drivers/char/diag/diagchar.h b/drivers/char/diag/diagchar.h
index d47b925..65db9f5 100644
--- a/drivers/char/diag/diagchar.h
+++ b/drivers/char/diag/diagchar.h
@@ -26,6 +26,8 @@
 #include <asm/atomic.h>
 #include "diagfwd_bridge.h"
 
+#define THRESHOLD_CLIENT_LIMIT	50
+
 /* Size of the USB buffers used for read and write*/
 #define USB_MAX_OUT_BUF 4096
 #define APPS_BUF_SIZE	4096
@@ -498,6 +500,7 @@
 	wait_queue_head_t wait_q;
 	struct diag_client_map *client_map;
 	int *data_ready;
+	atomic_t data_ready_notif[THRESHOLD_CLIENT_LIMIT];
 	int num_clients;
 	int polling_reg_flag;
 	int use_device_tree;
diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c
index 512b4f9..8c97324 100644
--- a/drivers/char/diag/diagchar_core.c
+++ b/drivers/char/diag/diagchar_core.c
@@ -138,7 +138,6 @@
 
 /* This is the max number of user-space clients supported at initialization*/
 static unsigned int max_clients = 15;
-static unsigned int threshold_client_limit = 50;
 module_param(max_clients, uint, 0);
 
 /* Timer variables */
@@ -345,7 +344,7 @@
 		if (i < driver->num_clients) {
 			diag_add_client(i, file);
 		} else {
-			if (i < threshold_client_limit) {
+			if (i < THRESHOLD_CLIENT_LIMIT) {
 				driver->num_clients++;
 				temp = krealloc(driver->client_map
 					, (driver->num_clients) * sizeof(struct
@@ -375,11 +374,17 @@
 			}
 		}
 		driver->data_ready[i] = 0x0;
+		atomic_set(&driver->data_ready_notif[i], 0);
 		driver->data_ready[i] |= MSG_MASKS_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
 		driver->data_ready[i] |= EVENT_MASKS_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
 		driver->data_ready[i] |= LOG_MASKS_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
 		driver->data_ready[i] |= DCI_LOG_MASKS_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
 		driver->data_ready[i] |= DCI_EVENT_MASKS_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
 
 		if (driver->ref_count == 0)
 			diag_mempool_init();
@@ -1801,8 +1806,10 @@
 		mutex_unlock(&driver->diagchar_mutex);
 		return -EINVAL;
 	}
-
-	driver->data_ready[i] |= DEINIT_TYPE;
+	if (!(driver->data_ready[i] & DEINIT_TYPE)) {
+		driver->data_ready[i] |= DEINIT_TYPE;
+		atomic_inc(&driver->data_ready_notif[i]);
+	}
 	mutex_unlock(&driver->diagchar_mutex);
 	wake_up_interruptible(&driver->wait_q);
 
@@ -2905,16 +2912,6 @@
 	return 0;
 }
 
-static int check_data_ready(int index)
-{
-	int data_type = 0;
-
-	mutex_lock(&driver->diagchar_mutex);
-	data_type = driver->data_ready[index];
-	mutex_unlock(&driver->diagchar_mutex);
-	return data_type;
-}
-
 static ssize_t diagchar_read(struct file *file, char __user *buf, size_t count,
 			  loff_t *ppos)
 {
@@ -2943,7 +2940,8 @@
 		pr_err("diag: bad address from user side\n");
 		return -EFAULT;
 	}
-	wait_event_interruptible(driver->wait_q, (check_data_ready(index)) > 0);
+	wait_event_interruptible(driver->wait_q,
+			atomic_read(&driver->data_ready_notif[index]) > 0);
 
 	mutex_lock(&driver->diagchar_mutex);
 
@@ -2954,6 +2952,7 @@
 		/*Copy the type of data being passed*/
 		data_type = driver->data_ready[index] & USER_SPACE_DATA_TYPE;
 		driver->data_ready[index] ^= USER_SPACE_DATA_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int));
 		/* place holder for number of data field */
 		ret += sizeof(int);
@@ -2967,11 +2966,13 @@
 		/* In case, the thread wakes up and the logging mode is
 		not memory device any more, the condition needs to be cleared */
 		driver->data_ready[index] ^= USER_SPACE_DATA_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 	}
 
 	if (driver->data_ready[index] & HDLC_SUPPORT_TYPE) {
 		data_type = driver->data_ready[index] & HDLC_SUPPORT_TYPE;
 		driver->data_ready[index] ^= HDLC_SUPPORT_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		COPY_USER_SPACE_OR_EXIT(buf, data_type, sizeof(int));
 		mutex_lock(&driver->md_session_lock);
 		session_info = diag_md_session_get_pid(current->tgid);
@@ -2993,6 +2994,7 @@
 		data_type = driver->data_ready[index] & DEINIT_TYPE;
 		COPY_USER_SPACE_OR_EXIT(buf, data_type, 4);
 		driver->data_ready[index] ^= DEINIT_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		mutex_unlock(&driver->diagchar_mutex);
 		diag_remove_client_entry(file);
 		return ret;
@@ -3014,6 +3016,7 @@
 		if (write_len > 0)
 			ret += write_len;
 		driver->data_ready[index] ^= MSG_MASKS_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		goto exit;
 	}
 
@@ -3047,6 +3050,7 @@
 		}
 		mutex_unlock(&driver->md_session_lock);
 		driver->data_ready[index] ^= EVENT_MASKS_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		goto exit;
 	}
 
@@ -3066,6 +3070,7 @@
 		if (write_len > 0)
 			ret += write_len;
 		driver->data_ready[index] ^= LOG_MASKS_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		goto exit;
 	}
 
@@ -3077,6 +3082,7 @@
 					*(driver->apps_req_buf),
 					driver->apps_req_buf_len);
 		driver->data_ready[index] ^= PKT_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		driver->in_busy_pktdata = 0;
 		goto exit;
 	}
@@ -3088,6 +3094,7 @@
 		COPY_USER_SPACE_OR_EXIT(buf+4, *(driver->dci_pkt_buf),
 					driver->dci_pkt_length);
 		driver->data_ready[index] ^= DCI_PKT_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		driver->in_busy_dcipktdata = 0;
 		goto exit;
 	}
@@ -3100,6 +3107,7 @@
 		COPY_USER_SPACE_OR_EXIT(buf + 8, (dci_ops_tbl[DCI_LOCAL_PROC].
 				event_mask_composite), DCI_EVENT_MASK_SIZE);
 		driver->data_ready[index] ^= DCI_EVENT_MASKS_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		goto exit;
 	}
 
@@ -3111,6 +3119,7 @@
 		COPY_USER_SPACE_OR_EXIT(buf+8, (dci_ops_tbl[DCI_LOCAL_PROC].
 				log_mask_composite), DCI_LOG_MASK_SIZE);
 		driver->data_ready[index] ^= DCI_LOG_MASKS_TYPE;
+		atomic_dec(&driver->data_ready_notif[index]);
 		goto exit;
 	}
 
@@ -3165,6 +3174,7 @@
 			exit_stat = diag_copy_dci(buf, count, entry, &ret);
 			mutex_lock(&driver->diagchar_mutex);
 			driver->data_ready[index] ^= DCI_DATA_TYPE;
+			atomic_dec(&driver->data_ready_notif[index]);
 			mutex_unlock(&driver->diagchar_mutex);
 			if (exit_stat == 1) {
 				put_task_struct(task_s);
diff --git a/drivers/char/diag/diagfwd.c b/drivers/char/diag/diagfwd.c
index c745024..ae450f3 100644
--- a/drivers/char/diag/diagfwd.c
+++ b/drivers/char/diag/diagfwd.c
@@ -225,6 +225,7 @@
 			 * situation.
 			 */
 			driver->data_ready[i] |= USER_SPACE_DATA_TYPE;
+			atomic_inc(&driver->data_ready_notif[i]);
 			pr_debug("diag: Force wakeup of logging process\n");
 			wake_up_interruptible(&driver->wait_q);
 			break;
@@ -499,8 +500,11 @@
 
 	mutex_lock(&driver->diagchar_mutex);
 	for (i = 0; i < driver->num_clients; i++)
-		if (driver->client_map[i].pid != 0)
+		if (driver->client_map[i].pid != 0 &&
+			!(driver->data_ready[i] & type)) {
 			driver->data_ready[i] |= type;
+			atomic_inc(&driver->data_ready_notif[i]);
+		}
 	wake_up_interruptible(&driver->wait_q);
 	mutex_unlock(&driver->diagchar_mutex);
 }
@@ -517,7 +521,11 @@
 				if (driver->client_map[j].pid != 0 &&
 					driver->client_map[j].pid ==
 					driver->md_session_map[i]->pid) {
-					driver->data_ready[j] |= type;
+					if (!(driver->data_ready[i] & type)) {
+						driver->data_ready[j] |= type;
+						atomic_inc(
+						&driver->data_ready_notif[j]);
+					}
 					break;
 				}
 			}
@@ -533,7 +541,10 @@
 	mutex_lock(&driver->diagchar_mutex);
 	for (i = 0; i < driver->num_clients; i++)
 		if (driver->client_map[i].pid == process_id) {
-			driver->data_ready[i] |= data_type;
+			if (!(driver->data_ready[i] & data_type)) {
+				driver->data_ready[i] |= data_type;
+				atomic_inc(&driver->data_ready_notif[i]);
+			}
 			break;
 		}
 	wake_up_interruptible(&driver->wait_q);
@@ -1727,6 +1738,10 @@
 							, GFP_KERNEL)) == NULL)
 		goto err;
 	kmemleak_not_leak(driver->data_ready);
+
+	for (i = 0; i < THRESHOLD_CLIENT_LIMIT; i++)
+		atomic_set(&driver->data_ready_notif[i], 0);
+
 	if (driver->apps_req_buf == NULL) {
 		driver->apps_req_buf = kzalloc(DIAG_MAX_REQ_SIZE, GFP_KERNEL);
 		if (!driver->apps_req_buf)
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index ee3c66c..0e6e8fa 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -303,6 +303,7 @@
 
 #define HID_BATTERY_QUIRK_PERCENT	(1 << 0) /* always reports percent */
 #define HID_BATTERY_QUIRK_FEATURE	(1 << 1) /* ask for feature report */
+#define HID_BATTERY_QUIRK_IGNORE	(1 << 2) /* completely ignore the battery */
 
 static const struct hid_device_id hid_battery_quirks[] = {
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
@@ -320,6 +321,9 @@
 	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
 		USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI),
 	  HID_BATTERY_QUIRK_PERCENT | HID_BATTERY_QUIRK_FEATURE },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM,
+		USB_DEVICE_ID_ELECOM_BM084),
+	  HID_BATTERY_QUIRK_IGNORE },
 	{}
 };
 
@@ -335,13 +339,45 @@
 	return quirks;
 }
 
+static int hidinput_scale_battery_capacity(struct hid_device *dev,
+					   int value)
+{
+	if (dev->battery_min < dev->battery_max &&
+	    value >= dev->battery_min && value <= dev->battery_max)
+		value = ((value - dev->battery_min) * 100) /
+			(dev->battery_max - dev->battery_min);
+
+	return value;
+}
+
+static int hidinput_query_battery_capacity(struct hid_device *dev)
+{
+	u8 *buf;
+	int ret;
+
+	buf = kmalloc(2, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2,
+				 dev->battery_report_type, HID_REQ_GET_REPORT);
+	if (ret != 2) {
+		kfree(buf);
+		return -ENODATA;
+	}
+
+	ret = hidinput_scale_battery_capacity(dev, buf[1]);
+	kfree(buf);
+	return ret;
+}
+
 static int hidinput_get_battery_property(struct power_supply *psy,
 					 enum power_supply_property prop,
 					 union power_supply_propval *val)
 {
 	struct hid_device *dev = power_supply_get_drvdata(psy);
+	int value;
 	int ret = 0;
-	__u8 *buf;
 
 	switch (prop) {
 	case POWER_SUPPLY_PROP_PRESENT:
@@ -350,29 +386,15 @@
 		break;
 
 	case POWER_SUPPLY_PROP_CAPACITY:
-
-		buf = kmalloc(2 * sizeof(__u8), GFP_KERNEL);
-		if (!buf) {
-			ret = -ENOMEM;
-			break;
+		if (dev->battery_report_type == HID_FEATURE_REPORT) {
+			value = hidinput_query_battery_capacity(dev);
+			if (value < 0)
+				return value;
+		} else  {
+			value = dev->battery_capacity;
 		}
-		ret = hid_hw_raw_request(dev, dev->battery_report_id, buf, 2,
-					 dev->battery_report_type,
-					 HID_REQ_GET_REPORT);
 
-		if (ret != 2) {
-			ret = -ENODATA;
-			kfree(buf);
-			break;
-		}
-		ret = 0;
-
-		if (dev->battery_min < dev->battery_max &&
-		    buf[1] >= dev->battery_min &&
-		    buf[1] <= dev->battery_max)
-			val->intval = (100 * (buf[1] - dev->battery_min)) /
-				(dev->battery_max - dev->battery_min);
-		kfree(buf);
+		val->intval = value;
 		break;
 
 	case POWER_SUPPLY_PROP_MODEL_NAME:
@@ -380,7 +402,22 @@
 		break;
 
 	case POWER_SUPPLY_PROP_STATUS:
-		val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+		if (!dev->battery_reported &&
+		    dev->battery_report_type == HID_FEATURE_REPORT) {
+			value = hidinput_query_battery_capacity(dev);
+			if (value < 0)
+				return value;
+
+			dev->battery_capacity = value;
+			dev->battery_reported = true;
+		}
+
+		if (!dev->battery_reported)
+			val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
+		else if (dev->battery_capacity == 100)
+			val->intval = POWER_SUPPLY_STATUS_FULL;
+		else
+			val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
 		break;
 
 	case POWER_SUPPLY_PROP_SCOPE:
@@ -395,27 +432,33 @@
 	return ret;
 }
 
-static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field)
+static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type, struct hid_field *field)
 {
-	struct power_supply_desc *psy_desc = NULL;
+	struct power_supply_desc *psy_desc;
 	struct power_supply_config psy_cfg = { .drv_data = dev, };
 	unsigned quirks;
 	s32 min, max;
+	int error;
 
-	if (field->usage->hid != HID_DC_BATTERYSTRENGTH)
-		return false;	/* no match */
+	if (dev->battery)
+		return 0;	/* already initialized? */
 
-	if (dev->battery != NULL)
-		goto out;	/* already initialized? */
+	quirks = find_battery_quirk(dev);
+
+	hid_dbg(dev, "device %x:%x:%x %d quirks %d\n",
+		dev->bus, dev->vendor, dev->product, dev->version, quirks);
+
+	if (quirks & HID_BATTERY_QUIRK_IGNORE)
+		return 0;
 
 	psy_desc = kzalloc(sizeof(*psy_desc), GFP_KERNEL);
-	if (psy_desc == NULL)
-		goto out;
+	if (!psy_desc)
+		return -ENOMEM;
 
 	psy_desc->name = kasprintf(GFP_KERNEL, "hid-%s-battery", dev->uniq);
-	if (psy_desc->name == NULL) {
-		kfree(psy_desc);
-		goto out;
+	if (!psy_desc->name) {
+		error = -ENOMEM;
+		goto err_free_mem;
 	}
 
 	psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
@@ -424,11 +467,6 @@
 	psy_desc->use_for_apm = 0;
 	psy_desc->get_property = hidinput_get_battery_property;
 
-	quirks = find_battery_quirk(dev);
-
-	hid_dbg(dev, "device %x:%x:%x %d quirks %d\n",
-		dev->bus, dev->vendor, dev->product, dev->version, quirks);
-
 	min = field->logical_minimum;
 	max = field->logical_maximum;
 
@@ -447,17 +485,20 @@
 
 	dev->battery = power_supply_register(&dev->dev, psy_desc, &psy_cfg);
 	if (IS_ERR(dev->battery)) {
-		hid_warn(dev, "can't register power supply: %ld\n",
-				PTR_ERR(dev->battery));
-		kfree(psy_desc->name);
-		kfree(psy_desc);
-		dev->battery = NULL;
-	} else {
-		power_supply_powers(dev->battery, &dev->dev);
+		error = PTR_ERR(dev->battery);
+		hid_warn(dev, "can't register power supply: %d\n", error);
+		goto err_free_name;
 	}
 
-out:
-	return true;
+	power_supply_powers(dev->battery, &dev->dev);
+	return 0;
+
+err_free_name:
+	kfree(psy_desc->name);
+err_free_mem:
+	kfree(psy_desc);
+	dev->battery = NULL;
+	return error;
 }
 
 static void hidinput_cleanup_battery(struct hid_device *dev)
@@ -473,16 +514,33 @@
 	kfree(psy_desc);
 	dev->battery = NULL;
 }
-#else  /* !CONFIG_HID_BATTERY_STRENGTH */
-static bool hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
-				   struct hid_field *field)
+
+static void hidinput_update_battery(struct hid_device *dev, int value)
 {
-	return false;
+	if (!dev->battery)
+		return;
+
+	if (value == 0 || value < dev->battery_min || value > dev->battery_max)
+		return;
+
+	dev->battery_capacity = hidinput_scale_battery_capacity(dev, value);
+	dev->battery_reported = true;
+	power_supply_changed(dev->battery);
+}
+#else  /* !CONFIG_HID_BATTERY_STRENGTH */
+static int hidinput_setup_battery(struct hid_device *dev, unsigned report_type,
+				  struct hid_field *field)
+{
+	return 0;
 }
 
 static void hidinput_cleanup_battery(struct hid_device *dev)
 {
 }
+
+static void hidinput_update_battery(struct hid_device *dev, int value)
+{
+}
 #endif	/* CONFIG_HID_BATTERY_STRENGTH */
 
 static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_field *field,
@@ -684,6 +742,11 @@
 			}
 			break;
 
+		case 0x3b: /* Battery Strength */
+			hidinput_setup_battery(device, HID_INPUT_REPORT, field);
+			usage->type = EV_PWR;
+			goto ignore;
+
 		case 0x3c: /* Invert */
 			map_key_clear(BTN_TOOL_RUBBER);
 			break;
@@ -924,11 +987,13 @@
 		break;
 
 	case HID_UP_GENDEVCTRLS:
-		if (hidinput_setup_battery(device, HID_INPUT_REPORT, field))
+		switch (usage->hid) {
+		case HID_DC_BATTERYSTRENGTH:
+			hidinput_setup_battery(device, HID_INPUT_REPORT, field);
+			usage->type = EV_PWR;
 			goto ignore;
-		else
-			goto unknown;
-		break;
+		}
+		goto unknown;
 
 	case HID_UP_HPVENDOR:	/* Reported on a Dutch layout HP5308 */
 		set_bit(EV_REP, input->evbit);
@@ -1006,7 +1071,6 @@
 	if (usage->code > max)
 		goto ignore;
 
-
 	if (usage->type == EV_ABS) {
 
 		int a = field->logical_minimum;
@@ -1065,14 +1129,19 @@
 	struct input_dev *input;
 	unsigned *quirks = &hid->quirks;
 
+	if (!usage->type)
+		return;
+
+	if (usage->type == EV_PWR) {
+		hidinput_update_battery(hid, value);
+		return;
+	}
+
 	if (!field->hidinput)
 		return;
 
 	input = field->hidinput->input;
 
-	if (!usage->type)
-		return;
-
 	if (usage->hat_min < usage->hat_max || usage->hat_dir) {
 		int hat_dir = usage->hat_dir;
 		if (!hat_dir)
@@ -1349,6 +1418,7 @@
 	struct hid_driver *drv = hid->driver;
 	struct hid_report_enum *rep_enum;
 	struct hid_report *rep;
+	struct hid_usage *usage;
 	int i, j;
 
 	rep_enum = &hid->report_enum[HID_FEATURE_REPORT];
@@ -1359,12 +1429,15 @@
 				continue;
 
 			for (j = 0; j < rep->field[i]->maxusage; j++) {
+				usage = &rep->field[i]->usage[j];
+
 				/* Verify if Battery Strength feature is available */
-				hidinput_setup_battery(hid, HID_FEATURE_REPORT, rep->field[i]);
+				if (usage->hid == HID_DC_BATTERYSTRENGTH)
+					hidinput_setup_battery(hid, HID_FEATURE_REPORT,
+							       rep->field[i]);
 
 				if (drv->feature_mapping)
-					drv->feature_mapping(hid, rep->field[i],
-							     rep->field[i]->usage + j);
+					drv->feature_mapping(hid, rep->field[i], usage);
 			}
 		}
 }
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index bc6ad5f..fa1f56b 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -576,10 +576,14 @@
 static inline void sony_schedule_work(struct sony_sc *sc,
 				      enum sony_worker which)
 {
+	unsigned long flags;
+
 	switch (which) {
 	case SONY_WORKER_STATE:
-		if (!sc->defer_initialization)
+		spin_lock_irqsave(&sc->lock, flags);
+		if (!sc->defer_initialization && sc->state_worker_initialized)
 			schedule_work(&sc->state_worker);
+		spin_unlock_irqrestore(&sc->lock, flags);
 		break;
 	case SONY_WORKER_HOTPLUG:
 		if (sc->hotplug_worker_initialized)
@@ -2489,13 +2493,18 @@
 
 static inline void sony_cancel_work_sync(struct sony_sc *sc)
 {
+	unsigned long flags;
+
 	if (sc->hotplug_worker_initialized)
 		cancel_work_sync(&sc->hotplug_worker);
-	if (sc->state_worker_initialized)
+	if (sc->state_worker_initialized) {
+		spin_lock_irqsave(&sc->lock, flags);
+		sc->state_worker_initialized = 0;
+		spin_unlock_irqrestore(&sc->lock, flags);
 		cancel_work_sync(&sc->state_worker);
+	}
 }
 
-
 static int sony_input_configured(struct hid_device *hdev,
 					struct hid_input *hidinput)
 {
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index e9ae3d5..3fd2185 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -28,13 +28,6 @@
 #include <linux/cdev.h>
 #include "input-compat.h"
 
-enum evdev_clock_type {
-	EV_CLK_REAL = 0,
-	EV_CLK_MONO,
-	EV_CLK_BOOT,
-	EV_CLK_MAX
-};
-
 struct evdev {
 	int open;
 	struct input_handle handle;
@@ -56,7 +49,7 @@
 	struct fasync_struct *fasync;
 	struct evdev *evdev;
 	struct list_head node;
-	unsigned int clk_type;
+	enum input_clock_type clk_type;
 	bool revoked;
 	unsigned long *evmasks[EV_CNT];
 	unsigned int bufsize;
@@ -155,16 +148,12 @@
 
 static void __evdev_queue_syn_dropped(struct evdev_client *client)
 {
+	ktime_t *ev_time = input_get_timestamp(client->evdev->handle.dev);
+	struct timespec64 ts = ktime_to_timespec64(ev_time[client->clk_type]);
 	struct input_event ev;
-	ktime_t time;
 
-	time = client->clk_type == EV_CLK_REAL ?
-			ktime_get_real() :
-			client->clk_type == EV_CLK_MONO ?
-				ktime_get() :
-				ktime_get_boottime();
-
-	ev.time = ktime_to_timeval(time);
+	ev.time.tv_sec = ts.tv_sec;
+	ev.time.tv_usec = ts.tv_nsec / NSEC_PER_USEC;
 	ev.type = EV_SYN;
 	ev.code = SYN_DROPPED;
 	ev.value = 0;
@@ -191,18 +180,18 @@
 static int evdev_set_clk_type(struct evdev_client *client, unsigned int clkid)
 {
 	unsigned long flags;
-	unsigned int clk_type;
+	enum input_clock_type clk_type;
 
 	switch (clkid) {
 
 	case CLOCK_REALTIME:
-		clk_type = EV_CLK_REAL;
+		clk_type = INPUT_CLK_REAL;
 		break;
 	case CLOCK_MONOTONIC:
-		clk_type = EV_CLK_MONO;
+		clk_type = INPUT_CLK_MONO;
 		break;
 	case CLOCK_BOOTTIME:
-		clk_type = EV_CLK_BOOT;
+		clk_type = INPUT_CLK_BOOT;
 		break;
 	default:
 		return -EINVAL;
@@ -304,12 +293,7 @@
 {
 	struct evdev *evdev = handle->private;
 	struct evdev_client *client;
-	ktime_t ev_time[EV_CLK_MAX];
-
-	ev_time[EV_CLK_MONO] = ktime_get();
-	ev_time[EV_CLK_REAL] = ktime_mono_to_real(ev_time[EV_CLK_MONO]);
-	ev_time[EV_CLK_BOOT] = ktime_mono_to_any(ev_time[EV_CLK_MONO],
-						 TK_OFFS_BOOT);
+	ktime_t *ev_time = input_get_timestamp(handle->dev);
 
 	rcu_read_lock();
 
diff --git a/drivers/input/input.c b/drivers/input/input.c
index baaddd1..805a475 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -401,6 +401,13 @@
 		if (dev->num_vals >= 2)
 			input_pass_values(dev, dev->vals, dev->num_vals);
 		dev->num_vals = 0;
+		/*
+		 * Reset the timestamp on flush so we won't end up
+		 * with a stale one. Note we only need to reset the
+		 * monolithic one as we use its presence when deciding
+		 * whether to generate a synthetic timestamp.
+		 */
+		dev->timestamp[INPUT_CLK_MONO] = ktime_set(0, 0);
 	} else if (dev->num_vals >= dev->max_vals - 2) {
 		dev->vals[dev->num_vals++] = input_value_sync;
 		input_pass_values(dev, dev->vals, dev->num_vals);
@@ -1904,6 +1911,47 @@
 EXPORT_SYMBOL(input_free_device);
 
 /**
+ * input_set_timestamp - set timestamp for input events
+ * @dev: input device to set timestamp for
+ * @timestamp: the time at which the event has occurred
+ *   in CLOCK_MONOTONIC
+ *
+ * This function is intended to provide to the input system a more
+ * accurate time of when an event actually occurred. The driver should
+ * call this function as soon as a timestamp is acquired ensuring
+ * clock conversions in input_set_timestamp are done correctly.
+ *
+ * The system entering a suspend between timestamp acquisition and
+ * calling input_set_timestamp can result in inaccurate conversions.
+ *
+ */
+void input_set_timestamp(struct input_dev *dev, ktime_t timestamp)
+{
+	dev->timestamp[INPUT_CLK_MONO] = timestamp;
+	dev->timestamp[INPUT_CLK_REAL] = ktime_mono_to_real(timestamp);
+	dev->timestamp[INPUT_CLK_BOOT] = ktime_mono_to_any(
+		timestamp, TK_OFFS_BOOT);
+}
+EXPORT_SYMBOL(input_set_timestamp);
+
+/**
+ * input_get_timestamp - get timestamp for input events
+ * @dev: input device to get timestamp from
+ *
+ * A valid timestamp is a timestamp of non-zero value.
+ */
+ktime_t *input_get_timestamp(struct input_dev *dev)
+{
+	const ktime_t invalid_timestamp = ktime_set(0, 0);
+
+	if (!ktime_compare(dev->timestamp[INPUT_CLK_MONO], invalid_timestamp))
+		input_set_timestamp(dev, ktime_get());
+
+	return dev->timestamp;
+}
+EXPORT_SYMBOL(input_get_timestamp);
+
+/**
  * input_set_capability - mark device as capable of a certain event
  * @dev: device that is capable of emitting or accepting event
  * @type: type of the event (EV_KEY, EV_REL, etc...)
diff --git a/drivers/input/touchscreen/stm/ftm4_ts.c b/drivers/input/touchscreen/stm/ftm4_ts.c
index fa2b59a..b2786f6 100644
--- a/drivers/input/touchscreen/stm/ftm4_ts.c
+++ b/drivers/input/touchscreen/stm/ftm4_ts.c
@@ -1314,6 +1314,21 @@
 #endif
 
 /**
+ * fts_hard_interrupt_handler()
+ * Called by the kernel when the touch interrupt occurs.
+ * This represents the top half of the interrupt.
+ *
+ * Set the input event timestamp here to ensure that we have an accurate
+ * estimate of when the touch event actually occurred.
+ */
+static irqreturn_t fts_hard_interrupt_handler(int irq, void *handle)
+{
+	struct fts_ts_info *info = handle;
+	input_set_timestamp(info->input_dev, ktime_get());
+	return IRQ_WAKE_THREAD;
+}
+
+/**
  * fts_interrupt_handler()
  *
  * Called by the kernel when an interrupt occurs (when the sensor
@@ -1932,7 +1947,7 @@
 		goto err_enable_irq;
 	}
 
-	retval = request_threaded_irq(info->irq, NULL,
+	retval = request_threaded_irq(info->irq, fts_hard_interrupt_handler,
 			fts_interrupt_handler, info->board->irq_type,
 			FTS_TS_DRV_NAME, info);
 	if (retval < 0) {
diff --git a/drivers/input/touchscreen/synaptics_dsx_htc/synaptics_dsx_core_htc.c b/drivers/input/touchscreen/synaptics_dsx_htc/synaptics_dsx_core_htc.c
index c070590..494aa5f 100644
--- a/drivers/input/touchscreen/synaptics_dsx_htc/synaptics_dsx_core_htc.c
+++ b/drivers/input/touchscreen/synaptics_dsx_htc/synaptics_dsx_core_htc.c
@@ -2488,6 +2488,20 @@
 	return;
 }
 
+/**
+ * Called by the kernel when the touch interrupt occurs.
+ * This represents the top half of the interrupt.
+ *
+ * Set the input event timestamp here to ensure that we have an accurate
+ * estimate of when the touch event actually occurred.
+ */
+static irqreturn_t synaptics_rmi4_hardirq(int irq, void *data)
+{
+	struct synaptics_rmi4_data *rmi4_data = data;
+	input_set_timestamp(rmi4_data->input_dev, ktime_get());
+	return IRQ_WAKE_THREAD;
+}
+
 static irqreturn_t synaptics_rmi4_irq(int irq, void *data)
 {
 	struct synaptics_rmi4_data *rmi4_data = data;
@@ -2588,7 +2602,8 @@
 #if IS_ENABLED(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_HTC)
 		enable_irq(rmi4_data->irq);
 #else
-		retval = request_threaded_irq(rmi4_data->irq, NULL,
+		retval = request_threaded_irq(rmi4_data->irq,
+				synaptics_rmi4_hardirq,
 				synaptics_rmi4_irq, bdata->irq_flags,
 				PLATFORM_DRIVER_NAME, rmi4_data);
 		if (retval < 0) {
@@ -5683,7 +5698,7 @@
 					"tp_direct_interrupt");
 
 #if IS_ENABLED(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX_CORE_HTC)
-	retval = request_threaded_irq(rmi4_data->irq, NULL,
+	retval = request_threaded_irq(rmi4_data->irq, synaptics_rmi4_hardirq,
 			synaptics_rmi4_irq,
 			IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
 			PLATFORM_DRIVER_NAME,
diff --git a/drivers/media/platform/msm/camera_v2/camera/camera.c b/drivers/media/platform/msm/camera_v2/camera/camera.c
index c620e44..f97e3ed 100644
--- a/drivers/media/platform/msm/camera_v2/camera/camera.c
+++ b/drivers/media/platform/msm/camera_v2/camera/camera.c
@@ -636,6 +636,7 @@
 	unsigned long opn_idx, idx;
 	BUG_ON(!pvdev);
 
+	mutex_lock(&pvdev->video_drvdata_mutex);
 	rc = camera_v4l2_fh_open(filep);
 	if (rc < 0) {
 		pr_err("%s : camera_v4l2_fh_open failed Line %d rc %d\n",
@@ -706,6 +707,7 @@
 	idx |= (1 << find_first_zero_bit((const unsigned long *)&opn_idx,
 				MSM_CAMERA_STREAM_CNT_BITS));
 	atomic_cmpxchg(&pvdev->opened, opn_idx, idx);
+	mutex_unlock(&pvdev->video_drvdata_mutex);
 
 	return rc;
 
@@ -720,6 +722,7 @@
 vb2_q_fail:
 	camera_v4l2_fh_release(filep);
 fh_open_fail:
+	mutex_unlock(&pvdev->video_drvdata_mutex);
 	return rc;
 }
 
@@ -750,6 +753,7 @@
 	if (WARN_ON(!session))
 		return -EIO;
 
+	mutex_lock(&pvdev->video_drvdata_mutex);
 	mutex_lock(&session->close_lock);
 	opn_idx = atomic_read(&pvdev->opened);
 	mask = (1 << sp->stream_id);
@@ -791,6 +795,7 @@
 	}
 
 	camera_v4l2_fh_release(filep);
+	mutex_unlock(&pvdev->video_drvdata_mutex);
 
 	return 0;
 }
@@ -937,6 +942,7 @@
 
 	*session = pvdev->vdev->num;
 	atomic_set(&pvdev->opened, 0);
+	mutex_init(&pvdev->video_drvdata_mutex);
 	video_set_drvdata(pvdev->vdev, pvdev);
 	device_init_wakeup(&pvdev->vdev->dev, 1);
 	goto init_end;
diff --git a/drivers/media/platform/msm/camera_v2/fd/Makefile b/drivers/media/platform/msm/camera_v2/fd/Makefile
index 8d01d3a..a1d33da 100644
--- a/drivers/media/platform/msm/camera_v2/fd/Makefile
+++ b/drivers/media/platform/msm/camera_v2/fd/Makefile
@@ -1,4 +1,3 @@
-GCC_VERSION      := $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
 ccflags-y += -Idrivers/media/video/msm
 ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
 ccflags-y += -Idrivers/media/platform/msm/camera_v2
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_10/Makefile b/drivers/media/platform/msm/camera_v2/jpeg_10/Makefile
index 0b8dc1d..72808f9 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_10/Makefile
+++ b/drivers/media/platform/msm/camera_v2/jpeg_10/Makefile
@@ -1,5 +1,3 @@
-GCC_VERSION      := $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
-
 ccflags-y += -Idrivers/media/platform/msm/camera_v2/jpeg_10
 ccflags-y += -Idrivers/media/platform/msm/camera_v2/sensor/io
 ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
diff --git a/drivers/media/platform/msm/camera_v2/jpeg_dma/Makefile b/drivers/media/platform/msm/camera_v2/jpeg_dma/Makefile
index 21cbadb..239b664 100644
--- a/drivers/media/platform/msm/camera_v2/jpeg_dma/Makefile
+++ b/drivers/media/platform/msm/camera_v2/jpeg_dma/Makefile
@@ -1,4 +1,3 @@
-GCC_VERSION      := $(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-version.sh $(CROSS_COMPILE)gcc)
 ccflags-y += -Idrivers/media/video/msm
 ccflags-y += -Idrivers/media/platform/msm/camera_v2/common
 obj-$(CONFIG_MSM_JPEGDMA) += msm_jpeg_dma_dev.o msm_jpeg_dma_hw.o
diff --git a/drivers/media/platform/msm/camera_v2/msm.h b/drivers/media/platform/msm/camera_v2/msm.h
index dce47bc..8bdb14f5 100644
--- a/drivers/media/platform/msm/camera_v2/msm.h
+++ b/drivers/media/platform/msm/camera_v2/msm.h
@@ -46,6 +46,7 @@
 struct msm_video_device {
 	struct video_device *vdev;
 	atomic_t opened;
+	struct mutex video_drvdata_mutex;
 };
 
 struct msm_queue_head {
diff --git a/drivers/misc/mnh/mnh-clk.c b/drivers/misc/mnh/mnh-clk.c
index 4bb51cf..9f82b14 100644
--- a/drivers/misc/mnh/mnh-clk.c
+++ b/drivers/misc/mnh/mnh-clk.c
@@ -1,7 +1,7 @@
 /*
  *
  * MNH Clock Driver
- * Copyright (c) 2016-2017, Intel Corporation.
+ * Copyright (c) 2016-2018, Intel Corporation.
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -24,6 +24,8 @@
 #include "mnh-hwio.h"
 #include "mnh-hwio-bases.h"
 #include "mnh-hwio-scu.h"
+#include "mnh-hwio-cpu.h"
+#include "mnh-hwio-ddr-ctl.h"
 #include "mnh-clk.h"
 #include "mnh-ddr.h"
 
@@ -38,6 +40,17 @@
 #define SCU_OUTf(...) \
 	HW_OUTf(HWIO_SCU_BASE_ADDR, SCU, __VA_ARGS__)
 
+#define MNH_CPU_IN(reg) \
+	HW_IN(HWIO_CPU_BASE_ADDR, CPU, reg)
+#define MNH_DDR_CTL_IN(reg) \
+	HW_IN(HWIO_DDR_CTL_BASE_ADDR, DDR_CTL, reg)
+#define MNH_DDR_CTL_INf(reg, fld) \
+	HW_INf(HWIO_DDR_CTL_BASE_ADDR, DDR_CTL, reg, fld)
+#define MNH_DDR_CTL_OUTf(reg, fld, val) \
+	HW_OUTf(HWIO_DDR_CTL_BASE_ADDR, DDR_CTL, reg, fld, val)
+#define MNH_DDR_CTL_OUT(reg, val) \
+	HW_OUT(HWIO_DDR_CTL_BASE_ADDR, DDR_CTL, reg, val)
+
 #define PLL_UNLOCK 0x4CD9
 #define LP4_LPC_FREQ_SWITCH 0x8A
 #define REF_CLK_KHZ 19200
@@ -531,74 +544,7 @@
  */
 int mnh_lpddr_freq_change(int index)
 {
-	int status = 0;
-	int timeout = 0;
-	enum mnh_lpddr_freq_type ddr_freq;
-
-	if (!mnh_clk)
-		return -ENODEV;
-
-	dev_dbg(mnh_clk->dev, "%s: %d\n", __func__, index);
-
-	if (index < LPDDR_FREQ_MIN || index > LPDDR_FREQ_MAX)
-		return -EINVAL;
-
-	/* Check the requested FSP is already in use */
-	ddr_freq = SCU_INf(LPDDR4_LOW_POWER_STS, LPDDR4_CUR_FSP);
-	if (ddr_freq == index) {
-		dev_dbg(mnh_clk->dev, "%s: requested fsp%d is in use\n",
-			__func__, index);
-		return 0;
-	}
-
-	/* Power up LPDDR PLL if the FSP setting uses it */
-	if (!SCU_INxf(LPDDR4_FSP_SETTING, index, FSP_SYS200_MODE))
-		mnh_lpddr_sys200_mode(false);
-
-	/* Disable LPC SW override */
-	SCU_OUTf(LPDDR4_LOW_POWER_CFG, LP4_FSP_SW_OVERRIDE, 0);
-
-	/* Configure FSP index */
-	SCU_OUTf(LPDDR4_LOW_POWER_CFG, LPC_FREQ_CHG_COPY_NUM, index);
-
-	/* Configure LPC cmd for frequency switch */
-	SCU_OUTf(LPDDR4_LOW_POWER_CFG, LPC_EXT_CMD, LP4_LPC_FREQ_SWITCH);
-
-	/* Initiate LPC cmd to LPDDR controller */
-	dev_dbg(mnh_clk->dev, "%s: lpddr freq switching from fsp%d to fsp%d\n",
-		__func__, ddr_freq, index);
-	SCU_OUTf(LPDDR4_LOW_POWER_CFG, LPC_EXT_CMD_REQ, 1);
-
-	/* Wait until LPC cmd process is done */
-	do {
-		status = SCU_INf(LPDDR4_LOW_POWER_STS, LPC_CMD_DONE);
-	} while ((++timeout < PLL_LOCK_TIMEOUT) && (status != 1));
-
-	/* Clear LPC cmd status */
-	SCU_OUTf(LPDDR4_LOW_POWER_STS, LPC_CMD_DONE, 1);
-
-	/* Check LPC error status */
-	if (SCU_INf(LPDDR4_LOW_POWER_STS, LPC_CMD_RSP) == LPC_CMD_ERR) {
-		/* Clear error status */
-		SCU_OUTf(LPDDR4_LOW_POWER_STS, LPC_CMD_RSP, 1);
-		dev_err(mnh_clk->dev, "Failed to process lpc cmd:0x%x\n",
-			LP4_LPC_FREQ_SWITCH);
-		return -EIO;
-	}
-
-	/* Check FSPx switch status */
-	if (SCU_INf(LPDDR4_LOW_POWER_STS, LPDDR4_CUR_FSP) != index) {
-		dev_err(mnh_clk->dev, "Failed to switch to fsp%d\n", index);
-		return -EIO;
-	}
-
-	/* Power down LPDDR PLL if the FSP setting doesn't use it */
-	if (SCU_INxf(LPDDR4_FSP_SETTING, index, FSP_SYS200_MODE))
-		mnh_lpddr_sys200_mode(true);
-
-	mnh_ddr_clr_int_status(mnh_clk->dev);
-
-	return 0;
+	return mnh_ddr_sw_switch(index);
 }
 EXPORT_SYMBOL_GPL(mnh_lpddr_freq_change);
 
@@ -881,8 +827,7 @@
 			       struct device_attribute *attr,
 			       char *buf)
 {
-	uint32_t var = SCU_INf(LPDDR4_LOW_POWER_STS,
-				LPDDR4_CUR_FSP);
+	uint32_t var = MNH_DDR_CTL_INf(133, CURRENT_REG_COPY);
 
 	dev_dbg(mnh_clk->dev, "%s: %d\n", __func__, var);
 	return snprintf(buf, PAGE_SIZE, "FSP%d\n", var);
diff --git a/drivers/misc/mnh/mnh-ddr-33-100-400-600.h b/drivers/misc/mnh/mnh-ddr-33-100-400-600.h
index f5b42e9..db3b540 100644
--- a/drivers/misc/mnh/mnh-ddr-33-100-400-600.h
+++ b/drivers/misc/mnh/mnh-ddr-33-100-400-600.h
@@ -79,10 +79,10 @@
 		0x0c000002 /* ctl 66 */,
 		0x00030103 /* ctl 67 */,
 		0x01031100 /* ctl 68 */,
-		0x000a0003 /* ctl 69 */,
-		0x001c0003 /* ctl 70 */,
-		0x006e0006 /* ctl 71 */,
-		0x00a6000a /* ctl 72 */,
+		0x000a000a /* ctl 69 */,
+		0x001c001c /* ctl 70 */,
+		0x006e006e /* ctl 71 */,
+		0x00a600a6 /* ctl 72 */,
 		0x03050505 /* ctl 73 */,
 		0x03010302 /* ctl 74 */,
 		0x03050505 /* ctl 75 */,
@@ -124,9 +124,9 @@
 		0x06030303 /* ctl 111 */,
 		0x00030a03 /* ctl 112 */,
 		0x02030200 /* ctl 113 */,
-		0x00070703 /* ctl 114 */,
+		0x00070903 /* ctl 114 */,
 		0x03020302 /* ctl 115 */,
-		0x02000707 /* ctl 116 */,
+		0x02000709 /* ctl 116 */,
 		0x07030203 /* ctl 117 */,
 		0x03020007 /* ctl 118 */,
 		0x07070302 /* ctl 119 */,
@@ -174,12 +174,12 @@
 		0x00313131 /* ctl 161 */,
 		0x00000000 /* ctl 162 */,
 		0x4d4d4d4d /* ctl 163 */,
-		0x4d4d4dc0 /* ctl 164 */,
+		0x4d4d4dd0 /* ctl 164 */,
 		0x0000004d /* ctl 165 */,
 		0x00000000 /* ctl 166 */,
 		0x06060606 /* ctl 167 */,
 		0x01000000 /* ctl 168 */,
-		0x00000001 /* ctl 169 */,
+		0x01010001 /* ctl 169 */,
 		0x00000000 /* ctl 170 */,
 		0x01000000 /* ctl 171 */,
 		0x00000001 /* ctl 172 */,
@@ -644,7 +644,7 @@
 		0x01000000 /* pi 70 */,
 		0x04040401 /* pi 71 */,
 		0x0a000004 /* pi 72 */,
-		0x01010128 /* pi 73 */,
+		0x01000028 /* pi 73 */,
 		0x00000001 /* pi 74 */,
 		0x00000000 /* pi 75 */,
 		0x00030003 /* pi 76 */,
@@ -663,8 +663,8 @@
 		0x00000000 /* pi 89 */,
 		0x001e0303 /* pi 90 */,
 		0x000007d0 /* pi 91 */,
-		0x01010300 /* pi 92 */,
-		0x01010101 /* pi 93 */,
+		0x00000300 /* pi 92 */,
+		0x01010000 /* pi 93 */,
 		0x00000101 /* pi 94 */,
 		0x00000000 /* pi 95 */,
 		0x00000000 /* pi 96 */,
@@ -688,7 +688,7 @@
 		0x00120024 /* pi 114 */,
 		0x00000000 /* pi 115 */,
 		0x00000000 /* pi 116 */,
-		0x01010100 /* pi 117 */,
+		0x01010000 /* pi 117 */,
 		0x00000001 /* pi 118 */,
 		0x010a140a /* pi 119 */,
 		0x00010011 /* pi 120 */,
@@ -719,7 +719,7 @@
 		0x34000000 /* pi 145 */,
 		0x00000000 /* pi 146 */,
 		0x00000000 /* pi 147 */,
-		0x01010000 /* pi 148 */,
+		0x00000000 /* pi 148 */,
 		0x00000101 /* pi 149 */,
 		0x31000600 /* pi 150 */,
 		0x064d4d00 /* pi 151 */,
@@ -766,7 +766,7 @@
 	},
 	{
 		0x76543210 /* phy 0 */,
-		0x0004f008 /* phy 1 */,
+		0x00051008 /* phy 1 */,
 		0x00020133 /* phy 2 */,
 		0x00000000 /* phy 3 */,
 		0x00000000 /* phy 4 */,
@@ -779,7 +779,7 @@
 		0x00000000 /* phy 11 */,
 		0x00000000 /* phy 12 */,
 		0x00000100 /* phy 13 */,
-		0x001700c0 /* phy 14 */,
+		0x00170080 /* phy 14 */,
 		0x020100cc /* phy 15 */,
 		0x00030066 /* phy 16 */,
 		0x00000000 /* phy 17 */,
@@ -848,7 +848,7 @@
 		0x00800080 /* phy 80 */,
 		0x00800080 /* phy 81 */,
 		0x00800080 /* phy 82 */,
-		0x00010019 /* phy 83 */,
+		0x00000019 /* phy 83 */,
 		0x000001d0 /* phy 84 */,
 		0x00000000 /* phy 85 */,
 		0x00000200 /* phy 86 */,
@@ -856,7 +856,7 @@
 		0x51816152 /* phy 88 */,
 		0xc0c08161 /* phy 89 */,
 		0x00010000 /* phy 90 */,
-		0x02001000 /* phy 91 */,
+		0x0200100c /* phy 91 */,
 		0x0c0432ff /* phy 92 */,
 		0x000f0c18 /* phy 93 */,
 		0x01000140 /* phy 94 */,
@@ -894,7 +894,7 @@
 		0x00000000 /* phy 126 */,
 		0x00000000 /* phy 127 */,
 		0x76543210 /* phy 128 */,
-		0x0004f008 /* phy 129 */,
+		0x00051008 /* phy 129 */,
 		0x00020133 /* phy 130 */,
 		0x00000000 /* phy 131 */,
 		0x00000000 /* phy 132 */,
@@ -907,7 +907,7 @@
 		0x00000000 /* phy 139 */,
 		0x00000000 /* phy 140 */,
 		0x00000100 /* phy 141 */,
-		0x001700c0 /* phy 142 */,
+		0x00170080 /* phy 142 */,
 		0x020100cc /* phy 143 */,
 		0x00030066 /* phy 144 */,
 		0x00000000 /* phy 145 */,
@@ -976,7 +976,7 @@
 		0x00800080 /* phy 208 */,
 		0x00800080 /* phy 209 */,
 		0x00800080 /* phy 210 */,
-		0x00010019 /* phy 211 */,
+		0x00000019 /* phy 211 */,
 		0x000001d0 /* phy 212 */,
 		0x00000000 /* phy 213 */,
 		0x00000200 /* phy 214 */,
@@ -984,7 +984,7 @@
 		0x51816152 /* phy 216 */,
 		0xc0c08161 /* phy 217 */,
 		0x00010000 /* phy 218 */,
-		0x02001000 /* phy 219 */,
+		0x0200100c /* phy 219 */,
 		0x0c0432ff /* phy 220 */,
 		0x000f0c18 /* phy 221 */,
 		0x01000140 /* phy 222 */,
@@ -1022,7 +1022,7 @@
 		0x00000000 /* phy 254 */,
 		0x00000000 /* phy 255 */,
 		0x76543210 /* phy 256 */,
-		0x0004f008 /* phy 257 */,
+		0x00051008 /* phy 257 */,
 		0x00020133 /* phy 258 */,
 		0x00000000 /* phy 259 */,
 		0x00000000 /* phy 260 */,
@@ -1035,7 +1035,7 @@
 		0x00000000 /* phy 267 */,
 		0x00000000 /* phy 268 */,
 		0x00000100 /* phy 269 */,
-		0x001700c0 /* phy 270 */,
+		0x00170080 /* phy 270 */,
 		0x020100cc /* phy 271 */,
 		0x00030066 /* phy 272 */,
 		0x00000000 /* phy 273 */,
@@ -1104,7 +1104,7 @@
 		0x00800080 /* phy 336 */,
 		0x00800080 /* phy 337 */,
 		0x00800080 /* phy 338 */,
-		0x00010019 /* phy 339 */,
+		0x00000019 /* phy 339 */,
 		0x000001d0 /* phy 340 */,
 		0x00000000 /* phy 341 */,
 		0x00000200 /* phy 342 */,
@@ -1112,7 +1112,7 @@
 		0x51816152 /* phy 344 */,
 		0xc0c08161 /* phy 345 */,
 		0x00010000 /* phy 346 */,
-		0x02001000 /* phy 347 */,
+		0x0200100c /* phy 347 */,
 		0x0c0432ff /* phy 348 */,
 		0x000f0c18 /* phy 349 */,
 		0x01000140 /* phy 350 */,
@@ -1150,7 +1150,7 @@
 		0x00000000 /* phy 382 */,
 		0x00000000 /* phy 383 */,
 		0x76543210 /* phy 384 */,
-		0x0004f008 /* phy 385 */,
+		0x00051008 /* phy 385 */,
 		0x00020133 /* phy 386 */,
 		0x00000000 /* phy 387 */,
 		0x00000000 /* phy 388 */,
@@ -1163,7 +1163,7 @@
 		0x00000000 /* phy 395 */,
 		0x00000000 /* phy 396 */,
 		0x00000100 /* phy 397 */,
-		0x001700c0 /* phy 398 */,
+		0x00170080 /* phy 398 */,
 		0x020100cc /* phy 399 */,
 		0x00030066 /* phy 400 */,
 		0x00000000 /* phy 401 */,
@@ -1232,7 +1232,7 @@
 		0x00800080 /* phy 464 */,
 		0x00800080 /* phy 465 */,
 		0x00800080 /* phy 466 */,
-		0x00010019 /* phy 467 */,
+		0x00000019 /* phy 467 */,
 		0x000001d0 /* phy 468 */,
 		0x00000000 /* phy 469 */,
 		0x00000200 /* phy 470 */,
@@ -1240,7 +1240,7 @@
 		0x51816152 /* phy 472 */,
 		0xc0c08161 /* phy 473 */,
 		0x00010000 /* phy 474 */,
-		0x02001000 /* phy 475 */,
+		0x0200100c /* phy 475 */,
 		0x0c0432ff /* phy 476 */,
 		0x000f0c18 /* phy 477 */,
 		0x01000140 /* phy 478 */,
@@ -1311,7 +1311,7 @@
 		0x000300ce /* phy 543 */,
 		0x03000300 /* phy 544 */,
 		0x03000300 /* phy 545 */,
-		0x00000300 /* phy 546 */,
+		0x000c0300 /* phy 546 */,
 		0xff020010 /* phy 547 */,
 		0x00000332 /* phy 548 */,
 		0x00000000 /* phy 549 */,
@@ -1439,7 +1439,7 @@
 		0x000300ce /* phy 671 */,
 		0x03000300 /* phy 672 */,
 		0x03000300 /* phy 673 */,
-		0x00000300 /* phy 674 */,
+		0x000c0300 /* phy 674 */,
 		0xff020010 /* phy 675 */,
 		0x00000332 /* phy 676 */,
 		0x00000000 /* phy 677 */,
@@ -1567,7 +1567,7 @@
 		0x000300ce /* phy 799 */,
 		0x03000300 /* phy 800 */,
 		0x03000300 /* phy 801 */,
-		0x00000300 /* phy 802 */,
+		0x000c0300 /* phy 802 */,
 		0xff020010 /* phy 803 */,
 		0x00000332 /* phy 804 */,
 		0x00000000 /* phy 805 */,
@@ -1695,7 +1695,7 @@
 		0x000300ce /* phy 927 */,
 		0x03000300 /* phy 928 */,
 		0x03000300 /* phy 929 */,
-		0x00000300 /* phy 930 */,
+		0x000c0300 /* phy 930 */,
 		0xff020010 /* phy 931 */,
 		0x00000332 /* phy 932 */,
 		0x00000000 /* phy 933 */,
@@ -1818,12 +1818,12 @@
 		0x0f1f0f1f /* phy 1050 */,
 		0x03000003 /* phy 1051 */,
 		0x00000300 /* phy 1052 */,
-		0x0b221b02 /* phy 1053 */,
-		0x09240b22 /* phy 1054 */,
+		0x07221702 /* phy 1053 */,
+		0x07240722 /* phy 1054 */,
 		0x00000000 /* phy 1055 */,
 		0x00000000 /* phy 1056 */,
 		0x05030000 /* phy 1057 */,
-		0x14000001 /* phy 1058 */,
+		0x14000801 /* phy 1058 */,
 		0x63c0ce00 /* phy 1059 */,
 		0x0000000e /* phy 1060 */,
 		0x001f0fc0 /* phy 1061 */,
@@ -1847,7 +1847,7 @@
 		0x00000000 /* phy 1079 */,
 		0x00000000 /* phy 1080 */,
 		0x00000000 /* phy 1081 */,
-		0x00000078 /* phy 1082 */,
+		0x0000007a /* phy 1082 */,
 		0x00000000 /* phy 1083 */,
 		0x00010108 /* phy 1084 */,
 		0x00000000 /* phy 1085 */,
@@ -1870,19 +1870,27 @@
 	{
 		{ 83, 0x00020119 } /* setA */,
 		{ 90, 0x01020000 } /* setA */,
+		{ 91, 0x02001000 } /* setA */,
 		{ 92, 0x0c053eff } /* setA */,
 		{ 211, 0x00020119 } /* setA */,
 		{ 218, 0x01020000 } /* setA */,
+		{ 219, 0x02001000 } /* setA */,
 		{ 220, 0x0c053eff } /* setA */,
 		{ 339, 0x00020119 } /* setA */,
 		{ 346, 0x01020000 } /* setA */,
+		{ 347, 0x02001000 } /* setA */,
 		{ 348, 0x0c053eff } /* setA */,
 		{ 467, 0x00020119 } /* setA */,
 		{ 474, 0x01020000 } /* setA */,
+		{ 475, 0x02001000 } /* setA */,
 		{ 476, 0x0c053eff } /* setA */,
+		{ 546, 0x00000300 } /* setA */,
 		{ 548, 0x0000033e } /* setA */,
+		{ 674, 0x00000300 } /* setA */,
 		{ 676, 0x0000033e } /* setA */,
+		{ 802, 0x00000300 } /* setA */,
 		{ 804, 0x0000033e } /* setA */,
+		{ 930, 0x00000300 } /* setA */,
 		{ 932, 0x0000033e } /* setA */,
 		{ 1045, 0x01221102 } /* setA */,
 		{ 1046, 0x00000122 } /* setA */,
@@ -1893,22 +1901,30 @@
 		{ 83, 0x0003011a } /* setB */,
 		{ 85, 0x01000000 } /* setB */,
 		{ 90, 0x02030000 } /* setB */,
+		{ 91, 0x02001000 } /* setB */,
 		{ 92, 0x0c073eff } /* setB */,
 		{ 211, 0x0003011a } /* setB */,
 		{ 213, 0x01000000 } /* setB */,
 		{ 218, 0x02030000 } /* setB */,
+		{ 219, 0x02001000 } /* setB */,
 		{ 220, 0x0c073eff } /* setB */,
 		{ 339, 0x0003011a } /* setB */,
 		{ 341, 0x01000000 } /* setB */,
 		{ 346, 0x02030000 } /* setB */,
+		{ 347, 0x02001000 } /* setB */,
 		{ 348, 0x0c073eff } /* setB */,
 		{ 467, 0x0003011a } /* setB */,
 		{ 469, 0x01000000 } /* setB */,
 		{ 474, 0x02030000 } /* setB */,
+		{ 475, 0x02001000 } /* setB */,
 		{ 476, 0x0c073eff } /* setB */,
+		{ 546, 0x00000300 } /* setB */,
 		{ 548, 0x0000033e } /* setB */,
+		{ 674, 0x00000300 } /* setB */,
 		{ 676, 0x0000033e } /* setB */,
+		{ 802, 0x00000300 } /* setB */,
 		{ 804, 0x0000033e } /* setB */,
+		{ 930, 0x00000300 } /* setB */,
 		{ 932, 0x0000033e } /* setB */,
 		{ 1045, 0x01221102 } /* setB */,
 		{ 1046, 0x00000122 } /* setB */,
diff --git a/drivers/misc/mnh/mnh-ddr.c b/drivers/misc/mnh/mnh-ddr.c
index 47f04c0..5d9a937 100644
--- a/drivers/misc/mnh/mnh-ddr.c
+++ b/drivers/misc/mnh/mnh-ddr.c
@@ -1,7 +1,7 @@
 /*
 *
 * MNH DDR Driver
-* Copyright (c) 2016-2017, Intel Corporation.
+* Copyright (c) 2016-2018, Intel Corporation.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
@@ -28,48 +28,52 @@
 
 #define MNH_DDR_CTL_IN(reg) \
 	HW_IN(HWIO_DDR_CTL_BASE_ADDR, DDR_CTL, reg)
-#define MNH_DDR_CTL_INf(...) \
-	HW_INf(HWIO_DDR_CTL_BASE_ADDR, DDR_CTL, __VA_ARGS__)
-#define MNH_DDR_CTL_OUT(...) \
-	HW_OUT(HWIO_DDR_CTL_BASE_ADDR, DDR_CTL, __VA_ARGS__)
-#define MNH_DDR_CTL_OUTf(...) \
-	HW_OUTf(HWIO_DDR_CTL_BASE_ADDR, DDR_CTL, __VA_ARGS__)
+#define MNH_DDR_CTL_INf(reg, fld) \
+	HW_INf(HWIO_DDR_CTL_BASE_ADDR, DDR_CTL, reg, fld)
+#define MNH_DDR_CTL_OUT(reg, val) \
+	HW_OUT(HWIO_DDR_CTL_BASE_ADDR, DDR_CTL, reg, val)
+#define MNH_DDR_CTL_OUTf(reg, fld, val) \
+	HW_OUTf(HWIO_DDR_CTL_BASE_ADDR, DDR_CTL, reg, fld, val)
 
-#define MNH_DDR_PI_INf(...) \
-	HW_INf(HWIO_DDR_PI_BASE_ADDR, DDR_PI, __VA_ARGS__)
-#define MNH_DDR_PI_OUTf(...) \
-	HW_OUTf(HWIO_DDR_PI_BASE_ADDR, DDR_PI, __VA_ARGS__)
-#define MNH_DDR_PI_OUT(...) \
-	HW_OUT(HWIO_DDR_PI_BASE_ADDR, DDR_PI, __VA_ARGS__)
+#define MNH_DDR_PI_INf(reg, fld) \
+	HW_INf(HWIO_DDR_PI_BASE_ADDR, DDR_PI, reg, fld)
+#define MNH_DDR_PI_OUTf(reg, fld, val) \
+	HW_OUTf(HWIO_DDR_PI_BASE_ADDR, DDR_PI, reg, fld, val)
+#define MNH_DDR_PI_OUT(reg, val) \
+	HW_OUT(HWIO_DDR_PI_BASE_ADDR, DDR_PI, reg, val)
 
-#define MNH_DDR_PHY_INf(...) \
-	HW_INf(HWIO_DDR_PHY_BASE_ADDR, DDR_PHY, __VA_ARGS__)
-#define MNH_DDR_PHY_OUTf(...) \
-	HW_OUTf(HWIO_DDR_PHY_BASE_ADDR, DDR_PHY, __VA_ARGS__)
-#define MNH_DDR_PHY_OUT(...) \
-	HW_OUT(HWIO_DDR_PHY_BASE_ADDR, DDR_PHY, __VA_ARGS__)
+#define MNH_DDR_PHY_INf(reg, fld) \
+	HW_INf(HWIO_DDR_PHY_BASE_ADDR, DDR_PHY, reg, fld)
+#define MNH_DDR_PHY_IN(reg) \
+	HW_IN(HWIO_DDR_PHY_BASE_ADDR, DDR_PHY, reg)
+#define MNH_DDR_PHY_OUTf(reg, fld, val) \
+	HW_OUTf(HWIO_DDR_PHY_BASE_ADDR, DDR_PHY, reg, fld, val)
+#define MNH_DDR_PHY_OUT(reg, val) \
+	HW_OUT(HWIO_DDR_PHY_BASE_ADDR, DDR_PHY, reg, val)
 
 #define MNH_SCU_IN(reg) \
 	HW_IN(HWIO_SCU_BASE_ADDR, SCU, reg)
-#define MNH_SCU_INf(...) \
-	HW_INf(HWIO_SCU_BASE_ADDR, SCU, __VA_ARGS__)
-#define MNH_SCU_INx(...) \
-	HW_INx(HWIO_SCU_BASE_ADDR, SCU, __VA_ARGS__)
-#define MNH_SCU_INxf(...) \
-	HW_INxf(HWIO_SCU_BASE_ADDR, SCU, __VA_ARGS__)
-#define MNH_SCU_OUTf(...) \
-	HW_OUTf(HWIO_SCU_BASE_ADDR, SCU, __VA_ARGS__)
-#define MNH_SCU_OUT(...) \
-	HW_OUT(HWIO_SCU_BASE_ADDR, SCU, __VA_ARGS__)
-#define MNH_SCU_OUTx(...) \
-	HW_OUTx(HWIO_SCU_BASE_ADDR, SCU, __VA_ARGS__)
+#define MNH_SCU_INf(reg, fld) \
+	HW_INf(HWIO_SCU_BASE_ADDR, SCU, reg, fld)
+#define MNH_SCU_INx(reg, inst) \
+	HW_INx(HWIO_SCU_BASE_ADDR, SCU, reg, inst)
+#define MNH_SCU_INxf(reg, inst, fld) \
+	HW_INxf(HWIO_SCU_BASE_ADDR, SCU, reg, inst, fld)
+#define MNH_SCU_OUTf(reg, fld, val) \
+	HW_OUTf(HWIO_SCU_BASE_ADDR, SCU, reg, fld, val)
+#define MNH_SCU_OUT(reg, val) \
+	HW_OUT(HWIO_SCU_BASE_ADDR, SCU, reg, val)
+#define MNH_SCU_OUTx(reg, inst, val) \
+	HW_OUTx(HWIO_SCU_BASE_ADDR, SCU, reg, inst, val)
+#define MNH_SCU_OUTxf(reg, inst, fld, val) \
+	HW_OUTxf(HWIO_SCU_BASE_ADDR, SCU, reg, inst, fld, val)
 
 #define MNH_RSTC_INf(fld) \
 	HW_INf(HWIO_SCU_BASE_ADDR, SCU, RSTC, fld)
-#define MNH_RSTC_OUTf(...) \
-	HW_OUTf(HWIO_SCU_BASE_ADDR, SCU, RSTC, __VA_ARGS__)
+#define MNH_RSTC_OUTf(fld, val) \
+	HW_OUTf(HWIO_SCU_BASE_ADDR, SCU, RSTC, fld, val)
 
-#define WRITE_DDR_REG_CONFIG(_state, ddrblock, regindex) \
+#define WRITE_DDR_REG_CONFIG(ddrblock, regindex) \
 do { \
 	if (_state->ddrblock[regindex]) { \
 		mnh_reg_write(_state->ddrblock##_base + \
@@ -78,7 +82,7 @@
 	} \
 } while (0)
 
-#define WRITE_DDR_PHY_CONFIG(_state, fsp, regindex)    \
+#define WRITE_DDR_PHY_CONFIG(fsp, regindex)    \
 do { \
 	if (_state->phy[fsp][regindex]) { \
 		mnh_reg_write(_state->phy_base + (regindex * sizeof(u32)), \
@@ -86,92 +90,178 @@
 	} \
 } while (0)
 
-#define WRITE_SET_ELEMENT(_state, regindex, regvalue)	\
+#define WRITE_SET_ELEMENT(regindex, regvalue)	\
 	mnh_reg_write(_state->phy_base + (regindex * sizeof(u32)),\
 		regvalue)
 
-#define WRITE_SCU_FSP(_state, fsp) \
+#define WRITE_SCU_FSP(fsp) \
 do { \
 	_state->fsps[fsp] &= 0xFFFFFF00;\
 	_state->fsps[fsp] |= 0x7d;\
 	MNH_SCU_OUTx(LPDDR4_FSP_SETTING, fsp, _state->fsps[fsp]); \
 } while (0)
 
-#define SAVE_CURRENT_FSP(dev, _state) \
-do { \
-	_state->suspend_fsp = \
-		MNH_SCU_INf(LPDDR4_LOW_POWER_STS, LPDDR4_CUR_FSP); \
-	dev_dbg(dev, "%s: saved fsp: %d\n", __func__, _state->suspend_fsp); \
-} while (0)
+#define WRITE_CLK_FROM_FSP(fsp) mnh_ddr_write_clk_from_fsp(fsp, 1)
+#define WRITE_CLK_FROM_FSP_NO_LOCK(fsp) mnh_ddr_write_clk_from_fsp(fsp, 0)
 
-#define SAVED_FSP(_state) _state->suspend_fsp
-
-#define WRITE_CLK_FROM_FSP(dev, _state, fsp) \
-do { \
-	if (fsp < (MNH_DDR_NUM_FSPS)) { \
-		MNH_SCU_OUTf(CCU_CLK_DIV, LPDDR4_REFCLK_DIV, \
-			MNH_SCU_INxf(LPDDR4_FSP_SETTING, fsp, \
-				FSP_LPDDR4_REFCLK_DIV)); \
-		MNH_SCU_OUTf(CCU_CLK_DIV, AXI_FABRIC_CLK_DIV, \
-			MNH_SCU_INxf(LPDDR4_FSP_SETTING, fsp, \
-				FSP_AXI_FABRIC_CLK_DIV)); \
-		MNH_SCU_OUTf(CCU_CLK_DIV, PCIE_AXI_CLK_DIV, \
-			MNH_SCU_INxf(LPDDR4_FSP_SETTING, fsp, \
-				FSP_PCIE_AXI_CLK_DIV)); \
-		MNH_SCU_OUTf(CCU_CLK_CTL, LP4_AXI_SYS200_MODE, \
-			MNH_SCU_INxf(LPDDR4_FSP_SETTING, fsp, \
-				FSP_SYS200_MODE)); \
-	} else \
-		dev_err(dev, "%s: invalid fsp 0x%x", __func__, fsp); \
-} while (0)
-
-#define SAVE_DDR_REG_CONFIG(_state, ddrblock, regindex) \
+#define SAVE_DDR_REG_CONFIG(ddrblock, regindex) \
 do { \
 	_state->ddrblock[regindex] = \
 		mnh_reg_read(_state->ddrblock##_base + \
-				(regindex * sizeof(u32))); \
+					((regindex) * sizeof(u32))); \
 } while (0)
 
-#define SAVE_DDR_PHY_REG_CONFIG(_state, fsp, regindex) \
+#define SAVE_DDR_PHY_REG_CONFIG(fsp, regindex) \
 do { \
 	_state->phy[fsp][regindex] = \
-		mnh_reg_read(_state->phy_base + (regindex * sizeof(u32))); \
+		mnh_reg_read(_state->phy_base + ((regindex) * sizeof(u32))); \
 } while (0)
 
-#define CLR_START(_state, ddrblock) (_state->ddrblock[0] &= (0xFFFFFFFE))
+#define CLR_START(ddrblock) (_state->ddrblock[0] &= (0xFFFFFFFE))
 
 /* timeout for training all FSPs */
 #define TRAINING_TIMEOUT msecs_to_jiffies(45)
+#define RESUME_TIMEOUT msecs_to_jiffies(7)
 
+#define MNH_DDR_ASSERT_ISO_N() \
+	do { \
+		gpiod_set_value_cansleep(_state->iso_n, 0); \
+		udelay(20); \
+	} while (0)
+
+#define MNH_DDR_DEASSERT_ISO_N() \
+	do { \
+		gpiod_set_value_cansleep(_state->iso_n, 1); \
+		udelay(20); \
+	} while (0)
+
+#define LP_CMD_FREQ_SWITCH 0x8A
 #define LP_CMD_EXIT_LP 0x81
 #define LP_CMD_DSRPD 0xFE
+#define LP_CMD_SRPD 0x3A
+#define LP_CMD_EXIT_SRPD 0x01
 
 /* INT status bits */
+#define DFS_COMPLETE_SBIT 31
+#define DFI_STATE_CHANGE_SBIT 28
+#define INHIBIT_DRAM_CMD_SBIT 27
 #define MR_WRITE_SBIT 26
 #define MR_READ_SBIT 23
+#define DFI_UPDATE_ERROR_SBIT 13
 #define BIST_SBIT 6
 #define LP_CMD_SBIT 5
 #define INIT_DONE_SBIT 4
 
+/* PI_INT_STATUS bits */
+#define PI_CONTROL_ERROR_BIT 1
+#define PI_INIT_DONE_BIT 0
+
+static struct mnh_ddr_internal_state *_state;
+
+static void mnh_ddr_disable_lp(void);
+static void mnh_ddr_enable_lp(void);
+
+/*
+ * Write the clk dividers from given FSP index.
+ *  fsp: the fsp index [0-3]
+ *  pll_freeze: define whether the function uses HW PLL freeze funtionality
+ *  inside the function to set all the dividers in one operation (with unfreeze)
+ *  0 to set it off
+ *  1 to set it on
+ */
+void mnh_ddr_write_clk_from_fsp(unsigned int fsp, int pll_freeze)
+{
+	if (fsp >= MNH_DDR_NUM_FSPS) {
+		pr_err("%s invalid fsp 0x%x", __func__, fsp);
+		return;
+	}
+
+	if (pll_freeze) {
+		MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0x4CD9);
+		MNH_SCU_OUTf(LPDDR4_REFCLK_PLL_CTRL, FRZ_PLL_IN, 1);
+	}
+	MNH_SCU_OUTf(CCU_CLK_DIV, LPDDR4_REFCLK_DIV,
+		MNH_SCU_INxf(LPDDR4_FSP_SETTING, fsp, FSP_LPDDR4_REFCLK_DIV));
+	MNH_SCU_OUTf(CCU_CLK_DIV, AXI_FABRIC_CLK_DIV,
+		MNH_SCU_INxf(LPDDR4_FSP_SETTING, fsp, FSP_AXI_FABRIC_CLK_DIV));
+	MNH_SCU_OUTf(CCU_CLK_DIV, PCIE_AXI_CLK_DIV,
+		MNH_SCU_INxf(LPDDR4_FSP_SETTING, fsp, FSP_PCIE_AXI_CLK_DIV));
+	MNH_SCU_OUTf(CCU_CLK_CTL, LP4_AXI_SYS200_MODE,
+		MNH_SCU_INxf(LPDDR4_FSP_SETTING, fsp, FSP_SYS200_MODE));
+	if (pll_freeze) {
+		MNH_SCU_OUTf(LPDDR4_REFCLK_PLL_CTRL, FRZ_PLL_IN, 0);
+		MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0);
+	}
+}
+
+static u32 mnh_ddr_sanity_check(void)
+{
+	/* just verify comm is actually up */
+	u32 val = MNH_DDR_CTL_IN(00);
+
+	/*
+	 * If above register reads either 0 or all FF, it indicates that
+	 * access to DDR CTL registers are not ready.
+	 */
+	if ((val == 0) || (val == 0xFFFFFFFF))
+		return 0;
+	else
+		return 1;
+}
+
 /* read entire int_status */
-u64 mnh_ddr_int_status(struct device *dev)
+u64 mnh_ddr_int_status(void)
 {
 	u64 int_stat = ((u64)MNH_DDR_CTL_IN(228) << 32) | MNH_DDR_CTL_IN(227);
 	return int_stat;
 }
 EXPORT_SYMBOL(mnh_ddr_int_status);
 
+int mnh_ddr_print_phy_status(void)
+{
+	int ret = 0;
+	/* call from a context where lp has already been disabled
+	 * so ctl and phy reg can be accessed. don't do it here
+	 */
+	if ((MNH_DDR_PHY_IN(00) != 0x76543210) ||
+		(MNH_DDR_PHY_IN(256) != 0x76543210) ||
+		(MNH_DDR_PHY_IN(384) != 0x76543210)) {
+		pr_err("%s ERROR PHY 00: 0x%08x 256: 0x%08x 384: 0x%08x\n",
+			__func__,
+			MNH_DDR_PHY_IN(00),
+			MNH_DDR_PHY_IN(256),
+			MNH_DDR_PHY_IN(384));
+		ret = -1;
+	}
+
+	if (MNH_DDR_PHY_INf(1099, PHY_AC_INIT_COMPLETE_OBS) != 0x000003f1) {
+		pr_info("%s PHY_AC_INIT_COMPLETE_OBS: 0x%08x\n",
+			__func__, MNH_DDR_PHY_INf(1099,
+					PHY_AC_INIT_COMPLETE_OBS));
+		ret = -1;
+	}
+	if (MNH_DDR_PHY_INf(1100, PHY_DS_INIT_COMPLETE_OBS) != 0x0000000f) {
+		pr_info("%s PHY_DS_INIT_COMPLETE_OBS: 0x%08x\n",
+			__func__, MNH_DDR_PHY_INf(1100,
+					PHY_DS_INIT_COMPLETE_OBS));
+		ret = -1;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(mnh_ddr_print_phy_status);
+
+
 /* clear entire int_status */
-int mnh_ddr_clr_int_status(struct device *dev)
+int mnh_ddr_clr_int_status(void)
 {
 	u64 stat = 0;
 
-	MNH_DDR_CTL_OUT(230, 0x0F);
 	MNH_DDR_CTL_OUT(229, 0xFFFFFFFF);
-	stat = mnh_ddr_int_status(dev);
+	MNH_DDR_CTL_OUT(230, 0x0F);
+	stat = mnh_ddr_int_status();
 	if (stat) {
-		dev_err(dev, "%s: int stat not all clear: %llx\n", __func__,
-			stat);
+		pr_err("%s: int stat not all clear: %llx\n",
+			__func__, stat);
 		return -EIO;
 	}
 	return 0;
@@ -202,7 +292,7 @@
 }
 
 /* clear single bit in int_status */
-static int mnh_ddr_clr_int_status_bit(struct device *dev, u8 sbit)
+static int mnh_ddr_clr_int_status_bit(u8 sbit)
 {
 	const u32 max_int_status_bit = 35;
 	const u32 first_upper_bit = 32;
@@ -216,17 +306,33 @@
 		MNH_DDR_CTL_OUT(229, 1 << sbit);
 
 	if (mnh_ddr_int_status_bit(sbit)) {
-		dev_err(dev, "%s: bit %d is still set.\n", __func__, sbit);
+		pr_err("%s: bit %d is still set.\n", __func__, sbit);
 		return -EIO;
 	}
 	return 0;
 }
 
-static int mnh_ddr_send_lp_cmd(struct device *dev, u8 cmd)
+/* read single bit in PI_INT_STATUS */
+static u32 mnh_ddr_pi_int_status_bit(u8 sbit)
+{
+	u32 status = 0;
+	const u32 max_int_status_bit = 24;
+
+	if (sbit > max_int_status_bit)
+		return -EINVAL;
+
+	status = MNH_DDR_PI_INf(172, PI_INT_STATUS);
+	pr_debug("%s: PI_INT_STATUS = 0x%x.\n", __func__, status);
+
+	status &= (1 << sbit);
+	return status;
+}
+
+static int mnh_ddr_send_lp_cmd(u8 cmd)
 {
 	u32 timeout = 100000;
 
-	dev_dbg(dev, "%s sending cmd: 0x%x\n", __func__, cmd);
+	pr_debug("%s sending cmd: 0x%x\n", __func__, cmd);
 	MNH_DDR_CTL_OUTf(112, LP_CMD, cmd);
 
 	while (!mnh_ddr_int_status_bit(LP_CMD_SBIT) && --timeout)
@@ -235,29 +341,85 @@
 	if (!mnh_ddr_int_status_bit(LP_CMD_SBIT))
 		return -ETIMEDOUT;
 
-	return mnh_ddr_clr_int_status_bit(dev, LP_CMD_SBIT);
+	return mnh_ddr_clr_int_status_bit(LP_CMD_SBIT);
+}
+
+/*
+ * Both chip 0 and chip 1 are written.
+ */
+int mnh_ddr_write_mode_reg(u8 modereg, u8 modevalue)
+{
+	const u64 writeable = 0x0000010101D3FE1E;
+	u32 val = 0;
+	unsigned long timeout = 0;
+	int ret = 0;
+
+	if ((modereg >= 64) ||
+		((writeable & (1ULL << modereg)) == 0)) {
+		pr_err("%s %d is not writeable.\n",
+			__func__, modereg);
+		return -EIO;
+	}
+
+	pr_debug("%s LP_STATE is 0x%x\n",
+		__func__, MNH_DDR_CTL_INf(121, LP_STATE));
+	val = 0xFF & modereg;
+
+	/*
+	 * bit 24 indicates all chip selects
+	 * bit 23 indicates indicates a single mode reg
+	 */
+	val |= (1 << 23) | (1 << 24);
+	MNH_DDR_CTL_OUTf(140, WRITE_MODEREG, val);
+	MNH_DDR_CTL_OUTf(160, MRSINGLE_DATA_0, modevalue);
+	/* trigger write */
+	val |= (1 << 25);
+	MNH_DDR_CTL_OUTf(140, WRITE_MODEREG, val);
+
+	timeout = jiffies + msecs_to_jiffies(500);
+	while (!mnh_ddr_int_status_bit(MR_WRITE_SBIT) &&
+	       time_before(jiffies, timeout)) {
+		udelay(100);
+	}
+
+	if (mnh_ddr_int_status_bit(MR_WRITE_SBIT)) {
+		mnh_ddr_clr_int_status_bit(MR_WRITE_SBIT);
+	} else {
+		pr_err("%s timeout on MR write done. %llx.",
+			__func__, mnh_ddr_int_status());
+		ret = -EIO;
+	}
+	val = MNH_DDR_CTL_INf(141, MRW_STATUS);
+	if (val) {
+		pr_err("%s ERROR status: 0x%x", __func__, val);
+		ret = -EIO;
+	}
+
+	return ret;
 }
 
 static void mnh_ddr_enable_lp(void)
 {
 	MNH_DDR_CTL_OUTf(124, LP_AUTO_SR_MC_GATE_IDLE, 0xFF);
-	MNH_DDR_CTL_OUTf(122, LP_AUTO_MEM_GATE_EN, 0x4);
-	MNH_DDR_CTL_OUTf(122, LP_AUTO_ENTRY_EN, 0x4);
-	MNH_DDR_CTL_OUTf(122, LP_AUTO_EXIT_EN, 0xF);
+	if (MNH_DDR_CTL_INf(122, LP_AUTO_EXIT_EN) == 0)
+		MNH_DDR_CTL_OUTf(122, LP_AUTO_EXIT_EN, 0xF);
+	if (MNH_DDR_CTL_INf(122, LP_AUTO_MEM_GATE_EN) == 0)
+		MNH_DDR_CTL_OUTf(122, LP_AUTO_MEM_GATE_EN, 0x4);
+	if (MNH_DDR_CTL_INf(122, LP_AUTO_ENTRY_EN) == 0)
+		MNH_DDR_CTL_OUTf(122, LP_AUTO_ENTRY_EN, 0x4);
 }
 
-static void mnh_ddr_disable_lp(struct device *dev)
+static void mnh_ddr_disable_lp(void)
 {
-	MNH_DDR_CTL_OUTf(124, LP_AUTO_SR_MC_GATE_IDLE, 0x00);
-	MNH_DDR_CTL_OUTf(122, LP_AUTO_MEM_GATE_EN, 0x0);
 	MNH_DDR_CTL_OUTf(122, LP_AUTO_ENTRY_EN, 0x0);
-	MNH_DDR_CTL_OUTf(122, LP_AUTO_EXIT_EN, 0x0);
-	mnh_ddr_send_lp_cmd(dev, LP_CMD_EXIT_LP);
+	mnh_ddr_send_lp_cmd(LP_CMD_EXIT_LP);
 }
 
 static void mnh_ddr_init_internal_state(struct mnh_ddr_internal_state *_state,
-					const struct mnh_ddr_reg_config *cfg)
+					const struct mnh_ddr_reg_config *cfg,
+					struct gpio_desc *iso_n)
 {
+	_state->iso_n = iso_n;
 	_state->ctl_base = HWIO_DDR_CTL_BASE_ADDR;
 	_state->pi_base = HWIO_DDR_PI_BASE_ADDR;
 	_state->phy_base = HWIO_DDR_PHY_BASE_ADDR;
@@ -278,18 +440,14 @@
 		&(cfg->pi[0]),
 		MNH_DDR_NUM_PI_REG * sizeof(u32));
 
-	_state->suspend_fsp = 0;
 	_state->tref[0] = cfg->ctl[56] & 0xFFFF;
 	_state->tref[1] = cfg->ctl[57] & 0xFFFF;
 	_state->tref[2] = cfg->ctl[58] & 0xFFFF;
 	_state->tref[3] = cfg->ctl[59] & 0xFFFF;
 }
 
-static void mnh_ddr_init_clocks(struct mnh_ddr_data *data)
+static void mnh_ddr_init_clocks(struct device *dev, int fsp)
 {
-	struct device *dev = &data->pdev->dev;
-	struct mnh_ddr_internal_state *_state = &data->_state;
-
 	int timeout = 0;
 
 	/* MNH_PLL_PASSCODE_SET */
@@ -312,44 +470,327 @@
 		dev_dbg(dev, "%s lpddr4 pll locked after %d iterations",
 			 __func__, timeout);
 
-	WRITE_SCU_FSP(_state, 0);
-	WRITE_SCU_FSP(_state, 1);
-	WRITE_SCU_FSP(_state, 2);
-	WRITE_SCU_FSP(_state, 3);
+	/* WA for old HW bug to keep AXI, PCIE_AXI clk divs as nonzero
+	 * during the first FSP switch
+	 */
+	MNH_SCU_OUTf(CCU_CLK_DIV, AXI_FABRIC_CLK_DIV, 0x1);
+	MNH_SCU_OUTf(CCU_CLK_DIV, PCIE_AXI_CLK_DIV, 0x3);
 
-	WRITE_CLK_FROM_FSP(dev, _state, SAVED_FSP(_state));
+	WRITE_SCU_FSP(0);
+	WRITE_SCU_FSP(1);
+	WRITE_SCU_FSP(2);
+	WRITE_SCU_FSP(3);
+
+	WRITE_CLK_FROM_FSP(fsp);
 	dev_dbg(dev, "%s lpddr4 pll locked", __func__);
 	MNH_SCU_OUTf(LPDDR4_LOW_POWER_CFG, LP4_FSP_SW_OVERRIDE, 0);
 	/* MNH_PLL_PASSCODE_CLR */
 	MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0x0);
 }
 
-static void mnh_ddr_pull_config(struct mnh_ddr_data *data)
+static void mnh_ddr_clear_lpc_status(void)
 {
-	struct mnh_ddr_internal_state *_state = &data->_state;
-
-	int index, fsp;
-	for (index = 0; index < MNH_DDR_NUM_CTL_REG; index++)
-		SAVE_DDR_REG_CONFIG(_state, ctl, index);
-	CLR_START(_state, ctl);
-
-	for (index = 0; index < MNH_DDR_NUM_PI_REG; index++)
-		SAVE_DDR_REG_CONFIG(_state, pi, index);
-	CLR_START(_state, pi);
-
-	for (fsp = 0; fsp < MNH_DDR_NUM_FSPS; fsp++) {
-		MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_INDEX, fsp);
-		for (index = 0; index < MNH_DDR_NUM_PHY_REG; index++)
-			SAVE_DDR_PHY_REG_CONFIG(_state, fsp, index);
+	/* Paranoia */
+	if (MNH_SCU_INf(SCU_IRQ_STATUS, LP4_FREQ_CHG_REQ)) {
+		pr_info("%s LP4_FREQ_CHG_REQ already set, clearing", __func__);
+		MNH_SCU_OUTf(SCU_IRQ_STATUS, LP4_FREQ_CHG_REQ, 1);
+	}
+	if (MNH_SCU_INf(SCU_IRQ_STATUS, LP4_LPC_CMD_DONE)) {
+		pr_info("%s LP4_LPC_CMD_DONE already set, clearing", __func__);
+		MNH_SCU_OUTf(SCU_IRQ_STATUS, LP4_LPC_CMD_DONE, 1);
+	}
+	if (MNH_SCU_INf(LPDDR4_LOW_POWER_STS, LPC_CMD_RSP)) {
+		pr_info("%s LPC_CMD_RSP already set, clearing", __func__);
+		MNH_SCU_OUTf(LPDDR4_LOW_POWER_STS, LPC_CMD_RSP, 1);
+	}
+	if (MNH_SCU_INf(LPDDR4_LOW_POWER_STS, LPC_CMD_DONE)) {
+		pr_info("%s LPC_CMD_DONE already set, clearing", __func__);
+		MNH_SCU_OUTf(LPDDR4_LOW_POWER_STS, LPC_CMD_DONE, 1);
 	}
 }
 
-int mnh_ddr_suspend(struct mnh_ddr_data *data, struct gpio_desc *iso_n)
-{
-	struct device *dev = &data->pdev->dev;
-	struct mnh_ddr_internal_state *_state = &data->_state;
+#define MR_TABLE_LEN 5
+const u8 mrw_fsps[MNH_DDR_NUM_FSPS][MR_TABLE_LEN][2] = {
+	{
+		{  1, 0x06 },
+		{  2, 0x00 },
+		{  3, 0x31 },
+		{ 11, 0x00 },
+		{ 22, 0x00 }
+	},
+	{
+		{  1, 0x06 },
+		{  2, 0x00 },
+		{  3, 0x31 },
+		{ 11, 0x00 },
+		{ 22, 0x00 },
+	},
+	{
+		{  1, 0x26 },
+		{  2, 0x12 },
+		{  3, 0x31 },
+		{ 11, 0x00 },
+		{ 22, 0x00 },
+	},
+	{
+		{  1, 0x46 },
+		{  2, 0x24 },
+		{  3, 0x31 },
+		{ 11, 0x00 },
+		{ 22, 0x00 },
+	}
+};
 
-	mnh_ddr_disable_lp(dev);
+int mnh_ddr_sw_switch(int index)
+{
+	static int iteration;
+	u8 fsop, fswr;
+	u8 mr13val;
+	int old_lpi_wakeup_en, show_log, timeout;
+	int i, ret = -EIO;
+
+	uint16_t upd_high[MNH_DDR_NUM_FSPS];
+	uint16_t upd_norm[MNH_DDR_NUM_FSPS];
+
+	if ((index < 0) || (index >= MNH_DDR_NUM_FSPS)) {
+		pr_err("%s %d is not a valid FSP\n",
+			__func__, index);
+		return -EINVAL;
+	} else if (MNH_DDR_CTL_INf(133, CURRENT_REG_COPY) == index) {
+		pr_info("%s %d is already in use - skipping\n",
+			__func__, index);
+		return 0;
+	}
+	show_log = iteration++ % 1000;
+	if ((show_log == 0) || (show_log == 1)) {
+		pr_info("%s #%d fsp %d -> %d DLL RESET\n",
+			__func__, (iteration - 1),
+			MNH_DDR_CTL_INf(133, CURRENT_REG_COPY), index);
+	}
+
+	if (!MNH_SCU_INxf(LPDDR4_FSP_SETTING, index, FSP_SYS200_MODE))
+		mnh_lpddr_sys200_mode(false);
+
+	old_lpi_wakeup_en = MNH_DDR_CTL_INf(120, LPI_WAKEUP_EN);
+	/* need to make sure to disable lp, so phy regs
+	 * and DRAM mode regs can be accessed.
+	 */
+	mnh_ddr_disable_lp();
+	mnh_ddr_clr_int_status();
+
+	/* set software control */
+	MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0x4CD9);
+	MNH_SCU_OUTxf(LPDDR4_FSP_SETTING, index, FSP_SW_CTRL, 1);
+	MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0);
+
+	mnh_ddr_clear_lpc_status();
+
+	/* step 0 */
+	MNH_DDR_CTL_OUTf(221, INHIBIT_DRAM_CMD, 3);
+	MNH_DDR_CTL_OUTf(120, LPI_WAKEUP_EN, 0);
+	udelay(10);
+	MNH_DDR_CTL_OUTf(54, AREFRESH, 1);
+	/* CDNS added this line */
+	MNH_DDR_PHY_OUTf(1098, PHY_INIT_UPDATE_CONFIG, 0);
+
+	timeout = 1000;
+	udelay(1); /* CDNS addition start */
+	MNH_DDR_CTL_OUTf(112, LP_CMD, 1);
+	while (!mnh_ddr_int_status_bit(LP_CMD_SBIT) &&
+		timeout-- > 0) {
+		udelay(1);
+	}
+	if (mnh_ddr_clr_int_status_bit(LP_CMD_SBIT)) {
+		pr_err("%s %d LP_CMD not clearing\n",
+			__func__, __LINE__);
+		ret = -EIO;
+		goto sw_switch_error_exit;
+	}  /* CDNS addition end */
+
+	timeout = 1000;
+	while (!mnh_ddr_int_status_bit(INHIBIT_DRAM_CMD_SBIT) && timeout-- > 0)
+		udelay(1);
+	if (mnh_ddr_clr_int_status_bit(INHIBIT_DRAM_CMD_SBIT)) {
+		pr_err("%s %d INHIBIT not clearing\n",
+			__func__, __LINE__);
+		ret = -EIO;
+		goto sw_switch_error_exit;
+	}
+
+	MNH_DDR_CTL_OUTf(223, CTRLUPD_REQ_PER_AREF_EN, 0);
+	MNH_DDR_CTL_OUTf(525, CTRLUPD_AREF_HP_ENABLE, 0);
+	MNH_DDR_PHY_OUTf(1100, PHY_UPDATE_MASK, 1);
+	/* step 1 */
+	upd_high[0] = MNH_DDR_CTL_INf(88, UPD_CTRLUPD_HIGH_THRESHOLD_F0);
+	upd_norm[0] = MNH_DDR_CTL_INf(88, UPD_CTRLUPD_NORM_THRESHOLD_F0);
+	upd_high[1] = MNH_DDR_CTL_INf(91, UPD_CTRLUPD_HIGH_THRESHOLD_F1);
+	upd_norm[1] = MNH_DDR_CTL_INf(90, UPD_CTRLUPD_NORM_THRESHOLD_F1);
+	upd_high[2] = MNH_DDR_CTL_INf(93, UPD_CTRLUPD_HIGH_THRESHOLD_F2);
+	upd_norm[2] = MNH_DDR_CTL_INf(93, UPD_CTRLUPD_NORM_THRESHOLD_F2);
+	upd_high[3] = MNH_DDR_CTL_INf(96, UPD_CTRLUPD_HIGH_THRESHOLD_F3);
+	upd_norm[3] = MNH_DDR_CTL_INf(95, UPD_CTRLUPD_NORM_THRESHOLD_F3);
+
+	MNH_DDR_CTL_OUTf(88, UPD_CTRLUPD_HIGH_THRESHOLD_F0, 0);
+	MNH_DDR_CTL_OUTf(88, UPD_CTRLUPD_NORM_THRESHOLD_F0, 0);
+	MNH_DDR_CTL_OUTf(91, UPD_CTRLUPD_HIGH_THRESHOLD_F1, 0);
+	MNH_DDR_CTL_OUTf(90, UPD_CTRLUPD_NORM_THRESHOLD_F1, 0);
+	MNH_DDR_CTL_OUTf(93, UPD_CTRLUPD_HIGH_THRESHOLD_F2, 0);
+	MNH_DDR_CTL_OUTf(93, UPD_CTRLUPD_NORM_THRESHOLD_F2, 0);
+	MNH_DDR_CTL_OUTf(96, UPD_CTRLUPD_HIGH_THRESHOLD_F3, 0);
+	MNH_DDR_CTL_OUTf(95, UPD_CTRLUPD_NORM_THRESHOLD_F3, 0);
+
+	fsop = MNH_DDR_CTL_INf(169, FSP_OP_CURRENT);
+	fswr = !MNH_DDR_CTL_INf(169, FSP_WR_CURRENT);
+
+	mr13val = (fsop << 7) | (fswr << 6) | (1 << 4);
+	if (mnh_ddr_write_mode_reg(13, mr13val)) {
+		pr_err("%s %d error writing MR13\n",
+			__func__, __LINE__);
+		ret = -EIO;
+		goto sw_switch_error_exit;
+	}
+
+	/* step 2 */
+	for (i = 0; i < MR_TABLE_LEN; i++) {
+		if (mnh_ddr_write_mode_reg(mrw_fsps[index][i][0],
+				mrw_fsps[index][i][1])) {
+			pr_err("%s %d error (%d %d) writing mr: 0x%02x\n",
+				__func__, __LINE__, index, i,
+				mrw_fsps[index][i][0]);
+			ret = -EIO;
+			goto sw_switch_error_exit;
+		}
+	}
+
+	/* step 3 */
+	fsop = (fsop == 1) ? 0 : 1;
+	mr13val = (fsop << 7) | (fswr << 6) | (1 << 4) | (1 << 3);
+	if (mnh_ddr_write_mode_reg(13, mr13val)) {
+		pr_err("%s %d error writing MR13\n",
+			__func__, __LINE__);
+		ret = -EIO;
+		goto sw_switch_error_exit;
+	}
+	fswr = (fswr == 1) ? 0 : 1;
+
+	/* step 4 */
+	mnh_ddr_send_lp_cmd(LP_CMD_SRPD);
+	/* step 5 */
+	/* removed using iso for switch */
+	/* step 6 moved to step 0 */
+	/* step 7 */
+	MNH_DDR_PHY_OUTf(1099, PHY_DLL_RST_EN, 1);
+
+	/* step 8 */
+	/* Prepare memory controller for switch */
+	MNH_SCU_OUTf(LPDDR4_LOW_POWER_CFG, LPC_FREQ_CHG_COPY_NUM, index);
+	MNH_SCU_OUTf(LPDDR4_LOW_POWER_CFG, LPC_EXT_CMD, LP_CMD_FREQ_SWITCH);
+	MNH_SCU_OUTf(LPDDR4_LOW_POWER_CFG, LPC_EXT_CMD_REQ, 1);
+
+	timeout = 1000;
+	while (!MNH_SCU_INf(SCU_IRQ_STATUS, LP4_FREQ_CHG_REQ) &&
+		(timeout-- > 0))
+		udelay(1);
+
+	if (!MNH_SCU_INf(SCU_IRQ_STATUS, LP4_FREQ_CHG_REQ)) {
+		pr_err("%s: Missed SCU_IRQ_STATUS.LP4_FREQ_CHG_REQ!\n",
+			__func__);
+		ret = -ETIME;
+		goto sw_switch_error_exit;
+	}
+
+	/* clear it */
+	MNH_SCU_OUTf(SCU_IRQ_STATUS, LP4_FREQ_CHG_REQ, 1);
+	/* load clock settings from the fsp of interest */
+	WRITE_CLK_FROM_FSP(index);
+	/* effect the clock change */
+	MNH_SCU_OUTf(LPDDR4_LOW_POWER_CFG, LP4_FSP_SW_OVERRIDE, 1);
+	udelay(100);
+	MNH_SCU_OUTf(LPDDR4_LOW_POWER_CFG, LP4_FSP_SW_OVERRIDE, 0);
+
+	/* step 9 */
+	MNH_DDR_PHY_OUTf(1099, PHY_DLL_RST_EN, 2);
+	/* step 10 */
+	/* inform memory controller freq change is done */
+	MNH_SCU_OUTf(LPDDR4_LOW_POWER_CFG, LP4_FREQ_CHG_ACK, 1);
+	timeout = 1000;
+	while (!MNH_SCU_INf(LPDDR4_LOW_POWER_STS, LPC_CMD_DONE) &&
+		(timeout-- > 0))
+		udelay(1);
+
+	if (!MNH_SCU_INf(LPDDR4_LOW_POWER_STS, LPC_CMD_DONE)) {
+		pr_err("%s Missed: LPDDR4_LOW_POWER_STS.LPC_CMD_DONE\n",
+			__func__);
+		ret = -ETIME;
+		goto sw_switch_error_exit;
+	}
+
+	/* steps 11 - 13 removed because we're not using iso */
+
+	MNH_SCU_OUTf(LPDDR4_LOW_POWER_CFG, LP4_FSP_SW_OVERRIDE, 0);
+	/* clear done */
+	MNH_SCU_OUTf(LPDDR4_LOW_POWER_STS, LPC_CMD_DONE, 1);
+	/* step 14 */
+	/* undo previous changes. */
+	MNH_DDR_CTL_OUTf(223, CTRLUPD_REQ_PER_AREF_EN, 1);
+	MNH_DDR_CTL_OUTf(525, CTRLUPD_AREF_HP_ENABLE, 1);
+	/* CDNS added this line */
+	MNH_DDR_PHY_OUTf(1098, PHY_INIT_UPDATE_CONFIG, 7);
+	MNH_DDR_PHY_OUTf(1100, PHY_UPDATE_MASK, 0);
+	MNH_DDR_CTL_OUTf(221, INHIBIT_DRAM_CMD, 0);
+	MNH_DDR_CTL_OUTf(88, UPD_CTRLUPD_HIGH_THRESHOLD_F0, upd_high[0]);
+	MNH_DDR_CTL_OUTf(88, UPD_CTRLUPD_NORM_THRESHOLD_F0, upd_norm[0]);
+	MNH_DDR_CTL_OUTf(91, UPD_CTRLUPD_HIGH_THRESHOLD_F1, upd_high[1]);
+	MNH_DDR_CTL_OUTf(90, UPD_CTRLUPD_NORM_THRESHOLD_F1, upd_norm[1]);
+	MNH_DDR_CTL_OUTf(93, UPD_CTRLUPD_HIGH_THRESHOLD_F2, upd_high[2]);
+	MNH_DDR_CTL_OUTf(93, UPD_CTRLUPD_NORM_THRESHOLD_F2, upd_norm[2]);
+	MNH_DDR_CTL_OUTf(96, UPD_CTRLUPD_HIGH_THRESHOLD_F3, upd_high[3]);
+	MNH_DDR_CTL_OUTf(95, UPD_CTRLUPD_NORM_THRESHOLD_F3, upd_norm[3]);
+
+	MNH_DDR_CTL_OUTf(120, LPI_WAKEUP_EN, old_lpi_wakeup_en);
+	MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0x4CD9);
+	MNH_SCU_OUTxf(LPDDR4_FSP_SETTING, index, FSP_SW_CTRL, 0);
+	MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0);
+
+	mnh_ddr_clr_int_status_bit(DFI_UPDATE_ERROR_SBIT);
+	mnh_ddr_clr_int_status_bit(DFS_COMPLETE_SBIT);
+	mnh_ddr_clr_int_status_bit(DFI_STATE_CHANGE_SBIT);
+	ret = 0;
+
+	if (MNH_SCU_INxf(LPDDR4_FSP_SETTING, index, FSP_SYS200_MODE))
+		mnh_lpddr_sys200_mode(true);
+
+sw_switch_error_exit:
+	if (mnh_ddr_print_phy_status())
+		ret = -EIO;
+
+	return ret;
+}
+
+static void mnh_ddr_pull_config(void)
+{
+	int index, fsp;
+	for (index = 0; index < MNH_DDR_NUM_CTL_REG; index++)
+		SAVE_DDR_REG_CONFIG(ctl, index);
+	CLR_START(ctl);
+
+	for (index = 0; index < MNH_DDR_NUM_PI_REG; index++)
+		SAVE_DDR_REG_CONFIG(pi, index);
+	CLR_START(pi);
+
+	for (fsp = 0; fsp < MNH_DDR_NUM_BANKED_FSPS; fsp++) {
+		MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_INDEX, fsp);
+		for (index = 0; index < MNH_DDR_NUM_PHY_REG; index++)
+			SAVE_DDR_PHY_REG_CONFIG(fsp, index);
+	}
+}
+
+int mnh_ddr_suspend(struct device *dev)
+{
+	if (WARN_ON(!_state))
+		return -ENOMEM;
+
+	mnh_ddr_disable_lp();
 
 	dev_dbg(dev, "%s: tref 0x%04x 0x%04x 0x%04x 0x%04x\n",
 		__func__, MNH_DDR_CTL_INf(56, TREF_F0),
@@ -368,12 +809,18 @@
 	MNH_DDR_CTL_OUTf(58, TREF_F2, _state->tref[2]);
 	MNH_DDR_CTL_OUTf(59, TREF_F3, _state->tref[3]);
 
-	/* resume to fsp3 */
-	mnh_lpddr_freq_change(LPDDR_FREQ_FSP3);
-	SAVE_CURRENT_FSP(dev, _state);
-	mnh_ddr_pull_config(data);
+	if (MNH_DDR_CTL_INf(133, CURRENT_REG_COPY) !=
+		LPDDR_FREQ_FSP3) {
+		/* resume to fsp3 */
+		if (mnh_lpddr_freq_change(LPDDR_FREQ_FSP3)) {
+			dev_err(dev, "%s ERROR suspend clock switch failed.",
+				__func__);
+			return -EIO;
+		}
+	}
+	mnh_ddr_pull_config();
 
-	mnh_ddr_send_lp_cmd(dev, LP_CMD_DSRPD);
+	mnh_ddr_send_lp_cmd(LP_CMD_DSRPD);
 	dev_dbg(dev, "%s LP_STATE is 0x%x", __func__,
 		MNH_DDR_CTL_INf(121, LP_STATE));
 
@@ -384,44 +831,40 @@
 	MNH_SCU_OUTf(CCU_CLK_CTL, LP4_REFCLKEN, 0);
 
 	udelay(1);
-	gpiod_set_value_cansleep(iso_n, 0);
-	udelay(1);
-
-	dev_dbg(dev, "%s done.", __func__);
+	MNH_DDR_ASSERT_ISO_N();
+	dev_dbg(dev, "%s iso is asserted", __func__);
 
 	return 0;
 }
 EXPORT_SYMBOL(mnh_ddr_suspend);
 
-int mnh_ddr_resume(struct mnh_ddr_data *data, struct gpio_desc *iso_n)
+int mnh_ddr_resume(struct device *dev)
 {
-	struct device *dev = &data->pdev->dev;
-	struct mnh_ddr_internal_state *_state = &data->_state;
-
 	int index, fsp;
-	int timeout = 0;
+	unsigned long timeout = 0;
+	unsigned long start_jiff, end_jiff;
 
-	mnh_ddr_init_clocks(data);
+	if (WARN_ON(!_state))
+		return -ENOMEM;
+
+	mnh_ddr_init_clocks(dev, LPDDR_FREQ_FSP3);
+
+	if (!mnh_ddr_sanity_check())
+		return -EIO;
 
 	for (index = 0; index < MNH_DDR_NUM_CTL_REG; index++)
-		WRITE_DDR_REG_CONFIG(_state, ctl, index);
-
-	MNH_DDR_CTL_OUTf(23, DFIBUS_FREQ_INIT, SAVED_FSP(_state));
-	MNH_DDR_CTL_OUTf(23, DFIBUS_BOOT_FREQ, 0);
-
-	MNH_DDR_CTL_OUTf(23, PHY_INDEP_TRAIN_MODE, 0);
-	MNH_DDR_CTL_OUTf(23, CDNS_INTRL0, 1);
+		WRITE_DDR_REG_CONFIG(ctl, index);
 
 	for (index = 0; index < MNH_DDR_NUM_PI_REG; index++)
-		WRITE_DDR_REG_CONFIG(_state, pi, index);
+		WRITE_DDR_REG_CONFIG(pi, index);
 
-	for (fsp = 0; fsp < MNH_DDR_NUM_FSPS; fsp++) {
+	for (fsp = 0; fsp < MNH_DDR_NUM_BANKED_FSPS; fsp++) {
 		MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_MULTICAST_EN, 0);
 		MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_INDEX, fsp);
 
 		for (index = 0; index < MNH_DDR_NUM_PHY_REG; index++) {
 			if (index != 1025)
-				WRITE_DDR_PHY_CONFIG(_state, fsp, index);
+				WRITE_DDR_PHY_CONFIG(fsp, index);
 		}
 		MNH_DDR_PHY_OUTf(1084, PHY_CAL_CLK_SELECT_0, 0x4);
 	}
@@ -441,75 +884,68 @@
 	udelay(1000);
 	MNH_DDR_PHY_OUTf(1051, PHY_SET_DFI_INPUT_RST_PAD, 1);
 
-	gpiod_set_value_cansleep(iso_n, 1);
+	MNH_DDR_DEASSERT_ISO_N();
 	udelay(1000);
-
 	MNH_DDR_CTL_OUTf(00, START, 1);
+	start_jiff = jiffies;
+	timeout = start_jiff + RESUME_TIMEOUT;
 
-	dev_dbg(dev, "%s waiting for init done.", __func__);
+	dev_dbg(dev, "%s waiting for ctl init done.", __func__);
+	while ((!mnh_ddr_int_status_bit(INIT_DONE_SBIT)) &&
+			time_before(jiffies, timeout))
+		udelay(10);
 
-	timeout = 0;
-	while ((timeout < 1000) && (!mnh_ddr_int_status_bit(INIT_DONE_SBIT))) {
-		udelay(1);
-		timeout++;
-	}
+	end_jiff = jiffies;
+	dev_dbg(dev, "%s time elapsed is %u ms",
+		__func__, jiffies_to_msecs(end_jiff - start_jiff));
 
 	if (!mnh_ddr_int_status_bit(INIT_DONE_SBIT)) {
 		dev_err(dev, "%s time out on init done %llx.\n",
-			__func__, mnh_ddr_int_status(dev));
+			__func__, mnh_ddr_int_status());
 		return -ETIMEDOUT;
 	}
 
 	/* need to clear PWRUP_SREFRESH_EXIT to clear interrupt status bit 0 */
 	MNH_DDR_CTL_OUTf(81, PWRUP_SREFRESH_EXIT, 0);
 	dev_dbg(dev, "%s got init done %llx.\n", __func__,
-		mnh_ddr_int_status(dev));
-	mnh_ddr_clr_int_status(dev);
-	mnh_lpddr_freq_change(SAVED_FSP(_state));
-
-	dev_dbg(dev, "%s: tref 0x%04x 0x%04x 0x%04x 0x%04x\n",
-		__func__, MNH_DDR_CTL_INf(56, TREF_F0),
-		MNH_DDR_CTL_INf(57, TREF_F1), MNH_DDR_CTL_INf(58, TREF_F2),
-		MNH_DDR_CTL_INf(59, TREF_F3));
-
+		mnh_ddr_int_status());
+	mnh_ddr_clr_int_status();
 	mnh_ddr_enable_lp();
 
 	return 0;
 }
 EXPORT_SYMBOL(mnh_ddr_resume);
 
-int mnh_ddr_po_init(struct mnh_ddr_data *data, struct gpio_desc *iso_n)
+int mnh_ddr_po_init(struct device *dev, struct gpio_desc *iso_n)
 {
-	struct device *dev = &data->pdev->dev;
-	struct mnh_ddr_internal_state *_state = &data->_state;
-
-	int index, setindex;
+	int index, setindex, pi_step;
 	unsigned long timeout;
 	const struct mnh_ddr_reg_config *cfg = &mnh_ddr_33_100_400_600;
 
-	mnh_ddr_init_internal_state(_state, cfg);
+	if (WARN_ON(!_state))
+		return -ENOMEM;
 
+	mnh_ddr_init_internal_state(_state, cfg, iso_n);
 	dev_dbg(dev, "%s start.", __func__);
 
 	/* deassert iso_n */
-	gpiod_set_value_cansleep(iso_n, 1);
+	MNH_DDR_DEASSERT_ISO_N();
+	mnh_ddr_init_clocks(dev, LPDDR_FREQ_FSP0);
 
-	mnh_ddr_init_clocks(data);
+	if (!mnh_ddr_sanity_check())
+		return -EIO;
 
 	for (index = 0; index < MNH_DDR_NUM_CTL_REG; index++)
-		WRITE_DDR_REG_CONFIG(_state, ctl, index);
-
-	/* Make sure DRAM will request refresh rate adjustments */
-	MNH_DDR_CTL_OUTf(164, MR13_DATA_0, 0xD0);
+		WRITE_DDR_REG_CONFIG(ctl, index);
 
 	for (index = 0; index < MNH_DDR_NUM_PI_REG; index++)
-		WRITE_DDR_REG_CONFIG(_state, pi, index);
+		WRITE_DDR_REG_CONFIG(pi, index);
 
 	MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_MULTICAST_EN, 1);
 	MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_INDEX, 0);
 
 	for (index = 0; index < MNH_DDR_NUM_PHY_REG; index++)
-		WRITE_DDR_PHY_CONFIG(_state, 0, index);
+		WRITE_DDR_PHY_CONFIG(0, index);
 
 	MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_MULTICAST_EN, 0);
 	MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_INDEX, 1);
@@ -518,9 +954,8 @@
 	setindex = 0;
 	while ((setindex < MNH_DDR_PHY_SET_SIZE) &&
 		(cfg->phy_setA[setindex][0] != 0xFFFFFFFF)) {
-		WRITE_SET_ELEMENT(_state,
-				  cfg->phy_setA[setindex][0],
-				  cfg->phy_setA[setindex][1]);
+		WRITE_SET_ELEMENT(cfg->phy_setA[setindex][0],
+			cfg->phy_setA[setindex][1]);
 		setindex++;
 	}
 
@@ -530,40 +965,148 @@
 	setindex = 0;
 	while ((setindex < MNH_DDR_PHY_SET_SIZE) &&
 		(cfg->phy_setB[setindex][0] != 0xFFFFFFFF)) {
-		WRITE_SET_ELEMENT(_state,
-				  cfg->phy_setB[setindex][0],
-				  cfg->phy_setB[setindex][1]);
+		WRITE_SET_ELEMENT(cfg->phy_setB[setindex][0],
+			cfg->phy_setB[setindex][1]);
 		setindex++;
 	}
 
+	/* set the index back to 1 to enable PI WA training */
+	MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_INDEX, 1);
+	/* Add SCU register change to enable SW switching for all 4 FSPs. */
+	MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0x4CD9);
+	MNH_SCU_OUTxf(LPDDR4_FSP_SETTING, 0, FSP_SW_CTRL, 1);
+	MNH_SCU_OUTxf(LPDDR4_FSP_SETTING, 1, FSP_SW_CTRL, 1);
+	MNH_SCU_OUTxf(LPDDR4_FSP_SETTING, 2, FSP_SW_CTRL, 1);
+	MNH_SCU_OUTxf(LPDDR4_FSP_SETTING, 3, FSP_SW_CTRL, 1);
+	MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0x0);
+
 	dev_dbg(dev, "%s begin training,", __func__);
 	MNH_DDR_PI_OUTf(00, PI_START, 1);
 	MNH_DDR_CTL_OUTf(00, START, 1);
 
-	timeout = jiffies + TRAINING_TIMEOUT;
-	while (time_before(jiffies, timeout) &&
-	       (!mnh_ddr_int_status_bit(INIT_DONE_SBIT)))
-		usleep_range(100, 200);
+	/*
+	 * The 11 DFS events during PI training are to be tracked,
+	 * and apply WA at correct points.
+	 * 1.	FSP0 to FSP1
+	 * 2.	FSP1 to FSP2
+	 * 3.	FSP2 to FSP1
+	 * 4.	FSP1 to FSP2
+	 * 5.	FSP2 to FSP1 - Program POSTDIV of FSP2 to 1 at
+	 * pi_freq_change_req event
+	 * 6.	FSP1 to FSP2 - Program POSTDIV of FSP2 to 0 at
+	 * pi_freq_change_req event, provide 400 MHz, set pi_freq_change_ack = 1
+	 * 7.	FSP2 to FSP3
+	 * 8.	FSP3 to FSP2
+	 * 9.	FSP2 to FSP3
+	 * 10.	FSP3 to FSP2 - Program POSTDIV of FSP2&FSP3 to 1 at
+	 * pi_freq_change_req event, Provide 300 MHz clock instead of usual
+	 * 400 MHz clock, set pi_freq_change_ack = 1
+	 * 11.	FSP2 to FSP3 - Program POSTDIV of FSP2&FSP3 to 0 at
+	 * pi_freq_change_req event, provide 600 MHz, set pi_freq_change_ack = 1
+	 *
+	 */
+	dev_info(dev, "%s PI WA training,", __func__);
+	for (pi_step = 1; pi_step <= 11; pi_step++) {
+		timeout = 10000;
+		while (!MNH_SCU_INf(SCU_IRQ_STATUS, LP4_FREQ_CHG_REQ) &&
+			  (timeout-- > 0))
+			udelay(1);
+		if (!MNH_SCU_INf(SCU_IRQ_STATUS, LP4_FREQ_CHG_REQ)) {
+			dev_err(dev, "%s: Missed SCU_IRQ_STATUS.LP4_FREQ_CHG_REQ! pi_step=%d, LPDDR4_REQ_FSP=0x%x\n",
+			__func__,
+			pi_step,
+			MNH_SCU_INf(LPDDR4_LOW_POWER_STS, LPDDR4_REQ_FSP));
+			return -ETIME;
+		}
+		dev_dbg(dev, "%s: pi_step=%d, LPDDR4_REQ_FSP=0x%x, INDEX=%d\n",
+			__func__,
+			pi_step,
+			MNH_SCU_INf(LPDDR4_LOW_POWER_STS, LPDDR4_REQ_FSP),
+			MNH_DDR_PHY_INf(1025, PHY_FREQ_SEL_INDEX));
+		/* clear it */
+		MNH_SCU_OUTf(SCU_IRQ_STATUS, LP4_FREQ_CHG_REQ, 1);
 
-	if (!mnh_ddr_int_status_bit(INIT_DONE_SBIT)) {
-		dev_err(dev, "%s timed out on init done.\n", __func__);
+		/* Change PHY PLL POSTDIV at Step 5,6,10,11 */
+		switch (pi_step) {
+		case 5:
+			/* change postdiv of CA PLL */
+			MNH_DDR_PHY_OUTf(1046, PHY_PLL_CTRL_CA, 0x322);
+			break;
+		case  6:
+			/* change postdiv of CA PLL for f2 register set */
+			MNH_DDR_PHY_OUTf(1046, PHY_PLL_CTRL_CA, 0x122);
+			break;
+		case 10:
+			/* change postdiv of CA PLL for f2/f3 register set */
+			MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_INDEX, 1);
+			MNH_DDR_PHY_OUTf(1046, PHY_PLL_CTRL_CA, 0x322);
+			MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_INDEX, 2);
+			MNH_DDR_PHY_OUTf(1046, PHY_PLL_CTRL_CA, 0x322);
+			break;
+		case 11:
+			/* change postdiv of CA PLL for f2 register set */
+			MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_INDEX, 1);
+			MNH_DDR_PHY_OUTf(1046, PHY_PLL_CTRL_CA, 0x122);
+			MNH_DDR_PHY_OUTf(1025, PHY_FREQ_SEL_INDEX, 2);
+			MNH_DDR_PHY_OUTf(1046, PHY_PLL_CTRL_CA, 0x122);
+			break;
+		}
+
+		/* Freeze the divider settings till all registers are updated */
+		MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0x4CD9);
+		MNH_SCU_OUTf(LPDDR4_REFCLK_PLL_CTRL, FRZ_PLL_IN, 1);
+		MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0x0);
+		/* load clock settings from the fsp of interest based on PI's
+		 * fsp request
+		 */
+		WRITE_CLK_FROM_FSP_NO_LOCK((MNH_SCU_INf(LPDDR4_LOW_POWER_STS,
+						LPDDR4_REQ_FSP)));
+		/* Provide 300 MHz clock */
+		if (pi_step == 10)
+			/* 1200 MHz div by 4 */
+			MNH_SCU_OUTf(CCU_CLK_DIV, LPDDR4_REFCLK_DIV, 3);
+		/* UnFreeze the divider settings till all regs are updated */
+		MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0x4CD9);
+		MNH_SCU_OUTf(LPDDR4_REFCLK_PLL_CTRL, FRZ_PLL_IN, 0);
+		MNH_SCU_OUTf(PLL_PASSCODE, PASSCODE, 0x0);
+		/* effect the clock change */
+		MNH_SCU_OUTf(LPDDR4_LOW_POWER_CFG,
+				 LP4_FSP_SW_OVERRIDE, 1);
+		/* inform memory controller freq change is done */
+		MNH_SCU_OUTf(LPDDR4_LOW_POWER_CFG,
+				 LP4_FREQ_CHG_ACK, 1);
+		MNH_SCU_OUTf(LPDDR4_LOW_POWER_CFG,
+				 LP4_FSP_SW_OVERRIDE, 0);
+	}
+
+	timeout = jiffies + msecs_to_jiffies(50);
+	while (!(mnh_ddr_int_status_bit(INIT_DONE_SBIT) &&
+			mnh_ddr_pi_int_status_bit(PI_INIT_DONE_BIT)) &&
+			time_before(jiffies, timeout))
+		udelay(100);
+
+	if (!mnh_ddr_int_status_bit(INIT_DONE_SBIT) ||
+		!mnh_ddr_pi_int_status_bit(PI_INIT_DONE_BIT)) {
+		dev_err(dev, "%s timed out on init done. 0x%llx 0x%08x\n",
+			__func__,
+			mnh_ddr_int_status(),
+			MNH_DDR_PI_INf(172, PI_INT_STATUS));
 		return -ETIMEDOUT;
 	}
 
 	dev_dbg(dev, "%s got init done %llx.\n", __func__,
-		 mnh_ddr_int_status(dev));
+		 mnh_ddr_int_status());
 
-	mnh_ddr_clr_int_status(dev);
+	mnh_ddr_clr_int_status();
 	MNH_DDR_CTL_OUTf(165, MR_FSP_DATA_VALID_F0_0, 1);
 	MNH_DDR_CTL_OUTf(165, MR_FSP_DATA_VALID_F1_0, 1);
 	MNH_DDR_CTL_OUTf(165, MR_FSP_DATA_VALID_F2_0, 1);
 	MNH_DDR_CTL_OUTf(166, MR_FSP_DATA_VALID_F3_0, 1);
 
-	dev_dbg(dev, "%s: tref 0x%04x 0x%04x 0x%04x 0x%04x\n",
-		__func__, MNH_DDR_CTL_INf(56, TREF_F0),
-		MNH_DDR_CTL_INf(57, TREF_F1), MNH_DDR_CTL_INf(58, TREF_F2),
-		MNH_DDR_CTL_INf(59, TREF_F3));
-
+	dev_dbg(dev, "%s done\n", __func__);
+	/* settings to take effect on resume */
+	MNH_DDR_CTL_OUTf(23, PHY_INDEP_TRAIN_MODE, 0);
+	MNH_DDR_CTL_OUTf(23, CDNS_INTRL0, 1);
 	mnh_ddr_enable_lp();
 
 	/* Enable FSP3 => 2400 */
@@ -573,10 +1116,8 @@
 }
 EXPORT_SYMBOL(mnh_ddr_po_init);
 
-u32 mnh_ddr_mbist(struct mnh_ddr_data *data, enum mnh_ddr_bist_type bist_type)
+u32 mnh_ddr_mbist(struct device *dev, enum mnh_ddr_bist_type bist_type)
 {
-	struct device *dev = &data->pdev->dev;
-
 	u32 result = 0;
 	u32 timeout = 1000000;
 	const u32 pattern[] = {
@@ -595,7 +1136,7 @@
 		return 0;
 	}
 
-	mnh_ddr_disable_lp(dev);
+	mnh_ddr_disable_lp();
 
 	old_in_order_accept = MNH_DDR_CTL_INf(223, IN_ORDER_ACCEPT);
 	MNH_DDR_CTL_OUTf(223, IN_ORDER_ACCEPT, 1);
@@ -625,14 +1166,14 @@
 
 	if (!mnh_ddr_int_status_bit(BIST_SBIT)) {
 		dev_err(dev, "%s: BIST timedout: %llx\n",
-			__func__, mnh_ddr_int_status(dev));
+			__func__, mnh_ddr_int_status());
 	} else {
 		result = MNH_DDR_CTL_INf(171, BIST_RESULT);
 		dev_info(dev, "%s: result 0x%02x\n", __func__, result);
 	}
 
 	MNH_DDR_CTL_OUTf(171, BIST_GO, 0);
-	mnh_ddr_clr_int_status(dev);
+	mnh_ddr_clr_int_status();
 
 	MNH_DDR_CTL_OUTf(223, IN_ORDER_ACCEPT,
 		old_in_order_accept);
@@ -642,10 +1183,15 @@
 }
 EXPORT_SYMBOL(mnh_ddr_mbist);
 
-int mnh_ddr_platform_init(struct platform_device *pdev,
-			  struct mnh_ddr_data *data)
+int mnh_ddr_platform_init(struct device *dev)
 {
-	data->pdev = pdev;
+	dev_dbg(dev, "%s\n", __func__);
+
+	_state = devm_kzalloc(dev, sizeof(struct mnh_ddr_internal_state),
+			      GFP_KERNEL);
+	if (!_state)
+		return -ENOMEM;
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(mnh_ddr_platform_init);
diff --git a/drivers/misc/mnh/mnh-ddr.h b/drivers/misc/mnh/mnh-ddr.h
index 372ca55..c609cc4 100644
--- a/drivers/misc/mnh/mnh-ddr.h
+++ b/drivers/misc/mnh/mnh-ddr.h
@@ -20,13 +20,13 @@
 #include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/gpio/consumer.h>
-#include <linux/platform_device.h>
 
 #define MNH_DDR_NUM_CTL_REG	(558 + 1)
 #define MNH_DDR_NUM_PHY_REG	(1100 + 1)
 #define MNH_DDR_NUM_PI_REG	(191 + 1)
 
 #define MNH_DDR_NUM_FSPS (4)
+#define MNH_DDR_NUM_BANKED_FSPS (3)
 #define MNH_DDR_NUM_BASES (3)
 
 /* arbitrary but sufficient size for phy deltas */
@@ -48,10 +48,10 @@
 	u32 pi_base;
 	u32 pi[MNH_DDR_NUM_PI_REG];
 	u32 phy_base;
-	u32 phy[MNH_DDR_NUM_FSPS][MNH_DDR_NUM_PHY_REG];
+	u32 phy[MNH_DDR_NUM_BANKED_FSPS][MNH_DDR_NUM_PHY_REG];
 	u32 fsps[MNH_DDR_NUM_FSPS];
-	u32 suspend_fsp;
 	u32 tref[MNH_DDR_NUM_FSPS];
+	struct gpio_desc *iso_n;
 };
 
 enum mnh_ddr_bist_type {
@@ -59,18 +59,15 @@
 	LIMITED_MOVI1_3N,
 };
 
-struct mnh_ddr_data {
-	struct platform_device *pdev;
-	struct mnh_ddr_internal_state _state;
-};
+/* mnh-ddr driver init called during mnh-sm probe */
+int mnh_ddr_platform_init(struct device *dev);
 
-int mnh_ddr_platform_init(struct platform_device *pdev,
-			  struct mnh_ddr_data *data);
-int mnh_ddr_po_init(struct mnh_ddr_data *data, struct gpio_desc *iso_n);
-int mnh_ddr_resume(struct mnh_ddr_data *data, struct gpio_desc *iso_n);
-int mnh_ddr_suspend(struct mnh_ddr_data *data, struct gpio_desc *iso_n);
-int mnh_ddr_clr_int_status(struct device *dev);
-u64 mnh_ddr_int_status(struct device *dev);
-u32 mnh_ddr_mbist(struct mnh_ddr_data *data, enum mnh_ddr_bist_type bist_type);
+int mnh_ddr_po_init(struct device *dev, struct gpio_desc *iso_n);
+int mnh_ddr_resume(struct device *dev);
+int mnh_ddr_suspend(struct device *dev);
+int mnh_ddr_clr_int_status(void);
+u64 mnh_ddr_int_status(void);
+u32 mnh_ddr_mbist(struct device *dev, enum mnh_ddr_bist_type bist_type);
+int mnh_ddr_sw_switch(int index);
 
 #endif /* __MNH_DDR_H__ */
diff --git a/drivers/misc/mnh/mnh-hwio-cpu.h b/drivers/misc/mnh/mnh-hwio-cpu.h
new file mode 100644
index 0000000..3644dea
--- /dev/null
+++ b/drivers/misc/mnh/mnh-hwio-cpu.h
@@ -0,0 +1,136 @@
+/* auto generated: Monday, August 15th, 2016 12:26:47pm */
+/* gucheng: edit 11/07/2018 for checkpatch */
+/*
+ * Copyright (c) 2016, Intel Corporation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of Intel nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific
+ * prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __MNH_HWIO_CPU_
+#define __MNH_HWIO_CPU_
+
+#define HWIO_CPU_CFG_REGOFF 0x0
+#define HWIO_CPU_CFG_ADDR(bAddr, regX) (bAddr + HWIO_CPU_CFG_REGOFF)
+#define HWIO_CPU_CFG_CLUSTERIDAFF2_FLDMASK (0xff000000)
+#define HWIO_CPU_CFG_CLUSTERIDAFF2_FLDSHFT (24)
+#define HWIO_CPU_CFG_CLUSTERIDAFF1_FLDMASK (0xff0000)
+#define HWIO_CPU_CFG_CLUSTERIDAFF1_FLDSHFT (16)
+#define HWIO_CPU_CFG_RSVD0_FLDMASK (0xfff0)
+#define HWIO_CPU_CFG_RSVD0_FLDSHFT (4)
+#define HWIO_CPU_CFG_VINITHI_FLDMASK (0x8)
+#define HWIO_CPU_CFG_VINITHI_FLDSHFT (3)
+#define HWIO_CPU_CFG_CFGTE_FLDMASK (0x4)
+#define HWIO_CPU_CFG_CFGTE_FLDSHFT (2)
+#define HWIO_CPU_CFG_CFGEND_FLDMASK (0x2)
+#define HWIO_CPU_CFG_CFGEND_FLDSHFT (1)
+#define HWIO_CPU_CFG_AA64nAA32_FLDMASK (0x1)
+#define HWIO_CPU_CFG_AA64nAA32_FLDSHFT (0)
+
+#define HWIO_CPU_RST_CFG_REGOFF 0x4
+#define HWIO_CPU_RST_CFG_ADDR(bAddr, regX) (bAddr + HWIO_CPU_RST_CFG_REGOFF)
+#define HWIO_CPU_RST_CFG_RSVD0_FLDMASK (0xfffffffc)
+#define HWIO_CPU_RST_CFG_RSVD0_FLDSHFT (2)
+#define HWIO_CPU_RST_CFG_DBGL1RSTDISABLE_FLDMASK (0x2)
+#define HWIO_CPU_RST_CFG_DBGL1RSTDISABLE_FLDSHFT (1)
+#define HWIO_CPU_RST_CFG_L2RSTDISABLE_FLDMASK (0x1)
+#define HWIO_CPU_RST_CFG_L2RSTDISABLE_FLDSHFT (0)
+
+#define HWIO_CPU_RST_STS_REGOFF 0x8
+#define HWIO_CPU_RST_STS_ADDR(bAddr, regX) (bAddr + HWIO_CPU_RST_STS_REGOFF)
+#define HWIO_CPU_RST_STS_RSVD0_FLDMASK (0xfffffffc)
+#define HWIO_CPU_RST_STS_RSVD0_FLDSHFT (2)
+#define HWIO_CPU_RST_STS_DBGRSTREQ_FLDMASK (0x2)
+#define HWIO_CPU_RST_STS_DBGRSTREQ_FLDSHFT (1)
+#define HWIO_CPU_RST_STS_WARMRSTREQ_FLDMASK (0x1)
+#define HWIO_CPU_RST_STS_WARMRSTREQ_FLDSHFT (0)
+
+#define HWIO_CPU_PWR_MGMT_CFG_REGOFF 0x0C
+#define HWIO_CPU_PWR_MGMT_CFG_ADDR(bAddr, regX) \
+			(bAddr + HWIO_CPU_PWR_MGMT_CFG_REGOFF)
+#define HWIO_CPU_PWR_MGMT_CFG_RSVD0_FLDMASK (0xfffffffc)
+#define HWIO_CPU_PWR_MGMT_CFG_RSVD0_FLDSHFT (2)
+#define HWIO_CPU_PWR_MGMT_CFG_L2FLUSHREQ_FLDMASK (0x2)
+#define HWIO_CPU_PWR_MGMT_CFG_L2FLUSHREQ_FLDSHFT (1)
+#define HWIO_CPU_PWR_MGMT_CFG_EVENTI_FLDMASK (0x1)
+#define HWIO_CPU_PWR_MGMT_CFG_EVENTI_FLDSHFT (0)
+
+#define HWIO_CPU_PWR_MGMT_STS_REGOFF 0x10
+#define HWIO_CPU_PWR_MGMT_STS_ADDR(bAddr, regX) \
+			(bAddr + HWIO_CPU_PWR_MGMT_STS_REGOFF)
+#define HWIO_CPU_PWR_MGMT_STS_RSVD0_FLDMASK (0xffffff00)
+#define HWIO_CPU_PWR_MGMT_STS_RSVD0_FLDSHFT (8)
+#define HWIO_CPU_PWR_MGMT_STS_CPUQACTIVE_FLDMASK (0x80)
+#define HWIO_CPU_PWR_MGMT_STS_CPUQACTIVE_FLDSHFT (7)
+#define HWIO_CPU_PWR_MGMT_STS_DBGPWRUPREQ_FLDMASK (0x40)
+#define HWIO_CPU_PWR_MGMT_STS_DBGPWRUPREQ_FLDSHFT (6)
+#define HWIO_CPU_PWR_MGMT_STS_DBGNOPWRDWN_FLDMASK (0x20)
+#define HWIO_CPU_PWR_MGMT_STS_DBGNOPWRDWN_FLDSHFT (5)
+#define HWIO_CPU_PWR_MGMT_STS_SMPEN_FLDMASK (0x10)
+#define HWIO_CPU_PWR_MGMT_STS_SMPEN_FLDSHFT (4)
+#define HWIO_CPU_PWR_MGMT_STS_L2FLUSHDONE_FLDMASK (0x8)
+#define HWIO_CPU_PWR_MGMT_STS_L2FLUSHDONE_FLDSHFT (3)
+#define HWIO_CPU_PWR_MGMT_STS_STANDBYWFE_FLDMASK (0x4)
+#define HWIO_CPU_PWR_MGMT_STS_STANDBYWFE_FLDSHFT (2)
+#define HWIO_CPU_PWR_MGMT_STS_STANDBYWFI_FLDMASK (0x2)
+#define HWIO_CPU_PWR_MGMT_STS_STANDBYWFI_FLDSHFT (1)
+#define HWIO_CPU_PWR_MGMT_STS_EVENTO_FLDMASK (0x1)
+#define HWIO_CPU_PWR_MGMT_STS_EVENTO_FLDSHFT (0)
+
+#define HWIO_CPU_DBG_CFG_REGOFF 0x14
+#define HWIO_CPU_DBG_CFG_ADDR(bAddr, regX) (bAddr + HWIO_CPU_DBG_CFG_REGOFF)
+#define HWIO_CPU_DBG_CFG_RSVD0_FLDMASK (0xfffffffc)
+#define HWIO_CPU_DBG_CFG_RSVD0_FLDSHFT (2)
+#define HWIO_CPU_DBG_CFG_NIDEN_FLDMASK (0x2)
+#define HWIO_CPU_DBG_CFG_NIDEN_FLDSHFT (1)
+#define HWIO_CPU_DBG_CFG_DBGEN_FLDMASK (0x1)
+#define HWIO_CPU_DBG_CFG_DBGEN_FLDSHFT (0)
+
+#define HWIO_CPU_STS_REGOFF 0x18
+#define HWIO_CPU_STS_ADDR(bAddr, regX) (bAddr + HWIO_CPU_STS_REGOFF)
+#define HWIO_CPU_STS_STATUS_FLDMASK (0xffffffff)
+#define HWIO_CPU_STS_STATUS_FLDSHFT (0)
+
+#define HWIO_CPU_SECURE_REGOFF 0x1C
+#define HWIO_CPU_SECURE_ADDR(bAddr, regX) (bAddr + HWIO_CPU_SECURE_REGOFF)
+#define HWIO_CPU_SECURE_SECURE_LOCK_FLDMASK (0x80000000)
+#define HWIO_CPU_SECURE_SECURE_LOCK_FLDSHFT (31)
+#define HWIO_CPU_SECURE_RSVD0_FLDMASK (0x7ffffff8)
+#define HWIO_CPU_SECURE_RSVD0_FLDSHFT (3)
+#define HWIO_CPU_SECURE_CP15SDISABLE_FLDMASK (0x4)
+#define HWIO_CPU_SECURE_CP15SDISABLE_FLDSHFT (2)
+#define HWIO_CPU_SECURE_SPNIDEN_FLDMASK (0x2)
+#define HWIO_CPU_SECURE_SPNIDEN_FLDSHFT (1)
+#define HWIO_CPU_SECURE_SPIDEN_FLDMASK (0x1)
+#define HWIO_CPU_SECURE_SPIDEN_FLDSHFT (0)
+
+#define HWIO_CPU_DBG_BUS_REGOFF 0x20
+#define HWIO_CPU_DBG_BUS_ADDR(bAddr, regX) (bAddr + HWIO_CPU_DBG_BUS_REGOFF)
+#define HWIO_CPU_DBG_BUS_RSVD0_FLDMASK (0xfffffffc)
+#define HWIO_CPU_DBG_BUS_RSVD0_FLDSHFT (2)
+#define HWIO_CPU_DBG_BUS_DBG_MUX_SEL_FLDMASK (0x3)
+#define HWIO_CPU_DBG_BUS_DBG_MUX_SEL_FLDSHFT (0)
+
+#endif /* __MNH_HWIO_CPU_ */
diff --git a/drivers/misc/mnh/mnh-hwio-scu.h b/drivers/misc/mnh/mnh-hwio-scu.h
index 965c450..38dff31 100644
--- a/drivers/misc/mnh/mnh-hwio-scu.h
+++ b/drivers/misc/mnh/mnh-hwio-scu.h
@@ -91,6 +91,8 @@
 #define HWIO_SCU_GPS_GPS_FLDMASK (0xffffffff)
 #define HWIO_SCU_GPS_GPS_FLDSHFT (0)
 
+#define MNH_BOOT_STAT  (HWIO_SCU_GPS_ADDR(HWIO_SCU_BASE_ADDR, 0))
+
 /* General Purpose Scratchpad */
 /*
  * Allocation:
diff --git a/drivers/misc/mnh/mnh-pcie.c b/drivers/misc/mnh/mnh-pcie.c
index d213336..8dc1b31 100644
--- a/drivers/misc/mnh/mnh-pcie.c
+++ b/drivers/misc/mnh/mnh-pcie.c
@@ -2013,10 +2013,14 @@
 	dev_dbg(&pdev->dev, "MNH PCIe driver is removed\n");
 }
 
-int mnh_pci_suspend(void)
+int mnh_pci_suspend(struct pci_dev *pdev)
 {
-	struct pci_dev *pdev = mnh_dev->pdev;
-	struct device *dev = &mnh_dev->pdev->dev;
+	struct device *dev;
+
+	if (!pdev || !pci_get_drvdata(pdev))
+		return 0;
+
+	dev = &pdev->dev;
 
 	dev_dbg(dev, "%s: enter\n", __func__);
 
@@ -2042,12 +2046,16 @@
 }
 EXPORT_SYMBOL_GPL(mnh_pci_suspend);
 
-int mnh_pci_resume(void)
+int mnh_pci_resume(struct pci_dev *pdev)
 {
-	struct pci_dev *pdev = mnh_dev->pdev;
-	struct device *dev = &mnh_dev->pdev->dev;
+	struct device *dev;
 	int ret = 0;
 
+	if (!pdev || !pci_get_drvdata(pdev))
+		return -ENODEV;
+
+	dev = &pdev->dev;
+
 	dev_dbg(dev, "%s: enter\n", __func__);
 
 	mnh_dev->powered = true;
diff --git a/drivers/misc/mnh/mnh-pcie.h b/drivers/misc/mnh/mnh-pcie.h
index e361557..c23bbf3 100644
--- a/drivers/misc/mnh/mnh-pcie.h
+++ b/drivers/misc/mnh/mnh-pcie.h
@@ -474,6 +474,6 @@
 void mnh_unmap_mem(
         dma_addr_t dma_addr, size_t size, enum dma_data_direction direction);
 
-int mnh_pci_suspend(void);
-int mnh_pci_resume(void);
+int mnh_pci_suspend(struct pci_dev *pdev);
+int mnh_pci_resume(struct pci_dev *pdev);
 #endif /* __MNH_PCIE_HOST */
diff --git a/drivers/misc/mnh/mnh-pwr.c b/drivers/misc/mnh/mnh-pwr.c
index cf9b761..4185429 100644
--- a/drivers/misc/mnh/mnh-pwr.c
+++ b/drivers/misc/mnh/mnh-pwr.c
@@ -264,7 +264,7 @@
 		 * Due to pcie failure, suspend the driver state only after
 		 * updating link status.
 		 */
-		ret = mnh_pci_suspend();
+		ret = mnh_pci_suspend(pcidev);
 		if (ret)
 			dev_warn(mnh_pwr->dev,
 				 "%s: mnh_pci_suspend failed (%d)\n",
@@ -273,7 +273,7 @@
 		mnh_pwr->pcie_failure = false;
 	} else {
 		/* suspend the driver state */
-		ret = mnh_pci_suspend();
+		ret = mnh_pci_suspend(pcidev);
 		if (ret)
 			dev_warn(mnh_pwr->dev,
 				 "%s: mnh_pci_suspend failed (%d)\n",
@@ -363,7 +363,7 @@
 		pci_restore_state(pcidev);
 
 		/* resume the driver state */
-		ret = mnh_pci_resume();
+		ret = mnh_pci_resume(pcidev);
 		if (ret) {
 			dev_err(mnh_pwr->dev, "%s: mnh_pci_resume failed (%d)\n",
 				__func__, ret);
diff --git a/drivers/misc/mnh/mnh-sm.c b/drivers/misc/mnh/mnh-sm.c
index 962c64f..965d735 100644
--- a/drivers/misc/mnh/mnh-sm.c
+++ b/drivers/misc/mnh/mnh-sm.c
@@ -191,9 +191,6 @@
 	/* state of the ddr channel */
 	enum mnh_ddr_status ddr_status;
 
-	/* mnh-ddr data */
-	struct mnh_ddr_data mnh_ddr_data;
-
 	/* pin used for ddr pad isolation */
 	struct gpio_desc *ddr_pad_iso_n_pin;
 
@@ -1383,9 +1380,8 @@
 		return -EINVAL;
 
 	mnh_pwr_set_state(MNH_PWR_S0);
-	mnh_ddr_po_init(&mnh_sm_dev->mnh_ddr_data,
-			mnh_sm_dev->ddr_pad_iso_n_pin);
-	mnh_ddr_mbist(&mnh_sm_dev->mnh_ddr_data, val);
+	mnh_ddr_po_init(mnh_sm_dev->dev, mnh_sm_dev->ddr_pad_iso_n_pin);
+	mnh_ddr_mbist(dev, val);
 	mnh_pwr_set_state(MNH_PWR_S4);
 
 	return count;
@@ -1495,8 +1491,7 @@
 	int ret;
 
 	/* Initialize DDR */
-	ret = mnh_ddr_po_init(&mnh_sm_dev->mnh_ddr_data,
-			      mnh_sm_dev->ddr_pad_iso_n_pin);
+	ret = mnh_ddr_po_init(mnh_sm_dev->dev, mnh_sm_dev->ddr_pad_iso_n_pin);
 	if (ret) {
 		dev_err(mnh_sm_dev->dev, "%s: ddr training failed (%d)\n",
 			__func__, ret);
@@ -1509,9 +1504,16 @@
 
 static int mnh_sm_resume_ddr(void)
 {
+	int ret;
+
 	/* deassert pad isolation, take ddr out of self-refresh mode */
-	mnh_ddr_resume(&mnh_sm_dev->mnh_ddr_data,
-		       mnh_sm_dev->ddr_pad_iso_n_pin);
+	ret = mnh_ddr_resume(mnh_sm_dev->dev);
+	if (ret) {
+		dev_err(mnh_sm_dev->dev, "%s: error resuming dram (%d)\n",
+			__func__, ret);
+		return ret;
+	}
+
 	mnh_sm_dev->ddr_status = MNH_DDR_ACTIVE;
 	return 0;
 }
@@ -1519,8 +1521,7 @@
 static int mnh_sm_suspend_ddr(void)
 {
 	/* put ddr into self-refresh mode, assert pad isolation */
-	mnh_ddr_suspend(&mnh_sm_dev->mnh_ddr_data,
-			mnh_sm_dev->ddr_pad_iso_n_pin);
+	mnh_ddr_suspend(mnh_sm_dev->dev);
 	mnh_sm_dev->ddr_status = MNH_DDR_SELF_REFRESH;
 	return 0;
 }
@@ -1663,16 +1664,23 @@
 	int err;
 	uint32_t val;
 
-	err = mnh_config_read(MNH_BOOT_TRACE, sizeof(val), &val);
+	err = mnh_config_read(MNH_BOOT_STAT, sizeof(val), &val);
+	if (err) {
+		dev_err(dev,
+			"%s: failed reading MNH_BOOT_STAT (%d)\n",
+			__func__, err);
+	} else {
+		dev_info(dev, "MNH_BOOT_STAT = 0x%x\n", val);
+	}
 
+	err = mnh_config_read(MNH_BOOT_TRACE, sizeof(val), &val);
 	if (err) {
 		dev_err(dev,
 			"%s: failed reading MNH_BOOT_TRACE (%d)\n",
 			__func__, err);
-		return;
+	} else {
+		dev_info(dev, "MNH_BOOT_TRACE = 0x%x\n", val);
 	}
-
-	dev_info(dev, "%s: MNH_BOOT_TRACE = 0x%x\n", __func__, val);
 }
 
 static void mnh_sm_enable_ready_irq(bool enable)
@@ -2435,7 +2443,7 @@
 	}
 
 	/* initialize mnh-ddr driver */
-	error = mnh_ddr_platform_init(pdev, &mnh_sm_dev->mnh_ddr_data);
+	error = mnh_ddr_platform_init(dev);
 	if (error) {
 		dev_err(dev, "failed to initialize mnh-ddr (%d)\n", error);
 		goto fail_probe_2;
diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c
index 1e3e175..125be43 100644
--- a/drivers/phy/phy-qcom-ufs.c
+++ b/drivers/phy/phy-qcom-ufs.c
@@ -802,6 +802,25 @@
 }
 EXPORT_SYMBOL(ufs_qcom_phy_dump_regs);
 
+void ufs_qcom_phy_print_phy_state(struct phy *generic_phy)
+{
+	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
+
+	dev_err(ufs_qcom_phy->dev, "phy->is_iface_clk_enabled = %x\n",
+		ufs_qcom_phy->is_iface_clk_enabled);
+	dev_err(ufs_qcom_phy->dev, "phy->is_ref_clk_enabled = %x\n",
+		ufs_qcom_phy->is_ref_clk_enabled);
+	dev_err(ufs_qcom_phy->dev, "phy->is_dev_ref_clk_enabled = %x\n",
+		ufs_qcom_phy->is_dev_ref_clk_enabled);
+	dev_err(ufs_qcom_phy->dev, "phy->is_powered_on = %x\n",
+		ufs_qcom_phy->is_powered_on);
+	dev_err(ufs_qcom_phy->dev, "phy->vdda_pll.enabled = %x\n",
+		ufs_qcom_phy->vdda_pll.enabled);
+	dev_err(ufs_qcom_phy->dev, "phy->vdda_phy.enabled = %x\n",
+		ufs_qcom_phy->vdda_phy.enabled);
+}
+EXPORT_SYMBOL(ufs_qcom_phy_print_phy_state);
+
 void ufs_qcom_phy_dbg_register_dump(struct phy *generic_phy)
 {
 	struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index 49c04c4..7729025 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, 2019, The Linux Foundation. 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
@@ -330,11 +330,9 @@
 static int ipa3_active_clients_panic_notifier(struct notifier_block *this,
 		unsigned long event, void *ptr)
 {
-	ipa3_active_clients_lock();
 	ipa3_active_clients_log_print_table(active_clients_table_buf,
 			IPA3_ACTIVE_CLIENTS_TABLE_BUF_SIZE);
 	IPAERR("%s", active_clients_table_buf);
-	ipa3_active_clients_unlock();
 
 	return NOTIFY_DONE;
 }
diff --git a/drivers/power/supply/qcom/smb-lib.c b/drivers/power/supply/qcom/smb-lib.c
index eaf5e36..850260e 100644
--- a/drivers/power/supply/qcom/smb-lib.c
+++ b/drivers/power/supply/qcom/smb-lib.c
@@ -4737,6 +4737,7 @@
 {
 	int rc = 0;
 	u8 stat;
+	bool debounce_done = false;
 
 	rc = smblib_set_prop_typec_power_role_locked(
 					chg, POWER_SUPPLY_TYPEC_PR_SINK);
@@ -4752,9 +4753,16 @@
 		smblib_err(chg, "Couldn't read Type-C status 4 rc=%d\n", rc);
 		return rc;
 	}
+	smblib_err(chg, "Type-C status 4 :%x\n", stat);
 
+	if ((stat & TYPEC_VBUS_STATUS_BIT) &&
+	   !(stat & TYPEC_DEBOUNCE_DONE_STATUS_BIT))
+		goto done;
+
+	debounce_done = true;
 	*attached = stat & CC_ATTACHED_BIT ? true : false;
 
+done:
 	rc = smblib_set_prop_typec_power_role_locked(
 					chg, POWER_SUPPLY_TYPEC_PR_NONE);
 	if (rc < 0) {
@@ -4762,6 +4770,9 @@
 		return rc;
 	}
 
+	if (!debounce_done)
+		return -EAGAIN;
+
 	return 0;
 }
 
diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c
index 7d54725..8eae291 100644
--- a/drivers/scsi/ufs/ufs-qcom.c
+++ b/drivers/scsi/ufs/ufs-qcom.c
@@ -2730,6 +2730,14 @@
 	ufs_qcom_ice_print_regs(host);
 }
 
+void ufs_qcom_print_phy_state(struct ufs_hba *hba)
+{
+	struct ufs_qcom_host *host = ufshcd_get_variant(hba);
+	struct phy *phy = host->generic_phy;
+
+	ufs_qcom_phy_print_phy_state(phy);
+}
+
 /**
  * struct ufs_hba_qcom_vops - UFS QCOM specific variant operations
  *
diff --git a/drivers/scsi/ufs/ufs-qcom.h b/drivers/scsi/ufs/ufs-qcom.h
index 12154b2..7df08db 100644
--- a/drivers/scsi/ufs/ufs-qcom.h
+++ b/drivers/scsi/ufs/ufs-qcom.h
@@ -412,4 +412,6 @@
 	return !!(host->caps & UFS_QCOM_CAP_SVS2);
 }
 
+void ufs_qcom_print_phy_state(struct ufs_hba *hba);
+
 #endif /* UFS_QCOM_H_ */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 6daa8ec..f88dc56 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -104,6 +104,10 @@
 
 static void ufshcd_update_error_stats(struct ufs_hba *hba, int type)
 {
+	hba->h8_err = false;
+	if (type == UFS_ERR_HIBERN8_EXIT || type == UFS_ERR_HIBERN8_ENTER)
+		hba->h8_err = true;
+
 	ufsdbg_set_err_state(hba);
 	if (type < UFS_ERR_MAX)
 		hba->ufs_stats.err_stats[type]++;
@@ -273,7 +277,7 @@
 				 UTP_TASK_REQ_COMPL |\
 				 UFSHCD_ERROR_MASK)
 /* UIC command timeout, unit: ms */
-#define UIC_CMD_TIMEOUT	500
+#define UIC_CMD_TIMEOUT	1500
 
 /* NOP OUT retries waiting for NOP IN response */
 #define NOP_OUT_RETRIES    10
@@ -286,7 +290,7 @@
 #define QUERY_REQ_TIMEOUT 1500 /* 1.5 seconds */
 
 /* Task management command timeout */
-#define TM_CMD_TIMEOUT	100 /* msecs */
+#define TM_CMD_TIMEOUT	1500 /* msecs */
 
 /* maximum number of retries for a general UIC command  */
 #define UFS_UIC_COMMAND_RETRIES 3
@@ -821,6 +825,14 @@
 	}
 }
 
+void ufshcd_print_phy_state(struct ufs_hba *hba)
+{
+	if (!(hba->ufshcd_dbg_print & UFSHCD_DBG_PRINT_UIC_ERR_HIST_EN))
+		return;
+
+	ufs_qcom_print_phy_state(hba);
+}
+
 static void ufshcd_print_uic_err_hist(struct ufs_hba *hba,
 		struct ufs_uic_err_reg_hist *err_hist, char *err_name)
 {
@@ -5511,10 +5523,10 @@
 				"Reject UPIU not fully implemented\n");
 			break;
 		default:
-			result = DID_ERROR << 16;
 			dev_err(hba->dev,
 				"Unexpected request response code = %x\n",
 				result);
+			result = DID_ERROR << 16;
 			break;
 		}
 		break;
@@ -6199,6 +6211,18 @@
 
 	hba = container_of(work, struct ufs_hba, eh_work);
 
+	if (hba->h8_err) {
+		dev_err(hba->dev, "%s: saved_err 0x%x saved_uic_err 0x%x",
+			__func__, hba->saved_err, hba->saved_uic_err);
+		ufshcd_print_host_regs(hba);
+		ufshcd_print_cmd_log(hba);
+		ufshcd_print_host_state(hba);
+		ufshcd_print_pwr_info(hba);
+		ufshcd_print_phy_state(hba);
+		hba->h8_err = false;
+		hba->silence_err_logs = true;
+	}
+
 	spin_lock_irqsave(hba->host->host_lock, flags);
 	ufsdbg_set_err_state(hba);
 
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index ed9d6a1e..716293c 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -867,6 +867,7 @@
 	u32 saved_ce_err;
 	bool silence_err_logs;
 	bool force_host_reset;
+	bool h8_err;
 
 	/* Device management request data */
 	struct ufs_dev_cmd dev_cmd;
diff --git a/drivers/soc/qcom/glink_spi_xprt.c b/drivers/soc/qcom/glink_spi_xprt.c
index a2673ef..a691c82 100644
--- a/drivers/soc/qcom/glink_spi_xprt.c
+++ b/drivers/soc/qcom/glink_spi_xprt.c
@@ -492,7 +492,7 @@
 					- read_id;
 
 		if ((offset + size_to_read) > size) {
-			pr_err("%s:wrong sz split_sz %u bufsz %u offset %u\n",
+			pr_err("%s:split_size %u buf size %u offset %u\n",
 				__func__, size_to_read, size, offset);
 			return -EINVAL;
 		}
@@ -549,7 +549,7 @@
 					- write_id;
 
 		if ((offset + size_to_write) > size) {
-			pr_err("%s:wrong sz split_sz %u bufsz %u offset %u\n",
+			pr_err("%s:split_size %u buf size %u offset %u\n",
 				__func__, size_to_write, size, offset);
 			return -EINVAL;
 		}
diff --git a/drivers/staging/qcacld-3.0/core/cds/src/cds_api.c b/drivers/staging/qcacld-3.0/core/cds/src/cds_api.c
index cf44c89..bce02ec 100644
--- a/drivers/staging/qcacld-3.0/core/cds/src/cds_api.c
+++ b/drivers/staging/qcacld-3.0/core/cds/src/cds_api.c
@@ -2712,8 +2712,8 @@
 inline void cds_pkt_stats_to_logger_thread(void *pl_hdr, void *pkt_dump,
 						void *data)
 {
-	if (cds_get_ring_log_level(RING_ID_PER_PACKET_STATS) !=
-						WLAN_LOG_LEVEL_ACTIVE)
+	if (cds_get_ring_log_level(RING_ID_PER_PACKET_STATS) <
+						WLAN_LOG_LEVEL_REPRO)
 		return;
 
 	wlan_pkt_stats_to_logger_thread(pl_hdr, pkt_dump, data);
diff --git a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_rx_api.h b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_rx_api.h
index db84567..f8783fc 100644
--- a/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_rx_api.h
+++ b/drivers/staging/qcacld-3.0/core/dp/ol/inc/ol_htt_rx_api.h
@@ -815,6 +815,12 @@
  * Return: number of buffers actually replenished
  */
 int htt_rx_msdu_buff_in_order_replenish(htt_pdev_handle pdev, uint32_t num);
+#else
+static inline
+int htt_rx_msdu_buff_in_order_replenish(htt_pdev_handle pdev, uint32_t num)
+{
+	return 0;
+}
 #endif
 
 /**
diff --git a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_defrag.c b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_defrag.c
index 4496b21..60ec626 100644
--- a/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_defrag.c
+++ b/drivers/staging/qcacld-3.0/core/dp/txrx/ol_rx_defrag.c
@@ -316,6 +316,26 @@
 
 #endif
 
+#ifndef CONFIG_HL_SUPPORT
+static int ol_rx_frag_get_inord_msdu_cnt(qdf_nbuf_t rx_ind_msg)
+{
+	uint32_t *msg_word;
+	uint8_t *rx_ind_data;
+	uint32_t msdu_cnt;
+
+	rx_ind_data = qdf_nbuf_data(rx_ind_msg);
+	msg_word = (uint32_t *)rx_ind_data;
+	msdu_cnt = HTT_RX_IN_ORD_PADDR_IND_MSDU_CNT_GET(*(msg_word + 1));
+
+	return msdu_cnt;
+}
+#else
+static int ol_rx_frag_get_inord_msdu_cnt(qdf_nbuf_t rx_ind_msg)
+{
+	return 0;
+}
+#endif
+
 /*
  * Process incoming fragments
  */
@@ -353,7 +373,10 @@
 		 * separate from normal frames
 		 */
 		ol_rx_reorder_flush_frag(htt_pdev, peer, tid, seq_num_start);
+	} else {
+		msdu_count = ol_rx_frag_get_inord_msdu_cnt(rx_frag_ind_msg);
 	}
+
 	pktlog_bit =
 		(htt_rx_amsdu_rx_in_order_get_pktlog(rx_frag_ind_msg) == 0x01);
 	ret = htt_rx_frag_pop(htt_pdev, rx_frag_ind_msg, &head_msdu,
@@ -389,7 +412,11 @@
 		htt_rx_desc_frame_free(htt_pdev, head_msdu);
 	}
 	/* request HTT to provide new rx MSDU buffers for the target to fill. */
-	htt_rx_msdu_buff_replenish(htt_pdev);
+	if (ol_cfg_is_full_reorder_offload(pdev->ctrl_pdev) &&
+	    !pdev->cfg.is_high_latency)
+		htt_rx_msdu_buff_in_order_replenish(htt_pdev, msdu_count);
+	else
+		htt_rx_msdu_buff_replenish(htt_pdev);
 }
 
 /*
diff --git a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_main.c b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_main.c
index fcd15a9..836ebc7 100644
--- a/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_main.c
+++ b/drivers/staging/qcacld-3.0/core/hdd/src/wlan_hdd_main.c
@@ -9319,7 +9319,7 @@
 
 	start_log.ring_id = RING_ID_PER_PACKET_STATS;
 	start_log.verbose_level =
-			enable ? WLAN_LOG_LEVEL_ACTIVE : WLAN_LOG_LEVEL_OFF;
+			enable ? WLAN_LOG_LEVEL_REPRO : WLAN_LOG_LEVEL_OFF;
 	start_log.ini_triggered = cds_is_packet_log_enabled();
 	start_log.user_triggered = user_triggered;
 	start_log.size = size;
diff --git a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_inside_api.h b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_inside_api.h
index 34b5f2c..77d4697 100644
--- a/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_inside_api.h
+++ b/drivers/staging/qcacld-3.0/core/sme/src/csr/csr_inside_api.h
@@ -94,10 +94,11 @@
 #define CSR_ACTIVE_SCAN_LIST_CMD_TIMEOUT (1000*30)
 
 /* ***************************************************************************
- * The MAX BSSID Count should be lower than the command timeout value and it
- * can be of a fraction of 1/3 to 1/2 of the total command timeout value.
+ * The MAX BSSID Count should be lower than the command timeout value.
+ * As in some case auth timeout can take upto 5 sec (in case of SAE auth) try
+ * (command timeout/5000 - 1) candidates.
  * ***************************************************************************/
-#define CSR_MAX_BSSID_COUNT     (SME_ACTIVE_LIST_CMD_TIMEOUT_VALUE/3000) - 2
+#define CSR_MAX_BSSID_COUNT     (SME_ACTIVE_LIST_CMD_TIMEOUT_VALUE/5000) - 1
 #define CSR_CUSTOM_CONC_GO_BI    100
 extern uint8_t csr_wpa_oui[][CSR_WPA_OUI_SIZE];
 bool csr_is_supported_channel(tpAniSirGlobal pMac, uint8_t channelId);
diff --git a/drivers/staging/qcacld-3.0/core/utils/logging/src/wlan_logging_sock_svc.c b/drivers/staging/qcacld-3.0/core/utils/logging/src/wlan_logging_sock_svc.c
index cf366b1a..d4c3274 100644
--- a/drivers/staging/qcacld-3.0/core/utils/logging/src/wlan_logging_sock_svc.c
+++ b/drivers/staging/qcacld-3.0/core/utils/logging/src/wlan_logging_sock_svc.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2014-2019 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -1161,7 +1161,7 @@
 
 	spin_lock_irqsave(&gwlan_logging.pkt_stats_lock, flags);
 
-	if (!gwlan_logging.pkt_stats_pcur_node || (NULL == pkt_stats_dump)) {
+	if (!gwlan_logging.pkt_stats_pcur_node) {
 		spin_unlock_irqrestore(&gwlan_logging.pkt_stats_lock, flags);
 		return;
 	}
@@ -1194,7 +1194,7 @@
 				pktlog_hdr->size),
 				data, pktlog_hdr->size);
 
-	if (pkt_stats_dump->type == STOP_MONITOR) {
+	if (pkt_stats_dump && pkt_stats_dump->type == STOP_MONITOR) {
 		wake_up_thread = true;
 		wlan_get_pkt_stats_free_node();
 	}
diff --git a/drivers/staging/qcacld-3.0/core/utils/pktlog/pktlog_internal.c b/drivers/staging/qcacld-3.0/core/utils/pktlog/pktlog_internal.c
index 94ef1a0..97d363b 100644
--- a/drivers/staging/qcacld-3.0/core/utils/pktlog/pktlog_internal.c
+++ b/drivers/staging/qcacld-3.0/core/utils/pktlog/pktlog_internal.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013-2019 The Linux Foundation. All rights reserved.
  *
  * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
  *
@@ -927,6 +927,7 @@
 	qdf_mem_copy(sw_event.sw_event,
 				 ((char *)fw_data->data + sizeof(struct ath_pktlog_hdr)),
 				 pl_hdr.size);
+	cds_pkt_stats_to_logger_thread(&pl_hdr, NULL, sw_event.sw_event);
 
 	return A_OK;
 }
diff --git a/drivers/staging/qcacld-3.0/core/wma/src/wma_main.c b/drivers/staging/qcacld-3.0/core/wma/src/wma_main.c
index f60892f..c2437202 100644
--- a/drivers/staging/qcacld-3.0/core/wma/src/wma_main.c
+++ b/drivers/staging/qcacld-3.0/core/wma/src/wma_main.c
@@ -6548,7 +6548,7 @@
 		return;
 	}
 
-	if (start_log->verbose_level == WLAN_LOG_LEVEL_ACTIVE) {
+	if (start_log->verbose_level >= WLAN_LOG_LEVEL_REPRO) {
 		pktlog_enable(scn, log_state, start_log->ini_triggered,
 			      start_log->user_triggered,
 			      start_log->is_iwpriv_command);
diff --git a/drivers/thermal/msm_lmh_dcvs.c b/drivers/thermal/msm_lmh_dcvs.c
index 33a031d..6ade8b40 100644
--- a/drivers/thermal/msm_lmh_dcvs.c
+++ b/drivers/thermal/msm_lmh_dcvs.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017, 2019 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. 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
@@ -206,7 +206,7 @@
 	uint32_t payload_len;
 
 	payload_len = ((enable_val1) ? 6 : 5) * sizeof(uint32_t);
-	payload = kcalloc((enable_val1) ? 6 : 5, sizeof(uint32_t), GFP_KERNEL);
+	payload = kzalloc(payload_len, GFP_KERNEL);
 	if (!payload)
 		return -ENOMEM;
 
@@ -230,7 +230,6 @@
 	ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, MSM_LIMITS_DCVSH), &desc_arg);
 
 	kfree(payload);
-
 	return ret;
 }
 
@@ -359,6 +358,7 @@
 	return msm_lmh_dcvs_write(hw->affinity, MSM_LIMITS_SUB_FN_THERMAL,
 				MSM_LIMIT_FREQ_CAP, freq,
 				freq >= hw->max_freq ? 0 : 1, 1);
+
 }
 
 static int lmh_get_cur_limit(int cpu, unsigned long *freq)
@@ -442,7 +442,7 @@
 
 	/* Enable the thermal algorithm early */
 	ret = msm_lmh_dcvs_write(hw->affinity, MSM_LIMITS_SUB_FN_THERMAL,
-		 MSM_LIMITS_ALGO_MODE_ENABLE, 1, 0, 0);
+				 MSM_LIMITS_ALGO_MODE_ENABLE, 1, 0, 0);
 	if (ret)
 		return ret;
 
diff --git a/drivers/thermal/msm_thermal.c b/drivers/thermal/msm_thermal.c
index c27e0f4..075e6eb 100644
--- a/drivers/thermal/msm_thermal.c
+++ b/drivers/thermal/msm_thermal.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. 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
@@ -1027,7 +1027,7 @@
 	uint32_t payload_len;
 
 	payload_len = ((enable_val1) ? 6 : 5) * sizeof(uint32_t);
-	payload = kcalloc((enable_val1) ? 6 : 5, sizeof(uint32_t), GFP_KERNEL);
+	payload = kzalloc(payload_len, GFP_KERNEL);
 	if (!payload)
 		return -ENOMEM;
 
@@ -1051,7 +1051,6 @@
 	ret = scm_call2(SCM_SIP_FNID(SCM_SVC_LMH, MSM_LIMITS_DCVSH), &desc_arg);
 
 	kfree(payload);
-
 	return ret;
 }
 
@@ -1066,7 +1065,7 @@
 	 * It is better to use max limits of cluster for given
 	 * cpu if cluster mitigation is supported. It ensures that it
 	 * requests aggregated max limits of all cpus in that cluster.
-	 */
+	 * */
 	if (core_ptr)
 		max_freq = cpus[cpu].parent_ptr->limited_max_freq;
 
@@ -1088,8 +1087,9 @@
 				cpus[cpu].parent_ptr->freq_idx_high].frequency;
 
 	ret = msm_lmh_dcvs_write(affinity, MSM_LIMITS_SUB_FN_THERMAL,
-					MSM_LIMITS_FREQ_CAP, max_freq,
-					max_freq >= hw_max_freq ? 0 : 1, 1);
+				 MSM_LIMITS_FREQ_CAP, max_freq,
+				 max_freq >= hw_max_freq ? 0 : 1, 1);
+
 	if (ret)
 		return ret;
 	/*
diff --git a/drivers/video/fbdev/msm/mdss_smmu.c b/drivers/video/fbdev/msm/mdss_smmu.c
index 62e2550..80eb297 100644
--- a/drivers/video/fbdev/msm/mdss_smmu.c
+++ b/drivers/video/fbdev/msm/mdss_smmu.c
@@ -573,7 +573,7 @@
 	}
 
 	*dma_addr = dma_map_single(mdss_smmu->base.dev, cpu_addr, size, dir);
-	if (IS_ERR_VALUE(*dma_addr)) {
+	if (dma_mapping_error(mdss_smmu->base.dev, *dma_addr)) {
 		pr_err("dma map single failed\n");
 		return -ENOMEM;
 	}
diff --git a/include/linux/compiler-clang.h b/include/linux/compiler-clang.h
index e75255b..f459110 100644
--- a/include/linux/compiler-clang.h
+++ b/include/linux/compiler-clang.h
@@ -24,3 +24,13 @@
  */
 #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__)
 
+#undef __no_sanitize_address
+#define __no_sanitize_address __attribute__((no_sanitize("address")))
+
+/* all clang versions usable with the kernel support KASAN ABI version 5 */
+#define KASAN_ABI_VERSION 5
+
+/* emulate gcc's __SANITIZE_ADDRESS__ flag */
+#if __has_feature(address_sanitizer)
+#define __SANITIZE_ADDRESS__
+#endif
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 5f31318..8571c02 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -521,10 +521,12 @@
 	 * battery is non-NULL.
 	 */
 	struct power_supply *battery;
+	__s32 battery_capacity;
 	__s32 battery_min;
 	__s32 battery_max;
 	__s32 battery_report_type;
 	__s32 battery_report_id;
+	bool battery_reported;
 #endif
 
 	unsigned int status;						/* see STAT flags above */
diff --git a/include/linux/input.h b/include/linux/input.h
index 1e96769..808a097 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -36,6 +36,13 @@
 	__s32 value;
 };
 
+enum input_clock_type {
+	INPUT_CLK_REAL = 0,
+	INPUT_CLK_MONO,
+	INPUT_CLK_BOOT,
+	INPUT_CLK_MAX
+};
+
 /**
  * struct input_dev - represents an input device
  * @name: name of the device
@@ -117,6 +124,8 @@
  * @vals: array of values queued in the current frame
  * @devres_managed: indicates that devices is managed with devres framework
  *	and needs not be explicitly unregistered or freed.
+ * @timestamp: storage for a timestamp set by input_set_timestamp called
+ *  by a driver
  */
 struct input_dev {
 	const char *name;
@@ -187,6 +196,8 @@
 	struct input_value *vals;
 
 	bool devres_managed;
+
+	ktime_t timestamp[INPUT_CLK_MAX];
 };
 #define to_input_dev(d) container_of(d, struct input_dev, dev)
 
@@ -381,6 +392,9 @@
 
 int input_flush_device(struct input_handle *handle, struct file *file);
 
+void input_set_timestamp(struct input_dev *dev, ktime_t timestamp);
+ktime_t *input_get_timestamp(struct input_dev *dev);
+
 void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);
 void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value);
 
diff --git a/include/linux/phy/phy-qcom-ufs.h b/include/linux/phy/phy-qcom-ufs.h
index 25e7a5f..3564b57 100644
--- a/include/linux/phy/phy-qcom-ufs.h
+++ b/include/linux/phy/phy-qcom-ufs.h
@@ -59,5 +59,6 @@
 const char *ufs_qcom_phy_name(struct phy *phy);
 int ufs_qcom_phy_configure_lpm(struct phy *generic_phy, bool enable);
 void ufs_qcom_phy_dbg_register_dump(struct phy *generic_phy);
+void ufs_qcom_phy_print_phy_state(struct phy *generic_phy);
 
 #endif /* PHY_QCOM_UFS_H_ */
diff --git a/lib/test_kasan.c b/lib/test_kasan.c
index 0e70ecc..8fcad61 100644
--- a/lib/test_kasan.c
+++ b/lib/test_kasan.c
@@ -440,6 +440,26 @@
 	p[1023] = 1;
 }
 
+static noinline void __init kasan_alloca_oob_left(void)
+{
+	volatile int i = 10;
+	char alloca_array[i];
+	char *p = alloca_array - 1;
+
+	pr_info("out-of-bounds to left on alloca\n");
+	*(volatile char *)p;
+}
+
+static noinline void __init kasan_alloca_oob_right(void)
+{
+	volatile int i = 10;
+	char alloca_array[i];
+	char *p = alloca_array + i;
+
+	pr_info("out-of-bounds to right on alloca\n");
+	*(volatile char *)p;
+}
+
 static int __init kmalloc_tests_init(void)
 {
 	/*
@@ -469,6 +489,8 @@
 	kmem_cache_oob();
 	kasan_stack_oob();
 	kasan_global_oob();
+	kasan_alloca_oob_left();
+	kasan_alloca_oob_right();
 	ksize_unpoisons_memory();
 	copy_user_test();
 	use_after_scope_test();
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c
index 2bfdb3c..3c57210 100644
--- a/mm/kasan/kasan.c
+++ b/mm/kasan/kasan.c
@@ -802,6 +802,55 @@
 }
 EXPORT_SYMBOL(__asan_unpoison_stack_memory);
 
+/* Emitted by compiler to poison alloca()ed objects. */
+void __asan_alloca_poison(unsigned long addr, size_t size)
+{
+	size_t rounded_up_size = round_up(size, KASAN_SHADOW_SCALE_SIZE);
+	size_t padding_size = round_up(size, KASAN_ALLOCA_REDZONE_SIZE) -
+			rounded_up_size;
+	size_t rounded_down_size = round_down(size, KASAN_SHADOW_SCALE_SIZE);
+
+	const void *left_redzone = (const void *)(addr -
+			KASAN_ALLOCA_REDZONE_SIZE);
+	const void *right_redzone = (const void *)(addr + rounded_up_size);
+
+	WARN_ON(!IS_ALIGNED(addr, KASAN_ALLOCA_REDZONE_SIZE));
+
+	kasan_unpoison_shadow((const void *)(addr + rounded_down_size),
+			      size - rounded_down_size);
+	kasan_poison_shadow(left_redzone, KASAN_ALLOCA_REDZONE_SIZE,
+			KASAN_ALLOCA_LEFT);
+	kasan_poison_shadow(right_redzone,
+			padding_size + KASAN_ALLOCA_REDZONE_SIZE,
+			KASAN_ALLOCA_RIGHT);
+}
+EXPORT_SYMBOL(__asan_alloca_poison);
+
+/* Emitted by compiler to unpoison alloca()ed areas when the stack unwinds. */
+void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom)
+{
+	if (unlikely(!stack_top || stack_top > stack_bottom))
+		return;
+
+	kasan_unpoison_shadow(stack_top, stack_bottom - stack_top);
+}
+EXPORT_SYMBOL(__asan_allocas_unpoison);
+
+/* Emitted by the compiler to [un]poison local variables. */
+#define DEFINE_ASAN_SET_SHADOW(byte) \
+	void __asan_set_shadow_##byte(const void *addr, size_t size)	\
+	{								\
+		__memset((void *)addr, 0x##byte, size);			\
+	}								\
+	EXPORT_SYMBOL(__asan_set_shadow_##byte)
+
+DEFINE_ASAN_SET_SHADOW(00);
+DEFINE_ASAN_SET_SHADOW(f1);
+DEFINE_ASAN_SET_SHADOW(f2);
+DEFINE_ASAN_SET_SHADOW(f3);
+DEFINE_ASAN_SET_SHADOW(f5);
+DEFINE_ASAN_SET_SHADOW(f8);
+
 #ifdef CONFIG_MEMORY_HOTPLUG
 static int kasan_mem_notifier(struct notifier_block *nb,
 			unsigned long action, void *data)
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 1229298..d9cf9e2 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -23,6 +23,14 @@
 #define KASAN_STACK_PARTIAL     0xF4
 #define KASAN_USE_AFTER_SCOPE   0xF8
 
+/*
+ * alloca redzone shadow values
+ */
+#define KASAN_ALLOCA_LEFT	0xCA
+#define KASAN_ALLOCA_RIGHT	0xCB
+
+#define KASAN_ALLOCA_REDZONE_SIZE	32
+
 /* Don't break randconfig/all*config builds */
 #ifndef KASAN_ABI_VERSION
 #define KASAN_ABI_VERSION 1
@@ -112,4 +120,48 @@
 static inline void quarantine_remove_cache(struct kmem_cache *cache) { }
 #endif
 
+/*
+ * Exported functions for interfaces called from assembly or from generated
+ * code. Declarations here to avoid warning about missing declarations.
+ */
+asmlinkage void kasan_unpoison_task_stack_below(const void *watermark);
+void __asan_register_globals(struct kasan_global *globals, size_t size);
+void __asan_unregister_globals(struct kasan_global *globals, size_t size);
+void __asan_loadN(unsigned long addr, size_t size);
+void __asan_storeN(unsigned long addr, size_t size);
+void __asan_handle_no_return(void);
+void __asan_poison_stack_memory(const void *addr, size_t size);
+void __asan_unpoison_stack_memory(const void *addr, size_t size);
+void __asan_alloca_poison(unsigned long addr, size_t size);
+void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom);
+
+void __asan_load1(unsigned long addr);
+void __asan_store1(unsigned long addr);
+void __asan_load2(unsigned long addr);
+void __asan_store2(unsigned long addr);
+void __asan_load4(unsigned long addr);
+void __asan_store4(unsigned long addr);
+void __asan_load8(unsigned long addr);
+void __asan_store8(unsigned long addr);
+void __asan_load16(unsigned long addr);
+void __asan_store16(unsigned long addr);
+
+void __asan_load1_noabort(unsigned long addr);
+void __asan_store1_noabort(unsigned long addr);
+void __asan_load2_noabort(unsigned long addr);
+void __asan_store2_noabort(unsigned long addr);
+void __asan_load4_noabort(unsigned long addr);
+void __asan_store4_noabort(unsigned long addr);
+void __asan_load8_noabort(unsigned long addr);
+void __asan_store8_noabort(unsigned long addr);
+void __asan_load16_noabort(unsigned long addr);
+void __asan_store16_noabort(unsigned long addr);
+
+void __asan_set_shadow_00(const void *addr, size_t size);
+void __asan_set_shadow_f1(const void *addr, size_t size);
+void __asan_set_shadow_f2(const void *addr, size_t size);
+void __asan_set_shadow_f3(const void *addr, size_t size);
+void __asan_set_shadow_f5(const void *addr, size_t size);
+void __asan_set_shadow_f8(const void *addr, size_t size);
+
 #endif
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index 775e214..d51bc21 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -102,6 +102,10 @@
 	case KASAN_USE_AFTER_SCOPE:
 		bug_type = "use-after-scope";
 		break;
+	case KASAN_ALLOCA_LEFT:
+	case KASAN_ALLOCA_RIGHT:
+		bug_type = "alloca-out-of-bounds";
+		break;
 	}
 
 	return bug_type;
@@ -134,7 +138,7 @@
 
 	pr_err("BUG: KASAN: %s in %pS\n",
 		bug_type, (void *)info->ip);
-	pr_err("%s of size %zu at addr %p by task %s/%d\n",
+	pr_err("%s of size %zu at addr %px by task %s/%d\n",
 		info->is_write ? "Write" : "Read", info->access_size,
 		info->access_addr, current->comm, task_pid_nr(current));
 }
@@ -205,7 +209,7 @@
 	const char *rel_type;
 	int rel_bytes;
 
-	pr_err("The buggy address belongs to the object at %p\n"
+	pr_err("The buggy address belongs to the object at %px\n"
 	       " which belongs to the cache %s of size %d\n",
 		object, cache->name, cache->object_size);
 
@@ -224,7 +228,7 @@
 	}
 
 	pr_err("The buggy address is located %d bytes %s of\n"
-	       " %d-byte region [%p, %p)\n",
+	       " %d-byte region [%px, %px)\n",
 		rel_bytes, rel_type, cache->object_size, (void *)object_addr,
 		(void *)(object_addr + cache->object_size));
 }
@@ -301,7 +305,7 @@
 		char shadow_buf[SHADOW_BYTES_PER_ROW];
 
 		snprintf(buffer, sizeof(buffer),
-			(i == 0) ? ">%p: " : " %p: ", kaddr);
+			(i == 0) ? ">%px: " : " %px: ", kaddr);
 		/*
 		 * We should not pass a shadow pointer to generic
 		 * function, because generic functions may try to
diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan
index 5d54925..d809e00 100644
--- a/scripts/Makefile.kasan
+++ b/scripts/Makefile.kasan
@@ -9,18 +9,7 @@
 
 CFLAGS_KASAN_MINIMAL := -fsanitize=kernel-address
 
-ifeq ($(cc-name),clang)
-CFLAGS_KASAN := $(call cc-option, -fsanitize=kernel-address \
-		-mllvm \
-		-asan-mapping-offset=$(KASAN_SHADOW_OFFSET) \
-		-asan-stack=1 -asan-globals=1 \
-		-asan-instrumentation-with-call-threshold=$(call_threshold))
-else
-CFLAGS_KASAN := $(call cc-option, -fsanitize=kernel-address \
-		-fasan-shadow-offset=$(KASAN_SHADOW_OFFSET) \
-		--param asan-stack=1 --param asan-globals=1 \
-		--param asan-instrumentation-with-call-threshold=$(call_threshold))
-endif
+cc-param = $(call cc-option, -mllvm -$(1), $(call cc-option, --param $(1)))
 
 ifeq ($(call cc-option, $(CFLAGS_KASAN_MINIMAL) -Werror),)
    ifneq ($(CONFIG_COMPILE_TEST),y)
@@ -28,13 +17,24 @@
             -fsanitize=kernel-address is not supported by compiler)
    endif
 else
-    ifeq ($(CFLAGS_KASAN),)
-        ifneq ($(CONFIG_COMPILE_TEST),y)
-            $(warning CONFIG_KASAN: compiler does not support all options.\
-                Trying minimal configuration)
-        endif
-        CFLAGS_KASAN := $(CFLAGS_KASAN_MINIMAL)
-    endif
+   # -fasan-shadow-offset fails without -fsanitize
+   CFLAGS_KASAN_SHADOW := $(call cc-option, -fsanitize=kernel-address \
+			-fasan-shadow-offset=$(KASAN_SHADOW_OFFSET), \
+			$(call cc-option, -fsanitize=kernel-address \
+			-mllvm -asan-mapping-offset=$(KASAN_SHADOW_OFFSET)))
+
+   ifeq ($(strip $(CFLAGS_KASAN_SHADOW)),)
+      CFLAGS_KASAN := $(CFLAGS_KASAN_MINIMAL)
+   else
+      # Now add all the compiler specific options that are valid standalone
+      CFLAGS_KASAN := $(CFLAGS_KASAN_SHADOW) \
+	$(call cc-param,asan-globals=1) \
+	$(call cc-param,asan-instrumentation-with-call-threshold=$(call_threshold)) \
+	$(call cc-param,asan-stack=1) \
+	$(call cc-param,asan-use-after-scope=1) \
+	$(call cc-param,asan-instrument-allocas=1)
+   endif
+
 endif
 
 CFLAGS_KASAN_NOSANITIZE := -fno-builtin
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index b393d29..31dc821 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -100,7 +100,7 @@
 	  { COMMON_IPC_PERMS, NULL } },
 	{ "netlink_route_socket",
 	  { COMMON_SOCK_PERMS,
-	    "nlmsg_read", "nlmsg_write", NULL } },
+	    "nlmsg_read", "nlmsg_write", "nlmsg_readpriv", NULL } },
 	{ "netlink_tcpdiag_socket",
 	  { COMMON_SOCK_PERMS,
 	    "nlmsg_read", "nlmsg_write", NULL } },
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 0464cbb..b45a3a7 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -78,6 +78,7 @@
 };
 #define POLICYDB_CAPABILITY_MAX (__POLICYDB_CAPABILITY_MAX - 1)
 
+extern int selinux_android_netlink_route;
 extern int selinux_policycap_netpeer;
 extern int selinux_policycap_openperm;
 extern int selinux_policycap_alwaysnetwork;
@@ -263,6 +264,7 @@
 extern void selnl_notify_setenforce(int val);
 extern void selnl_notify_policyload(u32 seqno);
 extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
+extern void selinux_nlmsg_init(void);
 
 #endif /* _SELINUX_SECURITY_H_ */
 
diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c
index 7f947f7..e7b7462 100644
--- a/security/selinux/nlmsgtab.c
+++ b/security/selinux/nlmsgtab.c
@@ -191,3 +191,27 @@
 
 	return err;
 }
+
+static void nlmsg_set_getlink_perm(u32 perm)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(nlmsg_route_perms); i++) {
+		if (nlmsg_route_perms[i].nlmsg_type == RTM_GETLINK) {
+			nlmsg_route_perms[i].perm = perm;
+			break;
+		}
+	}
+}
+
+/**
+ * Use nlmsg_readpriv as the permission for RTM_GETLINK messages if the
+ * netlink_route_getlink policy capability is set. Otherwise use nlmsg_read.
+ */
+void selinux_nlmsg_init(void)
+{
+	if (selinux_android_netlink_route)
+		nlmsg_set_getlink_perm(NETLINK_ROUTE_SOCKET__NLMSG_READPRIV);
+	else
+		nlmsg_set_getlink_perm(NETLINK_ROUTE_SOCKET__NLMSG_READ);
+}
diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c
index 01fbbbf..5ee23e3 100644
--- a/security/selinux/ss/policydb.c
+++ b/security/selinux/ss/policydb.c
@@ -2329,6 +2329,10 @@
 	p->reject_unknown = !!(le32_to_cpu(buf[1]) & REJECT_UNKNOWN);
 	p->allow_unknown = !!(le32_to_cpu(buf[1]) & ALLOW_UNKNOWN);
 
+	if ((le32_to_cpu(buf[1]) & POLICYDB_CONFIG_ANDROID_NETLINK_ROUTE)) {
+		p->android_netlink_route = 1;
+	}
+
 	if (p->policyvers >= POLICYDB_VERSION_POLCAP) {
 		rc = ebitmap_read(&p->policycaps, fp);
 		if (rc)
diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h
index 725d594..0d511cf 100644
--- a/security/selinux/ss/policydb.h
+++ b/security/selinux/ss/policydb.h
@@ -227,6 +227,7 @@
 /* The policy database */
 struct policydb {
 	int mls_enabled;
+	int android_netlink_route;
 
 	/* symbol tables */
 	struct symtab symtab[SYM_NUM];
@@ -313,6 +314,7 @@
 #define PERM_SYMTAB_SIZE 32
 
 #define POLICYDB_CONFIG_MLS    1
+#define POLICYDB_CONFIG_ANDROID_NETLINK_ROUTE    (1 << 31)
 
 /* the config flags related to unknown classes/perms are bits 2 and 3 */
 #define REJECT_UNKNOWN	0x00000002
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index 0a258c0..2b3907e 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -70,6 +70,7 @@
 #include "ebitmap.h"
 #include "audit.h"
 
+int selinux_android_netlink_route;
 int selinux_policycap_netpeer;
 int selinux_policycap_openperm;
 int selinux_policycap_alwaysnetwork;
@@ -1997,6 +1998,9 @@
 						  POLICYDB_CAPABILITY_OPENPERM);
 	selinux_policycap_alwaysnetwork = ebitmap_get_bit(&policydb.policycaps,
 						  POLICYDB_CAPABILITY_ALWAYSNETWORK);
+
+	selinux_android_netlink_route = policydb.android_netlink_route;
+	selinux_nlmsg_init();
 }
 
 static int security_preserve_bools(struct policydb *p);