diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index c4d2ae970be2..d7d58fecf1e4 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -1210,5 +1210,14 @@ config MSM_SLATECOM This handle the interrupts raised by BG and notify the G-link with interrupt event and event data. +config MSM_SLATECOM_INTERFACE + tristate "Driver support for Slate Communication" + depends on MSM_SLATECOM + help + Create a slate_com_dev device node for user space communication. + Single user space client can open device node for communication + from hardware. Hardware will provide access to read + registers and read/write AHB memory in the device. + source "drivers/soc/qcom/icnss2/Kconfig" endmenu diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 173dd1fa0f79..b26b9322cce3 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -109,3 +109,4 @@ obj-$(CONFIG_QCOM_QFPROM_SYS) += qfprom-sys.o obj-$(CONFIG_RENAME_BLOCK_DEVICE) += rename_block_device.o obj-$(CONFIG_QCOM_ADSP_MANUAL_VOTE) += adsp_vote_qmi.o adsp_lpm_voting_v01.o obj-$(CONFIG_MSM_SLATECOM) += slatecom_spi.o +obj-$(CONFIG_MSM_SLATECOM_INTERFACE) += slatecom_interface.o diff --git a/drivers/soc/qcom/slatecom_interface.c b/drivers/soc/qcom/slatecom_interface.c new file mode 100644 index 000000000000..4cb3cc7e7310 --- /dev/null +++ b/drivers/soc/qcom/slatecom_interface.c @@ -0,0 +1,626 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + */ +#define pr_fmt(msg) "slatecom_dev:" msg + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "slatecom.h" +#include "linux/slatecom_interface.h" +#include "slatecom_interface.h" +#include +#include +#include +#include +#include +#include +#include +#include + +#include "peripheral-loader.h" +#include "../../misc/qseecom_kernel.h" +//#include "pil_slate_intf.h" + +#define SLATECOM "slate_com_dev" + +#define SLATEDAEMON_LDO09_LPM_VTG 0 +#define SLATEDAEMON_LDO09_NPM_VTG 10000 + +#define SLATEDAEMON_LDO03_LPM_VTG 0 +#define SLATEDAEMON_LDO03_NPM_VTG 10000 + +#define MPPS_DOWN_EVENT_TO_SLATE_TIMEOUT 3000 +#define ADSP_DOWN_EVENT_TO_SLATE_TIMEOUT 3000 +#define MAX_APP_NAME_SIZE 100 + +/*pil_slate_intf.h*/ +#define RESULT_SUCCESS 0 +#define RESULT_FAILURE -1 + +/* tzapp command list.*/ +enum slate_tz_commands { + SLATEPIL_RAMDUMP, + SLATEPIL_IMAGE_LOAD, + SLATEPIL_AUTH_MDT, + SLATEPIL_DLOAD_CONT, + SLATEPIL_GET_SLATE_VERSION, + SLATEPIL_TWM_DATA, +}; + +/* tzapp slate request.*/ +struct tzapp_slate_req { + uint8_t tzapp_slate_cmd; + uint8_t padding[3]; + phys_addr_t address_fw; + size_t size_fw; +} __attribute__ ((__packed__)); + +/* tzapp slate response.*/ +struct tzapp_slate_rsp { + uint32_t tzapp_slate_cmd; + uint32_t slate_info_len; + int32_t status; + uint32_t slate_info[100]; +} __attribute__ ((__packed__)); + +enum { + SSR_DOMAIN_SLATE, + SSR_DOMAIN_MODEM, + SSR_DOMAIN_ADSP, + SSR_DOMAIN_MAX, +}; + +struct slatedaemon_priv { + void *pil_h; + struct qseecom_handle *qseecom_handle; + int app_status; + unsigned long attrs; + u32 cmd_status; + struct device *platform_dev; +}; + +struct slate_event { + enum slate_event_type e_type; +}; + +struct service_info { + const char name[32]; + int domain_id; + void *handle; + struct notifier_block *nb; +}; + +static char *ssr_domains[] = { + "slate-wear", + "modem", + "adsp", +}; + +static struct slatedaemon_priv *dev; +static unsigned int slatereset_gpio; +static DEFINE_MUTEX(slate_char_mutex); +static struct cdev slate_cdev; +static struct class *slate_class; +struct device *dev_ret; +static dev_t slate_dev; +static int device_open; +static void *handle; +static bool twm_exit; +static bool slate_app_running; +static struct slatecom_open_config_type config_type; +static DECLARE_COMPLETION(slate_modem_down_wait); +static DECLARE_COMPLETION(slate_adsp_down_wait); + +/** + * send_uevent(): send events to user space + * pce : ssr event handle value + * Return: 0 on success, standard Linux error code on error + * + * It adds pce value to event and broadcasts to user space. + */ +static int send_uevent(struct slate_event *pce) +{ + char event_string[32]; + char *envp[2] = { event_string, NULL }; + + snprintf(event_string, ARRAY_SIZE(event_string), + "SLATE_EVENT=%d", pce->e_type); + return kobject_uevent_env(&dev_ret->kobj, KOBJ_CHANGE, envp); +} + +static int slatecom_char_open(struct inode *inode, struct file *file) +{ + int ret; + + mutex_lock(&slate_char_mutex); + if (device_open == 1) { + pr_err("device is already open\n"); + mutex_unlock(&slate_char_mutex); + return -EBUSY; + } + device_open++; + handle = slatecom_open(&config_type); + mutex_unlock(&slate_char_mutex); + if (IS_ERR(handle)) { + device_open = 0; + ret = PTR_ERR(handle); + handle = NULL; + return ret; + } + return 0; +} + +static int slatechar_read_cmd(struct slate_ui_data *fui_obj_msg, + unsigned int type) +{ + void *read_buf; + int ret; + void __user *result = (void *) + (uintptr_t)fui_obj_msg->result; + + read_buf = kmalloc_array(fui_obj_msg->num_of_words, sizeof(uint32_t), + GFP_KERNEL); + if (read_buf == NULL) + return -ENOMEM; + switch (type) { + case REG_READ: + ret = slatecom_reg_read(handle, fui_obj_msg->cmd, + fui_obj_msg->num_of_words, + read_buf); + break; + case AHB_READ: + ret = slatecom_ahb_read(handle, + fui_obj_msg->slate_address, + fui_obj_msg->num_of_words, + read_buf); + break; + } + if (!ret && copy_to_user(result, read_buf, + fui_obj_msg->num_of_words * sizeof(uint32_t))) { + pr_err("copy to user failed\n"); + ret = -EFAULT; + } + kfree(read_buf); + return ret; +} + +static int slatechar_write_cmd(struct slate_ui_data *fui_obj_msg, unsigned int type) +{ + void *write_buf; + int ret = -EINVAL; + void __user *write = (void *) + (uintptr_t)fui_obj_msg->write; + + write_buf = kmalloc_array(fui_obj_msg->num_of_words, sizeof(uint32_t), + GFP_KERNEL); + if (write_buf == NULL) + return -ENOMEM; + write_buf = memdup_user(write, + fui_obj_msg->num_of_words * sizeof(uint32_t)); + if (IS_ERR(write_buf)) { + ret = PTR_ERR(write_buf); + kfree(write_buf); + return ret; + } + switch (type) { + case REG_WRITE: + ret = slatecom_reg_write(handle, fui_obj_msg->cmd, + fui_obj_msg->num_of_words, + write_buf); + break; + case AHB_WRITE: + ret = slatecom_ahb_write(handle, + fui_obj_msg->slate_address, + fui_obj_msg->num_of_words, + write_buf); + break; + } + kfree(write_buf); + return ret; +} + +int slate_soft_reset(void) +{ + pr_debug("do SLATE reset using gpio %d\n", slatereset_gpio); + if (!gpio_is_valid(slatereset_gpio)) { + pr_err("gpio %d is not valid\n", slatereset_gpio); + return -ENXIO; + } + if (gpio_direction_output(slatereset_gpio, 1)) + pr_err("gpio %d direction not set\n", slatereset_gpio); + + /* Sleep for 50ms for hardware to detect signal as high */ + msleep(50); + + gpio_set_value(slatereset_gpio, 0); + + /* Sleep for 50ms for hardware to detect signal as high */ + msleep(50); + gpio_set_value(slatereset_gpio, 1); + + return 0; +} +EXPORT_SYMBOL(slate_soft_reset); + +static int modem_down2_slate(void) +{ + complete(&slate_modem_down_wait); + return 0; +} + +static int adsp_down2_slate(void) +{ + complete(&slate_adsp_down_wait); + return 0; +} + +static long slate_com_ioctl(struct file *filp, + unsigned int ui_slatecom_cmd, unsigned long arg) +{ + int ret; + struct slate_ui_data ui_obj_msg; + + switch (ui_slatecom_cmd) { + case REG_READ: + case AHB_READ: + if (copy_from_user(&ui_obj_msg, (void __user *) arg, + sizeof(ui_obj_msg))) { + pr_err("The copy from user failed\n"); + ret = -EFAULT; + } + ret = slatechar_read_cmd(&ui_obj_msg, + ui_slatecom_cmd); + if (ret < 0) + pr_err("slatechar_read_cmd failed\n"); + break; + case AHB_WRITE: + case REG_WRITE: + if (copy_from_user(&ui_obj_msg, (void __user *) arg, + sizeof(ui_obj_msg))) { + pr_err("The copy from user failed\n"); + ret = -EFAULT; + } + ret = slatechar_write_cmd(&ui_obj_msg, ui_slatecom_cmd); + if (ret < 0) + pr_err("slatechar_write_cmd failed\n"); + break; + case SET_SPI_FREE: + ret = slatecom_set_spi_state(SLATECOM_SPI_FREE); + break; + case SET_SPI_BUSY: + ret = slatecom_set_spi_state(SLATECOM_SPI_BUSY); + break; + case SLATE_SOFT_RESET: + ret = slate_soft_reset(); + break; + case SLATE_MODEM_DOWN2_SLATE_DONE: + ret = modem_down2_slate(); + break; + case SLATE_ADSP_DOWN2_SLATE_DONE: + ret = adsp_down2_slate(); + break; + case SLATE_TWM_EXIT: + twm_exit = true; + ret = 0; + break; + case SLATE_APP_RUNNING: + slate_app_running = true; + ret = 0; + break; + case SLATE_WEAR_LOAD: + ret = 0; + if (dev->pil_h) { + pr_err("slate-wear is already loaded\n"); + ret = -EFAULT; + break; + } + dev->pil_h = subsystem_get_with_fwname("slate-wear", "slate-wear"); + if (!dev->pil_h) { + pr_err("failed to load slate-wear\n"); + ret = -EFAULT; + } + break; + case SLATE_WEAR_UNLOAD: + if (dev->pil_h) { + subsystem_put(dev->pil_h); + dev->pil_h = NULL; + slate_soft_reset(); + } + ret = 0; + break; + default: + ret = -ENOIOCTLCMD; + break; + } + return ret; +} + +static int slatecom_char_close(struct inode *inode, struct file *file) +{ + int ret; + + mutex_lock(&slate_char_mutex); + ret = slatecom_close(&handle); + device_open = 0; + mutex_unlock(&slate_char_mutex); + return ret; +} + +static int slate_daemon_probe(struct platform_device *pdev) +{ + struct device_node *node; + + node = pdev->dev.of_node; + + dev = kzalloc(sizeof(struct slatedaemon_priv), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->platform_dev = &pdev->dev; + pr_info("%s success\n", __func__); + + return 0; +} + +static const struct of_device_id slate_daemon_of_match[] = { + { .compatible = "qcom,slate-daemon", }, + { } +}; +MODULE_DEVICE_TABLE(of, slate_daemon_of_match); + +static struct platform_driver slate_daemon_driver = { + .probe = slate_daemon_probe, + .driver = { + .name = "slate-daemon", + .of_match_table = slate_daemon_of_match, + }, +}; + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = slatecom_char_open, + .release = slatecom_char_close, + .unlocked_ioctl = slate_com_ioctl, +}; + +/** + *ssr_slate_cb(): callback function is called + *by ssr framework when SLATE goes down, up and during ramdump + *collection. It handles SLATE shutdown and power up events. + */ +static int ssr_slate_cb(struct notifier_block *this, + unsigned long opcode, void *data) +{ + struct slate_event slatee; + + switch (opcode) { + case SUBSYS_BEFORE_SHUTDOWN: + pr_debug("Slate before shutdown\n"); + slatee.e_type = SLATE_BEFORE_POWER_DOWN; + slatecom_slatedown_handler(); + slatecom_set_spi_state(SLATECOM_SPI_BUSY); + send_uevent(&slatee); + break; + case SUBSYS_AFTER_SHUTDOWN: + pr_debug("Slate after shutdown\n"); + slatee.e_type = SLATE_AFTER_POWER_DOWN; + slatecom_slatedown_handler(); + send_uevent(&slatee); + break; + case SUBSYS_BEFORE_POWERUP: + pr_debug("Slate before powerup\n"); + slatee.e_type = SLATE_BEFORE_POWER_UP; + slatecom_slatedown_handler(); + send_uevent(&slatee); + break; + case SUBSYS_AFTER_POWERUP: + pr_debug("Slate after powerup\n"); + slatecom_set_spi_state(SLATECOM_SPI_FREE); + send_uevent(&slatee); + break; + } + return NOTIFY_DONE; +} + +/** + *ssr_modem_cb(): callback function is called + *by ssr framework when modem goes down, up and during ramdump + *collection. It handles modem shutdown and power up events. + */ +static int ssr_modem_cb(struct notifier_block *this, + unsigned long opcode, void *data) +{ + struct slate_event modeme; + int ret; + + switch (opcode) { + case SUBSYS_BEFORE_SHUTDOWN: + modeme.e_type = MODEM_BEFORE_POWER_DOWN; + reinit_completion(&slate_modem_down_wait); + send_uevent(&modeme); + ret = wait_for_completion_timeout(&slate_modem_down_wait, + msecs_to_jiffies(MPPS_DOWN_EVENT_TO_SLATE_TIMEOUT)); + if (!ret) + pr_err("Time out on modem down event\n"); + break; + case SUBSYS_AFTER_POWERUP: + modeme.e_type = MODEM_AFTER_POWER_UP; + send_uevent(&modeme); + break; + } + return NOTIFY_DONE; +} + +static int ssr_adsp_cb(struct notifier_block *this, + unsigned long opcode, void *data) +{ + struct slate_event adspe; + int ret; + + switch (opcode) { + case SUBSYS_BEFORE_SHUTDOWN: + adspe.e_type = ADSP_BEFORE_POWER_DOWN; + reinit_completion(&slate_adsp_down_wait); + send_uevent(&adspe); + ret = wait_for_completion_timeout(&slate_adsp_down_wait, + msecs_to_jiffies(ADSP_DOWN_EVENT_TO_SLATE_TIMEOUT)); + if (!ret) + pr_err("Time out on adsp down event\n"); + break; + case SUBSYS_AFTER_POWERUP: + adspe.e_type = ADSP_AFTER_POWER_UP; + send_uevent(&adspe); + break; + } + return NOTIFY_DONE; +} +bool is_twm_exit(void) +{ + if (twm_exit) { + twm_exit = false; + return true; + } + return false; +} +EXPORT_SYMBOL(is_twm_exit); + +bool is_slate_running(void) +{ + if (slate_app_running) { + slate_app_running = false; + return true; + } + return false; +} +EXPORT_SYMBOL(is_slate_running); + +static struct notifier_block ssr_modem_nb = { + .notifier_call = ssr_modem_cb, + .priority = 0, +}; + +static struct notifier_block ssr_adsp_nb = { + .notifier_call = ssr_adsp_cb, + .priority = 0, +}; + +static struct notifier_block ssr_slate_nb = { + .notifier_call = ssr_slate_cb, + .priority = 0, +}; + +static struct service_info service_data[3] = { + { + .name = "SSR_SLATE", + .domain_id = SSR_DOMAIN_SLATE, + .nb = &ssr_slate_nb, + .handle = NULL, + }, + { + .name = "SSR_MODEM", + .domain_id = SSR_DOMAIN_MODEM, + .nb = &ssr_modem_nb, + .handle = NULL, + }, + { + .name = "SSR_ADSP", + .domain_id = SSR_DOMAIN_ADSP, + .nb = &ssr_adsp_nb, + .handle = NULL, + }, +}; + +/** + * ssr_register checks that domain id should be in range and register + * SSR framework for value at domain id. + */ +static void ssr_register(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(service_data); i++) { + if ((service_data[i].domain_id < 0) || + (service_data[i].domain_id >= SSR_DOMAIN_MAX)) { + pr_err("Invalid service ID = %d\n", + service_data[i].domain_id); + } else { + service_data[i].handle = + subsys_notif_register_notifier( + ssr_domains[service_data[i].domain_id], + service_data[i].nb); + if (IS_ERR_OR_NULL(service_data[i].handle)) { + pr_err("subsys register failed for id = %d\n", + service_data[i].domain_id); + service_data[i].handle = NULL; + } + } + } + +} + +static int __init init_slate_com_dev(void) +{ + int ret; + + ret = alloc_chrdev_region(&slate_dev, 0, 1, SLATECOM); + if (ret < 0) { + pr_err("failed with error %d\n", ret); + return ret; + } + cdev_init(&slate_cdev, &fops); + + ret = cdev_add(&slate_cdev, slate_dev, 1); + if (ret < 0) { + unregister_chrdev_region(slate_dev, 1); + pr_err("device registration failed\n"); + return ret; + } + slate_class = class_create(THIS_MODULE, SLATECOM); + if (IS_ERR_OR_NULL(slate_class)) { + cdev_del(&slate_cdev); + unregister_chrdev_region(slate_dev, 1); + pr_err("class creation failed\n"); + return PTR_ERR(slate_class); + } + + dev_ret = device_create(slate_class, NULL, slate_dev, NULL, SLATECOM); + if (IS_ERR_OR_NULL(dev_ret)) { + class_destroy(slate_class); + cdev_del(&slate_cdev); + unregister_chrdev_region(slate_dev, 1); + pr_err("device create failed\n"); + return PTR_ERR(dev_ret); + } + + if (platform_driver_register(&slate_daemon_driver)) + pr_err("%s: failed to register slate-daemon register\n", __func__); + + ssr_register(); + + return 0; +} + +static void __exit exit_slate_com_dev(void) +{ + device_destroy(slate_class, slate_dev); + class_destroy(slate_class); + cdev_del(&slate_cdev); + unregister_chrdev_region(slate_dev, 1); + platform_driver_unregister(&slate_daemon_driver); +} + +module_init(init_slate_com_dev); +module_exit(exit_slate_com_dev); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/slatecom_interface.h b/drivers/soc/qcom/slatecom_interface.h new file mode 100644 index 000000000000..d16c935ca1a0 --- /dev/null +++ b/drivers/soc/qcom/slatecom_interface.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + */ +#ifndef SLATECOM_INTERFACE_H +#define SLATECOM_INTERFACE_H + +/* + * slate_soft_reset() - soft reset Blackghost + * Return 0 on success or -Ve on error + */ +int slate_soft_reset(void); + +/* + * is_twm_exit() + * Return true if device is booting up on TWM exit. + * value is auto cleared once read. + */ +bool is_twm_exit(void); + +/* + * is_slate_running() + * Return true if slate is running. + * value is auto cleared once read. + */ +bool is_slate_running(void); + +#endif /* SLATECOM_INTERFACE_H */ + diff --git a/include/uapi/linux/slatecom_interface.h b/include/uapi/linux/slatecom_interface.h new file mode 100644 index 000000000000..c76f2f037acb --- /dev/null +++ b/include/uapi/linux/slatecom_interface.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ +/* + * Copyright (c) 2017-2021, The Linux Foundation. All rights reserved. + */ +#ifndef LINUX_SLATECOM_INTERFACE_H +#define LINUX_SLATECOM_INTERFACE_H + +#include + +#define SLATECOM_REG_READ 0 +#define SLATECOM_AHB_READ 1 +#define SLATECOM_AHB_WRITE 2 +#define SLATECOM_SET_SPI_FREE 3 +#define SLATECOM_SET_SPI_BUSY 4 +#define SLATECOM_REG_WRITE 5 +#define SLATECOM_SOFT_RESET 6 +#define SLATECOM_MODEM_DOWN2_SLATE 7 +#define SLATECOM_TWM_EXIT 8 +#define SLATECOM_SLATE_APP_RUNNING 9 +#define SLATECOM_ADSP_DOWN2_SLATE 10 +#define SLATECOM_SLATE_WEAR_LOAD 11 +#define SLATECOM_SLATE_WEAR_UNLOAD 12 +#define EXCHANGE_CODE 'V' + +struct slate_ui_data { + __u64 __user write; + __u64 __user result; + __u32 slate_address; + __u32 cmd; + __u32 num_of_words; + __u8 __user *buffer; +}; + +enum slate_event_type { + SLATE_BEFORE_POWER_DOWN = 1, + SLATE_AFTER_POWER_DOWN, + SLATE_BEFORE_POWER_UP, + SLATE_AFTER_POWER_UP, + MODEM_BEFORE_POWER_DOWN, + MODEM_AFTER_POWER_UP, + ADSP_BEFORE_POWER_DOWN, + ADSP_AFTER_POWER_UP, + TWM_SLATE_AFTER_POWER_UP, +}; + +#define REG_READ \ + _IOWR(EXCHANGE_CODE, SLATECOM_REG_READ, \ + struct slate_ui_data) +#define AHB_READ \ + _IOWR(EXCHANGE_CODE, SLATECOM_AHB_READ, \ + struct slate_ui_data) +#define AHB_WRITE \ + _IOW(EXCHANGE_CODE, SLATECOM_AHB_WRITE, \ + struct slate_ui_data) +#define SET_SPI_FREE \ + _IOR(EXCHANGE_CODE, SLATECOM_SET_SPI_FREE, \ + struct slate_ui_data) +#define SET_SPI_BUSY \ + _IOR(EXCHANGE_CODE, SLATECOM_SET_SPI_BUSY, \ + struct slate_ui_data) +#define REG_WRITE \ + _IOWR(EXCHANGE_CODE, SLATECOM_REG_WRITE, \ + struct slate_ui_data) +#define SLATE_SOFT_RESET \ + _IOWR(EXCHANGE_CODE, SLATECOM_SOFT_RESET, \ + struct slate_ui_data) +#define SLATE_TWM_EXIT \ + _IOWR(EXCHANGE_CODE, SLATECOM_TWM_EXIT, \ + struct slate_ui_data) +#define SLATE_APP_RUNNING \ + _IOWR(EXCHANGE_CODE, SLATECOM_SLATE_APP_RUNNING, \ + struct slate_ui_data) +#define SLATE_MODEM_DOWN2_SLATE_DONE \ + _IOWR(EXCHANGE_CODE, SLATECOM_MODEM_DOWN2_SLATE, \ + struct slate_ui_data) +#define SLATE_WEAR_LOAD \ + _IOWR(EXCHANGE_CODE, SLATECOM_SLATE_WEAR_LOAD, \ + struct slate_ui_data) +#define SLATE_WEAR_UNLOAD \ + _IOWR(EXCHANGE_CODE, SLATECOM_SLATE_WEAR_UNLOAD, \ + struct slate_ui_data) +#define SLATE_ADSP_DOWN2_SLATE_DONE \ + _IOWR(EXCHANGE_CODE, SLATECOM_ADSP_DOWN2_SLATE, \ + struct slate_ui_data) + +#endif /* LINUX_SLATECOM_INTERFACE_H */ +