msm: camera: cpas: HW workaround for SDM670_1_1

HW workaround to select PriorityLvl for IFE0 and IFE1
because HurryLvl is not working as expected.

Change-Id: I3a16d6700b1afe008fb5a9763c5f3a41d2557134
Signed-off-by: Alok Pandey <akumarpa@codeaurora.org>
diff --git a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
index 4b16103..f9bda3b 100644
--- a/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
+++ b/Documentation/devicetree/bindings/media/video/msm-cam-cpas.txt
@@ -147,6 +147,13 @@
   Definition: Bool property specifying whether Clients are connected
               through CAMNOC for AXI access.
 
+- nvmem-cells
+  Usage: optional
+  Definition: nvmem cell node
+
+- nvmem-cell-names
+  Usage: required
+  Definition: If nvmem node is present, cell name is required
 ===================================================================
 Third Level Node - CAM AXI Port properties
 ===================================================================
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
index b18af0a..2837f5b 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.c
@@ -140,6 +140,7 @@
 	irq_handler_t irq_handler, void *irq_data)
 {
 	int rc = 0;
+	struct cam_cpas_private_soc *soc_private;
 
 	rc = cam_soc_util_get_dt_properties(soc_info);
 	if (rc) {
@@ -173,6 +174,12 @@
 		goto free_soc_private;
 	}
 
+	soc_private = soc_info->soc_private;
+	soc_private->soc_id = cam_soc_util_get_soc_id();
+	soc_private->hw_rev = cam_soc_util_get_hw_revision_node(soc_info);
+	CAM_DBG(CAM_CPAS, "soc id %d hw_rev %d",
+		soc_private->soc_id, soc_private->hw_rev);
+
 	return rc;
 
 free_soc_private:
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
index fe0187e..a93c063 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cam_cpas_soc.h
@@ -17,7 +17,6 @@
 #include "cam_cpas_hw.h"
 
 #define CAM_REGULATOR_LEVEL_MAX 16
-
 /**
  * struct cam_cpas_vdd_ahb_mapping : Voltage to ahb level mapping
  *
@@ -42,7 +41,8 @@
  * @axi_port_list_node : Node representing AXI Ports list
  * @num_vdd_ahb_mapping : Number of vdd to ahb level mapping supported
  * @vdd_ahb : AHB level mapping info for the supported vdd levels
- *
+ * @soc_id : SOC id
+ * @hw_rev : Camera hw revision
  */
 struct cam_cpas_private_soc {
 	const char *arch_compat;
@@ -54,6 +54,8 @@
 	struct device_node *axi_port_list_node;
 	uint32_t num_vdd_ahb_mapping;
 	struct cam_cpas_vdd_ahb_mapping vdd_ahb[CAM_REGULATOR_LEVEL_MAX];
+	uint32_t soc_id;
+	uint32_t hw_rev;
 };
 
 int cam_cpas_soc_init_resources(struct cam_hw_soc_info *soc_info,
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
index 0533ed8..8d08079 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.c
@@ -114,6 +114,15 @@
 		return -EINVAL;
 	}
 
+	rc = cam_common_util_get_string_index(soc_info->mem_block_name,
+		soc_info->num_mem_block, "core_top_csr_tcsr", &index);
+	if ((rc == 0) && (index < num_reg_map)) {
+		regbase_index[CAM_CPAS_REG_CSR_TCSR] = index;
+	} else {
+		CAM_DBG(CAM_CPAS, "regbase not found for CAMNOC, rc=%d, %d %d",
+			rc, index, num_reg_map);
+	}
+
 	return 0;
 }
 
@@ -456,6 +465,9 @@
 static int cam_cpastop_poweron(struct cam_hw_info *cpas_hw)
 {
 	int i;
+	struct cam_hw_soc_info *soc_info = &cpas_hw->soc_info;
+	struct cam_cpas *cpas_core = (struct cam_cpas *) cpas_hw->core_info;
+	struct cam_cpas_private_soc *soc_private = soc_info->soc_private;
 
 	cam_cpastop_reset_irq(cpas_hw);
 
@@ -478,6 +490,29 @@
 		}
 	}
 
