Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 47f70e71 authored by Jigarkumar Zala's avatar Jigarkumar Zala
Browse files

msm: camera: Fix CCI issues



Add spinlock to avoid race conditions in queue flags for CCI.

Change-Id: Ib3aded3a49490315682cc1f7b0ad5380210dd011
Signed-off-by: default avatarJigarkumar Zala <jzala@codeaurora.org>
parent d1a9fffc
Loading
Loading
Loading
Loading
+36 −1
Original line number Diff line number Diff line
/* 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
@@ -87,6 +87,7 @@ static int32_t cam_cci_validate_queue(struct cci_device *cci_dev,
	struct cam_hw_soc_info *soc_info =
		&cci_dev->soc_info;
	void __iomem *base = soc_info->reg_map[0].mem_base;
	unsigned long flags;

	read_val = cam_io_r_mb(base +
		CCI_I2C_M0_Q0_CUR_WORD_CNT_ADDR + reg_offset);
@@ -110,12 +111,16 @@ static int32_t cam_cci_validate_queue(struct cci_device *cci_dev,
			CCI_I2C_M0_Q0_EXEC_WORD_CNT_ADDR + reg_offset);
		reg_val = 1 << ((master * 2) + queue);
		CAM_DBG(CAM_CCI, "CCI_QUEUE_START_ADDR");
		spin_lock_irqsave(
			&cci_dev->cci_master_info[master].lock_q[queue], flags);
		atomic_set(&cci_dev->cci_master_info[master].
						done_pending[queue], 1);
		cam_io_w_mb(reg_val, base +
			CCI_QUEUE_START_ADDR);
		CAM_DBG(CAM_CCI, "wait_for_completion_timeout");
		atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
		spin_unlock_irqrestore(
			&cci_dev->cci_master_info[master].lock_q[queue], flags);
		rc = wait_for_completion_timeout(&cci_dev->
			cci_master_info[master].report_q[queue], CCI_TIMEOUT);
		if (rc <= 0) {
@@ -289,6 +294,7 @@ static int32_t cam_cci_wait_report_cmd(struct cci_device *cci_dev,
	enum cci_i2c_master_t master,
	enum cci_i2c_queue_t queue)
{
	unsigned long flags;
	struct cam_hw_soc_info *soc_info =
		&cci_dev->soc_info;
	void __iomem *base = soc_info->reg_map[0].mem_base;
@@ -296,8 +302,12 @@ static int32_t cam_cci_wait_report_cmd(struct cci_device *cci_dev,
	uint32_t reg_val = 1 << ((master * 2) + queue);

	cam_cci_load_report_cmd(cci_dev, master, queue);
	spin_lock_irqsave(
		&cci_dev->cci_master_info[master].lock_q[queue], flags);
	atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
	atomic_set(&cci_dev->cci_master_info[master].done_pending[queue], 1);
	spin_unlock_irqrestore(
		&cci_dev->cci_master_info[master].lock_q[queue], flags);
	cam_io_w_mb(reg_val, base +
		CCI_QUEUE_START_ADDR);

@@ -309,8 +319,13 @@ static int32_t cam_cci_transfer_end(struct cci_device *cci_dev,
	enum cci_i2c_queue_t queue)
{
	int32_t rc = 0;
	unsigned long flags;

	spin_lock_irqsave(&cci_dev->cci_master_info[master].
		lock_q[queue], flags);
	if (atomic_read(&cci_dev->cci_master_info[master].q_free[queue]) == 0) {
		spin_unlock_irqrestore(
			&cci_dev->cci_master_info[master].lock_q[queue], flags);
		rc = cam_cci_lock_queue(cci_dev, master, queue, 0);
		if (rc < 0) {
			CAM_ERR(CAM_CCI, "failed rc: %d", rc);
@@ -324,6 +339,8 @@ static int32_t cam_cci_transfer_end(struct cci_device *cci_dev,
	} else {
		atomic_set(&cci_dev->cci_master_info[master].
						done_pending[queue], 1);
		spin_unlock_irqrestore(
			&cci_dev->cci_master_info[master].lock_q[queue], flags);
		rc = cam_cci_wait(cci_dev, master, queue);
		if (rc < 0) {
			CAM_ERR(CAM_CCI, "failed rc %d", rc);
@@ -371,13 +388,18 @@ static void cam_cci_process_half_q(struct cci_device *cci_dev,
		&cci_dev->soc_info;
	void __iomem *base = soc_info->reg_map[0].mem_base;
	uint32_t reg_val = 1 << ((master * 2) + queue);
	unsigned long flags;

	spin_lock_irqsave(&cci_dev->cci_master_info[master].lock_q[queue],
		flags);
	if (atomic_read(&cci_dev->cci_master_info[master].q_free[queue]) == 0) {
		cam_cci_load_report_cmd(cci_dev, master, queue);
		atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 1);
		cam_io_w_mb(reg_val, base +
			CCI_QUEUE_START_ADDR);
	}
	spin_unlock_irqrestore(&cci_dev->cci_master_info[master].lock_q[queue],
		flags);
}

static int32_t cam_cci_process_full_q(struct cci_device *cci_dev,
@@ -385,16 +407,24 @@ static int32_t cam_cci_process_full_q(struct cci_device *cci_dev,
	enum cci_i2c_queue_t queue)
{
	int32_t rc = 0;
	unsigned long flags;


	spin_lock_irqsave(&cci_dev->cci_master_info[master].lock_q[queue],
		flags);
	if (atomic_read(&cci_dev->cci_master_info[master].q_free[queue]) == 1) {
		atomic_set(&cci_dev->cci_master_info[master].
						done_pending[queue], 1);
		spin_unlock_irqrestore(
			&cci_dev->cci_master_info[master].lock_q[queue], flags);
		rc = cam_cci_wait(cci_dev, master, queue);
		if (rc < 0) {
			CAM_ERR(CAM_CCI, "failed rc %d", rc);
			return rc;
		}
	} else {
		spin_unlock_irqrestore(
			&cci_dev->cci_master_info[master].lock_q[queue], flags);
		rc = cam_cci_wait_report_cmd(cci_dev, master, queue);
		if (rc < 0) {
			CAM_ERR(CAM_CCI, "failed rc %d", rc);
@@ -598,6 +628,7 @@ static int32_t cam_cci_data_queue(struct cci_device *cci_dev,
	struct cam_hw_soc_info *soc_info =
		&cci_dev->soc_info;
	void __iomem *base = soc_info->reg_map[0].mem_base;
	unsigned long flags;

	if (i2c_cmd == NULL) {
		CAM_ERR(CAM_CCI, "Failed: i2c cmd is NULL");
@@ -640,7 +671,11 @@ static int32_t cam_cci_data_queue(struct cci_device *cci_dev,
	cam_io_w_mb(val, base + CCI_I2C_M0_Q0_LOAD_DATA_ADDR +
		reg_offset);

	spin_lock_irqsave(&cci_dev->cci_master_info[master].lock_q[queue],
		flags);
	atomic_set(&cci_dev->cci_master_info[master].q_free[queue], 0);
	spin_unlock_irqrestore(&cci_dev->cci_master_info[master].lock_q[queue],
		flags);

	max_queue_size = cci_dev->cci_i2c_queue_info[master][queue].
			max_queue_size;
+26 −1
Original line number Diff line number Diff line
/* 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
@@ -64,6 +64,7 @@ irqreturn_t cam_cci_irq(int irq_num, void *data)
	struct cam_hw_soc_info *soc_info =
		&cci_dev->soc_info;
	void __iomem *base = soc_info->reg_map[0].mem_base;
	unsigned long flags;

	irq = cam_io_r_mb(base + CCI_IRQ_STATUS_0_ADDR);
	cam_io_w_mb(irq, base + CCI_IRQ_CLEAR_0_ADDR);
@@ -91,23 +92,35 @@ irqreturn_t cam_cci_irq(int irq_num, void *data)
		struct cam_cci_master_info *cci_master_info;

		cci_master_info = &cci_dev->cci_master_info[MASTER_0];
		spin_lock_irqsave(
			&cci_dev->cci_master_info[MASTER_0].lock_q[QUEUE_0],
			flags);
		atomic_set(&cci_master_info->q_free[QUEUE_0], 0);
		cci_master_info->status = 0;
		if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) {
			complete(&cci_master_info->report_q[QUEUE_0]);
			atomic_set(&cci_master_info->done_pending[QUEUE_0], 0);
		}
		spin_unlock_irqrestore(
			&cci_dev->cci_master_info[MASTER_0].lock_q[QUEUE_0],
			flags);
	}
	if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q1_REPORT_BMSK) {
		struct cam_cci_master_info *cci_master_info;

		cci_master_info = &cci_dev->cci_master_info[MASTER_0];
		spin_lock_irqsave(
			&cci_dev->cci_master_info[MASTER_0].lock_q[QUEUE_1],
			flags);
		atomic_set(&cci_master_info->q_free[QUEUE_1], 0);
		cci_master_info->status = 0;
		if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) {
			complete(&cci_master_info->report_q[QUEUE_1]);
			atomic_set(&cci_master_info->done_pending[QUEUE_1], 0);
		}
		spin_unlock_irqrestore(
			&cci_dev->cci_master_info[MASTER_0].lock_q[QUEUE_1],
			flags);
	}
	if (irq & CCI_IRQ_STATUS_0_I2C_M1_RD_DONE_BMSK) {
		cci_dev->cci_master_info[MASTER_1].status = 0;
@@ -117,23 +130,35 @@ irqreturn_t cam_cci_irq(int irq_num, void *data)
		struct cam_cci_master_info *cci_master_info;

		cci_master_info = &cci_dev->cci_master_info[MASTER_1];
		spin_lock_irqsave(
			&cci_dev->cci_master_info[MASTER_1].lock_q[QUEUE_0],
			flags);
		atomic_set(&cci_master_info->q_free[QUEUE_0], 0);
		cci_master_info->status = 0;
		if (atomic_read(&cci_master_info->done_pending[QUEUE_0]) == 1) {
			complete(&cci_master_info->report_q[QUEUE_0]);
			atomic_set(&cci_master_info->done_pending[QUEUE_0], 0);
		}
		spin_unlock_irqrestore(
			&cci_dev->cci_master_info[MASTER_1].lock_q[QUEUE_0],
			flags);
	}
	if (irq & CCI_IRQ_STATUS_0_I2C_M1_Q1_REPORT_BMSK) {
		struct cam_cci_master_info *cci_master_info;

		cci_master_info = &cci_dev->cci_master_info[MASTER_1];
		spin_lock_irqsave(
			&cci_dev->cci_master_info[MASTER_1].lock_q[QUEUE_1],
			flags);
		atomic_set(&cci_master_info->q_free[QUEUE_1], 0);
		cci_master_info->status = 0;
		if (atomic_read(&cci_master_info->done_pending[QUEUE_1]) == 1) {
			complete(&cci_master_info->report_q[QUEUE_1]);
			atomic_set(&cci_master_info->done_pending[QUEUE_1], 0);
		}
		spin_unlock_irqrestore(
			&cci_dev->cci_master_info[MASTER_1].lock_q[QUEUE_1],
			flags);
	}
	if (irq & CCI_IRQ_STATUS_0_I2C_M0_Q0Q1_HALT_ACK_BMSK) {
		cci_dev->cci_master_info[MASTER_0].reset_pending = TRUE;
+2 −1
Original line number Diff line number Diff line
/* 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
@@ -144,6 +144,7 @@ struct cam_cci_master_info {
	struct mutex mutex_q[NUM_QUEUES];
	struct completion report_q[NUM_QUEUES];
	atomic_t done_pending[NUM_QUEUES];
	spinlock_t lock_q[NUM_QUEUES];
};

struct cam_cci_clk_params_t {
+4 −2
Original line number Diff line number Diff line
/* 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
@@ -198,6 +198,8 @@ static void cam_cci_init_cci_params(struct cci_device *new_cci_dev)
			mutex_init(&new_cci_dev->cci_master_info[i].mutex_q[j]);
			init_completion(&new_cci_dev->
				cci_master_info[i].report_q[j]);
			spin_lock_init(
				&new_cci_dev->cci_master_info[i].lock_q[j]);
		}
	}
}