|
@@ -6,10 +6,78 @@
|
|
|
#include "cam_cci_dev.h"
|
|
|
#include "cam_cci_core.h"
|
|
|
|
|
|
+static int cam_cci_init_master(struct cci_device *cci_dev,
|
|
|
+ enum cci_i2c_master_t master)
|
|
|
+{
|
|
|
+ int i = 0, rc = 0;
|
|
|
+ void __iomem *base = NULL;
|
|
|
+ struct cam_hw_soc_info *soc_info = NULL;
|
|
|
+ uint32_t max_queue_0_size = 0, max_queue_1_size = 0;
|
|
|
+
|
|
|
+ soc_info = &cci_dev->soc_info;
|
|
|
+ base = soc_info->reg_map[0].mem_base;
|
|
|
+
|
|
|
+ if (cci_dev->hw_version != CCI_VERSION_1_2_9) {
|
|
|
+ max_queue_0_size = CCI_I2C_QUEUE_0_SIZE_V_1_2;
|
|
|
+ max_queue_1_size = CCI_I2C_QUEUE_1_SIZE_V_1_2;
|
|
|
+ } else {
|
|
|
+ max_queue_0_size = CCI_I2C_QUEUE_0_SIZE;
|
|
|
+ max_queue_1_size = CCI_I2C_QUEUE_1_SIZE;
|
|
|
+ }
|
|
|
+
|
|
|
+ cci_dev->master_active_slave[master]++;
|
|
|
+ if (!cci_dev->cci_master_info[master].is_initilized) {
|
|
|
+ /* Re-initialize the completion */
|
|
|
+ reinit_completion(
|
|
|
+ &cci_dev->cci_master_info[master].reset_complete);
|
|
|
+ reinit_completion(&cci_dev->cci_master_info[master].rd_done);
|
|
|
+
|
|
|
+ /* reinit the reports for the queue */
|
|
|
+ for (i = 0; i < NUM_QUEUES; i++)
|
|
|
+ reinit_completion(
|
|
|
+ &cci_dev->cci_master_info[master].report_q[i]);
|
|
|
+
|
|
|
+ /* Set reset pending flag to true */
|
|
|
+ cci_dev->cci_master_info[master].reset_pending = true;
|
|
|
+ cam_io_w_mb((master == MASTER_0) ?
|
|
|
+ CCI_M0_RESET_RMSK : CCI_M1_RESET_RMSK,
|
|
|
+ base + CCI_RESET_CMD_ADDR);
|
|
|
+ if (!wait_for_completion_timeout(
|
|
|
+ &cci_dev->cci_master_info[master].reset_complete,
|
|
|
+ CCI_TIMEOUT)) {
|
|
|
+ CAM_ERR(CAM_CCI,
|
|
|
+ "Failed: reset complete timeout for master: %d",
|
|
|
+ master);
|
|
|
+ rc = -ETIMEDOUT;
|
|
|
+ cci_dev->master_active_slave[master]--;
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+
|
|
|
+ flush_workqueue(cci_dev->write_wq[master]);
|
|
|
+
|
|
|
+ /* Setting up the queue size for master */
|
|
|
+ cci_dev->cci_i2c_queue_info[master][QUEUE_0].max_queue_size
|
|
|
+ = max_queue_0_size;
|
|
|
+ cci_dev->cci_i2c_queue_info[master][QUEUE_1].max_queue_size
|
|
|
+ = max_queue_1_size;
|
|
|
+
|
|
|
+ CAM_DBG(CAM_CCI, "CCI Master[%d] :: Q0: %d Q1: %d", master,
|
|
|
+ cci_dev->cci_i2c_queue_info[master][QUEUE_0]
|
|
|
+ .max_queue_size,
|
|
|
+ cci_dev->cci_i2c_queue_info[master][QUEUE_1]
|
|
|
+ .max_queue_size);
|
|
|
+
|
|
|
+ cci_dev->cci_master_info[master].status = 0;
|
|
|
+ cci_dev->cci_master_info[master].is_initilized = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
int cam_cci_init(struct v4l2_subdev *sd,
|
|
|
struct cam_cci_ctrl *c_ctrl)
|
|
|
{
|
|
|
- uint8_t i = 0, j = 0;
|
|
|
+ uint8_t i = 0;
|
|
|
int32_t rc = 0;
|
|
|
struct cci_device *cci_dev;
|
|
|
enum cci_i2c_master_t master = c_ctrl->cci_info->cci_i2c_master;
|
|
@@ -17,7 +85,6 @@ int cam_cci_init(struct v4l2_subdev *sd,
|
|
|
struct cam_axi_vote axi_vote = {0};
|
|
|
struct cam_hw_soc_info *soc_info = NULL;
|
|
|
void __iomem *base = NULL;
|
|
|
- uint32_t max_queue_0_size = 0, max_queue_1_size = 0;
|
|
|
|
|
|
cci_dev = v4l2_get_subdevdata(sd);
|
|
|
if (!cci_dev || !c_ctrl) {
|
|
@@ -39,78 +106,45 @@ int cam_cci_init(struct v4l2_subdev *sd,
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
- CAM_DBG(CAM_CCI, "Base address %pK", base);
|
|
|
+ if (master >= MASTER_MAX || master < 0) {
|
|
|
+ CAM_ERR(CAM_CCI, "Incorrect Master: %d", master);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!cci_dev->write_wq[master]) {
|
|
|
+ CAM_ERR(CAM_CCI, "Null memory for write wq[:%d]", master);
|
|
|
+ rc = -ENOMEM;
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
|
|
|
if (cci_dev->ref_count++) {
|
|
|
- CAM_DBG(CAM_CCI,
|
|
|
- "ref_count:%d, master:%d",
|
|
|
- cci_dev->ref_count, master);
|
|
|
- if (master < MASTER_MAX && master >= 0) {
|
|
|
- mutex_lock(&cci_dev->cci_master_info[master].mutex);
|
|
|
- flush_workqueue(cci_dev->write_wq[master]);
|
|
|
- /* Re-initialize the completion */
|
|
|
- reinit_completion(
|
|
|
- &cci_dev->cci_master_info[master].reset_complete);
|
|
|
- reinit_completion(
|
|
|
- &cci_dev->cci_master_info[master].rd_done);
|
|
|
- for (i = 0; i < NUM_QUEUES; i++)
|
|
|
- reinit_completion(
|
|
|
- &cci_dev->cci_master_info[master].report_q[i]);
|
|
|
- /* Set reset pending flag to true */
|
|
|
- cci_dev->cci_master_info[master].reset_pending = true;
|
|
|
- cci_dev->cci_master_info[master].status = 0;
|
|
|
- /* Set proper mask to RESET CMD address */
|
|
|
- if (master == MASTER_0)
|
|
|
- cam_io_w_mb(CCI_M0_RESET_RMSK,
|
|
|
- base + CCI_RESET_CMD_ADDR);
|
|
|
- else
|
|
|
- cam_io_w_mb(CCI_M1_RESET_RMSK,
|
|
|
- base + CCI_RESET_CMD_ADDR);
|
|
|
- /* wait for reset done irq */
|
|
|
- if (!wait_for_completion_timeout(
|
|
|
- &cci_dev->cci_master_info[master].reset_complete,
|
|
|
- CCI_TIMEOUT)) {
|
|
|
- CAM_ERR(CAM_CCI,
|
|
|
- "wait timeout for reset_complete for master: %d",
|
|
|
- master);
|
|
|
- reinit_completion(
|
|
|
- &cci_dev->cci_master_info[master].reset_complete
|
|
|
- );
|
|
|
- rc = -EINVAL;
|
|
|
- }
|
|
|
- cci_dev->cci_master_info[master].status = 0;
|
|
|
- mutex_unlock(&cci_dev->cci_master_info[master].mutex);
|
|
|
+ rc = cam_cci_init_master(cci_dev, master);
|
|
|
+ if (rc) {
|
|
|
+ CAM_ERR(CAM_CCI, "Failed to init: Master: %d: rc: %d",
|
|
|
+ master, rc);
|
|
|
+ cci_dev->ref_count--;
|
|
|
}
|
|
|
+ CAM_DBG(CAM_CCI, "ref_count %d, master: %d",
|
|
|
+ cci_dev->ref_count, master);
|
|
|
return rc;
|
|
|
}
|
|
|
|
|
|
ahb_vote.type = CAM_VOTE_ABSOLUTE;
|
|
|
ahb_vote.vote.level = CAM_LOWSVS_VOTE;
|
|
|
axi_vote.num_paths = 1;
|
|
|
- axi_vote.axi_path[0].path_data_type =
|
|
|
- CAM_AXI_PATH_DATA_ALL;
|
|
|
- axi_vote.axi_path[0].transac_type =
|
|
|
- CAM_AXI_TRANSACTION_WRITE;
|
|
|
- axi_vote.axi_path[0].camnoc_bw =
|
|
|
- CAM_CPAS_DEFAULT_AXI_BW;
|
|
|
- axi_vote.axi_path[0].mnoc_ab_bw =
|
|
|
- CAM_CPAS_DEFAULT_AXI_BW;
|
|
|
- axi_vote.axi_path[0].mnoc_ib_bw =
|
|
|
- CAM_CPAS_DEFAULT_AXI_BW;
|
|
|
-
|
|
|
- rc = cam_cpas_start(cci_dev->cpas_handle,
|
|
|
- &ahb_vote, &axi_vote);
|
|
|
- if (rc != 0)
|
|
|
- CAM_ERR(CAM_CCI, "CPAS start failed, rc: %d", rc);
|
|
|
+ axi_vote.axi_path[0].path_data_type = CAM_AXI_PATH_DATA_ALL;
|
|
|
+ axi_vote.axi_path[0].transac_type = CAM_AXI_TRANSACTION_WRITE;
|
|
|
+ axi_vote.axi_path[0].camnoc_bw = CAM_CPAS_DEFAULT_AXI_BW;
|
|
|
+ axi_vote.axi_path[0].mnoc_ab_bw = CAM_CPAS_DEFAULT_AXI_BW;
|
|
|
+ axi_vote.axi_path[0].mnoc_ib_bw = CAM_CPAS_DEFAULT_AXI_BW;
|
|
|
|
|
|
- cam_cci_get_clk_rates(cci_dev, c_ctrl);
|
|
|
+ rc = cam_cpas_start(cci_dev->cpas_handle, &ahb_vote, &axi_vote);
|
|
|
+ if (rc) {
|
|
|
+ CAM_ERR(CAM_CCI, "CPAS start failed rc= %d", rc);
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
|
|
|
- /* Re-initialize the completion */
|
|
|
- reinit_completion(&cci_dev->cci_master_info[master].reset_complete);
|
|
|
- reinit_completion(&cci_dev->cci_master_info[master].rd_done);
|
|
|
- for (i = 0; i < NUM_QUEUES; i++)
|
|
|
- reinit_completion(
|
|
|
- &cci_dev->cci_master_info[master].report_q[i]);
|
|
|
+ cam_cci_get_clk_rates(cci_dev, c_ctrl);
|
|
|
|
|
|
/* Enable Regulators and IRQ*/
|
|
|
rc = cam_soc_util_enable_platform_resource(soc_info, true,
|
|
@@ -121,73 +155,27 @@ int cam_cci_init(struct v4l2_subdev *sd,
|
|
|
goto platform_enable_failed;
|
|
|
}
|
|
|
|
|
|
- cci_dev->hw_version = cam_io_r_mb(base +
|
|
|
- CCI_HW_VERSION_ADDR);
|
|
|
+ cci_dev->hw_version = cam_io_r_mb(base + CCI_HW_VERSION_ADDR);
|
|
|
CAM_DBG(CAM_CCI, "hw_version = 0x%x", cci_dev->hw_version);
|
|
|
|
|
|
- cci_dev->payload_size =
|
|
|
- MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11;
|
|
|
+ cci_dev->payload_size = MSM_CCI_WRITE_DATA_PAYLOAD_SIZE_11;
|
|
|
cci_dev->support_seq_write = 1;
|
|
|
|
|
|
- if (cci_dev->hw_version == CCI_VERSION_1_2_9) {
|
|
|
- max_queue_0_size = CCI_I2C_QUEUE_0_SIZE_V_1_2;
|
|
|
- max_queue_1_size = CCI_I2C_QUEUE_1_SIZE_V_1_2;
|
|
|
- } else {
|
|
|
- max_queue_0_size = CCI_I2C_QUEUE_0_SIZE;
|
|
|
- max_queue_1_size = CCI_I2C_QUEUE_1_SIZE;
|
|
|
- }
|
|
|
-
|
|
|
- for (i = 0; i < NUM_MASTERS; i++) {
|
|
|
- for (j = 0; j < NUM_QUEUES; j++) {
|
|
|
- if (j == QUEUE_0)
|
|
|
- cci_dev->cci_i2c_queue_info[i][j].max_queue_size
|
|
|
- = max_queue_0_size;
|
|
|
- else
|
|
|
- cci_dev->cci_i2c_queue_info[i][j].max_queue_size
|
|
|
- = max_queue_1_size;
|
|
|
-
|
|
|
- CAM_DBG(CAM_CCI, "CCI Master[%d] :: Q0 : %d Q1 : %d", i,
|
|
|
- cci_dev->cci_i2c_queue_info[i][j].max_queue_size,
|
|
|
- cci_dev->cci_i2c_queue_info[i][j].max_queue_size);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- cci_dev->cci_master_info[master].reset_pending = true;
|
|
|
- cci_dev->cci_master_info[master].status = 0;
|
|
|
- cam_io_w_mb(CCI_RESET_CMD_RMSK, base +
|
|
|
- CCI_RESET_CMD_ADDR);
|
|
|
- cam_io_w_mb(0x1, base + CCI_RESET_CMD_ADDR);
|
|
|
- if (!wait_for_completion_timeout(
|
|
|
- &cci_dev->cci_master_info[master].reset_complete,
|
|
|
- CCI_TIMEOUT)) {
|
|
|
- CAM_ERR(CAM_CCI,
|
|
|
- "wait timeout for reset_complete for master: %d",
|
|
|
- master);
|
|
|
-
|
|
|
- rc = -ETIMEDOUT;
|
|
|
+ rc = cam_cci_init_master(cci_dev, master);
|
|
|
+ if (rc) {
|
|
|
+ CAM_ERR(CAM_CCI, "Failed to init: Master: %d, rc: %d",
|
|
|
+ master, rc);
|
|
|
goto reset_complete_failed;
|
|
|
}
|
|
|
+
|
|
|
for (i = 0; i < MASTER_MAX; i++)
|
|
|
cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES;
|
|
|
- cam_io_w_mb(CCI_IRQ_MASK_0_RMSK,
|
|
|
- base + CCI_IRQ_MASK_0_ADDR);
|
|
|
- cam_io_w_mb(CCI_IRQ_MASK_0_RMSK,
|
|
|
- base + CCI_IRQ_CLEAR_0_ADDR);
|
|
|
- cam_io_w_mb(CCI_IRQ_MASK_1_RMSK,
|
|
|
- base + CCI_IRQ_MASK_1_ADDR);
|
|
|
- cam_io_w_mb(CCI_IRQ_MASK_1_RMSK,
|
|
|
- base + CCI_IRQ_CLEAR_1_ADDR);
|
|
|
- cam_io_w_mb(0x1, base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
|
|
|
|
|
|
- for (i = 0; i < MASTER_MAX; i++) {
|
|
|
- if (!cci_dev->write_wq[i]) {
|
|
|
- CAM_ERR(CAM_CCI, "Failed to flush write wq");
|
|
|
- rc = -ENOMEM;
|
|
|
- goto reset_complete_failed;
|
|
|
- } else {
|
|
|
- flush_workqueue(cci_dev->write_wq[i]);
|
|
|
- }
|
|
|
- }
|
|
|
+ cam_io_w_mb(CCI_IRQ_MASK_0_RMSK, base + CCI_IRQ_MASK_0_ADDR);
|
|
|
+ cam_io_w_mb(CCI_IRQ_MASK_0_RMSK, base + CCI_IRQ_CLEAR_0_ADDR);
|
|
|
+ cam_io_w_mb(CCI_IRQ_MASK_1_RMSK, base + CCI_IRQ_MASK_1_ADDR);
|
|
|
+ cam_io_w_mb(CCI_IRQ_MASK_1_RMSK, base + CCI_IRQ_CLEAR_1_ADDR);
|
|
|
+ cam_io_w_mb(0x1, base + CCI_IRQ_GLOBAL_CLEAR_CMD_ADDR);
|
|
|
|
|
|
/* Set RD FIFO threshold for M0 & M1 */
|
|
|
if (cci_dev->hw_version != CCI_VERSION_1_2_9) {
|
|
@@ -203,7 +191,6 @@ int cam_cci_init(struct v4l2_subdev *sd,
|
|
|
|
|
|
reset_complete_failed:
|
|
|
cam_soc_util_disable_platform_resource(soc_info, 1, 1);
|
|
|
-
|
|
|
platform_enable_failed:
|
|
|
cci_dev->ref_count--;
|
|
|
cam_cpas_stop(cci_dev->cpas_handle);
|
|
@@ -223,9 +210,10 @@ static void cam_cci_init_cci_params(struct cci_device *new_cci_dev)
|
|
|
{
|
|
|
uint8_t i = 0, j = 0;
|
|
|
|
|
|
- for (i = 0; i < NUM_MASTERS; i++) {
|
|
|
+ for (i = 0; i < MASTER_MAX; i++) {
|
|
|
new_cci_dev->cci_master_info[i].status = 0;
|
|
|
new_cci_dev->cci_master_info[i].is_first_req = true;
|
|
|
+ new_cci_dev->cci_master_info[i].is_initilized = false;
|
|
|
mutex_init(&new_cci_dev->cci_master_info[i].mutex);
|
|
|
sema_init(&new_cci_dev->cci_master_info[i].master_sem, 1);
|
|
|
spin_lock_init(&new_cci_dev->cci_master_info[i].freq_cnt);
|
|
@@ -400,27 +388,34 @@ int cam_cci_parse_dt_info(struct platform_device *pdev,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-int cam_cci_soc_release(struct cci_device *cci_dev)
|
|
|
+int cam_cci_soc_release(struct cci_device *cci_dev,
|
|
|
+ enum cci_i2c_master_t master)
|
|
|
{
|
|
|
uint8_t i = 0, rc = 0;
|
|
|
- struct cam_hw_soc_info *soc_info =
|
|
|
- &cci_dev->soc_info;
|
|
|
+ struct cam_hw_soc_info *soc_info = &cci_dev->soc_info;
|
|
|
|
|
|
- if (!cci_dev->ref_count || cci_dev->cci_state != CCI_STATE_ENABLED) {
|
|
|
- CAM_ERR(CAM_CCI, "invalid ref count %d / cci state %d",
|
|
|
- cci_dev->ref_count, cci_dev->cci_state);
|
|
|
+ if (!cci_dev->ref_count || cci_dev->cci_state != CCI_STATE_ENABLED ||
|
|
|
+ !cci_dev->master_active_slave[master]) {
|
|
|
+ CAM_ERR(CAM_CCI,
|
|
|
+ "invalid cci_dev_ref count %u | cci state %d | master_ref_count %u",
|
|
|
+ cci_dev->ref_count, cci_dev->cci_state,
|
|
|
+ cci_dev->master_active_slave[master]);
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
+
|
|
|
+ if (--cci_dev->master_active_slave[master])
|
|
|
+ cci_dev->cci_master_info[master].is_initilized = false;
|
|
|
+
|
|
|
if (--cci_dev->ref_count) {
|
|
|
CAM_DBG(CAM_CCI, "ref_count Exit %d", cci_dev->ref_count);
|
|
|
return 0;
|
|
|
}
|
|
|
- for (i = 0; i < MASTER_MAX; i++)
|
|
|
+
|
|
|
+ for (i = 0; i < MASTER_MAX; i++) {
|
|
|
if (cci_dev->write_wq[i])
|
|
|
flush_workqueue(cci_dev->write_wq[i]);
|
|
|
-
|
|
|
- for (i = 0; i < MASTER_MAX; i++)
|
|
|
cci_dev->i2c_freq_mode[i] = I2C_MAX_MODES;
|
|
|
+ }
|
|
|
|
|
|
rc = cam_soc_util_disable_platform_resource(soc_info, true, true);
|
|
|
if (rc) {
|