Loading drivers/esoc/Kconfig +8 −0 Original line number Diff line number Diff line Loading @@ -58,4 +58,12 @@ config ESOC_MDM_DBG_ENG by command engine to the external modem. Also allows masking of certain notifications being sent to the external modem. config MDM_DBG_REQ_ENG tristate "manual request engine for 4x series external modems" depends on ESOC_MDM_DBG_ENG help Provides a user interface to handle incoming requests from the external modem. Allows for debugging of IPC mechanism between the external modem and the primary soc. endif drivers/esoc/esoc-mdm-dbg-eng.c +167 −2 Original line number Diff line number Diff line /* Copyright (c) 2015, The Linux Foundation. All rights reserved. /* Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -179,7 +179,165 @@ static ssize_t notifier_mask_store(struct device_driver *drv, const char *buf, } static DRIVER_ATTR(notifier_mask, S_IWUSR, NULL, notifier_mask_store); int mdm_dbg_eng_init(struct esoc_drv *esoc_drv) #ifdef CONFIG_MDM_DBG_REQ_ENG static struct esoc_clink *dbg_clink; /* Last recorded request from esoc */ static enum esoc_req last_req; static DEFINE_SPINLOCK(req_lock); /* * esoc_to_user: Conversion of esoc ids to user visible strings * id: esoc request, command, notifier, event id * str: string equivalent of the above */ struct esoc_to_user { unsigned int id; char str[20]; }; static struct esoc_to_user in_to_resp[] = { { .id = ESOC_IMG_XFER_DONE, .str = "XFER_DONE", }, { .id = ESOC_BOOT_DONE, .str = "BOOT_DONE", }, { .id = ESOC_BOOT_FAIL, .str = "BOOT_FAIL", }, { .id = ESOC_IMG_XFER_RETRY, .str = "XFER_RETRY", }, { .id = ESOC_IMG_XFER_FAIL, .str = "XFER_FAIL", }, { .id = ESOC_UPGRADE_AVAILABLE, .str = "UPGRADE", }, { .id = ESOC_DEBUG_DONE, .str = "DEBUG_DONE", }, { .id = ESOC_DEBUG_FAIL, .str = "DEBUG_FAIL", }, }; static struct esoc_to_user req_to_str[] = { { .id = ESOC_REQ_IMG, .str = "REQ_IMG", }, { .id = ESOC_REQ_DEBUG, .str = "REQ_DEBUG", }, { .id = ESOC_REQ_SHUTDOWN, .str = "REQ_SHUTDOWN", }, }; static ssize_t req_eng_resp_store(struct device_driver *drv, const char *buf, size_t count) { unsigned int i; const struct esoc_clink_ops *const clink_ops = dbg_clink->clink_ops; dev_dbg(&dbg_clink->dev, "user input req eng response %s\n", buf); for (i = 0; i < ARRAY_SIZE(in_to_resp); i++) { size_t len1 = strlen(buf); size_t len2 = strlen(in_to_resp[i].str); if (len1 == len2 && !strcmp(buf, in_to_resp[i].str)) { clink_ops->notify(in_to_resp[i].id, dbg_clink); break; } } if (i > ARRAY_SIZE(in_to_resp)) dev_err(&dbg_clink->dev, "Invalid resp %s, specified\n", buf); return count; } static DRIVER_ATTR(req_eng_resp, S_IWUSR, NULL, req_eng_resp_store); static ssize_t last_esoc_req_show(struct device_driver *drv, char *buf) { unsigned int i; unsigned long flags; size_t count; spin_lock_irqsave(&req_lock, flags); for (i = 0; i < ARRAY_SIZE(req_to_str); i++) { if (last_req == req_to_str[i].id) { count = snprintf(buf, PAGE_SIZE, "%s\n", req_to_str[i].str); break; } } spin_unlock_irqrestore(&req_lock, flags); return count; } static DRIVER_ATTR(last_esoc_req, S_IRUSR, last_esoc_req_show, NULL); static void esoc_handle_req(enum esoc_req req, struct esoc_eng *eng) { unsigned long flags; spin_lock_irqsave(&req_lock, flags); last_req = req; spin_unlock_irqrestore(&req_lock, flags); } static void esoc_handle_evt(enum esoc_evt evt, struct esoc_eng *eng) { } static struct esoc_eng dbg_req_eng = { .handle_clink_req = esoc_handle_req, .handle_clink_evt = esoc_handle_evt, }; int register_dbg_req_eng(struct esoc_clink *clink, struct device_driver *drv) { int ret; dbg_clink = clink; ret = driver_create_file(drv, &driver_attr_req_eng_resp); if (ret) return ret; ret = driver_create_file(drv, &driver_attr_last_esoc_req); if (ret) { dev_err(&clink->dev, "Unable to create last esoc req\n"); goto last_req_err; } ret = esoc_clink_register_req_eng(clink, &dbg_req_eng); if (ret) { pr_err("Unable to register req eng\n"); goto req_eng_fail; } spin_lock_init(&req_lock); return 0; last_req_err: driver_remove_file(drv, &driver_attr_last_esoc_req); req_eng_fail: driver_remove_file(drv, &driver_attr_req_eng_resp); return ret; } #else int register_dbg_req_eng(struct esoc_clink *clink, struct device_driver *d) { return 0; } #endif int mdm_dbg_eng_init(struct esoc_drv *esoc_drv, struct esoc_clink *clink) { int ret; struct device_driver *drv = &esoc_drv->driver; Loading @@ -194,7 +352,14 @@ int mdm_dbg_eng_init(struct esoc_drv *esoc_drv) pr_err("Unable to create notify mask file\n"); goto notify_mask_err; } ret = register_dbg_req_eng(clink, drv); if (ret) { pr_err("Failed to register esoc dbg req eng\n"); goto dbg_req_fail; } return 0; dbg_req_fail: driver_remove_file(drv, &driver_attr_notifier_mask); notify_mask_err: driver_remove_file(drv, &driver_attr_command_mask); cmd_mask_err: Loading drivers/esoc/esoc-mdm-drv.c +1 −1 Original line number Diff line number Diff line Loading @@ -258,7 +258,7 @@ int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv) ret = register_reboot_notifier(&mdm_drv->esoc_restart); if (ret) dev_err(&esoc_clink->dev, "register for reboot failed\n"); ret = mdm_dbg_eng_init(drv); ret = mdm_dbg_eng_init(drv, esoc_clink); if (ret) { debug_init_done = false; dev_err(&esoc_clink->dev, "dbg engine failure\n"); Loading drivers/esoc/esoc_client.c +3 −1 Original line number Diff line number Diff line /* Copyright (c) 2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -47,6 +47,8 @@ struct esoc_desc *devm_register_esoc_client(struct device *dev, for (index = 0;; index++) { esoc_prop = kasprintf(GFP_KERNEL, "esoc-%d", index); if (IS_ERR_OR_NULL(esoc_prop)) return ERR_PTR(-ENOMEM); parp = of_get_property(np, esoc_prop, NULL); if (parp == NULL) { dev_err(dev, "esoc device not present\n"); Loading drivers/esoc/mdm-dbg.h +4 −2 Original line number Diff line number Diff line Loading @@ -24,7 +24,8 @@ static inline bool dbg_check_notify_mask(unsigned int notify) return false; } static inline int mdm_dbg_eng_init(struct esoc_drv *drv) static inline int mdm_dbg_eng_init(struct esoc_drv *drv, struct esoc_clink *clink) { return 0; } Loading @@ -32,7 +33,8 @@ static inline int mdm_dbg_eng_init(struct esoc_drv *drv) #else extern bool dbg_check_cmd_mask(unsigned int cmd); extern bool dbg_check_notify_mask(unsigned int notify); extern int mdm_dbg_eng_init(struct esoc_drv *drv); extern int mdm_dbg_eng_init(struct esoc_drv *drv, struct esoc_clink *clink); #endif static inline bool mdm_dbg_stall_cmd(unsigned int cmd) Loading Loading
drivers/esoc/Kconfig +8 −0 Original line number Diff line number Diff line Loading @@ -58,4 +58,12 @@ config ESOC_MDM_DBG_ENG by command engine to the external modem. Also allows masking of certain notifications being sent to the external modem. config MDM_DBG_REQ_ENG tristate "manual request engine for 4x series external modems" depends on ESOC_MDM_DBG_ENG help Provides a user interface to handle incoming requests from the external modem. Allows for debugging of IPC mechanism between the external modem and the primary soc. endif
drivers/esoc/esoc-mdm-dbg-eng.c +167 −2 Original line number Diff line number Diff line /* Copyright (c) 2015, The Linux Foundation. All rights reserved. /* Copyright (c) 2015, 2017, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -179,7 +179,165 @@ static ssize_t notifier_mask_store(struct device_driver *drv, const char *buf, } static DRIVER_ATTR(notifier_mask, S_IWUSR, NULL, notifier_mask_store); int mdm_dbg_eng_init(struct esoc_drv *esoc_drv) #ifdef CONFIG_MDM_DBG_REQ_ENG static struct esoc_clink *dbg_clink; /* Last recorded request from esoc */ static enum esoc_req last_req; static DEFINE_SPINLOCK(req_lock); /* * esoc_to_user: Conversion of esoc ids to user visible strings * id: esoc request, command, notifier, event id * str: string equivalent of the above */ struct esoc_to_user { unsigned int id; char str[20]; }; static struct esoc_to_user in_to_resp[] = { { .id = ESOC_IMG_XFER_DONE, .str = "XFER_DONE", }, { .id = ESOC_BOOT_DONE, .str = "BOOT_DONE", }, { .id = ESOC_BOOT_FAIL, .str = "BOOT_FAIL", }, { .id = ESOC_IMG_XFER_RETRY, .str = "XFER_RETRY", }, { .id = ESOC_IMG_XFER_FAIL, .str = "XFER_FAIL", }, { .id = ESOC_UPGRADE_AVAILABLE, .str = "UPGRADE", }, { .id = ESOC_DEBUG_DONE, .str = "DEBUG_DONE", }, { .id = ESOC_DEBUG_FAIL, .str = "DEBUG_FAIL", }, }; static struct esoc_to_user req_to_str[] = { { .id = ESOC_REQ_IMG, .str = "REQ_IMG", }, { .id = ESOC_REQ_DEBUG, .str = "REQ_DEBUG", }, { .id = ESOC_REQ_SHUTDOWN, .str = "REQ_SHUTDOWN", }, }; static ssize_t req_eng_resp_store(struct device_driver *drv, const char *buf, size_t count) { unsigned int i; const struct esoc_clink_ops *const clink_ops = dbg_clink->clink_ops; dev_dbg(&dbg_clink->dev, "user input req eng response %s\n", buf); for (i = 0; i < ARRAY_SIZE(in_to_resp); i++) { size_t len1 = strlen(buf); size_t len2 = strlen(in_to_resp[i].str); if (len1 == len2 && !strcmp(buf, in_to_resp[i].str)) { clink_ops->notify(in_to_resp[i].id, dbg_clink); break; } } if (i > ARRAY_SIZE(in_to_resp)) dev_err(&dbg_clink->dev, "Invalid resp %s, specified\n", buf); return count; } static DRIVER_ATTR(req_eng_resp, S_IWUSR, NULL, req_eng_resp_store); static ssize_t last_esoc_req_show(struct device_driver *drv, char *buf) { unsigned int i; unsigned long flags; size_t count; spin_lock_irqsave(&req_lock, flags); for (i = 0; i < ARRAY_SIZE(req_to_str); i++) { if (last_req == req_to_str[i].id) { count = snprintf(buf, PAGE_SIZE, "%s\n", req_to_str[i].str); break; } } spin_unlock_irqrestore(&req_lock, flags); return count; } static DRIVER_ATTR(last_esoc_req, S_IRUSR, last_esoc_req_show, NULL); static void esoc_handle_req(enum esoc_req req, struct esoc_eng *eng) { unsigned long flags; spin_lock_irqsave(&req_lock, flags); last_req = req; spin_unlock_irqrestore(&req_lock, flags); } static void esoc_handle_evt(enum esoc_evt evt, struct esoc_eng *eng) { } static struct esoc_eng dbg_req_eng = { .handle_clink_req = esoc_handle_req, .handle_clink_evt = esoc_handle_evt, }; int register_dbg_req_eng(struct esoc_clink *clink, struct device_driver *drv) { int ret; dbg_clink = clink; ret = driver_create_file(drv, &driver_attr_req_eng_resp); if (ret) return ret; ret = driver_create_file(drv, &driver_attr_last_esoc_req); if (ret) { dev_err(&clink->dev, "Unable to create last esoc req\n"); goto last_req_err; } ret = esoc_clink_register_req_eng(clink, &dbg_req_eng); if (ret) { pr_err("Unable to register req eng\n"); goto req_eng_fail; } spin_lock_init(&req_lock); return 0; last_req_err: driver_remove_file(drv, &driver_attr_last_esoc_req); req_eng_fail: driver_remove_file(drv, &driver_attr_req_eng_resp); return ret; } #else int register_dbg_req_eng(struct esoc_clink *clink, struct device_driver *d) { return 0; } #endif int mdm_dbg_eng_init(struct esoc_drv *esoc_drv, struct esoc_clink *clink) { int ret; struct device_driver *drv = &esoc_drv->driver; Loading @@ -194,7 +352,14 @@ int mdm_dbg_eng_init(struct esoc_drv *esoc_drv) pr_err("Unable to create notify mask file\n"); goto notify_mask_err; } ret = register_dbg_req_eng(clink, drv); if (ret) { pr_err("Failed to register esoc dbg req eng\n"); goto dbg_req_fail; } return 0; dbg_req_fail: driver_remove_file(drv, &driver_attr_notifier_mask); notify_mask_err: driver_remove_file(drv, &driver_attr_command_mask); cmd_mask_err: Loading
drivers/esoc/esoc-mdm-drv.c +1 −1 Original line number Diff line number Diff line Loading @@ -258,7 +258,7 @@ int esoc_ssr_probe(struct esoc_clink *esoc_clink, struct esoc_drv *drv) ret = register_reboot_notifier(&mdm_drv->esoc_restart); if (ret) dev_err(&esoc_clink->dev, "register for reboot failed\n"); ret = mdm_dbg_eng_init(drv); ret = mdm_dbg_eng_init(drv, esoc_clink); if (ret) { debug_init_done = false; dev_err(&esoc_clink->dev, "dbg engine failure\n"); Loading
drivers/esoc/esoc_client.c +3 −1 Original line number Diff line number Diff line /* Copyright (c) 2014, The Linux Foundation. All rights reserved. /* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and Loading Loading @@ -47,6 +47,8 @@ struct esoc_desc *devm_register_esoc_client(struct device *dev, for (index = 0;; index++) { esoc_prop = kasprintf(GFP_KERNEL, "esoc-%d", index); if (IS_ERR_OR_NULL(esoc_prop)) return ERR_PTR(-ENOMEM); parp = of_get_property(np, esoc_prop, NULL); if (parp == NULL) { dev_err(dev, "esoc device not present\n"); Loading
drivers/esoc/mdm-dbg.h +4 −2 Original line number Diff line number Diff line Loading @@ -24,7 +24,8 @@ static inline bool dbg_check_notify_mask(unsigned int notify) return false; } static inline int mdm_dbg_eng_init(struct esoc_drv *drv) static inline int mdm_dbg_eng_init(struct esoc_drv *drv, struct esoc_clink *clink) { return 0; } Loading @@ -32,7 +33,8 @@ static inline int mdm_dbg_eng_init(struct esoc_drv *drv) #else extern bool dbg_check_cmd_mask(unsigned int cmd); extern bool dbg_check_notify_mask(unsigned int notify); extern int mdm_dbg_eng_init(struct esoc_drv *drv); extern int mdm_dbg_eng_init(struct esoc_drv *drv, struct esoc_clink *clink); #endif static inline bool mdm_dbg_stall_cmd(unsigned int cmd) Loading