+	if ((soc_private && soc_private->soc_id == SDM670_SOC_ID) &&
+		(soc_private->hw_rev == SDM670_V1_1)) {
+
+		struct cam_cpas_reg *reg_info;
+		int tcsr_index;
+		void __iomem *mem_base;
+
+		reg_info = &camnoc_info->errata_wa_list->tcsr_reg.
+			tcsr_conn_box_spare_0;
+		tcsr_index = cpas_core->regbase_index[CAM_CPAS_REG_CSR_TCSR];
+		if (tcsr_index == -1) {
+			CAM_DBG(CAM_CPAS, "index in not initialized");
+			return 0;
+		}
+		mem_base = soc_info->reg_map[tcsr_index].mem_base;
+
+		reg_info->value = TCSR_CONN_SET;
+		cam_io_w_mb(reg_info->value, mem_base + reg_info->offset);
+		CAM_DBG(CAM_CPAS, "tcsr(0x%lx) value %d",
+			(unsigned long int)mem_base + reg_info->offset,
+			cam_io_r_mb(mem_base + reg_info->offset));
+	}
+
 	return 0;
 }
 
@@ -489,6 +524,8 @@
 	int rc = 0;
 	struct cam_cpas_hw_errata_wa_list *errata_wa_list =
 		camnoc_info->errata_wa_list;
+	struct cam_cpas_private_soc *soc_private =
+		cpas_hw->soc_info.soc_private;
 
 	if (!errata_wa_list)
 		return 0;
@@ -512,6 +549,28 @@
 		}
 	}
 
