OSDN Git Service

Merge "msm: camera: cpp: Issue CPP HALT on page fault"
authorLinux Build Service Account <lnxbuild@quicinc.com>
Thu, 1 Jun 2017 08:53:01 +0000 (01:53 -0700)
committerGerrit - the friendly Code Review server <code-review@localhost>
Thu, 1 Jun 2017 08:53:00 +0000 (01:53 -0700)
drivers/media/platform/msm/camera_v2/common/cam_smmu_api.c
drivers/media/platform/msm/camera_v2/common/cam_smmu_api.h
drivers/media/platform/msm/camera_v2/isp/msm_isp_util.c
drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.c
drivers/media/platform/msm/camera_v2/pproc/cpp/msm_cpp.h

index 9a53c95..4d9dc06 100644 (file)
@@ -98,6 +98,7 @@ struct scratch_mapping {
 struct cam_context_bank_info {
        struct device *dev;
        struct dma_iommu_mapping *mapping;
+       enum iommu_attr attr;
        dma_addr_t va_start;
        size_t va_len;
        const char *name;
@@ -108,9 +109,8 @@ struct cam_context_bank_info {
        struct mutex lock;
        int handle;
        enum cam_smmu_ops_param state;
-       void (*handler[CAM_SMMU_CB_MAX])(struct iommu_domain *,
-               struct device *, unsigned long,
-               int, void*);
+       client_handler handler[CAM_SMMU_CB_MAX];
+       client_reset_handler hw_reset_handler[CAM_SMMU_CB_MAX];
        void *token[CAM_SMMU_CB_MAX];
        int cb_count;
        int ref_cnt;
@@ -354,9 +354,9 @@ static void cam_smmu_check_vaddr_in_range(int idx, void *vaddr)
 }
 
 void cam_smmu_reg_client_page_fault_handler(int handle,
-               void (*client_page_fault_handler)(struct iommu_domain *,
-               struct device *, unsigned long,
-               int, void*), void *token)
+               client_handler page_fault_handler,
+               client_reset_handler hw_reset_handler,
+               void *token)
 {
        int idx, i = 0;
 
@@ -380,7 +380,7 @@ void cam_smmu_reg_client_page_fault_handler(int handle,
                return;
        }
 
-       if (client_page_fault_handler) {
+       if (page_fault_handler) {
                if (iommu_cb_set.cb_info[idx].cb_count == CAM_SMMU_CB_MAX) {
                        pr_err("%s Should not regiester more handlers\n",
                                iommu_cb_set.cb_info[idx].name);
@@ -392,7 +392,9 @@ void cam_smmu_reg_client_page_fault_handler(int handle,
                        if (iommu_cb_set.cb_info[idx].token[i] == NULL) {
                                iommu_cb_set.cb_info[idx].token[i] = token;
                                iommu_cb_set.cb_info[idx].handler[i] =
-                                       client_page_fault_handler;
+                                       page_fault_handler;
+                               iommu_cb_set.cb_info[idx].hw_reset_handler[i] =
+                                       hw_reset_handler;
                                break;
                        }
                }
@@ -420,6 +422,7 @@ static int cam_smmu_iommu_fault_handler(struct iommu_domain *domain,
 {
        char *cb_name;
        int idx;
+       int j;
        struct cam_smmu_work_payload *payload;
 
        if (!token) {
@@ -453,6 +456,18 @@ static int cam_smmu_iommu_fault_handler(struct iommu_domain *domain,
        payload->token = token;
        payload->idx = idx;
 
+       /* trigger hw reset handler */
+       mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+       for (j = 0; j < CAM_SMMU_CB_MAX; j++) {
+               if ((iommu_cb_set.cb_info[idx].hw_reset_handler[j])) {
+                       iommu_cb_set.cb_info[idx].hw_reset_handler[j](
+                       payload->domain,
+                       payload->dev,
+                       iommu_cb_set.cb_info[idx].token[j]);
+               }
+       }
+       mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+
        mutex_lock(&iommu_cb_set.payload_list_lock);
        list_add_tail(&payload->list, &iommu_cb_set.payload_list);
        mutex_unlock(&iommu_cb_set.payload_list_lock);
@@ -1038,7 +1053,8 @@ static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd,
        buf = dma_buf_get(ion_fd);
        if (IS_ERR_OR_NULL(buf)) {
                rc = PTR_ERR(buf);
-               pr_err("Error: dma get buf failed. fd = %d\n", ion_fd);
+               pr_err("Error: dma get buf failed. fd = %d rc = %d\n",
+                     ion_fd, rc);
                goto err_out;
        }
 
@@ -1103,7 +1119,9 @@ static int cam_smmu_map_buffer_and_add_to_list(int idx, int ion_fd,
                rc = -ENOSPC;
                goto err_mapping_info;
        }
-       CDBG("ion_fd = %d, dev = %pK, paddr= %pK, len = %u\n", ion_fd,
+       CDBG("name %s ion_fd = %d, dev = %pK, paddr= %pK, len = %u\n",
+                       iommu_cb_set.cb_info[idx].name,
+                       ion_fd,
                        (void *)iommu_cb_set.cb_info[idx].dev,
                        (void *)*paddr_ptr, (unsigned int)*len_ptr);
 
@@ -1312,6 +1330,47 @@ int cam_smmu_get_handle(char *identifier, int *handle_ptr)
 }
 EXPORT_SYMBOL(cam_smmu_get_handle);
 
+
+int cam_smmu_set_attr(int handle, uint32_t flags, int32_t *data)
+{
+       int ret = 0, idx;
+       struct cam_context_bank_info *cb = NULL;
+       struct iommu_domain *domain = NULL;
+
+       CDBG("E: set_attr\n");
+       idx = GET_SMMU_TABLE_IDX(handle);
+       if (handle == HANDLE_INIT || idx < 0 || idx >= iommu_cb_set.cb_num) {
+               pr_err("Error: handle or index invalid. idx = %d hdl = %x\n",
+                       idx, handle);
+               return -EINVAL;
+       }
+       mutex_lock(&iommu_cb_set.cb_info[idx].lock);
+       if (iommu_cb_set.cb_info[idx].handle != handle) {
+               pr_err("Error: hdl is not valid, table_hdl = %x, hdl = %x\n",
+                       iommu_cb_set.cb_info[idx].handle, handle);
+               mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+               return -EINVAL;
+       }
+
+       if (iommu_cb_set.cb_info[idx].state == CAM_SMMU_DETACH) {
+               domain = iommu_cb_set.cb_info[idx].mapping->domain;
+               cb = &iommu_cb_set.cb_info[idx];
+               cb->attr |= flags;
+               /* set attributes */
+               ret = iommu_domain_set_attr(domain, cb->attr, (void *)data);
+               if (ret < 0) {
+                       pr_err("Error: set attr\n");
+                       return -ENODEV;
+               }
+       } else {
+               return -EINVAL;
+       }
+       mutex_unlock(&iommu_cb_set.cb_info[idx].lock);
+       return ret;
+}
+EXPORT_SYMBOL(cam_smmu_set_attr);
+
+
 int cam_smmu_ops(int handle, enum cam_smmu_ops_param ops)
 {
        int ret = 0, idx;
index 3b8481f..189ab24 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-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
@@ -49,6 +49,13 @@ enum cam_smmu_map_dir {
        CAM_SMMU_MAP_INVALID
 };
 
+typedef void (*client_handler)(struct iommu_domain *,
+               struct device *, unsigned long,
+               int, void*);
+
+typedef void (*client_reset_handler)(struct iommu_domain *,
+               struct device *, void*);
+
 /**
  * @param identifier: Unique identifier to be used by clients which they
  *                    should get from device tree. CAM SMMU driver will
@@ -61,6 +68,16 @@ enum cam_smmu_map_dir {
  */
 int cam_smmu_get_handle(char *identifier, int *handle_ptr);
 
+
+/**
+ * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
+ * @param flags   : SMMU attribute type
+ * @data             : Value of attribute
+ * @return Status of operation. Negative in case of error. Zero otherwise.
+ */
+int cam_smmu_set_attr(int handle, uint32_t flags, int32_t *data);
+
+
 /**
  * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
  * @param op    : Operation to be performed. Can be either CAM_SMMU_ATTACH
@@ -215,11 +232,12 @@ int cam_smmu_find_index_by_handle(int hdl);
 /**
  * @param handle: Handle to identify the CAM SMMU client (VFE, CPP, FD etc.)
  * @param client_page_fault_handler: It is triggered in IOMMU page fault
+ * @param client_hw_reset_handler: It is triggered in IOMMU page fault
  * @param token: It is input param when trigger page fault handler
  */
 void cam_smmu_reg_client_page_fault_handler(int handle,
-               void (*client_page_fault_handler)(struct iommu_domain *,
-               struct device *, unsigned long,
-               int, void*), void *token);
+       client_handler page_fault_handler,
+       client_reset_handler hw_reset_handler,
+       void *token);
 
 #endif /* _CAM_SMMU_API_H_ */
index 2f8134b..f19e6dd 100644 (file)
@@ -2318,7 +2318,9 @@ int msm_isp_open_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
 
        cam_smmu_reg_client_page_fault_handler(
                        vfe_dev->buf_mgr->iommu_hdl,
-                       msm_vfe_iommu_fault_handler, vfe_dev);
+                       msm_vfe_iommu_fault_handler,
+                       NULL,
+                       vfe_dev);
        mutex_unlock(&vfe_dev->core_mutex);
        mutex_unlock(&vfe_dev->realtime_mutex);
        return 0;
@@ -2367,7 +2369,7 @@ int msm_isp_close_node(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh)
        /* Unregister page fault handler */
        cam_smmu_reg_client_page_fault_handler(
                vfe_dev->buf_mgr->iommu_hdl,
-               NULL, vfe_dev);
+               NULL, NULL, vfe_dev);
 
        rc = vfe_dev->hw_info->vfe_ops.axi_ops.halt(vfe_dev, 1);
        if (rc <= 0)
index b7feb12..25fc34b 100644 (file)
@@ -424,7 +424,17 @@ static unsigned long msm_cpp_queue_buffer_info(struct cpp_device *cpp_dev,
 
        list_for_each_entry_safe(buff, save, buff_head, entry) {
                if (buff->map_info.buff_info.index == buffer_info->index) {
-                       pr_err("error buffer index already queued\n");
+                       pr_err("error buf index already queued\n");
+                       pr_err("error buf, fd %d idx %d native %d ssid %d %d\n",
+                               buffer_info->fd, buffer_info->index,
+                               buffer_info->native_buff,
+                               buff_queue->session_id,
+                               buff_queue->stream_id);
+                       pr_err("existing buf,fd %d idx %d native %d id %x\n",
+                               buff->map_info.buff_info.fd,
+                               buff->map_info.buff_info.index,
+                               buff->map_info.buff_info.native_buff,
+                               buff->map_info.buff_info.identity);
                        goto error;
                }
        }
@@ -439,6 +449,11 @@ static unsigned long msm_cpp_queue_buffer_info(struct cpp_device *cpp_dev,
        buff->map_info.buff_info = *buffer_info;
        buff->map_info.buf_fd = buffer_info->fd;
 
+       trace_printk("fd %d index %d native_buff %d ssid %d %d\n",
+               buffer_info->fd, buffer_info->index,
+               buffer_info->native_buff, buff_queue->session_id,
+               buff_queue->stream_id);
+
        if (buff_queue->security_mode == SECURE_MODE)
                rc = cam_smmu_get_stage2_phy_addr(cpp_dev->iommu_hdl,
                        buffer_info->fd, CAM_SMMU_MAP_RW,
@@ -469,6 +484,11 @@ static void msm_cpp_dequeue_buffer_info(struct cpp_device *cpp_dev,
 {
        int ret = -1;
 
+       trace_printk("fd %d index %d native_buf %d ssid %d %d\n",
+               buff->map_info.buf_fd, buff->map_info.buff_info.index,
+               buff->map_info.buff_info.native_buff, buff_queue->session_id,
+               buff_queue->stream_id);
+
        if (buff_queue->security_mode == SECURE_MODE)
                ret = cam_smmu_put_stage2_phy_addr(cpp_dev->iommu_hdl,
                        buff->map_info.buf_fd);
@@ -770,6 +790,36 @@ static int msm_cpp_dump_addr(struct cpp_device *cpp_dev,
        return 0;
 }
 
+static void msm_cpp_iommu_fault_reset_handler(
+       struct iommu_domain *domain, struct device *dev,
+       void *token)
+{
+       struct cpp_device *cpp_dev = NULL;
+
+       if (!token) {
+               pr_err("Invalid token\n");
+               return;
+       }
+
+       cpp_dev = token;
+
+       if (cpp_dev->fault_status != CPP_IOMMU_FAULT_NONE) {
+               pr_err("fault already detected %d\n", cpp_dev->fault_status);
+               return;
+       }
+
+       cpp_dev->fault_status = CPP_IOMMU_FAULT_DETECTED;
+
+       /* mask IRQ status */
+       msm_camera_io_w(0xB, cpp_dev->cpp_hw_base + 0xC);
+
+       pr_err("Issue CPP HALT %d\n", cpp_dev->fault_status);
+
+       /* MMSS_A_CPP_AXI_CMD = 0x16C, reset 0x1*/
+       msm_camera_io_w(0x1, cpp_dev->cpp_hw_base + 0x16C);
+
+}
+
 static void msm_cpp_iommu_fault_handler(struct iommu_domain *domain,
        struct device *dev, unsigned long iova, int flags, void *token)
 {
@@ -777,50 +827,94 @@ static void msm_cpp_iommu_fault_handler(struct iommu_domain *domain,
        struct msm_cpp_frame_info_t *processed_frame[MAX_CPP_PROCESSING_FRAME];
        int32_t i = 0, queue_len = 0;
        struct msm_device_queue *queue = NULL;
-       int32_t rc = 0;
+       int32_t ifd, ofd, dfd, t0fd, t1fd;
+       int counter = 0;
+       u32 result;
 
        if (token) {
                cpp_dev = token;
+
+               if (cpp_dev->fault_status != CPP_IOMMU_FAULT_DETECTED) {
+                       pr_err("fault recovery already done %d\n",
+                               cpp_dev->fault_status);
+                       return;
+               }
+
                disable_irq(cpp_dev->irq->start);
                if (atomic_read(&cpp_timer.used)) {
                        atomic_set(&cpp_timer.used, 0);
                        del_timer_sync(&cpp_timer.cpp_timer);
                }
-               mutex_lock(&cpp_dev->mutex);
                tasklet_kill(&cpp_dev->cpp_tasklet);
-               rc = cpp_load_fw(cpp_dev, cpp_dev->fw_name_bin);
-               if (rc < 0) {
-                       pr_err("load fw failure %d-retry\n", rc);
-                       rc = msm_cpp_reset_vbif_and_load_fw(cpp_dev);
-                       if (rc < 0) {
-                               msm_cpp_set_micro_irq_mask(cpp_dev, 1, 0x8);
-                               mutex_unlock(&cpp_dev->mutex);
-                               return;
-                       }
+
+               pr_err("in recovery, HALT status = 0x%x\n",
+                       msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10));
+
+               while (counter < MSM_CPP_POLL_RETRIES) {
+                       result = msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10);
+                       if (result & 0x2)
+                               break;
+                       usleep_range(100, 200);
+                       counter++;
                }
+               /* MMSS_A_CPP_IRQ_STATUS_0 = 0x10 */
+               pr_err("counter %d HALT status later = 0x%x\n",
+                       counter,
+                       msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10));
+
+               /* MMSS_A_CPP_RST_CMD_0 = 0x8 firmware reset = 0x3FFFF */
+               msm_camera_io_w(0x3FFFF, cpp_dev->cpp_hw_base + 0x8);
+
+               counter = 0;
+               while (counter < MSM_CPP_POLL_RETRIES) {
+                       result = msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10);
+                       if (result & 0x1)
+                               break;
+                       usleep_range(100, 200);
+                       counter++;
+               }
+
+               /* MMSS_A_CPP_IRQ_STATUS_0 = 0x10 */
+               pr_err("counter %d after reset IRQ_STATUS_0 = 0x%x\n",
+                       counter,
+                       msm_camera_io_r(cpp_dev->cpp_hw_base + 0x10));
+
+               /* MMSS_A_CPP_AXI_CMD = 0x16C, reset 0x1*/
+               msm_camera_io_w(0x0, cpp_dev->cpp_hw_base + 0x16C);
+
                queue = &cpp_timer.data.cpp_dev->processing_q;
                queue_len = queue->len;
                if (!queue_len) {
                        pr_err("%s:%d: Invalid queuelen\n", __func__, __LINE__);
-                       msm_cpp_set_micro_irq_mask(cpp_dev, 1, 0x8);
-                       mutex_unlock(&cpp_dev->mutex);
-                       return;
                }
+
                for (i = 0; i < queue_len; i++) {
                        if (cpp_timer.data.processed_frame[i]) {
                                processed_frame[i] =
                                        cpp_timer.data.processed_frame[i];
-                               pr_err("Fault on  identity=0x%x, frame_id=%03d\n",
+                               ifd = processed_frame[i]->input_buffer_info.fd;
+                               ofd = processed_frame[i]->
+                                       output_buffer_info[0].fd;
+                               dfd = processed_frame[i]->
+                                       duplicate_buffer_info.fd;
+                               t0fd = processed_frame[i]->
+                                       tnr_scratch_buffer_info[0].fd;
+                               t1fd = processed_frame[i]->
+                                       tnr_scratch_buffer_info[1].fd;
+                               pr_err("Fault on identity=0x%x, frame_id=%03d\n",
                                        processed_frame[i]->identity,
                                        processed_frame[i]->frame_id);
+                               pr_err("ifd %d ofd %d dfd %d t0fd %d t1fd %d\n",
+                                       ifd, ofd, dfd, t0fd, t1fd);
                                msm_cpp_dump_addr(cpp_dev, processed_frame[i]);
                                msm_cpp_dump_frame_cmd(processed_frame[i]);
                        }
                }
                msm_cpp_flush_queue_and_release_buffer(cpp_dev, queue_len);
-               msm_cpp_set_micro_irq_mask(cpp_dev, 1, 0x8);
-               mutex_unlock(&cpp_dev->mutex);
+               cpp_dev->fault_status = CPP_IOMMU_FAULT_RECOVERED;
+               pr_err("fault recovery successful\n");
        }
+       return;
 }
 
 static int cpp_init_mem(struct cpp_device *cpp_dev)
@@ -842,7 +936,9 @@ static int cpp_init_mem(struct cpp_device *cpp_dev)
        cpp_dev->iommu_hdl = iommu_hdl;
        cam_smmu_reg_client_page_fault_handler(
                        cpp_dev->iommu_hdl,
-                       msm_cpp_iommu_fault_handler, cpp_dev);
+                       msm_cpp_iommu_fault_handler,
+                       msm_cpp_iommu_fault_reset_handler,
+                       cpp_dev);
        return 0;
 }
 
@@ -981,6 +1077,7 @@ static int cpp_init_hardware(struct cpp_device *cpp_dev)
        int rc = 0;
        uint32_t vbif_version;
        cpp_dev->turbo_vote = 0;
+       cpp_dev->fault_status = CPP_IOMMU_FAULT_NONE;
 
        rc = msm_camera_regulator_enable(cpp_dev->cpp_vdd,
                cpp_dev->num_reg, true);
@@ -1115,7 +1212,7 @@ static void cpp_release_hardware(struct cpp_device *cpp_dev)
                rc = msm_cpp_update_bandwidth_setting(cpp_dev, 0, 0);
        }
        cpp_dev->stream_cnt = 0;
-
+       pr_info("cpp hw release done\n");
 }
 
 static int32_t cpp_load_fw(struct cpp_device *cpp_dev, char *fw_name_bin)
@@ -2536,6 +2633,16 @@ static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev,
                return -EINVAL;
        }
 
+       if (cpp_dev->fault_status == CPP_IOMMU_FAULT_RECOVERED) {
+               pr_err("Error, page fault occurred %d\n",
+                       cpp_dev->fault_status);
+               return -EINVAL;
+       } else if (cpp_dev->fault_status == CPP_IOMMU_FAULT_DETECTED) {
+               pr_err("drop frame, page fault occurred %d\n",
+                       cpp_dev->fault_status);
+               return -EFAULT;
+       }
+
        if (cpp_frame_msg[new_frame->msg_len - 1] !=
                MSM_CPP_MSG_ID_TRAILER) {
                pr_err("Invalid frame message\n");
@@ -3523,6 +3630,7 @@ STREAM_BUFF_END:
                break;
        case VIDIOC_MSM_CPP_IOMMU_ATTACH: {
                if (cpp_dev->iommu_state == CPP_IOMMU_STATE_DETACHED) {
+                       int32_t stall_disable;
                        struct msm_camera_smmu_attach_type cpp_attach_info;
 
                        if (ioctl_ptr->len !=
@@ -3540,7 +3648,10 @@ STREAM_BUFF_END:
                        }
 
                        cpp_dev->security_mode = cpp_attach_info.attach;
-
+                       stall_disable = 1;
+                       /* disable smmu stall on fault */
+                       cam_smmu_set_attr(cpp_dev->iommu_hdl,
+                               DOMAIN_ATTR_CB_STALL_DISABLE, &stall_disable);
                        if (cpp_dev->security_mode == SECURE_MODE) {
                                rc = cam_smmu_ops(cpp_dev->iommu_hdl,
                                        CAM_SMMU_ATTACH_SEC_CPP);
index a054480..f2c5447 100644 (file)
@@ -130,6 +130,12 @@ enum cpp_iommu_state {
        CPP_IOMMU_STATE_ATTACHED,
 };
 
+enum cpp_iommu_fault_state {
+       CPP_IOMMU_FAULT_NONE,
+       CPP_IOMMU_FAULT_DETECTED,
+       CPP_IOMMU_FAULT_RECOVERED,
+};
+
 enum msm_queue {
        MSM_CAM_Q_CTRL,     /* control command or control command status */
        MSM_CAM_Q_VFE_EVT,  /* adsp event */
@@ -287,6 +293,7 @@ struct cpp_device {
        struct msm_cpp_vbif_data *vbif_data;
        bool turbo_vote;
        struct cx_ipeak_client *cpp_cx_ipeak;
+       enum cpp_iommu_fault_state fault_status;
 };
 
 int msm_cpp_set_micro_clk(struct cpp_device *cpp_dev);