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;
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;
}
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;
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);
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;
}
}
{
char *cb_name;
int idx;
+ int j;
struct cam_smmu_work_payload *payload;
if (!token) {
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);
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;
}
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);
}
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;
-/* 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
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
*/
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
/**
* @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_ */
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;
}
}
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,
{
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);
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)
{
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)
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;
}
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);
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)
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");
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 !=
}
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);