From 6a16d11bf3f004b372f0609ccb4727f8364d4e28 Mon Sep 17 00:00:00 2001 From: Shashank Babu Chinta Venkata Date: Fri, 19 Jun 2020 14:31:39 -0700 Subject: [PATCH 1/2] input: touchscreen: st: add state machine for TUI error handling Introduce state machine for various checkpoints in trusted touch usecase. In error scenarios, this state machine helps driver to rollback to previous state. Change-Id: I8c8a0d23c0c44ed5e4be5e623680a250636b78f4 Signed-off-by: Shashank Babu Chinta Venkata --- drivers/input/touchscreen/st/fts.c | 604 +++++++++++++++++++++-------- drivers/input/touchscreen/st/fts.h | 53 ++- 2 files changed, 493 insertions(+), 164 deletions(-) diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c index 21c12f74fb99..18b321d50d02 100644 --- a/drivers/input/touchscreen/st/fts.c +++ b/drivers/input/touchscreen/st/fts.c @@ -124,6 +124,8 @@ static int fts_probe_delayed(struct fts_ts_info *info); #ifdef CONFIG_ST_TRUSTED_TOUCH +static void fts_trusted_touch_abort_handler(struct fts_ts_info *info, + int error); static struct hh_acl_desc *fts_vm_get_acl(enum hh_vm_names vm_name) { struct hh_acl_desc *acl_desc; @@ -264,7 +266,29 @@ static void fts_vm_deinit(struct fts_ts_info *info) #ifdef CONFIG_ARCH_QTI_VM static int fts_vm_mem_release(struct fts_ts_info *info); -static void fts_trusted_touch_vm_mode_disable(struct fts_ts_info *info); +static void fts_trusted_touch_tvm_vm_mode_disable(struct fts_ts_info *info); +static void fts_trusted_touch_abort_tvm(struct fts_ts_info *info); +static void fts_trusted_touch_event_notify(struct fts_ts_info *info, int event); + +static int fts_trusted_touch_get_tvm_driver_state(struct fts_ts_info *info) +{ + int state; + + mutex_lock(&info->vm_info->tvm_state_mutex); + state = atomic_read(&info->vm_info->tvm_state); + mutex_unlock(&info->vm_info->tvm_state_mutex); + + return state; + +} + +static void fts_trusted_touch_set_tvm_driver_state(struct fts_ts_info *info, + int state) +{ + mutex_lock(&info->vm_info->tvm_state_mutex); + atomic_set(&info->vm_info->tvm_state, state); + mutex_unlock(&info->vm_info->tvm_state_mutex); +} static int fts_sgl_cmp(const void *a, const void *b) { @@ -317,69 +341,131 @@ static int fts_vm_handle_vm_hardware(struct fts_ts_info *info) queue_delayed_work(info->fwu_workqueue, &info->fwu_work, msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + flushFIFO(); fts_interrupt_enable(info); + fts_trusted_touch_set_tvm_driver_state(info, TVM_INTERRUPT_ENABLED); return rc; } -static void fts_vm_irq_on_lend_callback(void *data, - unsigned long notif_type, - enum hh_irq_label label) +static void fts_trusted_touch_tvm_vm_mode_enable(struct fts_ts_info *info) { - struct fts_ts_info *info = data; + struct hh_sgl_desc *sgl_desc, *expected_sgl_desc; + struct hh_acl_desc *acl_desc; struct irq_data *irq_data; - int irq = 0; - int const resource_timeout = msecs_to_jiffies(2000); int rc = 0; + int irq = 0; + + if (fts_trusted_touch_get_tvm_driver_state(info) != + TVM_ALL_RESOURCES_LENT_NOTIFIED) { + pr_err("All lend notifications not received\n"); + fts_trusted_touch_event_notify(info, + TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING); + return; + } + + acl_desc = fts_vm_get_acl(HH_TRUSTED_VM); + if (IS_ERR(acl_desc)) { + pr_err("failed to populated acl data:rc=%d\n", + PTR_ERR(acl_desc)); + goto accept_fail; + } + + sgl_desc = hh_rm_mem_accept(info->vm_info->vm_mem_handle, + HH_RM_MEM_TYPE_IO, + HH_RM_TRANS_TYPE_LEND, + HH_RM_MEM_ACCEPT_VALIDATE_ACL_ATTRS | + HH_RM_MEM_ACCEPT_VALIDATE_LABEL | + HH_RM_MEM_ACCEPT_DONE, TRUSTED_TOUCH_MEM_LABEL, + acl_desc, NULL, NULL, 0); + if (IS_ERR_OR_NULL(sgl_desc)) { + pr_err("failed to do mem accept :rc=%d\n", + PTR_ERR(sgl_desc)); + goto acl_fail; + } + fts_trusted_touch_set_tvm_driver_state(info, TVM_IOMEM_ACCEPTED); + + /* Initiate i2c session on tvm */ + rc = pm_runtime_get_sync(info->client->adapter->dev.parent); + if (rc < 0) { + pr_err("failed to get sync rc:%d\n", rc); + goto acl_fail; + } + fts_trusted_touch_set_tvm_driver_state(info, TVM_I2C_SESSION_ACQUIRED); + + expected_sgl_desc = fts_vm_get_sgl(info->vm_info); + if (fts_vm_compare_sgl_desc(expected_sgl_desc, sgl_desc)) { + pr_err("IO sg list does not match\n"); + goto sgl_cmp_fail; + } + + kfree(expected_sgl_desc); + kfree(acl_desc); irq = hh_irq_accept(info->vm_info->irq_label, -1, IRQ_TYPE_LEVEL_HIGH); if (irq < 0) { pr_err("failed to accept irq\n"); - goto irq_fail; + goto accept_fail; } - atomic_set(&info->vm_info->tvm_owns_irq, 1); + fts_trusted_touch_set_tvm_driver_state(info, TVM_IRQ_ACCEPTED); + + irq_data = irq_get_irq_data(irq); if (!irq_data) { pr_err("Invalid irq data for trusted touch\n"); - goto irq_fail; + goto accept_fail; } if (!irq_data->hwirq) { pr_err("Invalid irq in irq data\n"); - goto irq_fail; + goto accept_fail; } if (irq_data->hwirq != info->vm_info->hw_irq) { pr_err("Invalid irq lent\n"); - goto irq_fail; + goto accept_fail; } pr_debug("irq:returned from accept:%d\n", irq); info->client->irq = irq; - if (!wait_for_completion_timeout(&info->resource_checkpoint, - resource_timeout)) { - pr_err("Resources not acquired in TVM\n"); - goto irq_fail; - } rc = fts_vm_handle_vm_hardware(info); if (rc) { pr_err(" Delayed probe failure on VM!\n"); - goto irq_fail; + goto accept_fail; } - atomic_set(&info->trusted_touch_enabled, 1); + pr_info("trusted touch enabled\n"); + return; -irq_fail: - fts_trusted_touch_vm_mode_disable(info); +sgl_cmp_fail: + kfree(expected_sgl_desc); +acl_fail: + kfree(acl_desc); +accept_fail: + fts_trusted_touch_abort_handler(info, + TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE); +} + +static void fts_vm_irq_on_lend_callback(void *data, unsigned long notif_type, + enum hh_irq_label label) +{ + struct fts_ts_info *info = data; + + pr_debug("received irq lend request for label:%d\n", label); + if (fts_trusted_touch_get_tvm_driver_state(info) == + TVM_IOMEM_LENT_NOTIFIED) { + fts_trusted_touch_set_tvm_driver_state(info, + TVM_ALL_RESOURCES_LENT_NOTIFIED); + } else { + fts_trusted_touch_set_tvm_driver_state(info, + TVM_IRQ_LENT_NOTIFIED); + } } static void fts_vm_mem_on_lend_handler(enum hh_mem_notifier_tag tag, unsigned long notif_type, void *entry_data, void *notif_msg) { struct hh_rm_notif_mem_shared_payload *payload; - struct hh_sgl_desc *sgl_desc, *expected_sgl_desc; - struct hh_acl_desc *acl_desc; struct trusted_touch_vm_info *vm_info; struct fts_ts_info *info; - int rc = 0; if (notif_type != HH_RM_NOTIF_MEM_SHARED || tag != HH_MEM_NOTIFIER_TAG_TOUCH) { @@ -403,62 +489,31 @@ static void fts_vm_mem_on_lend_handler(enum hh_mem_notifier_tag tag, if (payload->trans_type != HH_RM_TRANS_TYPE_LEND || payload->label != TRUSTED_TOUCH_MEM_LABEL) { pr_err("Invalid label or transaction type\n"); - goto onlend_fail; + return; } - - acl_desc = fts_vm_get_acl(HH_TRUSTED_VM); - if (IS_ERR(acl_desc)) { - pr_err("failed to populated acl data:rc=%d\n", - PTR_ERR(acl_desc)); - goto onlend_fail; - } - - sgl_desc = hh_rm_mem_accept(payload->mem_handle, HH_RM_MEM_TYPE_IO, - HH_RM_TRANS_TYPE_LEND, - HH_RM_MEM_ACCEPT_VALIDATE_ACL_ATTRS | - HH_RM_MEM_ACCEPT_VALIDATE_LABEL | - HH_RM_MEM_ACCEPT_DONE, payload->label, acl_desc, - NULL, NULL, 0); - if (IS_ERR_OR_NULL(sgl_desc)) { - pr_err("failed to do mem accept :rc=%d\n", - PTR_ERR(sgl_desc)); - goto acl_fail; - } - atomic_set(&vm_info->tvm_owns_iomem, 1); - - /* Initiate i2c session on tvm */ - rc = pm_runtime_get_sync(info->client->adapter->dev.parent); - if (rc < 0) { - pr_err("failed to get sync rc:%d\n", rc); - (void)fts_vm_mem_release(info); - atomic_set(&info->vm_info->tvm_owns_iomem, 0); - goto acl_fail; - } - complete(&info->resource_checkpoint); - - expected_sgl_desc = fts_vm_get_sgl(vm_info); - if (fts_vm_compare_sgl_desc(expected_sgl_desc, sgl_desc)) { - pr_err("IO sg list does not match\n"); - goto sgl_cmp_fail; - } - vm_info->vm_mem_handle = payload->mem_handle; - kfree(expected_sgl_desc); - kfree(acl_desc); - return; + pr_debug("received mem lend request with handle:%d\n", + vm_info->vm_mem_handle); -sgl_cmp_fail: - kfree(expected_sgl_desc); -acl_fail: - kfree(acl_desc); -onlend_fail: - fts_trusted_touch_vm_mode_disable(info); + if (fts_trusted_touch_get_tvm_driver_state(info) == + TVM_IRQ_LENT_NOTIFIED) { + fts_trusted_touch_set_tvm_driver_state(info, + TVM_ALL_RESOURCES_LENT_NOTIFIED); + } else { + fts_trusted_touch_set_tvm_driver_state(info, + TVM_IOMEM_LENT_NOTIFIED); + } } static int fts_vm_mem_release(struct fts_ts_info *info) { int rc = 0; + if (!info->vm_info->vm_mem_handle) { + pr_err("Invalid memory handle\n"); + return -EINVAL; + } + rc = hh_rm_mem_release(info->vm_info->vm_mem_handle, 0); if (rc) pr_err("VM mem release failed: rc=%d\n", rc); @@ -468,45 +523,59 @@ static int fts_vm_mem_release(struct fts_ts_info *info) HH_MEM_NOTIFIER_TAG_TOUCH, 0); if (rc) pr_err("Failed to notify mem release to PVM: rc=%d\n"); + pr_info("vm mem release succeded\n"); info->vm_info->vm_mem_handle = 0; return rc; } -static void fts_trusted_touch_vm_mode_disable(struct fts_ts_info *info) +static void fts_trusted_touch_tvm_vm_mode_disable(struct fts_ts_info *info) { int rc = 0; - - if (atomic_read(&info->vm_info->tvm_owns_iomem) && - atomic_read(&info->vm_info->tvm_owns_irq)) - fts_interrupt_disable(info); - - if (atomic_read(&info->vm_info->tvm_owns_iomem)) { - flushFIFO(); - release_all_touches(info); - rc = fts_vm_mem_release(info); - if (rc) - pr_err("Failed to release mem rc:%d\n", rc); - else - atomic_set(&info->vm_info->tvm_owns_iomem, 0); - pm_runtime_put_sync(info->client->adapter->dev.parent); + if (atomic_read(&info->trusted_touch_abort_status)) { + fts_trusted_touch_abort_tvm(info); + return; } - if (atomic_read(&info->vm_info->tvm_owns_irq)) { - rc = hh_irq_release(info->vm_info->irq_label); - if (rc) - pr_err("Failed to release irq rc:%d\n", rc); - else - atomic_set(&info->vm_info->tvm_owns_irq, 0); + fts_interrupt_disable(info); + fts_trusted_touch_set_tvm_driver_state(info, + TVM_INTERRUPT_DISABLED); - rc = hh_irq_release_notify(info->vm_info->irq_label); - if (rc) - pr_err("Failed to notify release irq rc:%d\n", rc); + rc = hh_irq_release(info->vm_info->irq_label); + if (rc) { + pr_err("Failed to release irq rc:%d\n", rc); + goto error; + } else { + fts_trusted_touch_set_tvm_driver_state(info, + TVM_IRQ_RELEASED); } + rc = hh_irq_release_notify(info->vm_info->irq_label); + if (rc) + pr_err("Failed to notify release irq rc:%d\n", rc); + + pr_info("vm irq release succeded\n"); + + flushFIFO(); + release_all_touches(info); + rc = fts_vm_mem_release(info); + if (rc) { + pr_err("Failed to release mem rc:%d\n", rc); + goto error; + } else { + fts_trusted_touch_set_tvm_driver_state(info, + TVM_IOMEM_RELEASED); + } + pm_runtime_put_sync(info->client->adapter->dev.parent); + fts_trusted_touch_set_tvm_driver_state(info, + TVM_I2C_SESSION_RELEASED); + fts_trusted_touch_set_tvm_driver_state(info, TRUSTED_TOUCH_TVM_INIT); atomic_set(&info->trusted_touch_enabled, 0); - reinit_completion(&info->resource_checkpoint); - pr_debug("trusted touch disabled\n"); + pr_info("trusted touch disabled\n"); + return; +error: + fts_trusted_touch_abort_handler(info, + TRUSTED_TOUCH_EVENT_RELEASE_FAILURE); } static int fts_handle_trusted_touch_tvm(struct fts_ts_info *info, int value) @@ -515,13 +584,14 @@ static int fts_handle_trusted_touch_tvm(struct fts_ts_info *info, int value) switch (value) { case 0: - if (atomic_read(&info->trusted_touch_enabled) == 0) { + if (atomic_read(&info->trusted_touch_enabled) == 0 && + (atomic_read(&info->trusted_touch_abort_status) == 0)) { pr_err("Trusted touch is already disabled\n"); break; } if (atomic_read(&info->trusted_touch_mode) == TRUSTED_TOUCH_VM_MODE) { - fts_trusted_touch_vm_mode_disable(info); + fts_trusted_touch_tvm_vm_mode_disable(info); } else { pr_err("Unsupported trusted touch mode\n"); } @@ -535,7 +605,7 @@ static int fts_handle_trusted_touch_tvm(struct fts_ts_info *info, int value) } if (atomic_read(&info->trusted_touch_mode) == TRUSTED_TOUCH_VM_MODE) { - pr_err("Cannot turnon trusted touch(vm mode) in VM\n"); + fts_trusted_touch_tvm_vm_mode_enable(info); } else { pr_err("Unsupported trusted touch mode\n"); } @@ -550,8 +620,117 @@ static int fts_handle_trusted_touch_tvm(struct fts_ts_info *info, int value) return err; } +static void fts_trusted_touch_abort_tvm(struct fts_ts_info *info) +{ + int rc = 0; + int tvm_state = fts_trusted_touch_get_tvm_driver_state(info); + + if (tvm_state >= TRUSTED_TOUCH_TVM_STATE_MAX) { + pr_err("invalid tvm driver state: %d\n", tvm_state); + return; + } + + switch (tvm_state) { + case TVM_INTERRUPT_ENABLED: + fts_interrupt_disable(info); + case TVM_IRQ_ACCEPTED: + case TVM_INTERRUPT_DISABLED: + rc = hh_irq_release(info->vm_info->irq_label); + if (rc) + pr_err("Failed to release irq rc:%d\n", rc); + rc = hh_irq_release_notify(info->vm_info->irq_label); + if (rc) + pr_err("Failed to notify irq release rc:%d\n", rc); + case TVM_I2C_SESSION_ACQUIRED: + case TVM_IOMEM_ACCEPTED: + case TVM_IRQ_RELEASED: + flushFIFO(); + release_all_touches(info); + rc = fts_vm_mem_release(info); + if (rc) + pr_err("Failed to release mem rc:%d\n", rc); + case TVM_IOMEM_RELEASED: + pm_runtime_put_sync(info->client->adapter->dev.parent); + case TVM_I2C_SESSION_RELEASED: + case TVM_IOMEM_LENT_NOTIFIED: + case TVM_IRQ_LENT_NOTIFIED: + case TVM_ALL_RESOURCES_LENT_NOTIFIED: + case TRUSTED_TOUCH_TVM_INIT: + atomic_set(&info->trusted_touch_enabled, 0); + } + + atomic_set(&info->trusted_touch_abort_status, 0); + fts_trusted_touch_set_tvm_driver_state(info, TRUSTED_TOUCH_TVM_INIT); +} + #else +static void fts_bus_put(struct fts_ts_info *info); +static int fts_trusted_touch_get_pvm_driver_state(struct fts_ts_info *info) +{ + int state; + + mutex_lock(&info->vm_info->pvm_state_mutex); + state = atomic_read(&info->vm_info->pvm_state); + mutex_unlock(&info->vm_info->pvm_state_mutex); + + return state; + +} + +static void fts_trusted_touch_set_pvm_driver_state(struct fts_ts_info *info, + int state) +{ + mutex_lock(&info->vm_info->pvm_state_mutex); + atomic_set(&info->vm_info->pvm_state, state); + mutex_unlock(&info->vm_info->pvm_state_mutex); +} + +static void fts_trusted_touch_abort_pvm(struct fts_ts_info *info) +{ + int rc = 0; + int pvm_state = fts_trusted_touch_get_pvm_driver_state(info); + + if (pvm_state >= TRUSTED_TOUCH_PVM_STATE_MAX) { + pr_err("Invalid driver state: %d\n", pvm_state); + return; + } + + switch (pvm_state) { + case PVM_IRQ_RELEASE_NOTIFIED: + case PVM_ALL_RESOURCES_RELEASE_NOTIFIED: + case PVM_IRQ_LENT: + case PVM_IRQ_LENT_NOTIFIED: + rc = hh_irq_reclaim(info->vm_info->irq_label); + if (rc) + pr_err("failed to reclaim irq on pvm rc:%d\n", rc); + case PVM_IRQ_RECLAIMED: + case PVM_IOMEM_LENT: + case PVM_IOMEM_LENT_NOTIFIED: + case PVM_IOMEM_RELEASE_NOTIFIED: + rc = hh_rm_mem_reclaim(info->vm_info->vm_mem_handle, 0); + if (rc) + pr_err("failed to reclaim iomem on pvm rc:%d\n", rc); + info->vm_info->vm_mem_handle = 0; + case PVM_IOMEM_RECLAIMED: + case PVM_INTERRUPT_DISABLED: + flushFIFO(); + fts_interrupt_enable(info); + case PVM_I2C_RESOURCE_ACQUIRED: + case PVM_INTERRUPT_ENABLED: + fts_bus_put(info); + complete(&info->trusted_touch_powerdown); + case TRUSTED_TOUCH_PVM_INIT: + case PVM_I2C_RESOURCE_RELEASED: + atomic_set(&info->trusted_touch_enabled, 0); + } + + atomic_set(&info->trusted_touch_abort_status, 0); + + fts_trusted_touch_set_pvm_driver_state(info, TRUSTED_TOUCH_PVM_INIT); +} + + static int fts_clk_prepare_enable(struct fts_ts_info *info) { int ret; @@ -616,45 +795,85 @@ static struct hh_notify_vmid_desc *fts_vm_get_vmid(hh_vmid_t vmid) return vmid_desc; } -static void fts_trusted_touch_complete(struct fts_ts_info *info) +static void fts_trusted_touch_pvm_vm_mode_disable(struct fts_ts_info *info) { - if (atomic_read(&info->vm_info->pvm_owns_iomem) && - atomic_read(&info->vm_info->pvm_owns_irq)) { - fts_interrupt_enable(info); - fts_bus_put(info); - complete(&info->trusted_touch_powerdown); - atomic_set(&info->trusted_touch_enabled, 0); - pr_debug("reenabled interrupts on PVM\n"); - } else { - pr_err("PVM does not own irq and IOMEM\n"); - } -} - -static void fts_vm_irq_on_release_callback(void *data, - unsigned long notif_type, - enum hh_irq_label label) -{ - struct fts_ts_info *info = data; int rc = 0; + if (atomic_read(&info->trusted_touch_abort_status)) { + fts_trusted_touch_abort_pvm(info); + return; + } + + if (fts_trusted_touch_get_pvm_driver_state(info) != + PVM_ALL_RESOURCES_RELEASE_NOTIFIED) + pr_err("all release notifications are not received yet\n"); + rc = hh_irq_reclaim(info->vm_info->irq_label); - if (rc) + if (rc) { pr_err("failed to reclaim irq on pvm rc:%d\n", rc); - else - atomic_set(&info->vm_info->pvm_owns_irq, 1); + goto error; + } + fts_trusted_touch_set_pvm_driver_state(info, + PVM_IRQ_RECLAIMED); + + rc = hh_rm_mem_reclaim(info->vm_info->vm_mem_handle, 0); + if (rc) { + pr_err("Trusted touch VM mem reclaim failed rc:%d\n", rc); + goto error; + } + fts_trusted_touch_set_pvm_driver_state(info, PVM_IOMEM_RECLAIMED); + info->vm_info->vm_mem_handle = 0; + + flushFIFO(); + fts_interrupt_enable(info); + fts_trusted_touch_set_pvm_driver_state(info, PVM_INTERRUPT_ENABLED); + fts_bus_put(info); + fts_trusted_touch_set_pvm_driver_state(info, + PVM_I2C_RESOURCE_RELEASED); + complete(&info->trusted_touch_powerdown); + fts_trusted_touch_set_pvm_driver_state(info, + TRUSTED_TOUCH_PVM_INIT); + atomic_set(&info->trusted_touch_enabled, 0); + return; +error: + fts_trusted_touch_abort_handler(info, + TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE); +} + +static void fts_vm_irq_on_release_callback(void *data, unsigned long notif_type, + enum hh_irq_label label) +{ + struct fts_ts_info *info = data; + + if (notif_type != HH_RM_NOTIF_VM_IRQ_RELEASED) { + pr_err("invalid notification type\n"); + return; + } + + if (fts_trusted_touch_get_pvm_driver_state(info) == + PVM_IOMEM_RELEASE_NOTIFIED) { + fts_trusted_touch_set_pvm_driver_state(info, + PVM_ALL_RESOURCES_RELEASE_NOTIFIED); + } else { + fts_trusted_touch_set_pvm_driver_state(info, + PVM_IRQ_RELEASE_NOTIFIED); + } } static void fts_vm_mem_on_release_handler(enum hh_mem_notifier_tag tag, unsigned long notif_type, void *entry_data, void *notif_msg) { - struct hh_rm_notif_mem_released_payload *payload; + struct hh_rm_notif_mem_released_payload *release_payload; struct trusted_touch_vm_info *vm_info; struct fts_ts_info *info; - int rc = 0; - if (notif_type != HH_RM_NOTIF_MEM_RELEASED || - tag != HH_MEM_NOTIFIER_TAG_TOUCH) { - pr_err(" Invalid tag or command passed\n"); + if (notif_type != HH_RM_NOTIF_MEM_RELEASED) { + pr_err(" Invalid notification type\n"); + return; + } + + if (tag != HH_MEM_NOTIFIER_TAG_TOUCH) { + pr_err(" Invalid tag\n"); return; } @@ -663,7 +882,6 @@ static void fts_vm_mem_on_release_handler(enum hh_mem_notifier_tag tag, return; } - payload = (struct hh_rm_notif_mem_released_payload *)notif_msg; info = (struct fts_ts_info *)entry_data; vm_info = info->vm_info; if (!vm_info) { @@ -671,18 +889,20 @@ static void fts_vm_mem_on_release_handler(enum hh_mem_notifier_tag tag, return; } - if (payload->mem_handle != vm_info->vm_mem_handle) { + release_payload = (struct hh_rm_notif_mem_released_payload *)notif_msg; + if (release_payload->mem_handle != vm_info->vm_mem_handle) { pr_err("Invalid mem handle detected\n"); return; } - rc = hh_rm_mem_reclaim(payload->mem_handle, 0); - if (rc) { - pr_err("Trusted touch VM mem release failed rc:%d\n", rc); - return; + if (fts_trusted_touch_get_pvm_driver_state(info) == + PVM_IRQ_RELEASE_NOTIFIED) { + fts_trusted_touch_set_pvm_driver_state(info, + PVM_ALL_RESOURCES_RELEASE_NOTIFIED); + } else { + fts_trusted_touch_set_pvm_driver_state(info, + PVM_IOMEM_RELEASE_NOTIFIED); } - atomic_set(&vm_info->pvm_owns_iomem, 1); - vm_info->vm_mem_handle = 0; } static int fts_vm_mem_lend(struct fts_ts_info *info) @@ -716,6 +936,9 @@ static int fts_vm_mem_lend(struct fts_ts_info *info) rc); goto error; } + pr_info("vm mem lend succeded\n"); + + fts_trusted_touch_set_pvm_driver_state(info, PVM_IOMEM_LENT); hh_rm_get_vmid(HH_TRUSTED_VM, &trusted_vmid); @@ -727,6 +950,7 @@ static int fts_vm_mem_lend(struct fts_ts_info *info) pr_err("Failed to notify mem lend to hypervisor rc:%d\n", rc); goto vmid_error; } + fts_trusted_touch_set_pvm_driver_state(info, PVM_IOMEM_LENT_NOTIFIED); info->vm_info->vm_mem_handle = mem_handle; vmid_error: @@ -739,7 +963,7 @@ sgl_error: return rc; } -static int fts_trusted_touch_vm_mode_enable(struct fts_ts_info *info) +static int fts_trusted_touch_pvm_vm_mode_enable(struct fts_ts_info *info) { int rc = 0; struct trusted_touch_vm_info *vm_info = info->vm_info; @@ -750,37 +974,44 @@ static int fts_trusted_touch_vm_mode_enable(struct fts_ts_info *info) rc = -EIO; return rc; } + fts_trusted_touch_set_pvm_driver_state(info, PVM_I2C_RESOURCE_ACQUIRED); /* flush pending interurpts from FIFO */ fts_interrupt_disable(info); + fts_trusted_touch_set_pvm_driver_state(info, PVM_INTERRUPT_DISABLED); + flushFIFO(); release_all_touches(info); rc = fts_vm_mem_lend(info); if (rc) { pr_err("Failed to lend memory\n"); - return -EINVAL; + goto error; } - atomic_set(&vm_info->pvm_owns_iomem, 0); rc = hh_irq_lend_v2(vm_info->irq_label, vm_info->vm_name, info->client->irq, &fts_vm_irq_on_release_callback, info); if (rc) { pr_err("Failed to lend irq\n"); - return -EINVAL; + goto error; } - atomic_set(&vm_info->pvm_owns_irq, 0); + pr_info("vm irq lend succeded for irq:%d\n", info->client->irq); + fts_trusted_touch_set_pvm_driver_state(info, PVM_IRQ_LENT); rc = hh_irq_lend_notify(vm_info->irq_label); if (rc) { pr_err("Failed to notify irq\n"); - return -EINVAL; + goto error; } + fts_trusted_touch_set_pvm_driver_state(info, PVM_IRQ_LENT_NOTIFIED); reinit_completion(&info->trusted_touch_powerdown); atomic_set(&info->trusted_touch_enabled, 1); pr_debug("trusted touch enabled\n"); return rc; +error: + fts_trusted_touch_abort_handler(info, TRUSTED_TOUCH_EVENT_LEND_FAILURE); + return rc; } static int fts_handle_trusted_touch_pvm(struct fts_ts_info *info, int value) @@ -789,13 +1020,14 @@ static int fts_handle_trusted_touch_pvm(struct fts_ts_info *info, int value) switch (value) { case 0: - if (atomic_read(&info->trusted_touch_enabled) == 0) { + if (atomic_read(&info->trusted_touch_enabled) == 0 && + (atomic_read(&info->trusted_touch_abort_status) == 0)) { pr_err("Trusted touch is already disabled\n"); break; } if (atomic_read(&info->trusted_touch_mode) == TRUSTED_TOUCH_VM_MODE) { - fts_trusted_touch_complete(info); + fts_trusted_touch_pvm_vm_mode_disable(info); } else { pr_err("Unsupported trusted touch mode\n"); } @@ -809,7 +1041,7 @@ static int fts_handle_trusted_touch_pvm(struct fts_ts_info *info, int value) } if (atomic_read(&info->trusted_touch_mode) == TRUSTED_TOUCH_VM_MODE) { - err = fts_trusted_touch_vm_mode_enable(info); + err = fts_trusted_touch_pvm_vm_mode_enable(info); } else { pr_err("Unsupported trusted touch mode\n"); } @@ -823,6 +1055,18 @@ static int fts_handle_trusted_touch_pvm(struct fts_ts_info *info, int value) return err; } #endif +static void fts_trusted_touch_event_notify(struct fts_ts_info *info, int event) +{ + atomic_set(&info->trusted_touch_event, event); + sysfs_notify(&info->client->dev.kobj, NULL, "trusted_touch_event"); +} + +static void fts_trusted_touch_abort_handler(struct fts_ts_info *info, int error) +{ + atomic_set(&info->trusted_touch_abort_status, error); + pr_err("TUI session aborted with failure:%d\n", error); + fts_trusted_touch_event_notify(info, error); +} static int fts_vm_init(struct fts_ts_info *info) { @@ -849,9 +1093,8 @@ static int fts_vm_init(struct fts_ts_info *info) vm_info->mem_cookie = mem_cookie; rc = hh_irq_wait_for_lend_v2(vm_info->irq_label, HH_PRIMARY_VM, &fts_vm_irq_on_lend_callback, info); - atomic_set(&vm_info->tvm_owns_irq, 0); - atomic_set(&vm_info->tvm_owns_iomem, 0); - init_completion(&info->resource_checkpoint); + mutex_init(&info->vm_info->tvm_state_mutex); + fts_trusted_touch_set_tvm_driver_state(info, TRUSTED_TOUCH_TVM_INIT); #else mem_cookie = hh_mem_notifier_register(HH_MEM_NOTIFIER_TAG_TOUCH, fts_vm_mem_on_release_handler, info); @@ -861,8 +1104,8 @@ static int fts_vm_init(struct fts_ts_info *info) goto init_fail; } vm_info->mem_cookie = mem_cookie; - atomic_set(&vm_info->pvm_owns_irq, 1); - atomic_set(&vm_info->pvm_owns_iomem, 1); + mutex_init(&info->vm_info->pvm_state_mutex); + fts_trusted_touch_set_pvm_driver_state(info, TRUSTED_TOUCH_PVM_INIT); #endif return rc; init_fail: @@ -936,6 +1179,58 @@ static void fts_trusted_touch_init(struct fts_ts_info *info) atomic_set(&info->trusted_touch_initialized, 1); } +static ssize_t trusted_touch_event_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info; + + if (!client) + return scnprintf(buf, PAGE_SIZE, "client is null\n"); + + info = i2c_get_clientdata(client); + if (!info) { + logError(0, "info is null\n"); + return scnprintf(buf, PAGE_SIZE, "info is null\n"); + } + + return scnprintf(buf, PAGE_SIZE, "%d", + atomic_read(&info->trusted_touch_event)); +} + +static ssize_t trusted_touch_event_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fts_ts_info *info; + unsigned long value; + int err = 0; + + if (!client) + return -EIO; + info = i2c_get_clientdata(client); + if (!info) { + logError(0, "info is null\n"); + return -EIO; + } + if (count > 2) + return -EINVAL; + + err = kstrtoul(buf, 10, &value); + if (err != 0) + return err; + + if (!atomic_read(&info->trusted_touch_initialized)) + return -EIO; + + if (value) + return -EIO; + + atomic_set(&info->trusted_touch_event, value); + + return count; +} + static ssize_t fts_trusted_touch_enable_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -3572,6 +3867,7 @@ static DEVICE_ATTR(trusted_touch_enable, 0664, fts_trusted_touch_enable_show, fts_trusted_touch_enable_store); +static DEVICE_ATTR_RW(trusted_touch_event); #endif /* /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049 */ @@ -3625,6 +3921,7 @@ static struct attribute *fts_attr_group[] = { #endif #ifdef CONFIG_ST_TRUSTED_TOUCH &dev_attr_trusted_touch_enable.attr, + &dev_attr_trusted_touch_event.attr, #endif NULL, }; @@ -4324,17 +4621,6 @@ static irqreturn_t fts_interrupt_handler(int irq, void *handle) pr_err("%s: Invalid info\n", __func__); return IRQ_HANDLED; } -#ifdef CONFIG_ST_TRUSTED_TOUCH -#ifndef CONFIG_ARCH_QTI_VM - if (atomic_read(&info->vm_info->pvm_owns_iomem) && - atomic_read(&info->vm_info->pvm_owns_irq) && - atomic_read(&info->trusted_touch_enabled)) { - pr_err("%s: Cannot service interrupts in PVM while trusted touch is enabled\n", - __func__); - return IRQ_HANDLED; - } -#endif -#endif disable_irq_nosync(info->client->irq); queue_work(info->event_wq, &info->work); diff --git a/drivers/input/touchscreen/st/fts.h b/drivers/input/touchscreen/st/fts.h index 9be46c210d69..354265d2585b 100644 --- a/drivers/input/touchscreen/st/fts.h +++ b/drivers/input/touchscreen/st/fts.h @@ -217,6 +217,48 @@ enum trusted_touch_mode_config { TRUSTED_TOUCH_MODE_NONE }; +enum trusted_touch_pvm_states { + TRUSTED_TOUCH_PVM_INIT, + PVM_I2C_RESOURCE_ACQUIRED, + PVM_INTERRUPT_DISABLED, + PVM_IOMEM_LENT, + PVM_IOMEM_LENT_NOTIFIED, + PVM_IRQ_LENT, + PVM_IRQ_LENT_NOTIFIED, + PVM_IOMEM_RELEASE_NOTIFIED, + PVM_IRQ_RELEASE_NOTIFIED, + PVM_ALL_RESOURCES_RELEASE_NOTIFIED, + PVM_IRQ_RECLAIMED, + PVM_IOMEM_RECLAIMED, + PVM_INTERRUPT_ENABLED, + PVM_I2C_RESOURCE_RELEASED, + TRUSTED_TOUCH_PVM_STATE_MAX +}; + +enum trusted_touch_tvm_states { + TRUSTED_TOUCH_TVM_INIT, + TVM_IOMEM_LENT_NOTIFIED, + TVM_IRQ_LENT_NOTIFIED, + TVM_ALL_RESOURCES_LENT_NOTIFIED, + TVM_IOMEM_ACCEPTED, + TVM_I2C_SESSION_ACQUIRED, + TVM_IRQ_ACCEPTED, + TVM_INTERRUPT_ENABLED, + TVM_INTERRUPT_DISABLED, + TVM_IRQ_RELEASED, + TVM_IOMEM_RELEASED, + TVM_I2C_SESSION_RELEASED, + TRUSTED_TOUCH_TVM_STATE_MAX +}; + +#define TRUSTED_TOUCH_EVENT_LEND_FAILURE -1 +#define TRUSTED_TOUCH_EVENT_LEND_NOTIFICATION_FAILURE -2 +#define TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE -3 +#define TRUSTED_TOUCH_EVENT_FUNCTIONAL_FAILURE -4 +#define TRUSTED_TOUCH_EVENT_RELEASE_FAILURE -5 +#define TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE -6 +#define TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING 5 + #ifdef CONFIG_ST_TRUSTED_TOUCH #define TRUSTED_TOUCH_MEM_LABEL 0x7 @@ -230,11 +272,11 @@ struct trusted_touch_vm_info { u32 iomem_list_size; void *mem_cookie; #ifdef CONFIG_ARCH_QTI_VM - atomic_t tvm_owns_iomem; - atomic_t tvm_owns_irq; + struct mutex tvm_state_mutex; + atomic_t tvm_state; #else - atomic_t pvm_owns_iomem; - atomic_t pvm_owns_irq; + struct mutex pvm_state_mutex; + atomic_t pvm_state; #endif }; #endif @@ -359,11 +401,12 @@ struct fts_ts_info { struct mutex fts_clk_io_ctrl_mutex; const char *touch_environment; struct completion trusted_touch_powerdown; - struct completion resource_checkpoint; struct clk *core_clk; struct clk *iface_clk; atomic_t trusted_touch_initialized; atomic_t trusted_touch_enabled; + atomic_t trusted_touch_event; + atomic_t trusted_touch_abort_status; atomic_t delayed_vm_probe_pending; atomic_t trusted_touch_mode; #endif From f57ed9297ed69dbd55e60c2ce39a3d307660c540 Mon Sep 17 00:00:00 2001 From: Shashank Babu Chinta Venkata Date: Mon, 28 Sep 2020 01:54:39 -0700 Subject: [PATCH 2/2] input: touchscreen: st: toggle reset gpio during error on SVM Toggle reset gpio of touch controller during an error scenario on Secondary Virtual Machine(SVM). Change-Id: I5d296248c88a6eef13887884892894faa4c984e6 Signed-off-by: Shashank Babu Chinta Venkata --- drivers/input/touchscreen/st/fts.c | 68 ++++++++++++++++++- drivers/input/touchscreen/st/fts.h | 18 ++++- drivers/input/touchscreen/st/fts_lib/ftsIO.c | 49 +++++++++++-- .../input/touchscreen/st/fts_lib/ftsTool.c | 8 ++- 4 files changed, 132 insertions(+), 11 deletions(-) diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c index 18b321d50d02..89549315d215 100644 --- a/drivers/input/touchscreen/st/fts.c +++ b/drivers/input/touchscreen/st/fts.c @@ -270,6 +270,49 @@ static void fts_trusted_touch_tvm_vm_mode_disable(struct fts_ts_info *info); static void fts_trusted_touch_abort_tvm(struct fts_ts_info *info); static void fts_trusted_touch_event_notify(struct fts_ts_info *info, int event); +void fts_trusted_touch_tvm_i2c_failure_report(struct fts_ts_info *info) +{ + pr_err("initiating trusted touch abort due to i2c failure\n"); + fts_trusted_touch_abort_handler(info, + TRUSTED_TOUCH_EVENT_I2C_FAILURE); +} + +static void fts_trusted_touch_reset_gpio_toggle(struct fts_ts_info *info) +{ + void __iomem *base; + + base = ioremap(TOUCH_RESET_GPIO_BASE, TOUCH_RESET_GPIO_SIZE); + writel_relaxed(0x1, base + TOUCH_RESET_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + writel_relaxed(0x0, base + TOUCH_RESET_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + iounmap(base); +} + +static void fts_trusted_touch_intr_gpio_toggle(struct fts_ts_info *info, + bool enable) +{ + void __iomem *base; + u32 val; + + base = ioremap(TOUCH_INTR_GPIO_BASE, TOUCH_INTR_GPIO_SIZE); + val = readl_relaxed(base + TOUCH_RESET_GPIO_OFFSET); + if (enable) { + val |= BIT(0); + writel_relaxed(val, base + TOUCH_INTR_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + } else { + val &= ~BIT(0); + writel_relaxed(val, base + TOUCH_INTR_GPIO_OFFSET); + /* wait until toggle to finish*/ + wmb(); + } + iounmap(base); +} + static int fts_trusted_touch_get_tvm_driver_state(struct fts_ts_info *info) { int state; @@ -401,6 +444,7 @@ static void fts_trusted_touch_tvm_vm_mode_enable(struct fts_ts_info *info) kfree(expected_sgl_desc); kfree(acl_desc); + fts_trusted_touch_intr_gpio_toggle(info, false); irq = hh_irq_accept(info->vm_info->irq_label, -1, IRQ_TYPE_LEVEL_HIGH); if (irq < 0) { pr_err("failed to accept irq\n"); @@ -1065,6 +1109,14 @@ static void fts_trusted_touch_abort_handler(struct fts_ts_info *info, int error) { atomic_set(&info->trusted_touch_abort_status, error); pr_err("TUI session aborted with failure:%d\n", error); +#ifdef CONFIG_ARCH_QTI_VM + pr_err("Resetting touch controller\n"); + if (fts_trusted_touch_get_tvm_driver_state(info) >= TVM_IOMEM_ACCEPTED + && error == TRUSTED_TOUCH_EVENT_I2C_FAILURE) { + pr_err("Resetting touch controller\n"); + fts_trusted_touch_reset_gpio_toggle(info); + } +#endif fts_trusted_touch_event_notify(info, error); } @@ -4621,6 +4673,7 @@ static irqreturn_t fts_interrupt_handler(int irq, void *handle) pr_err("%s: Invalid info\n", __func__); return IRQ_HANDLED; } + disable_irq_nosync(info->client->irq); queue_work(info->event_wq, &info->work); @@ -4700,13 +4753,26 @@ static void fts_interrupt_uninstall(struct fts_ts_info *info) static void fts_interrupt_enable(struct fts_ts_info *info) { + int ret = 0; #ifdef FTS_USE_POLLING_MODE hrtimer_start(&info->timer, ktime_set(0, 10000000), HRTIMER_MODE_REL); #else enable_irq(info->client->irq); #endif /* enable the touch IC irq */ - fts_enableInterrupt(); + ret = fts_enableInterrupt(); +#ifdef CONFIG_ST_TRUSTED_TOUCH +#ifndef CONFIG_ARCH_QTI_VM + if ((ret < 0) && atomic_read(&info->trusted_touch_enabled)) { + fts_chip_powercycle2(info, 300); + ret = fts_system_reset(); + ret |= fts_mode_handler(info, 0); + ret |= fts_enableInterrupt(); + if (ret < 0) + pr_err("failed to enable touch interrupts\n"); + } +#endif +#endif } static void fts_interrupt_disable(struct fts_ts_info *info) diff --git a/drivers/input/touchscreen/st/fts.h b/drivers/input/touchscreen/st/fts.h index 354265d2585b..2745a18d4238 100644 --- a/drivers/input/touchscreen/st/fts.h +++ b/drivers/input/touchscreen/st/fts.h @@ -251,17 +251,25 @@ enum trusted_touch_tvm_states { TRUSTED_TOUCH_TVM_STATE_MAX }; +#ifdef CONFIG_ST_TRUSTED_TOUCH +#define TRUSTED_TOUCH_MEM_LABEL 0x7 + +#define TOUCH_RESET_GPIO_BASE 0xF116000 +#define TOUCH_RESET_GPIO_SIZE 0x1000 +#define TOUCH_RESET_GPIO_OFFSET 0x4 +#define TOUCH_INTR_GPIO_BASE 0xF117000 +#define TOUCH_INTR_GPIO_SIZE 0x1000 +#define TOUCH_INTR_GPIO_OFFSET 0x8 + #define TRUSTED_TOUCH_EVENT_LEND_FAILURE -1 #define TRUSTED_TOUCH_EVENT_LEND_NOTIFICATION_FAILURE -2 #define TRUSTED_TOUCH_EVENT_ACCEPT_FAILURE -3 #define TRUSTED_TOUCH_EVENT_FUNCTIONAL_FAILURE -4 #define TRUSTED_TOUCH_EVENT_RELEASE_FAILURE -5 #define TRUSTED_TOUCH_EVENT_RECLAIM_FAILURE -6 +#define TRUSTED_TOUCH_EVENT_I2C_FAILURE -7 #define TRUSTED_TOUCH_EVENT_NOTIFICATIONS_PENDING 5 -#ifdef CONFIG_ST_TRUSTED_TOUCH -#define TRUSTED_TOUCH_MEM_LABEL 0x7 - struct trusted_touch_vm_info { enum hh_irq_label irq_label; enum hh_vm_names vm_name; @@ -279,6 +287,10 @@ struct trusted_touch_vm_info { atomic_t pvm_state; #endif }; + +#ifdef CONFIG_ARCH_QTI_VM +void fts_trusted_touch_tvm_i2c_failure_report(struct fts_ts_info *info); +#endif #endif /* diff --git a/drivers/input/touchscreen/st/fts_lib/ftsIO.c b/drivers/input/touchscreen/st/fts_lib/ftsIO.c index ba0fb18e3307..12f02d9424ff 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsIO.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsIO.c @@ -34,6 +34,7 @@ * */ +#include #include #include #include @@ -141,13 +142,32 @@ int fts_readCmd(u8 *cmd, int cmdLength, u8 *outBuf, int byteToRead) while (retry < I2C_RETRY && ret < OK) { ret = i2c_transfer(client->adapter, I2CMsg, 2); retry++; - if (ret < OK) + if (ret < OK) { +#ifdef CONFIG_ST_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + if (atomic_read(&info->trusted_touch_enabled) && + ret == -ECONNRESET){ + pr_err("failed i2c read reacquiring session\n"); + pm_runtime_put_sync( + info->client->adapter->dev.parent); + pm_runtime_get_sync( + info->client->adapter->dev.parent); + } +#endif +#endif msleep(I2C_WAIT_BEFORE_RETRY); + } } if (ret < 0) { +#ifdef CONFIG_ST_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + pr_err("initiating abort due to i2c xfer failure\n"); + fts_trusted_touch_tvm_i2c_failure_report(info); +#endif +#endif logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_R); - return ERROR_I2C_R; + return ret; } memcpy(outBuf, buf + cmdLength, byteToRead); @@ -189,13 +209,34 @@ int fts_writeCmd(u8 *cmd, int cmdLength) while (retry < I2C_RETRY && ret < OK) { ret = i2c_transfer(client->adapter, I2CMsg, 1); retry++; - if (ret < OK) + if (ret < OK) { +#ifdef CONFIG_ST_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + if (atomic_read(&info->trusted_touch_enabled) && + ret == -ECONNRESET){ + pr_err("failed i2c write reacquiring session\n"); + pm_runtime_put_sync( + info->client->adapter->dev.parent); + pm_runtime_get_sync( + info->client->adapter->dev.parent); + + } +#endif +#endif msleep(I2C_WAIT_BEFORE_RETRY); + logError(1, "ERROR: %d\n", ret); + } //logError(1,"%s fts_writeCmd: attempt %d\n", tag, retry); } if (ret < 0) { +#ifdef CONFIG_ST_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + pr_err("initiating abort due to i2c xfer failure\n"); + fts_trusted_touch_tvm_i2c_failure_report(info); +#endif +#endif logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); - return ERROR_I2C_W; + return ret; } return OK; } diff --git a/drivers/input/touchscreen/st/fts_lib/ftsTool.c b/drivers/input/touchscreen/st/fts_lib/ftsTool.c index 543682d1e3c6..55285fd18892 100644 --- a/drivers/input/touchscreen/st/fts_lib/ftsTool.c +++ b/drivers/input/touchscreen/st/fts_lib/ftsTool.c @@ -789,11 +789,13 @@ int fts_disableInterrupt(void) int fts_enableInterrupt(void) { u8 cmd[4] = { FTS_CMD_HW_REG_W, 0x00, 0x00, IER_ENABLE }; + int ret = 0; u16ToU8_be(IER_ADDR, &cmd[1]); - if (fts_writeCmd(cmd, 4) < 0) { - logError(1, "%s %s: ERROR %02X\n", tag, __func__, ERROR_I2C_W); - return ERROR_I2C_W; + ret = fts_writeCmd(cmd, 4); + if (ret < 0) { + logError(1, "%s %s: ERROR %d\n", tag, __func__, ret); + return ret; } logError(0, "%s Interrupt Enabled!\n", tag); return OK;