+	if ((soc_private && soc_private->soc_id == SDM670_SOC_ID) &&
+		(soc_private->hw_rev == SDM670_V1_1)) {
+
+		struct cam_cpas_reg *reg_info;
+		int tcsr_index;
+		void __iomem *mem_base;
+
+		reg_info = &camnoc_info->errata_wa_list->tcsr_reg.
+			tcsr_conn_box_spare_0;
+		reg_info->value = TCSR_CONN_RESET;
+		tcsr_index = cpas_core->regbase_index[CAM_CPAS_REG_CSR_TCSR];
+		if (tcsr_index == -1) {
+			CAM_DBG(CAM_CPAS, "index in not initialized");
+			return 0;
+		}
+		mem_base = soc_info->reg_map[tcsr_index].mem_base;
+		cam_io_w_mb(reg_info->value, mem_base + reg_info->offset);
+		CAM_DBG(CAM_CPAS, "tcsr(0x%lx) value %d",
+			(unsigned long int)mem_base + reg_info->offset,
+			cam_io_r_mb(mem_base + reg_info->offset));
+	}
+
 	return rc;
 }
 
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
index 73f7e9b..080f6e6 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cam_cpastop_hw.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -162,14 +162,27 @@
 };
 
 /**
+ * struct cam_camnoc_tcsr_regs : Top control Status register
+ *
+ * @tcsr_conn_box_spare_0: spare register to select PriorityLvl
+ *         for IFE0 and IFE1 (HW workaround for SDM670 1.1)
+ *
+ */
+struct cam_camnoc_tcsr_regs {
+	struct cam_cpas_reg tcsr_conn_box_spare_0;
+};
+
+/**
  * struct cam_cpas_hw_errata_wa_list : List of HW Errata workaround info
  *
  * @camnoc_flush_slave_pending_trans: Errata workaround info for flushing
  *         camnoc slave pending transactions before turning off CPAS_TOP gdsc
+ * @tcsr_reg: HW workaround to select PriorityLvl for IFE0 and IFE(SDM670_1.1)
  *
  */
 struct cam_cpas_hw_errata_wa_list {
 	struct cam_cpas_hw_errata_wa camnoc_flush_slave_pending_trans;
+	struct cam_camnoc_tcsr_regs tcsr_reg;
 };
 
 /**
@@ -200,6 +213,7 @@
 	uint32_t errlog3_high;
 };
 
+
 /**
  * struct cam_camnoc_info : Overall CAMNOC settings info
  *
@@ -210,7 +224,6 @@
  * @irq_err_size: Array size of IRQ Error settings
  * @err_logger: Pointer to CAMNOC IRQ Error logger read registers
  * @errata_wa_list: HW Errata workaround info
- *
  */
 struct cam_camnoc_info {
 	struct cam_camnoc_specific *specific;
diff --git a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h
index 3c572f0..c1048a0 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/cpas_top/cpastop_v170_110.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -14,6 +14,8 @@
 #define _CPASTOP_V170_110_H_
 
 #define TEST_IRQ_ENABLE 0
+#define TCSR_CONN_RESET 0x0
+#define TCSR_CONN_SET  0x3
 
 static struct cam_camnoc_irq_sbm cam_cpas110_irq_sbm = {
 	.sbm_enable = {
@@ -528,6 +530,14 @@
 			.value = 0, /* expected to be 0 */
 		},
 	},
+	.tcsr_reg = {
+		.tcsr_conn_box_spare_0 = {
+			.enable = true,
+			.access_type = CAM_REG_TYPE_READ_WRITE,
+			.masked_value = 0,
+			.offset = 0xB3E4,
+		},
+	},
 };
 
 static struct cam_camnoc_info cam170_cpas110_camnoc_info = {
diff --git a/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h b/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h
index c844ef7..d1492fe 100644
--- a/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h
+++ b/drivers/media/platform/msm/camera/cam_cpas/include/cam_cpas_api.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2017-2018, 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
@@ -32,6 +32,7 @@
 enum cam_cpas_reg_base {
 	CAM_CPAS_REG_CPASTOP,
 	CAM_CPAS_REG_CAMNOC,
+	CAM_CPAS_REG_CSR_TCSR,
 	CAM_CPAS_REG_CAMSS,
 	CAM_CPAS_REG_MAX
 };
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.c b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.c
index 0b1896f..d0a13ab 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.c
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.c
@@ -15,8 +15,62 @@
 #include <linux/slab.h>
 #include <linux/gpio.h>
 #include <linux/of_gpio.h>
+#include <soc/qcom/socinfo.h>
 #include "cam_soc_util.h"
 #include "cam_debug_util.h"
+#include <linux/nvmem-consumer.h>
+
+uint32_t cam_soc_util_get_soc_id(void)
+{
+	return socinfo_get_id();
+}
+#if defined(CONFIG_NVMEM) && defined(CONFIG_QCOM_QFPROM)
+uint32_t cam_soc_util_get_hw_revision_node(struct cam_hw_soc_info *soc_info)
+{
+	struct nvmem_cell *cell;
+	ssize_t len;
+	uint32_t *buf, hw_rev;
+	struct platform_device *pdev;
+
+	pdev = soc_info->pdev;
+	/* read the soc hw revision and select revision node */
+	cell = nvmem_cell_get(&pdev->dev, "minor_rev");
+	if (IS_ERR_OR_NULL(cell)) {
+		if (PTR_ERR(cell) == -EPROBE_DEFER) {
+			CAM_ERR(CAM_UTIL, "Err to get nvmem cell: ret=%ld",
+				PTR_ERR(cell));
+			return -EINVAL;
+		}
+		CAM_ERR(CAM_UTIL, "No DTS entry");
+		return 0;
+	}
+
+	if (PTR_ERR(cell) == -ENOENT) {
+		CAM_DBG(CAM_UTIL, "nvme cell not found");
+		return 0;
+	}
+
+	buf = nvmem_cell_read(cell, &len);
+	nvmem_cell_put(cell);
+
+	if (IS_ERR_OR_NULL(buf)) {
+		CAM_ERR(CAM_UTIL, "Unable to read nvmem cell: ret=%ld",
+			PTR_ERR(buf));
+		return -EINVAL;
+	}
+
+	CAM_DBG(CAM_UTIL, "hw_rev = %u", *buf);
+	hw_rev = (*buf >> 28) & 0x3;
+	kfree(buf);
+
+	return hw_rev;
+}
+#else
+uint32_t cam_soc_util_get_hw_revision_node(struct cam_hw_soc_info *soc_info)
+{
+	return 0;
+}
+#endif
 
 int cam_soc_util_get_level_from_string(const char *string,
 	enum cam_vote_level *level)
diff --git a/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h
index 4b57d54..1f2d46d 100644
--- a/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h
+++ b/drivers/media/platform/msm/camera/cam_utils/cam_soc_util.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2018, 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
@@ -40,6 +40,11 @@
 /* maximum number of device clock */
 #define CAM_SOC_MAX_CLK             32
 
+/* soc id */
+#define SDM670_SOC_ID 336
+
+/* Minor Version */
+#define SDM670_V1_1 0x1
 /**
  * enum cam_vote_level - Enum for voting level
  *
@@ -615,5 +620,23 @@
 
 int cam_soc_util_clk_enable_default(struct cam_hw_soc_info *soc_info,
 	enum cam_vote_level clk_level);
+/**
+ * cam_soc_util_get_soc_id()
+ *
+ * @brief:           Read soc id
+ *
+ * @return           SOC id
+ */
+uint32_t cam_soc_util_get_soc_id(void);
 
+/**
+ * cam_soc_util_get_hw_revision_node()
+ *
+ * @brief:           Camera HW ID
+ *
+ * @soc_info:        Device soc information
+ *
+ * @return           HW id
+ */
+uint32_t cam_soc_util_get_hw_revision_node(struct cam_hw_soc_info *soc_info);
 #endif /* _CAM_SOC_UTIL_H_ */