From d5c077b09953ef58fa56cbf0d74e2b8db10f1113 Mon Sep 17 00:00:00 2001 From: Shashank Babu Chinta Venkata Date: Wed, 27 May 2020 17:02:03 -0700 Subject: [PATCH] input: touchscreen: st: add trusted touch support Add Trusted touch support to lahaina MTP Touchscreen driver. This feature enables driver to operate in trusted touch mode in trusted virtual machine(TVM). The primary virtual machine(PVM) lends resources such as IO memory and touch irq to TVM prior to trusted touch is initiated and reclaims those resources back after trusted touch usecase finishes.The resource requirements of TVM such as clock, regulators etc are taken care by PVM. Change-Id: I584fa3c96796b8a34f1948847e83aac7ce913d94 Signed-off-by: Shashank Babu Chinta Venkata --- drivers/input/touchscreen/st/Kconfig | 10 + drivers/input/touchscreen/st/fts.c | 1124 ++++++++++++++++++++++++-- drivers/input/touchscreen/st/fts.h | 43 + 3 files changed, 1097 insertions(+), 80 deletions(-) diff --git a/drivers/input/touchscreen/st/Kconfig b/drivers/input/touchscreen/st/Kconfig index fc1e7abb5ce4..11a6e333f043 100644 --- a/drivers/input/touchscreen/st/Kconfig +++ b/drivers/input/touchscreen/st/Kconfig @@ -20,3 +20,13 @@ config TOUCHSCREEN_ST_I2C depends on TOUCHSCREEN_ST help This enables support for ST touch panel over I2C based touchscreens. + +config ST_TRUSTED_TOUCH + bool "STMicroelectronics Trusted Touch" + depends on TOUCHSCREEN_ST + help + Say Y here to enable STMicroelectronics Trusted Touch in supported drivers. + + If unsure, say N. + +#endif diff --git a/drivers/input/touchscreen/st/fts.c b/drivers/input/touchscreen/st/fts.c index 4b1b3864f894..c1e6d31bae73 100644 --- a/drivers/input/touchscreen/st/fts.c +++ b/drivers/input/touchscreen/st/fts.c @@ -11,7 +11,7 @@ */ #include - +#include #include #include #include @@ -27,7 +27,8 @@ #include /*#include */ #include - +#include +#include #include #include #include @@ -55,6 +56,24 @@ #include "fts_lib/ftsTool.h" #include "linux/moduleparam.h" +#if defined(CONFIG_ST_TRUSTED_TOUCH) + +#include +#include +#include + +#include +#include +#include +#include +#include +#include "linux/haven/hh_irq_lend.h" +#include "linux/haven/hh_msgq.h" +#include "linux/haven/hh_mem_notifier.h" +#include "linux/haven/hh_rm_drv.h" +#include +#endif + #define LINK_KOBJ_NAME "tp" #define FTS_DVDD_VOL_MIN 1800000 @@ -96,6 +115,873 @@ do { \ static struct class *fts_cmd_class; #endif +static void fts_interrupt_disable(struct fts_ts_info *info); +static void fts_interrupt_enable(struct fts_ts_info *info); +static irqreturn_t fts_interrupt_handler(int irq, void *handle); +static int fts_probe_delayed(struct fts_ts_info *info); + +#ifdef CONFIG_ST_TRUSTED_TOUCH + +static struct hh_acl_desc *fts_vm_get_acl(enum hh_vm_names vm_name) +{ + struct hh_acl_desc *acl_desc; + hh_vmid_t vmid; + + hh_rm_get_vmid(vm_name, &vmid); + + acl_desc = kzalloc(offsetof(struct hh_acl_desc, acl_entries[1]), + GFP_KERNEL); + if (!acl_desc) + return ERR_PTR(ENOMEM); + + acl_desc->n_acl_entries = 1; + acl_desc->acl_entries[0].vmid = vmid; + acl_desc->acl_entries[0].perms = HH_RM_ACL_R | HH_RM_ACL_W; + + return acl_desc; +} + +static struct hh_sgl_desc *fts_vm_get_sgl(struct trusted_touch_vm_info *vm_info) +{ + struct hh_sgl_desc *sgl_desc; + int i; + + sgl_desc = kzalloc(offsetof(struct hh_sgl_desc, + sgl_entries[vm_info->iomem_list_size]), GFP_KERNEL); + if (!sgl_desc) + return ERR_PTR(ENOMEM); + + sgl_desc->n_sgl_entries = vm_info->iomem_list_size; + + for (i = 0; i < vm_info->iomem_list_size; i++) { + sgl_desc->sgl_entries[i].ipa_base = vm_info->iomem_bases[i]; + sgl_desc->sgl_entries[i].size = vm_info->iomem_sizes[i]; + } + + return sgl_desc; +} + +static int fts_populate_vm_info(struct fts_ts_info *info) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info; + struct device_node *np = info->client->dev.of_node; + int num_regs, num_sizes = 0; + + vm_info = kzalloc(sizeof(struct trusted_touch_vm_info), GFP_KERNEL); + if (!vm_info) { + rc = -ENOMEM; + goto error; + } + + info->vm_info = vm_info; + vm_info->irq_label = HH_IRQ_LABEL_TRUSTED_TOUCH; + vm_info->vm_name = HH_TRUSTED_VM; + rc = of_property_read_u32(np, "st,trusted-touch-spi-irq", + &vm_info->hw_irq); + if (rc) { + pr_err("Failed to read trusted touch SPI irq:%d\n", rc); + goto vm_error; + } + num_regs = of_property_count_u32_elems(np, + "st,trusted-touch-io-bases"); + if (num_regs < 0) { + pr_err("Invalid number of IO regions specified\n"); + rc = -EINVAL; + goto vm_error; + } + + num_sizes = of_property_count_u32_elems(np, + "st,trusted-touch-io-sizes"); + if (num_sizes < 0) { + pr_err("Invalid number of IO regions specified\n"); + rc = -EINVAL; + goto vm_error; + } + + if (num_regs != num_sizes) { + pr_err("IO bases and sizes doe not match\n"); + rc = -EINVAL; + goto vm_error; + } + + vm_info->iomem_list_size = num_regs; + + vm_info->iomem_bases = kcalloc(num_regs, sizeof(*vm_info->iomem_bases), + GFP_KERNEL); + if (!vm_info->iomem_bases) { + rc = -ENOMEM; + goto vm_error; + } + + rc = of_property_read_u32_array(np, "st,trusted-touch-io-bases", + vm_info->iomem_bases, vm_info->iomem_list_size); + if (rc) { + pr_err("Failed to read trusted touch io bases:%d\n", rc); + goto io_bases_error; + } + + vm_info->iomem_sizes = kzalloc( + sizeof(*vm_info->iomem_sizes) * num_sizes, GFP_KERNEL); + if (!vm_info->iomem_sizes) { + rc = -ENOMEM; + goto io_bases_error; + } + + rc = of_property_read_u32_array(np, "st,trusted-touch-io-sizes", + vm_info->iomem_sizes, vm_info->iomem_list_size); + if (rc) { + pr_err("Failed to read trusted touch io sizes:%d\n", rc); + goto io_sizes_error; + } + return rc; + +io_sizes_error: + kfree(vm_info->iomem_sizes); +io_bases_error: + kfree(vm_info->iomem_bases); +vm_error: + kfree(vm_info); +error: + return rc; +} + +static void fts_destroy_vm_info(struct fts_ts_info *info) +{ + kfree(info->vm_info->iomem_sizes); + kfree(info->vm_info->iomem_bases); + kfree(info->vm_info); +} + +static void fts_vm_deinit(struct fts_ts_info *info) +{ + if (info->vm_info->mem_cookie) + hh_mem_notifier_unregister(info->vm_info->mem_cookie); + fts_destroy_vm_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 int fts_sgl_cmp(const void *a, const void *b) +{ + struct hh_sgl_entry *left = (struct hh_sgl_entry *)a; + struct hh_sgl_entry *right = (struct hh_sgl_entry *)b; + + return (left->ipa_base - right->ipa_base); +} + +static int fts_vm_compare_sgl_desc(struct hh_sgl_desc *expected, + struct hh_sgl_desc *received) +{ + int idx; + + if (expected->n_sgl_entries != received->n_sgl_entries) + return -E2BIG; + sort(received->sgl_entries, received->n_sgl_entries, + sizeof(received->sgl_entries[0]), fts_sgl_cmp, NULL); + sort(expected->sgl_entries, expected->n_sgl_entries, + sizeof(expected->sgl_entries[0]), fts_sgl_cmp, NULL); + + for (idx = 0; idx < expected->n_sgl_entries; idx++) { + struct hh_sgl_entry *left = &expected->sgl_entries[idx]; + struct hh_sgl_entry *right = &received->sgl_entries[idx]; + + if ((left->ipa_base != right->ipa_base) || + (left->size != right->size)) { + pr_err("sgl mismatch: left_base:%d right base:%d left size:%d right size:%d\n", + left->ipa_base, right->ipa_base, + left->size, right->size); + return -EINVAL; + } + } + return 0; +} + +static int fts_vm_handle_vm_hardware(struct fts_ts_info *info) +{ + int rc = 0; + + if (atomic_read(&info->delayed_vm_probe_pending)) { + rc = fts_probe_delayed(info); + if (rc) { + pr_err(" Delayed probe failure on VM!\n"); + return rc; + } + atomic_set(&info->delayed_vm_probe_pending, 0); + return rc; + } + + queue_delayed_work(info->fwu_workqueue, &info->fwu_work, + msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + fts_interrupt_enable(info); + return rc; +} + +static void fts_vm_irq_on_lend_callback(void *data, enum hh_irq_label label) +{ + struct fts_ts_info *info = data; + struct irq_data *irq_data; + int irq = 0; + int const resource_timeout = msecs_to_jiffies(2000); + int rc = 0; + + 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; + } + atomic_set(&info->vm_info->tvm_owns_irq, 1); + irq_data = irq_get_irq_data(irq); + if (!irq_data) { + pr_err("Invalid irq data for trusted touch\n"); + goto irq_fail; + } + if (!irq_data->hwirq) { + pr_err("Invalid irq in irq data\n"); + goto irq_fail; + } + if (irq_data->hwirq != info->vm_info->hw_irq) { + pr_err("Invalid irq lent\n"); + goto irq_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; + } + + atomic_set(&info->trusted_touch_enabled, 1); + return; +irq_fail: + fts_trusted_touch_vm_mode_disable(info); +} + +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) { + pr_err("Invalid command passed from rm\n"); + return; + } + + if (!entry_data || !notif_msg) { + pr_err("Invalid entry data passed from rm\n"); + return; + } + + info = (struct fts_ts_info *)entry_data; + vm_info = info->vm_info; + if (!vm_info) { + pr_err("Invalid vm_info\n"); + return; + } + + payload = (struct hh_rm_notif_mem_shared_payload *)notif_msg; + 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; + } + + 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; + +sgl_cmp_fail: + kfree(expected_sgl_desc); +acl_fail: + kfree(acl_desc); +onlend_fail: + fts_trusted_touch_vm_mode_disable(info); +} + +static int fts_vm_mem_release(struct fts_ts_info *info) +{ + int rc = 0; + + rc = hh_rm_mem_release(info->vm_info->vm_mem_handle, 0); + if (rc) + pr_err("VM mem release failed: rc=%d\n", rc); + + rc = hh_rm_mem_notify(info->vm_info->vm_mem_handle, + HH_RM_MEM_NOTIFY_OWNER, + HH_MEM_NOTIFIER_TAG_TOUCH, 0); + if (rc) + pr_err("Failed to notify mem release to PVM: rc=%d\n"); + + info->vm_info->vm_mem_handle = 0; + return rc; +} + +static void fts_trusted_touch_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->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); + } + atomic_set(&info->trusted_touch_enabled, 0); + reinit_completion(&info->resource_checkpoint); + pr_debug("trusted touch disabled\n"); +} + +static int fts_handle_trusted_touch_tvm(struct fts_ts_info *info, int value) +{ + int err = 0; + + switch (value) { + case 0: + if (atomic_read(&info->trusted_touch_enabled) == 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); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + case 1: + if (atomic_read(&info->trusted_touch_enabled)) { + pr_err("Trusted touch usecase underway\n"); + err = -EBUSY; + break; + } + if (atomic_read(&info->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + pr_err("Cannot turnon trusted touch(vm mode) in VM\n"); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + default: + dev_err(&info->client->dev, "unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + + return err; +} + +#else + +static int fts_clk_prepare_enable(struct fts_ts_info *info) +{ + int ret; + + ret = clk_prepare_enable(info->iface_clk); + if (ret) { + dev_err(&info->client->dev, + "error on clk_prepare_enable(iface_clk):%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(info->core_clk); + if (ret) { + clk_disable_unprepare(info->iface_clk); + dev_err(&info->client->dev, + "error clk_prepare_enable(core_clk):%d\n", ret); + } + return ret; +} + +static void fts_clk_disable_unprepare(struct fts_ts_info *info) +{ + clk_disable_unprepare(info->core_clk); + clk_disable_unprepare(info->iface_clk); +} + +static int fts_bus_get(struct fts_ts_info *info) +{ + int rc = 0; + + mutex_lock(&info->fts_clk_io_ctrl_mutex); + rc = pm_runtime_get_sync(info->client->adapter->dev.parent); + if (rc >= 0 && info->core_clk != NULL && info->iface_clk != NULL) { + rc = fts_clk_prepare_enable(info); + if (rc) + pm_runtime_put_sync(info->client->adapter->dev.parent); + } + mutex_unlock(&info->fts_clk_io_ctrl_mutex); + return rc; +} + +static void fts_bus_put(struct fts_ts_info *info) +{ + mutex_lock(&info->fts_clk_io_ctrl_mutex); + if (info->core_clk != NULL && info->iface_clk != NULL) + fts_clk_disable_unprepare(info); + pm_runtime_put_sync(info->client->adapter->dev.parent); + mutex_unlock(&info->fts_clk_io_ctrl_mutex); +} + +static struct hh_notify_vmid_desc *fts_vm_get_vmid(hh_vmid_t vmid) +{ + struct hh_notify_vmid_desc *vmid_desc; + + vmid_desc = kzalloc(offsetof(struct hh_notify_vmid_desc, + vmid_entries[1]), GFP_KERNEL); + if (!vmid_desc) + return ERR_PTR(ENOMEM); + + vmid_desc->n_vmid_entries = 1; + vmid_desc->vmid_entries[0].vmid = vmid; + return vmid_desc; +} + +static void fts_trusted_touch_complete(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, enum hh_irq_label label) +{ + struct fts_ts_info *info = data; + int rc = 0; + + rc = hh_irq_reclaim(info->vm_info->irq_label); + if (rc) + pr_err("failed to reclaim irq on pvm rc:%d\n", rc); + else + atomic_set(&info->vm_info->pvm_owns_irq, 1); +} + +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 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"); + return; + } + + if (!entry_data || !notif_msg) { + pr_err(" Invalid data or notification message\n"); + 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) { + pr_err(" Invalid vm_info\n"); + return; + } + + if (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; + } + 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) +{ + struct hh_acl_desc *acl_desc; + struct hh_sgl_desc *sgl_desc; + struct hh_notify_vmid_desc *vmid_desc; + hh_memparcel_handle_t mem_handle; + hh_vmid_t trusted_vmid; + int rc = 0; + + acl_desc = fts_vm_get_acl(HH_TRUSTED_VM); + if (IS_ERR(acl_desc)) { + pr_err("Failed to get acl of IO memories for Trusted touch\n"); + PTR_ERR(acl_desc); + return -EINVAL; + } + + sgl_desc = fts_vm_get_sgl(info->vm_info); + if (IS_ERR(sgl_desc)) { + pr_err("Failed to get sgl of IO memories for Trusted touch\n"); + PTR_ERR(sgl_desc); + rc = -EINVAL; + goto sgl_error; + } + + rc = hh_rm_mem_lend(HH_RM_MEM_TYPE_IO, 0, TRUSTED_TOUCH_MEM_LABEL, + acl_desc, sgl_desc, NULL, &mem_handle); + if (rc) { + pr_err("Failed to lend IO memories for Trusted touch rc:%d\n", + rc); + goto error; + } + + hh_rm_get_vmid(HH_TRUSTED_VM, &trusted_vmid); + + vmid_desc = fts_vm_get_vmid(trusted_vmid); + + rc = hh_rm_mem_notify(mem_handle, HH_RM_MEM_NOTIFY_RECIPIENT, + HH_MEM_NOTIFIER_TAG_TOUCH, vmid_desc); + if (rc) { + pr_err("Failed to notify mem lend to hypervisor rc:%d\n", rc); + goto vmid_error; + } + + info->vm_info->vm_mem_handle = mem_handle; +vmid_error: + kfree(vmid_desc); +error: + kfree(sgl_desc); +sgl_error: + kfree(acl_desc); + + return rc; +} + +static int fts_trusted_touch_vm_mode_enable(struct fts_ts_info *info) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info = info->vm_info; + + /* i2c session start and resource acquire */ + if (fts_bus_get(info) < 0) { + dev_err(&info->client->dev, "fts_bus_get failed\n"); + rc = -EIO; + return rc; + } + + /* flush pending interurpts from FIFO */ + fts_interrupt_disable(info); + flushFIFO(); + release_all_touches(info); + + rc = fts_vm_mem_lend(info); + if (rc) { + pr_err("Failed to lend memory\n"); + return -EINVAL; + } + atomic_set(&vm_info->pvm_owns_iomem, 0); + + rc = hh_irq_lend(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; + } + atomic_set(&vm_info->pvm_owns_irq, 0); + + reinit_completion(&info->trusted_touch_powerdown); + atomic_set(&info->trusted_touch_enabled, 1); + pr_debug("trusted touch enabled\n"); + return rc; +} + +static int fts_handle_trusted_touch_pvm(struct fts_ts_info *info, int value) +{ + int err = 0; + + switch (value) { + case 0: + if (atomic_read(&info->trusted_touch_enabled) == 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); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + case 1: + if (atomic_read(&info->trusted_touch_enabled)) { + pr_err("Trusted touch usecase underway\n"); + err = -EBUSY; + break; + } + if (atomic_read(&info->trusted_touch_mode) == + TRUSTED_TOUCH_VM_MODE) { + err = fts_trusted_touch_vm_mode_enable(info); + } else { + pr_err("Unsupported trusted touch mode\n"); + } + break; + + default: + dev_err(&info->client->dev, "unsupported value: %lu\n", value); + err = -EINVAL; + break; + } + return err; +} +#endif + +static int fts_vm_init(struct fts_ts_info *info) +{ + int rc = 0; + struct trusted_touch_vm_info *vm_info; + void *mem_cookie; + + rc = fts_populate_vm_info(info); + if (rc) { + pr_err("Cannot setup vm pipeline\n"); + rc = -EINVAL; + goto fail; + } + + vm_info = info->vm_info; +#ifdef CONFIG_ARCH_QTI_VM + mem_cookie = hh_mem_notifier_register(HH_MEM_NOTIFIER_TAG_TOUCH, + fts_vm_mem_on_lend_handler, info); + if (!mem_cookie) { + pr_err("Failed to register on lend mem notifier\n"); + rc = -EINVAL; + goto init_fail; + } + vm_info->mem_cookie = mem_cookie; + rc = hh_irq_wait_for_lend(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); +#else + mem_cookie = hh_mem_notifier_register(HH_MEM_NOTIFIER_TAG_TOUCH, + fts_vm_mem_on_release_handler, info); + if (!mem_cookie) { + pr_err("Failed to register on release mem notifier\n"); + rc = -EINVAL; + 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); +#endif + return rc; +init_fail: + fts_vm_deinit(info); +fail: + return rc; +} + +static void fts_dt_parse_trusted_touch_info(struct fts_ts_info *info) +{ + struct device_node *np = info->client->dev.of_node; + int rc = 0; + const char *selection; + const char *environment; + + rc = of_property_read_string(np, "st,trusted-touch-mode", + &selection); + if (rc) { + dev_warn(&info->client->dev, + "%s: No trusted touch mode selection made\n", __func__); + } + + if (!strcmp(selection, "vm_mode")) { + atomic_set(&info->trusted_touch_mode, TRUSTED_TOUCH_VM_MODE); + pr_err("Selected trusted touch mode to VM mode\n"); + } else { + atomic_set(&info->trusted_touch_mode, TRUSTED_TOUCH_MODE_NONE); + pr_err("Invalid trusted_touch mode\n"); + } + + rc = of_property_read_string(np, "st,touch-environment", + &environment); + if (rc) { + dev_warn(&info->client->dev, + "%s: No trusted touch mode environment\n", __func__); + } + info->touch_environment = environment; + pr_err("Trusted touch environment:%s\n", + info->touch_environment); +} + +static void fts_trusted_touch_init(struct fts_ts_info *info) +{ + int rc = 0; + + atomic_set(&info->trusted_touch_initialized, 0); + init_completion(&info->trusted_touch_powerdown); + fts_dt_parse_trusted_touch_info(info); + + /* Get clocks */ + info->core_clk = devm_clk_get(info->client->dev.parent, + "m-ahb"); + if (IS_ERR(info->core_clk)) { + info->core_clk = NULL; + dev_warn(&info->client->dev, + "%s: core_clk is not defined\n", __func__); + } + + info->iface_clk = devm_clk_get(info->client->dev.parent, + "se-clk"); + if (IS_ERR(info->iface_clk)) { + info->iface_clk = NULL; + dev_warn(&info->client->dev, + "%s: iface_clk is not defined\n", __func__); + } + if (atomic_read(&info->trusted_touch_mode) == TRUSTED_TOUCH_VM_MODE) { + rc = fts_vm_init(info); + if (rc) + pr_err("Failed to init VM\n"); + } + atomic_set(&info->trusted_touch_initialized, 1); +} + +static ssize_t fts_trusted_touch_enable_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_enabled)); +} + +static ssize_t fts_trusted_touch_enable_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; + +#ifdef CONFIG_ARCH_QTI_VM + err = fts_handle_trusted_touch_tvm(info, value); + if (err) { + pr_err("Failed to handle trusted touch in tvm\n"); + return -EINVAL; + } +#else + err = fts_handle_trusted_touch_pvm(info, value); + if (err) { + pr_err("Failed to handle trusted touch in pvm\n"); + return -EINVAL; + } +#endif + err = count; + return err; +} + +#endif + //struct chipInfo ftsInfo; /** @@ -1919,7 +2805,7 @@ static ssize_t fts_add_custom_gesture_store(struct device *dev, tag, __func__); logError(1, " mask: ERROR %08X\n", res); } else { - /*if(check_feature_feasibility(info,*/ + /*if (check_feature_feasibility(info,*/ /*FEAT_GESTURE)==OK)*/ /*info->gesture_enabled =*/ /*isAnyGestureActive();*/ @@ -2026,7 +2912,7 @@ static ssize_t fts_remove_custom_gesture_store(struct device *dev, "%s %s:error in enable gesture mask:%08X\n", tag, __func__, res); } else { - /*if(check_feature_feasibility*/ + /*if (check_feature_feasibility*/ /*(info,FEAT_GESTURE)==OK)*/ /*info->gesture_enabled = */ /*isAnyGestureActive();*/ @@ -2665,6 +3551,13 @@ static DEVICE_ATTR_RW(fts_remove_custom_gesture); #endif #endif +#ifdef CONFIG_ST_TRUSTED_TOUCH +static DEVICE_ATTR(trusted_touch_enable, + 0664, + fts_trusted_touch_enable_show, + fts_trusted_touch_enable_store); +#endif + /* /sys/devices/soc.0/f9928000.i2c/i2c-6/6-0049 */ static struct attribute *fts_attr_group[] = { &dev_attr_fts_fwupdate.attr, @@ -2713,6 +3606,9 @@ static struct attribute *fts_attr_group[] = { &dev_attr_fts_remove_custom_gesture.attr, #endif +#endif +#ifdef CONFIG_ST_TRUSTED_TOUCH + &dev_attr_trusted_touch_enable.attr, #endif NULL, }; @@ -3408,6 +4304,21 @@ static irqreturn_t fts_interrupt_handler(int irq, void *handle) { struct fts_ts_info *info = handle; + if (!info) { + 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); @@ -3451,6 +4362,15 @@ static int fts_interrupt_install(struct fts_ts_info *info) hrtimer_init(&info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); info->timer.function = fts_timer_func; hrtimer_start(&info->timer, ktime_set(1, 0), HRTIMER_MODE_REL); +#else +#ifdef CONFIG_ARCH_QTI_VM + logError(0, "%s Interrupt Mode\n", tag); + if (request_threaded_irq(info->client->irq, NULL, fts_interrupt_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, info->client->name, info)) { + logError(1, "%s Request irq failed\n", tag); + kfree(info->event_dispatch_table); + error = -EBUSY; + } #else logError(0, "%s Interrupt Mode\n", tag); if (request_threaded_irq(info->client->irq, NULL, fts_interrupt_handler, @@ -3459,6 +4379,7 @@ static int fts_interrupt_install(struct fts_ts_info *info) kfree(info->event_dispatch_table); error = -EBUSY; } +#endif #endif return error; } @@ -4086,6 +5007,12 @@ static void fts_suspend_work(struct work_struct *work) info = container_of(work, struct fts_ts_info, suspend_work); +#ifdef CONFIG_ST_TRUSTED_TOUCH + if (atomic_read(&info->trusted_touch_enabled)) + wait_for_completion_interruptible( + &info->trusted_touch_powerdown); +#endif + __pm_wakeup_event(info->wakeup_source, HZ); info->resume_bit = 0; @@ -4548,13 +5475,109 @@ static int check_default_tp(struct device_node *dt, const char *prop) return ret; } +static int fts_probe_delayed(struct fts_ts_info *info) +{ + int error = 0; + int retval = 0; + +/* Avoid setting up hardware for TVM during probe */ +#ifdef CONFIG_ST_TRUSTED_TOUCH +#ifdef CONFIG_ARCH_QTI_VM + if (!atomic_read(&info->delayed_vm_probe_pending)) { + atomic_set(&info->delayed_vm_probe_pending, 1); + return 0; + } + goto tvm_setup; +#endif +#endif + logError(0, "%s SET Regulators:\n", tag); + retval = fts_get_reg(info, true); + if (retval < 0) { + logError(1, "%s ERROR: %s: Failed to get regulators\n", + tag, __func__); + goto Exit_1; + } + retval = fts_enable_reg(info, true); + if (retval < 0) { + logError(1, + "%s %s: ERROR Failed to enable regulators\n", + tag, __func__); + goto Exit_2; + } + + logError(0, "%s SET GPIOS:\n", tag); + retval = fts_set_gpio(info); + if (retval < 0) { + logError(1, "%s %s: ERROR Failed to set up GPIO's\n", + tag, __func__); + goto Exit_2; + } + + info->client->irq = gpio_to_irq(info->bdata->irq_gpio); + retval = fts_pinctrl_init(info); + if (!retval && info->ts_pinctrl) { + /* + * Pinctrl handle is optional. If pinctrl handle is + * found let pins to be configured in active state. + * If not found continue further without error. + */ + retval = pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_active); + if (retval < 0) { + logError(1, + "%s: Failed to select %s pinstate %d\n", + __func__, PINCTRL_STATE_ACTIVE, retval); + } + } + +#ifdef CONFIG_ARCH_QTI_VM +tvm_setup: +#endif + /* init hardware device */ + logError(0, "%s Device Initialization:\n", tag); + error = fts_init(info); + if (error < OK) { + logError(1, "%s Cannot initialize the device ERROR %08X\n", + tag, error); + error = -ENODEV; +#ifdef CONFIG_ARCH_QTI_VM + return error; +#endif + goto Exit_3; + } + + queue_delayed_work(info->fwu_workqueue, &info->fwu_work, + msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + return error; + +Exit_3: + if (info->ts_pinctrl) { + if (IS_ERR_OR_NULL(info->pinctrl_state_release)) { + devm_pinctrl_put(info->ts_pinctrl); + info->ts_pinctrl = NULL; + } else { + if (pinctrl_select_state(info->ts_pinctrl, + info->pinctrl_state_release)) + logError(1, "%s:Failed to select %s pinstate\n", + __func__, PINCTRL_STATE_RELEASE); + } + } + fts_enable_reg(info, false); + fts_gpio_setup(info->bdata->irq_gpio, false, 0, 0); + fts_gpio_setup(info->bdata->reset_gpio, false, 0, 0); + +Exit_2: + fts_get_reg(info, false); +Exit_1: + return error; +} + static int fts_probe_internal(struct i2c_client *client, const struct i2c_device_id *idp) { struct fts_ts_info *info = NULL; int error = 0; struct device_node *dp = client->dev.of_node; - int retval; int skip_5_1 = 0; logError(0, "%s %s: driver probe begin!\n", tag, __func__); @@ -4570,7 +5593,7 @@ static int fts_probe_internal(struct i2c_client *client, info = kzalloc(sizeof(struct fts_ts_info), GFP_KERNEL); if (!info) { logError(1, - "%s Out of memory, can't to allocate struct info!\n", + "%s can't allocate struct info!\n", tag); error = -ENOMEM; goto ProbeErrorExit_0; @@ -4601,51 +5624,12 @@ static int fts_probe_internal(struct i2c_client *client, parse_dt(&client->dev, info->bdata); } - logError(0, "%s SET Regulators:\n", tag); - retval = fts_get_reg(info, true); - if (retval < 0) { - logError(1, "%s ERROR: %s: Failed to get regulators\n", - tag, __func__); - goto ProbeErrorExit_1; - } - - retval = fts_enable_reg(info, true); - if (retval < 0) { - logError(1, "%s %s: ERROR Failed to enable regulators\n", - tag, __func__); - goto ProbeErrorExit_2; - } - - logError(0, "%s SET GPIOS:\n", tag); - retval = fts_set_gpio(info); - if (retval < 0) { - logError(1, "%s %s: ERROR Failed to set up GPIO's\n", - tag, __func__); - goto ProbeErrorExit_2; - } - info->client->irq = gpio_to_irq(info->bdata->irq_gpio); - - retval = fts_pinctrl_init(info); - if (!retval && info->ts_pinctrl) { - /* - * Pinctrl handle is optional. If pinctrl handle is found - * let pins to be configured in active state. If not - * found continue further without error. - */ - retval = pinctrl_select_state(info->ts_pinctrl, - info->pinctrl_state_active); - if (retval < 0) { - logError(1, "%s: Failed to select %s pinstate %d\n", - __func__, PINCTRL_STATE_ACTIVE, retval); - } - } - logError(0, "%s SET Auto Fw Update:\n", tag); info->fwu_workqueue = alloc_workqueue("fts-fwu-queue", WQ_UNBOUND|WQ_HIGHPRI|WQ_CPU_INTENSIVE, 1); if (!info->fwu_workqueue) { logError(1, "%s ERROR: Cannot create fwu work thread\n", tag); - goto ProbeErrorExit_3; + goto ProbeErrorExit_1; } INIT_DELAYED_WORK(&info->fwu_work, fts_fw_update_auto); @@ -4761,16 +5745,6 @@ static int fts_probe_internal(struct i2c_client *client, info->stylus_id = 0; #endif - /* init hardware device */ - logError(0, "%s Device Initialization:\n", tag); - error = fts_init(info); - if (error < OK) { - logError(1, "%s Cannot initialize the device ERROR %08X\n", - tag, error); - error = -ENODEV; - goto ProbeErrorExit_6; - } - /* * init feature switches (by default all the features * are disable, if one feature want to be enabled from @@ -4789,6 +5763,11 @@ static int fts_probe_internal(struct i2c_client *client, info->resume_bit = 1; info->notifier = fts_noti_block; +#ifdef CONFIG_ST_TRUSTED_TOUCH + fts_trusted_touch_init(info); + mutex_init(&(info->fts_clk_io_ctrl_mutex)); +#endif + logError(0, "%s SET Device File Nodes:\n", tag); /* sysfs stuff */ info->attrs.attrs = fts_attr_group; @@ -4872,10 +5851,13 @@ static int fts_probe_internal(struct i2c_client *client, } } - queue_delayed_work(info->fwu_workqueue, &info->fwu_work, - msecs_to_jiffies(EXP_FN_WORK_DELAY_MS)); + error = fts_probe_delayed(info); + if (error) { + logError(1, "%s ERROR: Failed to enable resources\n", + tag); + goto ProbeErrorExit_11; + } logError(1, "%s Probe Finished!\n", tag); - return OK; /* error exit path */ @@ -4900,9 +5882,10 @@ ProbeErrorExit_8: #endif ProbeErrorExit_7: +#ifdef CONFIG_ST_TRUSTED_TOUCH + fts_vm_deinit(info); +#endif /* fb_unregister_client(&info->notifier); */ - -ProbeErrorExit_6: input_unregister_device(info->input_dev); ProbeErrorExit_5_1: @@ -4916,25 +5899,6 @@ ProbeErrorExit_4: destroy_workqueue(info->fwu_workqueue); wakeup_source_unregister(info->wakeup_source); -ProbeErrorExit_3: - if (info->ts_pinctrl) { - if (IS_ERR_OR_NULL(info->pinctrl_state_release)) { - devm_pinctrl_put(info->ts_pinctrl); - info->ts_pinctrl = NULL; - } else { - if (pinctrl_select_state(info->ts_pinctrl, - info->pinctrl_state_release)) - logError(1, "%s:Failed to select %s pinstate\n", - __func__, PINCTRL_STATE_RELEASE); - } - } - fts_enable_reg(info, false); - fts_gpio_setup(info->bdata->irq_gpio, false, 0, 0); - fts_gpio_setup(info->bdata->reset_gpio, false, 0, 0); - -ProbeErrorExit_2: - fts_get_reg(info, false); - ProbeErrorExit_1: kfree(info->i2c_data); ProbeErrorExit_0P1: diff --git a/drivers/input/touchscreen/st/fts.h b/drivers/input/touchscreen/st/fts.h index 065ffc6d9c8d..9be46c210d69 100644 --- a/drivers/input/touchscreen/st/fts.h +++ b/drivers/input/touchscreen/st/fts.h @@ -25,6 +25,7 @@ /*#include */ #include #include +#include #include "fts_lib/ftsSoftware.h" #include "fts_lib/ftsHardware.h" @@ -211,6 +212,33 @@ struct event_dispatch_handler_t { void (*handler)(struct fts_ts_info *info, unsigned char *data); }; +enum trusted_touch_mode_config { + TRUSTED_TOUCH_VM_MODE, + TRUSTED_TOUCH_MODE_NONE +}; + +#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; + u32 hw_irq; + hh_memparcel_handle_t vm_mem_handle; + u32 *iomem_bases; + u32 *iomem_sizes; + u32 iomem_list_size; + void *mem_cookie; +#ifdef CONFIG_ARCH_QTI_VM + atomic_t tvm_owns_iomem; + atomic_t tvm_owns_irq; +#else + atomic_t pvm_owns_iomem; + atomic_t pvm_owns_irq; +#endif +}; +#endif + /* * struct fts_ts_info - FTS capacitive touch screen device information * @dev: Pointer to the structure device @@ -325,12 +353,27 @@ struct fts_ts_info { int aoi_top; int aoi_bottom; int aoi_right; + +#ifdef CONFIG_ST_TRUSTED_TOUCH + struct trusted_touch_vm_info *vm_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 delayed_vm_probe_pending; + atomic_t trusted_touch_mode; +#endif }; extern struct chipInfo ftsInfo; int fts_chip_powercycle(struct fts_ts_info *info); int fts_chip_powercycle2(struct fts_ts_info *info, unsigned long sleep); +void release_all_touches(struct fts_ts_info *info); /*int fts_get_fw_version(struct fts_ts_info *info);*/ /*extern unsigned int le_to_uint(const unsigned char *ptr);*/ /*extern unsigned int be_to_uint(const unsigned char *ptr);*/