diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c index 4b91e3c9eafa..343374b66825 100644 --- a/drivers/remoteproc/qcom_common.c +++ b/drivers/remoteproc/qcom_common.c @@ -4,7 +4,7 @@ * * Copyright (C) 2016 Linaro Ltd * Copyright (C) 2015 Sony Mobile Communications Inc - * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2013, 2020-2021 The Linux Foundation. All rights reserved. */ #include @@ -12,20 +12,27 @@ #include #include #include -#include #include #include #include #include #include +#include +#include #include "remoteproc_internal.h" #include "qcom_common.h" +#define SSR_NOTIF_TIMEOUT CONFIG_RPROC_SSR_NOTIF_TIMEOUT + #define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev) #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev) #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev) +#define GLINK_SUBDEV_NAME "glink" +#define SMD_SUBDEV_NAME "smd" +#define SSR_SUBDEV_NAME "ssr" + #define MAX_NUM_OF_SS 10 #define MAX_REGION_NAME_LENGTH 16 #define SBL_MINIDUMP_SMEM_ID 602 @@ -84,12 +91,36 @@ struct minidump_global_toc { struct qcom_ssr_subsystem { const char *name; struct srcu_notifier_head notifier_list; + struct srcu_notifier_head early_notifier_list; struct list_head list; }; +static struct kobject *sysfs_kobject; +bool qcom_device_shutdown_in_progress; +EXPORT_SYMBOL(qcom_device_shutdown_in_progress); + static LIST_HEAD(qcom_ssr_subsystem_list); static DEFINE_MUTEX(qcom_ssr_subsys_lock); +static const char * const ssr_timeout_msg = "srcu notifier chain for %s:%s taking too long"; + +static ssize_t qcom_rproc_shutdown_request_store(struct kobject *kobj, struct kobj_attribute *attr, + const char *buf, size_t count) +{ + bool val; + int ret; + + ret = kstrtobool(buf, &val); + if (ret) + return ret; + + qcom_device_shutdown_in_progress = val; + pr_info("qcom rproc: Device shutdown requested: %s\n", val ? "true" : "false"); + return count; +} +static struct kobj_attribute shutdown_requested_attr = __ATTR(shutdown_in_progress, 0220, NULL, + qcom_rproc_shutdown_request_store); + static void qcom_minidump_cleanup(struct rproc *rproc) { struct rproc_dump_segment *entry, *tmp; @@ -101,7 +132,8 @@ static void qcom_minidump_cleanup(struct rproc *rproc) } } -static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsystem *subsystem) +static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsystem *subsystem, + rproc_dumpfn_t dumpfn) { struct minidump_region __iomem *ptr; struct minidump_region region; @@ -131,7 +163,7 @@ static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsy } da = le64_to_cpu(region.address); size = le32_to_cpu(region.size); - rproc_coredump_add_custom_segment(rproc, da, size, NULL, name); + rproc_coredump_add_custom_segment(rproc, da, size, dumpfn, name); } } @@ -139,7 +171,7 @@ static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsy return 0; } -void qcom_minidump(struct rproc *rproc, unsigned int minidump_id) +void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, rproc_dumpfn_t dumpfn) { int ret; struct minidump_subsystem *subsystem; @@ -163,35 +195,83 @@ void qcom_minidump(struct rproc *rproc, unsigned int minidump_id) */ if (subsystem->regions_baseptr == 0 || le32_to_cpu(subsystem->status) != 1 || - le32_to_cpu(subsystem->enabled) != MD_SS_ENABLED || - le32_to_cpu(subsystem->encryption_status) != MD_SS_ENCR_DONE) { + le32_to_cpu(subsystem->enabled) != MD_SS_ENABLED) { + return rproc_coredump(rproc); + } + + if (le32_to_cpu(subsystem->encryption_status) != MD_SS_ENCR_DONE) { dev_err(&rproc->dev, "Minidump not ready, skipping\n"); return; } - ret = qcom_add_minidump_segments(rproc, subsystem); + rproc_coredump_cleanup(rproc); + + ret = qcom_add_minidump_segments(rproc, subsystem, dumpfn); if (ret) { dev_err(&rproc->dev, "Failed with error: %d while adding minidump entries\n", ret); goto clean_minidump; } - rproc_coredump_using_sections(rproc); + + if (rproc->elf_class == ELFCLASS64) + rproc_coredump_using_sections(rproc); + else + rproc_coredump(rproc); clean_minidump: qcom_minidump_cleanup(rproc); } EXPORT_SYMBOL_GPL(qcom_minidump); -static int glink_subdev_start(struct rproc_subdev *subdev) +static int glink_early_ssr_notifier_event(struct notifier_block *this, + unsigned long code, void *data) +{ + struct qcom_rproc_glink *glink = container_of(this, struct qcom_rproc_glink, nb); + + qcom_glink_early_ssr_notify(glink->edge); + return NOTIFY_DONE; +} + +static int glink_subdev_prepare(struct rproc_subdev *subdev) { struct qcom_rproc_glink *glink = to_glink_subdev(subdev); + trace_rproc_qcom_event(dev_name(glink->dev->parent), GLINK_SUBDEV_NAME, "prepare"); + glink->edge = qcom_glink_smem_register(glink->dev, glink->node); return PTR_ERR_OR_ZERO(glink->edge); } +static int glink_subdev_start(struct rproc_subdev *subdev) +{ + struct qcom_rproc_glink *glink = to_glink_subdev(subdev); + + trace_rproc_qcom_event(dev_name(glink->dev->parent), GLINK_SUBDEV_NAME, "start"); + + glink->nb.notifier_call = glink_early_ssr_notifier_event; + + glink->notifier_handle = qcom_register_early_ssr_notifier(glink->ssr_name, &glink->nb); + if (IS_ERR(glink->notifier_handle)) { + dev_err(glink->dev, "Failed to register for SSR notifier\n"); + glink->notifier_handle = NULL; + } + + return qcom_glink_smem_start(glink->edge); +} + static void glink_subdev_stop(struct rproc_subdev *subdev, bool crashed) { struct qcom_rproc_glink *glink = to_glink_subdev(subdev); + int ret; + + if (!glink->edge) + return; + trace_rproc_qcom_event(dev_name(glink->dev->parent), GLINK_SUBDEV_NAME, + crashed ? "crash stop" : "stop"); + + ret = qcom_unregister_early_ssr_notifier(glink->notifier_handle, &glink->nb); + if (ret) + dev_err(glink->dev, "Error in unregistering notifier\n"); + glink->notifier_handle = NULL; qcom_glink_smem_unregister(glink->edge); glink->edge = NULL; @@ -201,6 +281,8 @@ static void glink_subdev_unprepare(struct rproc_subdev *subdev) { struct qcom_rproc_glink *glink = to_glink_subdev(subdev); + trace_rproc_qcom_event(dev_name(glink->dev->parent), GLINK_SUBDEV_NAME, "unprepare"); + qcom_glink_ssr_notify(glink->ssr_name); } @@ -225,6 +307,7 @@ void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink, glink->dev = dev; glink->subdev.start = glink_subdev_start; + glink->subdev.prepare = glink_subdev_prepare; glink->subdev.stop = glink_subdev_stop; glink->subdev.unprepare = glink_subdev_unprepare; @@ -295,6 +378,8 @@ static int smd_subdev_start(struct rproc_subdev *subdev) { struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); + trace_rproc_qcom_event(dev_name(smd->dev->parent), SMD_SUBDEV_NAME, "start"); + smd->edge = qcom_smd_register_edge(smd->dev, smd->node); return PTR_ERR_OR_ZERO(smd->edge); @@ -304,6 +389,11 @@ static void smd_subdev_stop(struct rproc_subdev *subdev, bool crashed) { struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); + if (!smd->edge) + return; + trace_rproc_qcom_event(dev_name(smd->dev->parent), SMD_SUBDEV_NAME, + crashed ? "crash stop" : "stop"); + qcom_smd_unregister_edge(smd->edge); smd->edge = NULL; } @@ -348,6 +438,9 @@ static struct qcom_ssr_subsystem *qcom_ssr_get_subsys(const char *name) { struct qcom_ssr_subsystem *info; + if (!name) + return ERR_PTR(-EINVAL); + mutex_lock(&qcom_ssr_subsys_lock); /* Match in the global qcom_ssr_subsystem_list with name */ list_for_each_entry(info, &qcom_ssr_subsystem_list, list) @@ -361,6 +454,7 @@ static struct qcom_ssr_subsystem *qcom_ssr_get_subsys(const char *name) } info->name = kstrdup_const(name, GFP_KERNEL); srcu_init_notifier_head(&info->notifier_list); + srcu_init_notifier_head(&info->early_notifier_list); /* Add to global notification list */ list_add_tail(&info->list, &qcom_ssr_subsystem_list); @@ -370,6 +464,34 @@ static struct qcom_ssr_subsystem *qcom_ssr_get_subsys(const char *name) return info; } +void *qcom_register_early_ssr_notifier(const char *name, struct notifier_block *nb) +{ + struct qcom_ssr_subsystem *info; + + info = qcom_ssr_get_subsys(name); + if (IS_ERR(info)) + return info; + + srcu_notifier_chain_register(&info->early_notifier_list, nb); + + return &info->early_notifier_list; +} +EXPORT_SYMBOL(qcom_register_early_ssr_notifier); + +int qcom_unregister_early_ssr_notifier(void *notify, struct notifier_block *nb) +{ + return srcu_notifier_chain_unregister(notify, nb); +} +EXPORT_SYMBOL(qcom_unregister_early_ssr_notifier); + +void qcom_notify_early_ssr_clients(struct rproc_subdev *subdev) +{ + struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev); + + srcu_notifier_call_chain(&ssr->info->early_notifier_list, QCOM_SSR_BEFORE_SHUTDOWN, NULL); +} +EXPORT_SYMBOL(qcom_notify_early_ssr_clients); + /** * qcom_register_ssr_notifier() - register SSR notification handler * @name: Subsystem's SSR name @@ -396,6 +518,21 @@ void *qcom_register_ssr_notifier(const char *name, struct notifier_block *nb) } EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier); +static void ssr_notif_timeout_handler(struct timer_list *t) +{ + struct qcom_rproc_ssr *ssr = from_timer(ssr, t, timer); + + if (IS_ENABLED(CONFIG_QCOM_PANIC_ON_NOTIF_TIMEOUT) && + system_state != SYSTEM_RESTART && + system_state != SYSTEM_POWER_OFF && + system_state != SYSTEM_HALT && + !qcom_device_shutdown_in_progress) + panic(ssr_timeout_msg, ssr->info->name, subdevice_state_string[ssr->notification]); + else + WARN(1, ssr_timeout_msg, ssr->info->name, + subdevice_state_string[ssr->notification]); +} + /** * qcom_unregister_ssr_notifier() - unregister SSR notification handler * @notify: subsystem cookie returned from qcom_register_ssr_notifier @@ -412,6 +549,16 @@ int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb) } EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier); +static inline void notify_ssr_clients(struct qcom_rproc_ssr *ssr, struct qcom_ssr_notify_data *data) +{ + unsigned long timeout; + + timeout = jiffies + msecs_to_jiffies(SSR_NOTIF_TIMEOUT); + mod_timer(&ssr->timer, timeout); + srcu_notifier_call_chain(&ssr->info->notifier_list, ssr->notification, data); + del_timer_sync(&ssr->timer); +} + static int ssr_notify_prepare(struct rproc_subdev *subdev) { struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev); @@ -420,8 +567,10 @@ static int ssr_notify_prepare(struct rproc_subdev *subdev) .crashed = false, }; - srcu_notifier_call_chain(&ssr->info->notifier_list, - QCOM_SSR_BEFORE_POWERUP, &data); + trace_rproc_qcom_event(ssr->info->name, SSR_SUBDEV_NAME, "prepare"); + + ssr->notification = QCOM_SSR_BEFORE_POWERUP; + notify_ssr_clients(ssr, &data); return 0; } @@ -433,8 +582,10 @@ static int ssr_notify_start(struct rproc_subdev *subdev) .crashed = false, }; - srcu_notifier_call_chain(&ssr->info->notifier_list, - QCOM_SSR_AFTER_POWERUP, &data); + trace_rproc_qcom_event(ssr->info->name, SSR_SUBDEV_NAME, "start"); + + ssr->notification = QCOM_SSR_AFTER_POWERUP; + notify_ssr_clients(ssr, &data); return 0; } @@ -446,8 +597,10 @@ static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed) .crashed = crashed, }; - srcu_notifier_call_chain(&ssr->info->notifier_list, - QCOM_SSR_BEFORE_SHUTDOWN, &data); + trace_rproc_qcom_event(ssr->info->name, SSR_SUBDEV_NAME, crashed ? "crash stop" : "stop"); + + ssr->notification = QCOM_SSR_BEFORE_SHUTDOWN; + notify_ssr_clients(ssr, &data); } static void ssr_notify_unprepare(struct rproc_subdev *subdev) @@ -458,8 +611,10 @@ static void ssr_notify_unprepare(struct rproc_subdev *subdev) .crashed = false, }; - srcu_notifier_call_chain(&ssr->info->notifier_list, - QCOM_SSR_AFTER_SHUTDOWN, &data); + trace_rproc_qcom_event(ssr->info->name, SSR_SUBDEV_NAME, "unprepare"); + + ssr->notification = QCOM_SSR_AFTER_SHUTDOWN; + notify_ssr_clients(ssr, &data); } /** @@ -483,6 +638,8 @@ void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, return; } + timer_setup(&ssr->timer, ssr_notif_timeout_handler, 0); + ssr->info = info; ssr->subdev.prepare = ssr_notify_prepare; ssr->subdev.start = ssr_notify_start; @@ -505,5 +662,78 @@ void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr) } EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev); +static void qcom_check_ssr_status(void *data, struct rproc *rproc) +{ + if (!atomic_read(&rproc->power) || + rproc->state == RPROC_RUNNING || + qcom_device_shutdown_in_progress || + system_state == SYSTEM_RESTART || + system_state == SYSTEM_POWER_OFF || + system_state == SYSTEM_HALT) + return; + + panic("Panicking, remoteproc %s failed to recover!\n", rproc->name); +} + +static void rproc_recovery_notifier(void *data, struct rproc *rproc) +{ + const char *recovery = rproc->recovery_disabled ? "disabled" : "enabled"; + + trace_rproc_qcom_event(rproc->name, "recovery", recovery); + pr_info("qcom rproc: %s: recovery %s\n", rproc->name, recovery); +} + +static int __init qcom_common_init(void) +{ + int ret = 0; + + qcom_device_shutdown_in_progress = false; + + sysfs_kobject = kobject_create_and_add("qcom_rproc", kernel_kobj); + if (!sysfs_kobject) { + pr_err("qcom rproc: failed to create sysfs kobject\n"); + return -EINVAL; + } + + ret = sysfs_create_file(sysfs_kobject, &shutdown_requested_attr.attr); + if (ret) { + pr_err("qcom rproc: failed to create sysfs file\n"); + goto remove_kobject; + } + + ret = register_trace_android_vh_rproc_recovery(qcom_check_ssr_status, NULL); + if (ret) { + pr_err("qcom rproc: failed to register trace hooks\n"); + goto remove_sysfs; + } + + ret = register_trace_android_vh_rproc_recovery_set(rproc_recovery_notifier, NULL); + if (ret) { + pr_err("qcom rproc: failed to register recovery_set vendor hook\n"); + goto unregister_rproc_recovery_vh; + } + + return 0; + +unregister_rproc_recovery_vh: + unregister_trace_android_vh_rproc_recovery(qcom_check_ssr_status, NULL); +remove_sysfs: + sysfs_remove_file(sysfs_kobject, &shutdown_requested_attr.attr); +remove_kobject: + kobject_put(sysfs_kobject); + return ret; + +} +module_init(qcom_common_init); + +static void __exit qcom_common_exit(void) +{ + unregister_trace_android_vh_rproc_recovery_set(rproc_recovery_notifier, NULL); + sysfs_remove_file(sysfs_kobject, &shutdown_requested_attr.attr); + kobject_put(sysfs_kobject); + unregister_trace_android_vh_rproc_recovery(qcom_check_ssr_status, NULL); +} +module_exit(qcom_common_exit); + MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h index c35adf730be0..af55811b6fc8 100644 --- a/drivers/remoteproc/qcom_common.h +++ b/drivers/remoteproc/qcom_common.h @@ -1,10 +1,28 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ #ifndef __RPROC_QCOM_COMMON_H__ #define __RPROC_QCOM_COMMON_H__ +#include #include #include "remoteproc_internal.h" #include +#include + +static const char * const subdevice_state_string[] = { + [QCOM_SSR_BEFORE_POWERUP] = "before_powerup", + [QCOM_SSR_AFTER_POWERUP] = "after_powerup", + [QCOM_SSR_BEFORE_SHUTDOWN] = "before_shutdown", + [QCOM_SSR_AFTER_SHUTDOWN] = "after_shutdown", +}; + +struct reg_info { + struct regulator *reg; + int uV; + int uA; +}; struct qcom_sysmon; @@ -16,6 +34,9 @@ struct qcom_rproc_glink { struct device *dev; struct device_node *node; struct qcom_glink *edge; + + struct notifier_block nb; + void *notifier_handle; }; struct qcom_rproc_subdev { @@ -30,10 +51,17 @@ struct qcom_ssr_subsystem; struct qcom_rproc_ssr { struct rproc_subdev subdev; + enum qcom_ssr_notify_type notification; + struct timer_list timer; struct qcom_ssr_subsystem *info; }; -void qcom_minidump(struct rproc *rproc, unsigned int minidump_id); +extern bool qcom_device_shutdown_in_progress; + +typedef void (*rproc_dumpfn_t)(struct rproc *rproc, struct rproc_dump_segment *segment, + void *dest, size_t offset, size_t size); + +void qcom_minidump(struct rproc *rproc, unsigned int minidump_id, rproc_dumpfn_t dumpfn); void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink, const char *ssr_name); @@ -46,6 +74,7 @@ void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr, const char *ssr_name); +void qcom_notify_early_ssr_clients(struct rproc_subdev *subdev); void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr); #if IS_ENABLED(CONFIG_QCOM_SYSMON) @@ -54,6 +83,8 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, int ssctl_instance); void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon); bool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon); +uint32_t qcom_sysmon_get_txn_id(struct qcom_sysmon *sysmon); +int qcom_sysmon_get_reason(struct qcom_sysmon *sysmon, char *buf, size_t len); #else static inline struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, const char *name, @@ -70,6 +101,16 @@ static inline bool qcom_sysmon_shutdown_acked(struct qcom_sysmon *sysmon) { return false; } + +static inline uint32_t qcom_sysmon_get_txn_id(struct qcom_sysmon *sysmon) +{ + return 0; +} + +int qcom_sysmon_get_reason(struct qcom_sysmon *sysmon, char *buf, size_t len) +{ + return -ENODEV; +} #endif #endif diff --git a/drivers/remoteproc/qcom_pil_info.c b/drivers/remoteproc/qcom_pil_info.c index aca21560e20b..b1dc11bd3e6c 100644 --- a/drivers/remoteproc/qcom_pil_info.c +++ b/drivers/remoteproc/qcom_pil_info.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2019-2020 Linaro Ltd. + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */ #include #include @@ -26,6 +27,53 @@ struct pil_reloc { static struct pil_reloc _reloc __read_mostly; static DEFINE_MUTEX(pil_reloc_lock); +static bool timeouts_disabled; + +/** + * qcom_pil_timeouts_disabled() - Check if pil timeouts are disabled in imem + * + * Return: true if the value 0x53444247 is set in the disable timeout pil + * imem region, false otherwise. + */ +bool qcom_pil_timeouts_disabled(void) +{ + struct device_node *np; + struct resource imem; + void __iomem *base; + int ret; + const char *prop = "qcom,msm-imem-pil-disable-timeout"; + + np = of_find_compatible_node(NULL, NULL, prop); + if (!np) { + pr_err("%s entry missing!\n", prop); + goto out; + } + + ret = of_address_to_resource(np, 0, &imem); + of_node_put(np); + if (ret < 0) { + pr_err("address to resource conversion failed for %s\n", prop); + goto out; + } + + base = ioremap(imem.start, resource_size(&imem)); + if (!base) { + pr_err("failed to map PIL disable timeouts region\n"); + goto out; + } + + if (__raw_readl(base) == 0x53444247) { + pr_info("pil-imem set to disable pil timeouts\n"); + timeouts_disabled = true; + } else + timeouts_disabled = false; + + iounmap(base); + +out: + return timeouts_disabled; +} +EXPORT_SYMBOL(qcom_pil_timeouts_disabled); static int qcom_pil_info_init(void) { @@ -73,6 +121,7 @@ int qcom_pil_info_store(const char *image, phys_addr_t base, size_t size) { char buf[PIL_RELOC_NAME_LEN]; void __iomem *entry; + size_t entry_size; int ret; int i; @@ -104,7 +153,8 @@ int qcom_pil_info_store(const char *image, phys_addr_t base, size_t size) return -ENOMEM; found_unused: - memcpy_toio(entry, image, strnlen(image, PIL_RELOC_NAME_LEN)); + entry_size = min(strlen(image), PIL_RELOC_ENTRY_SIZE - 1); + memcpy_toio(entry, image, entry_size); found_existing: /* Use two writel() as base is only aligned to 4 bytes on odd entries */ writel(base, entry + PIL_RELOC_NAME_LEN); diff --git a/drivers/remoteproc/qcom_pil_info.h b/drivers/remoteproc/qcom_pil_info.h index 0dce6142935e..b433c01821ec 100644 --- a/drivers/remoteproc/qcom_pil_info.h +++ b/drivers/remoteproc/qcom_pil_info.h @@ -1,9 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ #ifndef __QCOM_PIL_INFO_H__ #define __QCOM_PIL_INFO_H__ #include int qcom_pil_info_store(const char *image, phys_addr_t base, size_t size); +bool qcom_pil_timeouts_disabled(void); #endif diff --git a/drivers/remoteproc/qcom_q6v5.c b/drivers/remoteproc/qcom_q6v5.c index 5280ec9b5449..004a51aaa789 100644 --- a/drivers/remoteproc/qcom_q6v5.c +++ b/drivers/remoteproc/qcom_q6v5.c @@ -4,44 +4,22 @@ * * Copyright (C) 2016-2018 Linaro Ltd. * Copyright (C) 2014 Sony Mobile Communications AB - * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2013, 2020-2021, The Linux Foundation. All rights reserved. */ #include #include -#include #include #include -#include #include #include #include +#include #include "qcom_common.h" #include "qcom_q6v5.h" +#include -#define Q6V5_LOAD_STATE_MSG_LEN 64 #define Q6V5_PANIC_DELAY_MS 200 -static int q6v5_load_state_toggle(struct qcom_q6v5 *q6v5, bool enable) -{ - char buf[Q6V5_LOAD_STATE_MSG_LEN]; - int ret; - - if (!q6v5->qmp) - return 0; - - ret = snprintf(buf, sizeof(buf), - "{class: image, res: load_state, name: %s, val: %s}", - q6v5->load_state, enable ? "on" : "off"); - - WARN_ON(ret >= Q6V5_LOAD_STATE_MSG_LEN); - - ret = qmp_send(q6v5->qmp, buf, sizeof(buf)); - if (ret) - dev_err(q6v5->dev, "failed to toggle load state\n"); - - return ret; -} - /** * qcom_q6v5_prepare() - reinitialize the qcom_q6v5 context before start * @q6v5: reference to qcom_q6v5 context to be reinitialized @@ -50,20 +28,6 @@ static int q6v5_load_state_toggle(struct qcom_q6v5 *q6v5, bool enable) */ int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5) { - int ret; - - ret = icc_set_bw(q6v5->path, 0, UINT_MAX); - if (ret < 0) { - dev_err(q6v5->dev, "failed to set bandwidth request\n"); - return ret; - } - - ret = q6v5_load_state_toggle(q6v5, true); - if (ret) { - icc_set_bw(q6v5->path, 0, 0); - return ret; - } - reinit_completion(&q6v5->start_done); reinit_completion(&q6v5->stop_done); @@ -85,15 +49,50 @@ EXPORT_SYMBOL_GPL(qcom_q6v5_prepare); int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5) { disable_irq(q6v5->handover_irq); - q6v5_load_state_toggle(q6v5, false); - - /* Disable interconnect vote, in case handover never happened */ - icc_set_bw(q6v5->path, 0, 0); return !q6v5->handover_issued; } EXPORT_SYMBOL_GPL(qcom_q6v5_unprepare); +void qcom_q6v5_register_ssr_subdev(struct qcom_q6v5 *q6v5, struct rproc_subdev *ssr_subdev) +{ + q6v5->ssr_subdev = ssr_subdev; +} +EXPORT_SYMBOL(qcom_q6v5_register_ssr_subdev); + +static void qcom_q6v5_crash_handler_work(struct work_struct *work) +{ + struct qcom_q6v5 *q6v5 = container_of(work, struct qcom_q6v5, crash_handler); + struct rproc *rproc = q6v5->rproc; + struct rproc_subdev *subdev; + int votes; + + mutex_lock(&rproc->lock); + + rproc->state = RPROC_CRASHED; + + votes = atomic_xchg(&rproc->power, 0); + /* if votes are zero, rproc has already been shutdown */ + if (votes == 0) { + mutex_unlock(&rproc->lock); + return; + } + + list_for_each_entry_reverse(subdev, &rproc->subdevs, node) { + if (subdev->stop) + subdev->stop(subdev, true); + } + + mutex_unlock(&rproc->lock); + + /* + * Temporary workaround until ramdump userspace application calls + * sync() and fclose() on attempting the dump. + */ + msleep(100); + panic("Panicking, remoteproc %s crashed\n", q6v5->rproc->name); +} + static irqreturn_t q6v5_wdog_interrupt(int irq, void *data) { struct qcom_q6v5 *q6v5 = data; @@ -102,6 +101,7 @@ static irqreturn_t q6v5_wdog_interrupt(int irq, void *data) /* Sometimes the stop triggers a watchdog rather than a stop-ack */ if (!q6v5->running) { + dev_info(q6v5->dev, "received wdog irq while q6 is offline\n"); complete(&q6v5->stop_done); return IRQ_HANDLED; } @@ -112,7 +112,21 @@ static irqreturn_t q6v5_wdog_interrupt(int irq, void *data) else dev_err(q6v5->dev, "watchdog without message\n"); - rproc_report_crash(q6v5->rproc, RPROC_WATCHDOG); + q6v5->running = false; + trace_rproc_qcom_event(dev_name(q6v5->dev), "q6v5_wdog", msg); + dev_err(q6v5->dev, "rproc recovery state: %s\n", + q6v5->rproc->recovery_disabled ? + "disabled and lead to device crash" : + "enabled and kick reovery process"); + + if (q6v5->rproc->recovery_disabled) { + schedule_work(&q6v5->crash_handler); + } else { + if (q6v5->ssr_subdev) + qcom_notify_early_ssr_clients(q6v5->ssr_subdev); + + rproc_report_crash(q6v5->rproc, RPROC_WATCHDOG); + } return IRQ_HANDLED; } @@ -123,6 +137,11 @@ static irqreturn_t q6v5_fatal_interrupt(int irq, void *data) size_t len; char *msg; + if (!q6v5->running) { + dev_info(q6v5->dev, "received fatal irq while q6 is offline\n"); + return IRQ_HANDLED; + } + msg = qcom_smem_get(QCOM_SMEM_HOST_ANY, q6v5->crash_reason, &len); if (!IS_ERR(msg) && len > 0 && msg[0]) dev_err(q6v5->dev, "fatal error received: %s\n", msg); @@ -130,7 +149,18 @@ static irqreturn_t q6v5_fatal_interrupt(int irq, void *data) dev_err(q6v5->dev, "fatal error without message\n"); q6v5->running = false; - rproc_report_crash(q6v5->rproc, RPROC_FATAL_ERROR); + trace_rproc_qcom_event(dev_name(q6v5->dev), "q6v5_fatal", msg); + dev_err(q6v5->dev, "rproc recovery state: %s\n", + q6v5->rproc->recovery_disabled ? "disabled and lead to device crash" : + "enabled and kick reovery process"); + if (q6v5->rproc->recovery_disabled) { + schedule_work(&q6v5->crash_handler); + } else { + if (q6v5->ssr_subdev) + qcom_notify_early_ssr_clients(q6v5->ssr_subdev); + + rproc_report_crash(q6v5->rproc, RPROC_FATAL_ERROR); + } return IRQ_HANDLED; } @@ -172,8 +202,6 @@ static irqreturn_t q6v5_handover_interrupt(int irq, void *data) if (q6v5->handover) q6v5->handover(q6v5); - icc_set_bw(q6v5->path, 0, 0); - q6v5->handover_issued = true; return IRQ_HANDLED; @@ -201,8 +229,10 @@ int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon) q6v5->running = false; - /* Don't perform SMP2P dance if sysmon already shut down the remote */ - if (qcom_sysmon_shutdown_acked(sysmon)) + /* Don't perform SMP2P dance if sysmon already shut + * down the remote or if it isn't running + */ + if (q6v5->rproc->state != RPROC_RUNNING || qcom_sysmon_shutdown_acked(sysmon)) return 0; qcom_smem_state_update_bits(q6v5->state, @@ -240,32 +270,42 @@ EXPORT_SYMBOL_GPL(qcom_q6v5_panic); * @pdev: platform_device reference for acquiring resources * @rproc: associated remoteproc instance * @crash_reason: SMEM id for crash reason string, or 0 if none - * @load_state: load state resource string * @handover: function to be called when proxy resources should be released * * Return: 0 on success, negative errno on failure */ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, - struct rproc *rproc, int crash_reason, const char *load_state, + struct rproc *rproc, int crash_reason, void (*handover)(struct qcom_q6v5 *q6v5)) { int ret; + struct resource *res; q6v5->rproc = rproc; q6v5->dev = &pdev->dev; q6v5->crash_reason = crash_reason; q6v5->handover = handover; + q6v5->ssr_subdev = NULL; init_completion(&q6v5->start_done); init_completion(&q6v5->stop_done); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (res) { + q6v5->rmb_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(q6v5->rmb_base)) + q6v5->rmb_base = NULL; + } else + q6v5->rmb_base = NULL; + + q6v5->wdog_irq = platform_get_irq_byname(pdev, "wdog"); if (q6v5->wdog_irq < 0) return q6v5->wdog_irq; ret = devm_request_threaded_irq(&pdev->dev, q6v5->wdog_irq, NULL, q6v5_wdog_interrupt, - IRQF_TRIGGER_RISING | IRQF_ONESHOT, + IRQF_ONESHOT, "q6v5 wdog", q6v5); if (ret) { dev_err(&pdev->dev, "failed to acquire wdog IRQ\n"); @@ -325,45 +365,17 @@ int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, return ret; } - q6v5->state = devm_qcom_smem_state_get(&pdev->dev, "stop", &q6v5->stop_bit); + q6v5->state = qcom_smem_state_get(&pdev->dev, "stop", &q6v5->stop_bit); if (IS_ERR(q6v5->state)) { dev_err(&pdev->dev, "failed to acquire stop state\n"); return PTR_ERR(q6v5->state); } - q6v5->load_state = devm_kstrdup_const(&pdev->dev, load_state, GFP_KERNEL); - q6v5->qmp = qmp_get(&pdev->dev); - if (IS_ERR(q6v5->qmp)) { - if (PTR_ERR(q6v5->qmp) != -ENODEV) - return dev_err_probe(&pdev->dev, PTR_ERR(q6v5->qmp), - "failed to acquire load state\n"); - q6v5->qmp = NULL; - } else if (!q6v5->load_state) { - if (!load_state) - dev_err(&pdev->dev, "load state resource string empty\n"); - - qmp_put(q6v5->qmp); - return load_state ? -ENOMEM : -EINVAL; - } - - q6v5->path = devm_of_icc_get(&pdev->dev, NULL); - if (IS_ERR(q6v5->path)) - return dev_err_probe(&pdev->dev, PTR_ERR(q6v5->path), - "failed to acquire interconnect path\n"); + INIT_WORK(&q6v5->crash_handler, qcom_q6v5_crash_handler_work); return 0; } EXPORT_SYMBOL_GPL(qcom_q6v5_init); -/** - * qcom_q6v5_deinit() - deinitialize the q6v5 common struct - * @q6v5: reference to qcom_q6v5 context to be deinitialized - */ -void qcom_q6v5_deinit(struct qcom_q6v5 *q6v5) -{ - qmp_put(q6v5->qmp); -} -EXPORT_SYMBOL_GPL(qcom_q6v5_deinit); - MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Qualcomm Peripheral Image Loader for Q6V5"); diff --git a/drivers/remoteproc/qcom_q6v5.h b/drivers/remoteproc/qcom_q6v5.h index 5a859c41896e..709f8fc8256f 100644 --- a/drivers/remoteproc/qcom_q6v5.h +++ b/drivers/remoteproc/qcom_q6v5.h @@ -1,13 +1,20 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ #ifndef __QCOM_Q6V5_H__ #define __QCOM_Q6V5_H__ #include #include -#include -struct icc_path; +#define RMB_BOOT_WAIT_REG 0x8 +#define RMB_BOOT_CONT_REG 0xC +#define RMB_Q6_BOOT_STATUS_REG 0x10 + +#define RMB_POLL_MAX_TIMES 250 + struct rproc; struct qcom_smem_state; struct qcom_sysmon; @@ -16,11 +23,9 @@ struct qcom_q6v5 { struct device *dev; struct rproc *rproc; + void __iomem *rmb_base; + struct qcom_smem_state *state; - struct qmp *qmp; - - struct icc_path *path; - unsigned stop_bit; int wdog_irq; @@ -29,6 +34,10 @@ struct qcom_q6v5 { int handover_irq; int stop_irq; + struct rproc_subdev *ssr_subdev; + + struct work_struct crash_handler; + bool handover_issued; struct completion start_done; @@ -38,15 +47,13 @@ struct qcom_q6v5 { bool running; - const char *load_state; void (*handover)(struct qcom_q6v5 *q6v5); }; int qcom_q6v5_init(struct qcom_q6v5 *q6v5, struct platform_device *pdev, - struct rproc *rproc, int crash_reason, const char *load_state, + struct rproc *rproc, int crash_reason, void (*handover)(struct qcom_q6v5 *q6v5)); -void qcom_q6v5_deinit(struct qcom_q6v5 *q6v5); - +void qcom_q6v5_register_ssr_subdev(struct qcom_q6v5 *q6v5, struct rproc_subdev *ssr_subdev); int qcom_q6v5_prepare(struct qcom_q6v5 *q6v5); int qcom_q6v5_unprepare(struct qcom_q6v5 *q6v5); int qcom_q6v5_request_stop(struct qcom_q6v5 *q6v5, struct qcom_sysmon *sysmon); diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c b/drivers/remoteproc/qcom_q6v5_adsp.c index 2f3b9f54251e..c10d452c9703 100644 --- a/drivers/remoteproc/qcom_q6v5_adsp.c +++ b/drivers/remoteproc/qcom_q6v5_adsp.c @@ -32,7 +32,6 @@ /* time out value */ #define ACK_TIMEOUT 1000 -#define ACK_TIMEOUT_US 1000000 #define BOOT_FSM_TIMEOUT 10000 /* mask values */ #define EVB_MASK GENMASK(27, 4) @@ -52,8 +51,6 @@ #define QDSP6SS_CORE_CBCR 0x20 #define QDSP6SS_SLEEP_CBCR 0x3c -#define QCOM_Q6V5_RPROC_PROXY_PD_MAX 3 - struct adsp_pil_data { int crash_reason_smem; const char *firmware_name; @@ -61,13 +58,9 @@ struct adsp_pil_data { const char *ssr_name; const char *sysmon_name; int ssctl_id; - bool is_wpss; - bool auto_boot; const char **clk_ids; int num_clks; - const char **proxy_pd_names; - const char *load_state; }; struct qcom_adsp { @@ -100,151 +93,11 @@ struct qcom_adsp { void *mem_region; size_t mem_size; - struct device *proxy_pds[QCOM_Q6V5_RPROC_PROXY_PD_MAX]; - size_t proxy_pd_count; - struct qcom_rproc_glink glink_subdev; struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; - - int (*shutdown)(struct qcom_adsp *adsp); }; -static int qcom_rproc_pds_attach(struct device *dev, struct qcom_adsp *adsp, - const char **pd_names) -{ - struct device **devs = adsp->proxy_pds; - size_t num_pds = 0; - int ret; - int i; - - if (!pd_names) - return 0; - - /* Handle single power domain */ - if (dev->pm_domain) { - devs[0] = dev; - pm_runtime_enable(dev); - return 1; - } - - while (pd_names[num_pds]) - num_pds++; - - if (num_pds > ARRAY_SIZE(adsp->proxy_pds)) - return -E2BIG; - - for (i = 0; i < num_pds; i++) { - devs[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]); - if (IS_ERR_OR_NULL(devs[i])) { - ret = PTR_ERR(devs[i]) ? : -ENODATA; - goto unroll_attach; - } - } - - return num_pds; - -unroll_attach: - for (i--; i >= 0; i--) - dev_pm_domain_detach(devs[i], false); - - return ret; -} - -static void qcom_rproc_pds_detach(struct qcom_adsp *adsp, struct device **pds, - size_t pd_count) -{ - struct device *dev = adsp->dev; - int i; - - /* Handle single power domain */ - if (dev->pm_domain && pd_count) { - pm_runtime_disable(dev); - return; - } - - for (i = 0; i < pd_count; i++) - dev_pm_domain_detach(pds[i], false); -} - -static int qcom_rproc_pds_enable(struct qcom_adsp *adsp, struct device **pds, - size_t pd_count) -{ - int ret; - int i; - - for (i = 0; i < pd_count; i++) { - dev_pm_genpd_set_performance_state(pds[i], INT_MAX); - ret = pm_runtime_get_sync(pds[i]); - if (ret < 0) { - pm_runtime_put_noidle(pds[i]); - dev_pm_genpd_set_performance_state(pds[i], 0); - goto unroll_pd_votes; - } - } - - return 0; - -unroll_pd_votes: - for (i--; i >= 0; i--) { - dev_pm_genpd_set_performance_state(pds[i], 0); - pm_runtime_put(pds[i]); - } - - return ret; -} - -static void qcom_rproc_pds_disable(struct qcom_adsp *adsp, struct device **pds, - size_t pd_count) -{ - int i; - - for (i = 0; i < pd_count; i++) { - dev_pm_genpd_set_performance_state(pds[i], 0); - pm_runtime_put(pds[i]); - } -} - -static int qcom_wpss_shutdown(struct qcom_adsp *adsp) -{ - unsigned int val; - - regmap_write(adsp->halt_map, adsp->halt_lpass + LPASS_HALTREQ_REG, 1); - - /* Wait for halt ACK from QDSP6 */ - regmap_read_poll_timeout(adsp->halt_map, - adsp->halt_lpass + LPASS_HALTACK_REG, val, - val, 1000, ACK_TIMEOUT_US); - - /* Assert the WPSS PDC Reset */ - reset_control_assert(adsp->pdc_sync_reset); - - /* Place the WPSS processor into reset */ - reset_control_assert(adsp->restart); - - /* wait after asserting subsystem restart from AOSS */ - usleep_range(200, 205); - - /* Remove the WPSS reset */ - reset_control_deassert(adsp->restart); - - /* De-assert the WPSS PDC Reset */ - reset_control_deassert(adsp->pdc_sync_reset); - - usleep_range(100, 105); - - clk_bulk_disable_unprepare(adsp->num_clks, adsp->clks); - - regmap_write(adsp->halt_map, adsp->halt_lpass + LPASS_HALTREQ_REG, 0); - - /* Wait for halt ACK from QDSP6 */ - regmap_read_poll_timeout(adsp->halt_map, - adsp->halt_lpass + LPASS_HALTACK_REG, val, - !val, 1000, ACK_TIMEOUT_US); - - return 0; -} - static int qcom_adsp_shutdown(struct qcom_adsp *adsp) { unsigned long timeout; @@ -332,18 +185,18 @@ static int adsp_start(struct rproc *rproc) int ret; unsigned int val; - ret = qcom_q6v5_prepare(&adsp->q6v5); - if (ret) - return ret; + qcom_q6v5_prepare(&adsp->q6v5); ret = clk_prepare_enable(adsp->xo); if (ret) goto disable_irqs; - ret = qcom_rproc_pds_enable(adsp, adsp->proxy_pds, - adsp->proxy_pd_count); - if (ret < 0) + dev_pm_genpd_set_performance_state(adsp->dev, INT_MAX); + ret = pm_runtime_get_sync(adsp->dev); + if (ret) { + pm_runtime_put_noidle(adsp->dev); goto disable_xo_clk; + } ret = clk_bulk_prepare_enable(adsp->num_clks, adsp->clks); if (ret) { @@ -388,7 +241,8 @@ static int adsp_start(struct rproc *rproc) disable_adsp_clks: clk_bulk_disable_unprepare(adsp->num_clks, adsp->clks); disable_power_domain: - qcom_rproc_pds_disable(adsp, adsp->proxy_pds, adsp->proxy_pd_count); + dev_pm_genpd_set_performance_state(adsp->dev, 0); + pm_runtime_put(adsp->dev); disable_xo_clk: clk_disable_unprepare(adsp->xo); disable_irqs: @@ -402,7 +256,8 @@ static void qcom_adsp_pil_handover(struct qcom_q6v5 *q6v5) struct qcom_adsp *adsp = container_of(q6v5, struct qcom_adsp, q6v5); clk_disable_unprepare(adsp->xo); - qcom_rproc_pds_disable(adsp, adsp->proxy_pds, adsp->proxy_pd_count); + dev_pm_genpd_set_performance_state(adsp->dev, 0); + pm_runtime_put(adsp->dev); } static int adsp_stop(struct rproc *rproc) @@ -415,7 +270,7 @@ static int adsp_stop(struct rproc *rproc) if (ret == -ETIMEDOUT) dev_err(adsp->dev, "timed out on wait\n"); - ret = adsp->shutdown(adsp); + ret = qcom_adsp_shutdown(adsp); if (ret) dev_err(adsp->dev, "failed to shutdown: %d\n", ret); @@ -571,7 +426,6 @@ static int adsp_alloc_memory_region(struct qcom_adsp *adsp) static int adsp_probe(struct platform_device *pdev) { const struct adsp_pil_data *desc; - const char *firmware_name; struct qcom_adsp *adsp; struct rproc *rproc; int ret; @@ -580,22 +434,12 @@ static int adsp_probe(struct platform_device *pdev) if (!desc) return -EINVAL; - firmware_name = desc->firmware_name; - ret = of_property_read_string(pdev->dev.of_node, "firmware-name", - &firmware_name); - if (ret < 0 && ret != -EINVAL) { - dev_err(&pdev->dev, "unable to read firmware-name\n"); - return ret; - } - rproc = rproc_alloc(&pdev->dev, pdev->name, &adsp_ops, - firmware_name, sizeof(*adsp)); + desc->firmware_name, sizeof(*adsp)); if (!rproc) { dev_err(&pdev->dev, "unable to allocate remoteproc\n"); return -ENOMEM; } - - rproc->auto_boot = desc->auto_boot; rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); adsp = (struct qcom_adsp *)rproc->priv; @@ -604,11 +448,6 @@ static int adsp_probe(struct platform_device *pdev) adsp->info_name = desc->sysmon_name; platform_set_drvdata(pdev, adsp); - if (desc->is_wpss) - adsp->shutdown = qcom_wpss_shutdown; - else - adsp->shutdown = qcom_adsp_shutdown; - ret = adsp_alloc_memory_region(adsp); if (ret) goto free_rproc; @@ -617,13 +456,7 @@ static int adsp_probe(struct platform_device *pdev) if (ret) goto free_rproc; - ret = qcom_rproc_pds_attach(adsp->dev, adsp, - desc->proxy_pd_names); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to attach proxy power domains\n"); - goto free_rproc; - } - adsp->proxy_pd_count = ret; + pm_runtime_enable(adsp->dev); ret = adsp_init_reset(adsp); if (ret) @@ -634,7 +467,7 @@ static int adsp_probe(struct platform_device *pdev) goto disable_pm; ret = qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem, - desc->load_state, qcom_adsp_pil_handover); + qcom_adsp_pil_handover); if (ret) goto disable_pm; @@ -655,8 +488,7 @@ static int adsp_probe(struct platform_device *pdev) return 0; disable_pm: - qcom_rproc_pds_detach(adsp, adsp->proxy_pds, adsp->proxy_pd_count); - + pm_runtime_disable(adsp->dev); free_rproc: rproc_free(rproc); @@ -669,11 +501,10 @@ static int adsp_remove(struct platform_device *pdev) rproc_del(adsp->rproc); - qcom_q6v5_deinit(&adsp->q6v5); qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); qcom_remove_sysmon_subdev(adsp->sysmon); qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev); - qcom_rproc_pds_detach(adsp, adsp->proxy_pds, adsp->proxy_pd_count); + pm_runtime_disable(adsp->dev); rproc_free(adsp->rproc); return 0; @@ -685,16 +516,11 @@ static const struct adsp_pil_data adsp_resource_init = { .ssr_name = "lpass", .sysmon_name = "adsp", .ssctl_id = 0x14, - .is_wpss = false, - .auto_boot = true, .clk_ids = (const char*[]) { "sway_cbcr", "lpass_ahbs_aon_cbcr", "lpass_ahbm_aon_cbcr", "qdsp6ss_xo", "qdsp6ss_sleep", "qdsp6ss_core", NULL }, .num_clks = 7, - .proxy_pd_names = (const char*[]) { - "cx", NULL - }, }; static const struct adsp_pil_data cdsp_resource_init = { @@ -703,39 +529,15 @@ static const struct adsp_pil_data cdsp_resource_init = { .ssr_name = "cdsp", .sysmon_name = "cdsp", .ssctl_id = 0x17, - .is_wpss = false, - .auto_boot = true, .clk_ids = (const char*[]) { "sway", "tbu", "bimc", "ahb_aon", "q6ss_slave", "q6ss_master", "q6_axim", NULL }, .num_clks = 7, - .proxy_pd_names = (const char*[]) { - "cx", NULL - }, -}; - -static const struct adsp_pil_data wpss_resource_init = { - .crash_reason_smem = 626, - .firmware_name = "wpss.mdt", - .ssr_name = "wpss", - .sysmon_name = "wpss", - .ssctl_id = 0x19, - .is_wpss = true, - .auto_boot = false, - .load_state = "wpss", - .clk_ids = (const char*[]) { - "ahb_bdg", "ahb", "rscp", NULL - }, - .num_clks = 3, - .proxy_pd_names = (const char*[]) { - "cx", "mx", NULL - }, }; static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,qcs404-cdsp-pil", .data = &cdsp_resource_init }, - { .compatible = "qcom,sc7280-wpss-pil", .data = &wpss_resource_init }, { .compatible = "qcom,sdm845-adsp-pil", .data = &adsp_resource_init }, { }, }; diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index af217de75e4d..093e7837c24a 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -4,7 +4,7 @@ * * Copyright (C) 2016 Linaro Ltd. * Copyright (C) 2014 Sony Mobile Communications AB - * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2013, 2020-2021 The Linux Foundation. All rights reserved. */ #include @@ -77,14 +77,6 @@ #define HALT_ACK_TIMEOUT_US 100000 -/* QACCEPT Register Offsets */ -#define QACCEPT_ACCEPT_REG 0x0 -#define QACCEPT_ACTIVE_REG 0x4 -#define QACCEPT_DENY_REG 0x8 -#define QACCEPT_REQ_REG 0xC - -#define QACCEPT_TIMEOUT_US 50 - /* QDSP6SS_RESET */ #define Q6SS_STOP_CORE BIT(0) #define Q6SS_CORE_ARES BIT(1) @@ -125,12 +117,6 @@ #define QDSP6SS_BOOT_CMD 0x404 #define BOOT_FSM_TIMEOUT 10000 -struct reg_info { - struct regulator *reg; - int uV; - int uA; -}; - struct qcom_mss_reg_res { const char *supply; int uV; @@ -140,20 +126,17 @@ struct qcom_mss_reg_res { struct rproc_hexagon_res { const char *hexagon_mba_image; struct qcom_mss_reg_res *proxy_supply; - struct qcom_mss_reg_res *fallback_proxy_supply; struct qcom_mss_reg_res *active_supply; char **proxy_clk_names; char **reset_clk_names; char **active_clk_names; + char **active_pd_names; char **proxy_pd_names; int version; bool need_mem_protection; bool has_alt_reset; bool has_mba_logs; bool has_spare_reg; - bool has_qaccept_regs; - bool has_ext_cntl_regs; - bool has_vq6; }; struct q6v5 { @@ -169,18 +152,8 @@ struct q6v5 { u32 halt_q6; u32 halt_modem; u32 halt_nc; - u32 halt_vq6; u32 conn_box; - u32 qaccept_mdm; - u32 qaccept_cx; - u32 qaccept_axi; - - u32 axim1_clk_off; - u32 crypto_clk_off; - u32 force_clk_on; - u32 rscc_disable; - struct reset_control *mss_restart; struct reset_control *pdc_reset; @@ -189,24 +162,25 @@ struct q6v5 { struct clk *active_clks[8]; struct clk *reset_clks[4]; struct clk *proxy_clks[4]; + struct device *active_pds[1]; struct device *proxy_pds[3]; int active_clk_count; int reset_clk_count; int proxy_clk_count; + int active_pd_count; int proxy_pd_count; struct reg_info active_regs[1]; - struct reg_info proxy_regs[1]; - struct reg_info fallback_proxy_regs[2]; + struct reg_info proxy_regs[3]; int active_reg_count; int proxy_reg_count; - int fallback_proxy_reg_count; bool dump_mba_loaded; size_t current_dump_size; size_t total_dump_size; phys_addr_t mba_phys; + void *mba_region; size_t mba_size; size_t dp_size; @@ -218,14 +192,10 @@ struct q6v5 { struct qcom_rproc_subdev smd_subdev; struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; - struct platform_device *bam_dmux; bool need_mem_protection; bool has_alt_reset; bool has_mba_logs; bool has_spare_reg; - bool has_qaccept_regs; - bool has_ext_cntl_regs; - bool has_vq6; int mpss_perm; int mba_perm; const char *hexagon_mdt_image; @@ -238,7 +208,6 @@ enum { MSS_MSM8996, MSS_MSM8998, MSS_SC7180, - MSS_SC7280, MSS_SDM845, }; @@ -433,7 +402,7 @@ static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm, current_perm, next, perms); } -static void q6v5_debug_policy_load(struct q6v5 *qproc, void *mba_region) +static void q6v5_debug_policy_load(struct q6v5 *qproc) { const struct firmware *dp_fw; @@ -441,7 +410,7 @@ static void q6v5_debug_policy_load(struct q6v5 *qproc, void *mba_region) return; if (SZ_1M + dp_fw->size <= qproc->mba_size) { - memcpy(mba_region + SZ_1M, dp_fw->data, dp_fw->size); + memcpy(qproc->mba_region + SZ_1M, dp_fw->data, dp_fw->size); qproc->dp_size = dp_fw->size; } @@ -451,7 +420,6 @@ static void q6v5_debug_policy_load(struct q6v5 *qproc, void *mba_region) static int q6v5_load(struct rproc *rproc, const struct firmware *fw) { struct q6v5 *qproc = rproc->priv; - void *mba_region; /* MBA is restricted to a maximum size of 1M */ if (fw->size > qproc->mba_size || fw->size > SZ_1M) { @@ -459,16 +427,8 @@ static int q6v5_load(struct rproc *rproc, const struct firmware *fw) return -EINVAL; } - mba_region = memremap(qproc->mba_phys, qproc->mba_size, MEMREMAP_WC); - if (!mba_region) { - dev_err(qproc->dev, "unable to map memory region: %pa+%zx\n", - &qproc->mba_phys, qproc->mba_size); - return -EBUSY; - } - - memcpy(mba_region, fw->data, fw->size); - q6v5_debug_policy_load(qproc, mba_region); - memunmap(mba_region); + memcpy(qproc->mba_region, fw->data, fw->size); + q6v5_debug_policy_load(qproc); return 0; } @@ -499,12 +459,6 @@ static int q6v5_reset_assert(struct q6v5 *qproc) regmap_update_bits(qproc->conn_map, qproc->conn_box, AXI_GATING_VALID_OVERRIDE, 0); ret = reset_control_deassert(qproc->mss_restart); - } else if (qproc->has_ext_cntl_regs) { - regmap_write(qproc->conn_map, qproc->rscc_disable, 0); - reset_control_assert(qproc->pdc_reset); - reset_control_assert(qproc->mss_restart); - reset_control_deassert(qproc->pdc_reset); - ret = reset_control_deassert(qproc->mss_restart); } else { ret = reset_control_assert(qproc->mss_restart); } @@ -522,7 +476,7 @@ static int q6v5_reset_deassert(struct q6v5 *qproc) ret = reset_control_reset(qproc->mss_restart); writel(0, qproc->rmb_base + RMB_MBA_ALT_RESET); reset_control_deassert(qproc->pdc_reset); - } else if (qproc->has_spare_reg || qproc->has_ext_cntl_regs) { + } else if (qproc->has_spare_reg) { ret = reset_control_reset(qproc->mss_restart); } else { ret = reset_control_deassert(qproc->mss_restart); @@ -581,7 +535,6 @@ static void q6v5_dump_mba_logs(struct q6v5 *qproc) { struct rproc *rproc = qproc->rproc; void *data; - void *mba_region; if (!qproc->has_mba_logs) return; @@ -590,16 +543,12 @@ static void q6v5_dump_mba_logs(struct q6v5 *qproc) qproc->mba_size)) return; - mba_region = memremap(qproc->mba_phys, qproc->mba_size, MEMREMAP_WC); - if (!mba_region) + data = vmalloc(MBA_LOG_SIZE); + if (!data) return; - data = vmalloc(MBA_LOG_SIZE); - if (data) { - memcpy(data, mba_region, MBA_LOG_SIZE); - dev_coredumpv(&rproc->dev, data, MBA_LOG_SIZE, GFP_KERNEL); - } - memunmap(mba_region); + memcpy(data, qproc->mba_region, MBA_LOG_SIZE); + dev_coredumpv(&rproc->dev, data, MBA_LOG_SIZE, GFP_KERNEL); } static int q6v5proc_reset(struct q6v5 *qproc) @@ -636,7 +585,7 @@ static int q6v5proc_reset(struct q6v5 *qproc) } goto pbl_wait; - } else if (qproc->version == MSS_SC7180 || qproc->version == MSS_SC7280) { + } else if (qproc->version == MSS_SC7180) { val = readl(qproc->reg_base + QDSP6SS_SLEEP); val |= Q6SS_CBCR_CLKEN; writel(val, qproc->reg_base + QDSP6SS_SLEEP); @@ -819,89 +768,6 @@ static int q6v5proc_reset(struct q6v5 *qproc) return ret; } -static int q6v5proc_enable_qchannel(struct q6v5 *qproc, struct regmap *map, u32 offset) -{ - unsigned int val; - int ret; - - if (!qproc->has_qaccept_regs) - return 0; - - if (qproc->has_ext_cntl_regs) { - regmap_write(qproc->conn_map, qproc->rscc_disable, 0); - regmap_write(qproc->conn_map, qproc->force_clk_on, 1); - - ret = regmap_read_poll_timeout(qproc->halt_map, qproc->axim1_clk_off, val, - !val, 1, Q6SS_CBCR_TIMEOUT_US); - if (ret) { - dev_err(qproc->dev, "failed to enable axim1 clock\n"); - return -ETIMEDOUT; - } - } - - regmap_write(map, offset + QACCEPT_REQ_REG, 1); - - /* Wait for accept */ - ret = regmap_read_poll_timeout(map, offset + QACCEPT_ACCEPT_REG, val, val, 5, - QACCEPT_TIMEOUT_US); - if (ret) { - dev_err(qproc->dev, "qchannel enable failed\n"); - return -ETIMEDOUT; - } - - return 0; -} - -static void q6v5proc_disable_qchannel(struct q6v5 *qproc, struct regmap *map, u32 offset) -{ - int ret; - unsigned int val, retry; - unsigned int nretry = 10; - bool takedown_complete = false; - - if (!qproc->has_qaccept_regs) - return; - - while (!takedown_complete && nretry) { - nretry--; - - /* Wait for active transactions to complete */ - regmap_read_poll_timeout(map, offset + QACCEPT_ACTIVE_REG, val, !val, 5, - QACCEPT_TIMEOUT_US); - - /* Request Q-channel transaction takedown */ - regmap_write(map, offset + QACCEPT_REQ_REG, 0); - - /* - * If the request is denied, reset the Q-channel takedown request, - * wait for active transactions to complete and retry takedown. - */ - retry = 10; - while (retry) { - usleep_range(5, 10); - retry--; - ret = regmap_read(map, offset + QACCEPT_DENY_REG, &val); - if (!ret && val) { - regmap_write(map, offset + QACCEPT_REQ_REG, 1); - break; - } - - ret = regmap_read(map, offset + QACCEPT_ACCEPT_REG, &val); - if (!ret && !val) { - takedown_complete = true; - break; - } - } - - if (!retry) - break; - } - - /* Rely on mss_restart to clear out pending transactions on takedown failure */ - if (!takedown_complete) - dev_err(qproc->dev, "qchannel takedown failed\n"); -} - static void q6v5proc_halt_axi_port(struct q6v5 *qproc, struct regmap *halt_map, u32 offset) @@ -929,8 +795,7 @@ static void q6v5proc_halt_axi_port(struct q6v5 *qproc, regmap_write(halt_map, offset + AXI_HALTREQ_REG, 0); } -static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw, - const char *fw_name) +static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw) { unsigned long dma_attrs = DMA_ATTR_FORCE_CONTIGUOUS; dma_addr_t phys; @@ -941,7 +806,7 @@ static int q6v5_mpss_init_image(struct q6v5 *qproc, const struct firmware *fw, void *ptr; int ret; - metadata = qcom_mdt_read_metadata(fw, &size, fw_name, qproc->dev); + metadata = qcom_mdt_read_metadata(qproc->dev, fw, qproc->hexagon_mdt_image, &size, NULL); if (IS_ERR(metadata)) return PTR_ERR(metadata); @@ -1008,28 +873,25 @@ static int q6v5_mba_load(struct q6v5 *qproc) int xfermemop_ret; bool mba_load_err = false; - ret = qcom_q6v5_prepare(&qproc->q6v5); - if (ret) - return ret; + qcom_q6v5_prepare(&qproc->q6v5); + + ret = q6v5_pds_enable(qproc, qproc->active_pds, qproc->active_pd_count); + if (ret < 0) { + dev_err(qproc->dev, "failed to enable active power domains\n"); + goto disable_irqs; + } ret = q6v5_pds_enable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); if (ret < 0) { dev_err(qproc->dev, "failed to enable proxy power domains\n"); - goto disable_irqs; - } - - ret = q6v5_regulator_enable(qproc, qproc->fallback_proxy_regs, - qproc->fallback_proxy_reg_count); - if (ret) { - dev_err(qproc->dev, "failed to enable fallback proxy supplies\n"); - goto disable_proxy_pds; + goto disable_active_pds; } ret = q6v5_regulator_enable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); if (ret) { dev_err(qproc->dev, "failed to enable proxy supplies\n"); - goto disable_fallback_proxy_reg; + goto disable_proxy_pds; } ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks, @@ -1066,12 +928,6 @@ static int q6v5_mba_load(struct q6v5 *qproc) goto assert_reset; } - ret = q6v5proc_enable_qchannel(qproc, qproc->halt_map, qproc->qaccept_axi); - if (ret) { - dev_err(qproc->dev, "failed to enable axi bridge\n"); - goto disable_active_clks; - } - /* * Some versions of the MBA firmware will upon boot wipe the MPSS region as well, so provide * the Q6 access to this region. @@ -1118,13 +974,8 @@ static int q6v5_mba_load(struct q6v5 *qproc) halt_axi_ports: q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); - if (qproc->has_vq6) - q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_vq6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); - q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_mdm); - q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_cx); - q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_axi); mba_load_err = true; reclaim_mba: xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true, @@ -1154,11 +1005,10 @@ static int q6v5_mba_load(struct q6v5 *qproc) disable_proxy_reg: q6v5_regulator_disable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); -disable_fallback_proxy_reg: - q6v5_regulator_disable(qproc, qproc->fallback_proxy_regs, - qproc->fallback_proxy_reg_count); disable_proxy_pds: q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); +disable_active_pds: + q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count); disable_irqs: qcom_q6v5_unprepare(&qproc->q6v5); @@ -1174,8 +1024,6 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) qproc->dp_size = 0; q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); - if (qproc->has_vq6) - q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_vq6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); if (qproc->version == MSS_MSM8996) { @@ -1188,24 +1036,6 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) writel(val, qproc->reg_base + QDSP6SS_PWR_CTL_REG); } - if (qproc->has_ext_cntl_regs) { - regmap_write(qproc->conn_map, qproc->rscc_disable, 1); - - ret = regmap_read_poll_timeout(qproc->halt_map, qproc->axim1_clk_off, val, - !val, 1, Q6SS_CBCR_TIMEOUT_US); - if (ret) - dev_err(qproc->dev, "failed to enable axim1 clock\n"); - - ret = regmap_read_poll_timeout(qproc->halt_map, qproc->crypto_clk_off, val, - !val, 1, Q6SS_CBCR_TIMEOUT_US); - if (ret) - dev_err(qproc->dev, "failed to enable crypto clock\n"); - } - - q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_mdm); - q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_cx); - q6v5proc_disable_qchannel(qproc, qproc->halt_map, qproc->qaccept_axi); - q6v5_reset_assert(qproc); q6v5_clk_disable(qproc->dev, qproc->reset_clks, @@ -1214,6 +1044,7 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) qproc->active_clk_count); q6v5_regulator_disable(qproc, qproc->active_regs, qproc->active_reg_count); + q6v5_pds_disable(qproc, qproc->active_pds, qproc->active_pd_count); /* In case of failure or coredump scenario where reclaiming MBA memory * could not happen reclaim it here. @@ -1229,8 +1060,6 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) qproc->proxy_pd_count); q6v5_clk_disable(qproc->dev, qproc->proxy_clks, qproc->proxy_clk_count); - q6v5_regulator_disable(qproc, qproc->fallback_proxy_regs, - qproc->fallback_proxy_reg_count); q6v5_regulator_disable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); } @@ -1291,7 +1120,7 @@ static int q6v5_mpss_load(struct q6v5 *qproc) /* Initialize the RMB validator */ writel(0, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); - ret = q6v5_mpss_init_image(qproc, fw, qproc->hexagon_mdt_image); + ret = q6v5_mpss_init_image(qproc, fw); if (ret) goto release_firmware; @@ -1609,30 +1438,27 @@ static void qcom_msa_handover(struct qcom_q6v5 *q6v5) qproc->proxy_clk_count); q6v5_regulator_disable(qproc, qproc->proxy_regs, qproc->proxy_reg_count); - q6v5_regulator_disable(qproc, qproc->fallback_proxy_regs, - qproc->fallback_proxy_reg_count); q6v5_pds_disable(qproc, qproc->proxy_pds, qproc->proxy_pd_count); } static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) { struct of_phandle_args args; - int halt_cell_cnt = 3; + struct resource *res; int ret; - qproc->reg_base = devm_platform_ioremap_resource_byname(pdev, "qdsp6"); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qdsp6"); + qproc->reg_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(qproc->reg_base)) return PTR_ERR(qproc->reg_base); - qproc->rmb_base = devm_platform_ioremap_resource_byname(pdev, "rmb"); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rmb"); + qproc->rmb_base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(qproc->rmb_base)) return PTR_ERR(qproc->rmb_base); - if (qproc->has_vq6) - halt_cell_cnt++; - ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, - "qcom,halt-regs", halt_cell_cnt, 0, &args); + "qcom,halt-regs", 3, 0, &args); if (ret < 0) { dev_err(&pdev->dev, "failed to parse qcom,halt-regs\n"); return -EINVAL; @@ -1647,52 +1473,6 @@ static int q6v5_init_mem(struct q6v5 *qproc, struct platform_device *pdev) qproc->halt_modem = args.args[1]; qproc->halt_nc = args.args[2]; - if (qproc->has_vq6) - qproc->halt_vq6 = args.args[3]; - - if (qproc->has_qaccept_regs) { - ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, - "qcom,qaccept-regs", - 3, 0, &args); - if (ret < 0) { - dev_err(&pdev->dev, "failed to parse qaccept-regs\n"); - return -EINVAL; - } - - qproc->qaccept_mdm = args.args[0]; - qproc->qaccept_cx = args.args[1]; - qproc->qaccept_axi = args.args[2]; - } - - if (qproc->has_ext_cntl_regs) { - ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, - "qcom,ext-regs", - 2, 0, &args); - if (ret < 0) { - dev_err(&pdev->dev, "failed to parse ext-regs index 0\n"); - return -EINVAL; - } - - qproc->conn_map = syscon_node_to_regmap(args.np); - of_node_put(args.np); - if (IS_ERR(qproc->conn_map)) - return PTR_ERR(qproc->conn_map); - - qproc->force_clk_on = args.args[0]; - qproc->rscc_disable = args.args[1]; - - ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, - "qcom,ext-regs", - 2, 1, &args); - if (ret < 0) { - dev_err(&pdev->dev, "failed to parse ext-regs index 1\n"); - return -EINVAL; - } - - qproc->axim1_clk_off = args.args[0]; - qproc->crypto_clk_off = args.args[1]; - } - if (qproc->has_spare_reg) { ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, "qcom,spare-regs", @@ -1784,7 +1564,7 @@ static int q6v5_init_reset(struct q6v5 *qproc) return PTR_ERR(qproc->mss_restart); } - if (qproc->has_alt_reset || qproc->has_spare_reg || qproc->has_ext_cntl_regs) { + if (qproc->has_alt_reset || qproc->has_spare_reg) { qproc->pdc_reset = devm_reset_control_get_exclusive(qproc->dev, "pdc_reset"); if (IS_ERR(qproc->pdc_reset)) { @@ -1825,6 +1605,12 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc) qproc->mba_phys = r.start; qproc->mba_size = resource_size(&r); + qproc->mba_region = devm_ioremap_wc(qproc->dev, qproc->mba_phys, qproc->mba_size); + if (!qproc->mba_region) { + dev_err(qproc->dev, "unable to map memory region: %pa+%zx\n", + &r.start, qproc->mba_size); + return -EBUSY; + } if (!child) { node = of_parse_phandle(qproc->dev->of_node, @@ -1851,7 +1637,6 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc) static int q6v5_probe(struct platform_device *pdev) { const struct rproc_hexagon_res *desc; - struct device_node *node; struct q6v5 *qproc; struct rproc *rproc; const char *mba_image; @@ -1867,10 +1652,8 @@ static int q6v5_probe(struct platform_device *pdev) mba_image = desc->hexagon_mba_image; ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name", 0, &mba_image); - if (ret < 0 && ret != -EINVAL) { - dev_err(&pdev->dev, "unable to read mba firmware-name\n"); + if (ret < 0 && ret != -EINVAL) return ret; - } rproc = rproc_alloc(&pdev->dev, pdev->name, &q6v5_ops, mba_image, sizeof(*qproc)); @@ -1888,16 +1671,11 @@ static int q6v5_probe(struct platform_device *pdev) qproc->hexagon_mdt_image = "modem.mdt"; ret = of_property_read_string_index(pdev->dev.of_node, "firmware-name", 1, &qproc->hexagon_mdt_image); - if (ret < 0 && ret != -EINVAL) { - dev_err(&pdev->dev, "unable to read mpss firmware-name\n"); + if (ret < 0 && ret != -EINVAL) goto free_rproc; - } platform_set_drvdata(pdev, qproc); - qproc->has_qaccept_regs = desc->has_qaccept_regs; - qproc->has_ext_cntl_regs = desc->has_ext_cntl_regs; - qproc->has_vq6 = desc->has_vq6; qproc->has_spare_reg = desc->has_spare_reg; ret = q6v5_init_mem(qproc, pdev); if (ret) @@ -1947,24 +1725,21 @@ static int q6v5_probe(struct platform_device *pdev) } qproc->active_reg_count = ret; + ret = q6v5_pds_attach(&pdev->dev, qproc->active_pds, + desc->active_pd_names); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to attach active power domains\n"); + goto free_rproc; + } + qproc->active_pd_count = ret; + ret = q6v5_pds_attach(&pdev->dev, qproc->proxy_pds, desc->proxy_pd_names); - /* Fallback to regulators for old device trees */ - if (ret == -ENODATA && desc->fallback_proxy_supply) { - ret = q6v5_regulator_init(&pdev->dev, - qproc->fallback_proxy_regs, - desc->fallback_proxy_supply); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to get fallback proxy regulators.\n"); - goto free_rproc; - } - qproc->fallback_proxy_reg_count = ret; - } else if (ret < 0) { + if (ret < 0) { dev_err(&pdev->dev, "Failed to init power domains\n"); - goto free_rproc; - } else { - qproc->proxy_pd_count = ret; + goto detach_active_pds; } + qproc->proxy_pd_count = ret; qproc->has_alt_reset = desc->has_alt_reset; ret = q6v5_init_reset(qproc); @@ -1975,7 +1750,7 @@ static int q6v5_probe(struct platform_device *pdev) qproc->need_mem_protection = desc->need_mem_protection; qproc->has_mba_logs = desc->has_mba_logs; - ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM, "modem", + ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM, qcom_msa_handover); if (ret) goto detach_proxy_pds; @@ -1995,10 +1770,6 @@ static int q6v5_probe(struct platform_device *pdev) if (ret) goto remove_sysmon_subdev; - node = of_get_compatible_child(pdev->dev.of_node, "qcom,bam-dmux"); - qproc->bam_dmux = of_platform_device_create(node, NULL, &pdev->dev); - of_node_put(node); - return 0; remove_sysmon_subdev: @@ -2009,6 +1780,8 @@ static int q6v5_probe(struct platform_device *pdev) qcom_remove_glink_subdev(rproc, &qproc->glink_subdev); detach_proxy_pds: q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); +detach_active_pds: + q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count); free_rproc: rproc_free(rproc); @@ -2020,17 +1793,15 @@ static int q6v5_remove(struct platform_device *pdev) struct q6v5 *qproc = platform_get_drvdata(pdev); struct rproc *rproc = qproc->rproc; - if (qproc->bam_dmux) - of_platform_device_destroy(&qproc->bam_dmux->dev, NULL); rproc_del(rproc); - qcom_q6v5_deinit(&qproc->q6v5); qcom_remove_sysmon_subdev(qproc->sysmon); qcom_remove_ssr_subdev(rproc, &qproc->ssr_subdev); qcom_remove_smd_subdev(rproc, &qproc->smd_subdev); qcom_remove_glink_subdev(rproc, &qproc->glink_subdev); q6v5_pds_detach(qproc, qproc->proxy_pds, qproc->proxy_pd_count); + q6v5_pds_detach(qproc, qproc->active_pds, qproc->active_pd_count); rproc_free(rproc); @@ -2054,6 +1825,10 @@ static const struct rproc_hexagon_res sc7180_mss = { "nav", NULL }, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, .proxy_pd_names = (char*[]){ "cx", "mx", @@ -2064,40 +1839,9 @@ static const struct rproc_hexagon_res sc7180_mss = { .has_alt_reset = false, .has_mba_logs = true, .has_spare_reg = true, - .has_qaccept_regs = false, - .has_ext_cntl_regs = false, - .has_vq6 = false, .version = MSS_SC7180, }; -static const struct rproc_hexagon_res sc7280_mss = { - .hexagon_mba_image = "mba.mbn", - .proxy_clk_names = (char*[]){ - "xo", - "pka", - NULL - }, - .active_clk_names = (char*[]){ - "iface", - "offline", - "snoc_axi", - NULL - }, - .proxy_pd_names = (char*[]){ - "cx", - "mss", - NULL - }, - .need_mem_protection = true, - .has_alt_reset = false, - .has_mba_logs = true, - .has_spare_reg = false, - .has_qaccept_regs = true, - .has_ext_cntl_regs = true, - .has_vq6 = true, - .version = MSS_SC7280, -}; - static const struct rproc_hexagon_res sdm845_mss = { .hexagon_mba_image = "mba.mbn", .proxy_clk_names = (char*[]){ @@ -2117,6 +1861,10 @@ static const struct rproc_hexagon_res sdm845_mss = { "mnoc_axi", NULL }, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, .proxy_pd_names = (char*[]){ "cx", "mx", @@ -2127,9 +1875,6 @@ static const struct rproc_hexagon_res sdm845_mss = { .has_alt_reset = true, .has_mba_logs = false, .has_spare_reg = false, - .has_qaccept_regs = false, - .has_ext_cntl_regs = false, - .has_vq6 = false, .version = MSS_SDM845, }; @@ -2158,9 +1903,6 @@ static const struct rproc_hexagon_res msm8998_mss = { .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, - .has_qaccept_regs = false, - .has_ext_cntl_regs = false, - .has_vq6 = false, .version = MSS_MSM8998, }; @@ -2192,22 +1934,12 @@ static const struct rproc_hexagon_res msm8996_mss = { .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, - .has_qaccept_regs = false, - .has_ext_cntl_regs = false, - .has_vq6 = false, .version = MSS_MSM8996, }; static const struct rproc_hexagon_res msm8916_mss = { .hexagon_mba_image = "mba.mbn", .proxy_supply = (struct qcom_mss_reg_res[]) { - { - .supply = "pll", - .uA = 100000, - }, - {} - }, - .fallback_proxy_supply = (struct qcom_mss_reg_res[]) { { .supply = "mx", .uV = 1050000, @@ -2216,6 +1948,10 @@ static const struct rproc_hexagon_res msm8916_mss = { .supply = "cx", .uA = 100000, }, + { + .supply = "pll", + .uA = 100000, + }, {} }, .proxy_clk_names = (char*[]){ @@ -2228,31 +1964,16 @@ static const struct rproc_hexagon_res msm8916_mss = { "mem", NULL }, - .proxy_pd_names = (char*[]){ - "mx", - "cx", - NULL - }, .need_mem_protection = false, .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, - .has_qaccept_regs = false, - .has_ext_cntl_regs = false, - .has_vq6 = false, .version = MSS_MSM8916, }; static const struct rproc_hexagon_res msm8974_mss = { .hexagon_mba_image = "mba.b00", .proxy_supply = (struct qcom_mss_reg_res[]) { - { - .supply = "pll", - .uA = 100000, - }, - {} - }, - .fallback_proxy_supply = (struct qcom_mss_reg_res[]) { { .supply = "mx", .uV = 1050000, @@ -2261,6 +1982,10 @@ static const struct rproc_hexagon_res msm8974_mss = { .supply = "cx", .uA = 100000, }, + { + .supply = "pll", + .uA = 100000, + }, {} }, .active_supply = (struct qcom_mss_reg_res[]) { @@ -2281,18 +2006,10 @@ static const struct rproc_hexagon_res msm8974_mss = { "mem", NULL }, - .proxy_pd_names = (char*[]){ - "mx", - "cx", - NULL - }, .need_mem_protection = false, .has_alt_reset = false, .has_mba_logs = false, .has_spare_reg = false, - .has_qaccept_regs = false, - .has_ext_cntl_regs = false, - .has_vq6 = false, .version = MSS_MSM8974, }; @@ -2303,7 +2020,6 @@ static const struct of_device_id q6v5_of_match[] = { { .compatible = "qcom,msm8996-mss-pil", .data = &msm8996_mss}, { .compatible = "qcom,msm8998-mss-pil", .data = &msm8998_mss}, { .compatible = "qcom,sc7180-mss-pil", .data = &sc7180_mss}, - { .compatible = "qcom,sc7280-mss-pil", .data = &sc7280_mss}, { .compatible = "qcom,sdm845-mss-pil", .data = &sdm845_mss}, { }, }; diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 6ae39c5653b1..8714d172390a 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -4,44 +4,69 @@ * * Copyright (C) 2016 Linaro Ltd * Copyright (C) 2014 Sony Mobile Communications AB - * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2013, 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. */ #include +#include #include #include #include #include #include #include +#include +#include #include #include #include #include #include #include +#include #include #include #include +#include + +#define CREATE_TRACE_POINTS +#include #include "qcom_common.h" #include "qcom_pil_info.h" #include "qcom_q6v5.h" #include "remoteproc_internal.h" +#define XO_FREQ 19200000 +#define PIL_TZ_AVG_BW 0 +#define PIL_TZ_PEAK_BW UINT_MAX +#define QMP_MSG_LEN 64 + +static struct icc_path *scm_perf_client; +static int scm_pas_bw_count; +static DEFINE_MUTEX(scm_pas_bw_mutex); +bool timeout_disabled; + struct adsp_data { int crash_reason_smem; const char *firmware_name; + const char *dtb_firmware_name; int pas_id; + int dtb_pas_id; + bool free_after_auth_reset; unsigned int minidump_id; + bool uses_elf64; bool has_aggre2_clk; bool auto_boot; + bool dma_phys_below_32b; + char **active_pd_names; char **proxy_pd_names; - const char *load_state; const char *ssr_name; const char *sysmon_name; + const char *qmp_name; int ssctl_id; }; @@ -56,20 +81,38 @@ struct qcom_adsp { struct regulator *cx_supply; struct regulator *px_supply; + struct reg_info *regs; + int reg_cnt; + struct device *active_pds[1]; struct device *proxy_pds[3]; + const char *qmp_name; + struct qmp *qmp; + int active_pd_count; int proxy_pd_count; int pas_id; + int dtb_pas_id; + const char *dtb_fw_name; + struct qcom_mdt_metadata *mdata; + struct qcom_mdt_metadata dtb_mdata; unsigned int minidump_id; + bool retry_shutdown; + struct icc_path *bus_client; int crash_reason_smem; bool has_aggre2_clk; + bool dma_phys_below_32b; const char *info_name; struct completion start_done; struct completion stop_done; + phys_addr_t dtb_mem_phys; + phys_addr_t dtb_mem_reloc; + void *dtb_mem_region; + size_t dtb_mem_size; + phys_addr_t mem_phys; phys_addr_t mem_reloc; void *mem_region; @@ -79,15 +122,59 @@ struct qcom_adsp { struct qcom_rproc_subdev smd_subdev; struct qcom_rproc_ssr ssr_subdev; struct qcom_sysmon *sysmon; - - struct qcom_scm_pas_metadata pas_metadata; + const struct firmware *dtb_firmware; }; +static ssize_t txn_id_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = container_of(dev, struct platform_device, dev); + struct qcom_adsp *adsp = (struct qcom_adsp *)platform_get_drvdata(pdev); + + return sysfs_emit(buf, "%zu\n", qcom_sysmon_get_txn_id(adsp->sysmon)); +} +static DEVICE_ATTR_RO(txn_id); + +void adsp_segment_dump(struct rproc *rproc, struct rproc_dump_segment *segment, + void *dest, size_t offset, size_t size) +{ + struct qcom_adsp *adsp = rproc->priv; + int total_offset; + + total_offset = segment->da + segment->offset + offset - adsp->mem_phys; + if (total_offset < 0 || total_offset + size > adsp->mem_size) { + dev_err(adsp->dev, + "invalid copy request for segment %pad with offset %zu and size %zu)\n", + &segment->da, offset, size); + memset(dest, 0xff, size); + return; + } + + memcpy_fromio(dest, adsp->mem_region + total_offset, size); +} + static void adsp_minidump(struct rproc *rproc) { struct qcom_adsp *adsp = rproc->priv; - qcom_minidump(rproc, adsp->minidump_id); + trace_rproc_qcom_event(dev_name(adsp->dev), "adsp_minidump", "enter"); + + if (rproc->dump_conf == RPROC_COREDUMP_DISABLED) + goto exit; + + qcom_minidump(rproc, adsp->minidump_id, adsp_segment_dump); + +exit: + trace_rproc_qcom_event(dev_name(adsp->dev), "adsp_minidump", "exit"); +} + +static int adsp_toggle_load_state(struct qmp *qmp, const char *name, bool enable) +{ + char buf[QMP_MSG_LEN] = {}; + + snprintf(buf, sizeof(buf), + "{class: image, res: load_state, name: %s, val: %s}", + name, enable ? "on" : "off"); + return qmp_send(qmp, buf, sizeof(buf)); } static int adsp_pds_enable(struct qcom_adsp *adsp, struct device **pds, @@ -128,19 +215,80 @@ static void adsp_pds_disable(struct qcom_adsp *adsp, struct device **pds, } } -static int adsp_unprepare(struct rproc *rproc) +static int scm_pas_enable_bw(void) { - struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + int ret = 0; - /* - * adsp_load() did pass pas_metadata to the SCM driver for storing - * metadata context. It might have been released already if - * auth_and_reset() was successful, but in other cases clean it up - * here. - */ - qcom_scm_pas_metadata_release(&adsp->pas_metadata); + if (IS_ERR(scm_perf_client)) + return -EINVAL; - return 0; + mutex_lock(&scm_pas_bw_mutex); + if (!scm_pas_bw_count) { + ret = icc_set_bw(scm_perf_client, PIL_TZ_AVG_BW, + PIL_TZ_PEAK_BW); + if (ret) + goto err_bus; + } + + scm_pas_bw_count++; + mutex_unlock(&scm_pas_bw_mutex); + return ret; + +err_bus: + pr_err("scm-pas: Bandwidth request failed (%d)\n", ret); + icc_set_bw(scm_perf_client, 0, 0); + + mutex_unlock(&scm_pas_bw_mutex); + return ret; +} + +static void scm_pas_disable_bw(void) +{ + if (IS_ERR(scm_perf_client)) + return; + + mutex_lock(&scm_pas_bw_mutex); + if (scm_pas_bw_count-- == 1) + icc_set_bw(scm_perf_client, 0, 0); + mutex_unlock(&scm_pas_bw_mutex); +} + +static void adsp_add_coredump_segments(struct qcom_adsp *adsp, const struct firmware *fw) +{ + struct rproc *rproc = adsp->rproc; + struct rproc_dump_segment *entry; + struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data; + struct elf32_phdr *phdr, *phdrs = (struct elf32_phdr *)(fw->data + ehdr->e_phoff); + uint32_t elf_min_addr = U32_MAX; + bool relocatable = false; + int ret; + int i; + + for (i = 0; i < ehdr->e_phnum; i++) { + phdr = &phdrs[i]; + if (phdr->p_type != PT_LOAD || + (phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH || + !phdr->p_memsz) + continue; + + if (phdr->p_flags & QCOM_MDT_RELOCATABLE) + relocatable = true; + + elf_min_addr = min(phdr->p_paddr, elf_min_addr); + + ret = rproc_coredump_add_segment(rproc, phdr->p_paddr, phdr->p_memsz); + if (ret) { + dev_err(adsp->dev, "failed to add rproc segment: %d\n", ret); + rproc_coredump_cleanup(adsp->rproc); + return; + } + } + + list_for_each_entry(entry, &rproc->dump_segments, node) + entry->da = adsp->mem_phys + entry->da - elf_min_addr; + + if (relocatable) + adsp->mem_reloc = adsp->mem_phys + adsp->mem_reloc - elf_min_addr; } static int adsp_load(struct rproc *rproc, const struct firmware *fw) @@ -148,82 +296,220 @@ static int adsp_load(struct rproc *rproc, const struct firmware *fw) struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; int ret; - ret = qcom_mdt_pas_init(adsp->dev, fw, rproc->firmware, adsp->pas_id, - adsp->mem_phys, &adsp->pas_metadata); - if (ret) - return ret; + trace_rproc_qcom_event(dev_name(adsp->dev), "adsp_load", "enter"); - ret = qcom_mdt_load_no_init(adsp->dev, fw, rproc->firmware, adsp->pas_id, - adsp->mem_region, adsp->mem_phys, adsp->mem_size, - &adsp->mem_reloc); - if (ret) - return ret; + rproc_coredump_cleanup(adsp->rproc); - qcom_pil_info_store(adsp->info_name, adsp->mem_phys, adsp->mem_size); + scm_pas_enable_bw(); - return 0; + if (!adsp->dtb_pas_id || !adsp->dtb_fw_name) { + scm_pas_disable_bw(); + return 0; + } + + ret = request_firmware(&adsp->dtb_firmware, adsp->dtb_fw_name, adsp->dev); + if (ret) { + dev_err(adsp->dev, "request_firmware failed for %s: %d\n", adsp->dtb_fw_name, ret); + goto exit; + } + + ret = qcom_mdt_load_no_free(adsp->dev, adsp->dtb_firmware, adsp->dtb_fw_name, + adsp->dtb_pas_id, adsp->dtb_mem_region, adsp->dtb_mem_phys, + adsp->dtb_mem_size, &adsp->dtb_mem_reloc, adsp->dma_phys_below_32b, + &adsp->dtb_mdata); + if (ret) { + dev_err(adsp->dev, "failed to load %s: %d\n", adsp->dtb_fw_name, ret); + release_firmware(adsp->dtb_firmware); + goto exit; + } + +exit: + trace_rproc_qcom_event(dev_name(adsp->dev), "adsp_load", "exit"); + scm_pas_disable_bw(); + + return ret; +} + +static void disable_regulators(struct qcom_adsp *adsp) +{ + int i; + + for (i = 0; i < adsp->reg_cnt; i++) { + regulator_set_voltage(adsp->regs[i].reg, 0, INT_MAX); + regulator_set_load(adsp->regs[i].reg, 0); + regulator_disable(adsp->regs[i].reg); + } +} + +static int enable_regulators(struct qcom_adsp *adsp) +{ + int i, rc = 0; + + for (i = 0; i < adsp->reg_cnt; i++) { + regulator_set_voltage(adsp->regs[i].reg, adsp->regs[i].uV, INT_MAX); + regulator_set_load(adsp->regs[i].reg, adsp->regs[i].uA); + rc = regulator_enable(adsp->regs[i].reg); + if (rc) { + dev_err(adsp->dev, "Regulator enable failed(rc:%d)\n", + rc); + goto err_enable; + } + } + return rc; + +err_enable: + disable_regulators(adsp); + return rc; +} + +static int do_bus_scaling(struct qcom_adsp *adsp, bool enable) +{ + int rc = 0; + u32 avg_bw = enable ? PIL_TZ_AVG_BW : 0; + u32 peak_bw = enable ? PIL_TZ_PEAK_BW : 0; + + if (IS_ERR(adsp->bus_client)) + dev_err(adsp->dev, "Bus scaling not setup for %s\n", + adsp->rproc->name); + else + rc = icc_set_bw(adsp->bus_client, avg_bw, peak_bw); + + if (rc) + dev_err(adsp->dev, "bandwidth request failed(rc:%d)\n", rc); + + return rc; } static int adsp_start(struct rproc *rproc) { struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; - int ret; + int i, ret; + const struct firmware *fw = NULL; - ret = qcom_q6v5_prepare(&adsp->q6v5); - if (ret) - return ret; + trace_rproc_qcom_event(dev_name(adsp->dev), "adsp_start", "enter"); - ret = adsp_pds_enable(adsp, adsp->proxy_pds, adsp->proxy_pd_count); + qcom_q6v5_prepare(&adsp->q6v5); + + ret = do_bus_scaling(adsp, true); if (ret < 0) goto disable_irqs; + ret = adsp_pds_enable(adsp, adsp->active_pds, adsp->active_pd_count); + if (ret < 0) + goto unscale_bus; + + ret = adsp_pds_enable(adsp, adsp->proxy_pds, adsp->proxy_pd_count); + if (ret < 0) + goto disable_active_pds; + + if (adsp->qmp) { + ret = adsp_toggle_load_state(adsp->qmp, adsp->qmp_name, true); + if (ret) + goto disable_proxy_pds; + } + ret = clk_prepare_enable(adsp->xo); if (ret) - goto disable_proxy_pds; + goto disable_load_state; ret = clk_prepare_enable(adsp->aggre2_clk); if (ret) goto disable_xo_clk; - ret = regulator_enable(adsp->cx_supply); + ret = enable_regulators(adsp); if (ret) goto disable_aggre2_clk; - ret = regulator_enable(adsp->px_supply); + scm_pas_enable_bw(); + trace_rproc_qcom_event(dev_name(adsp->dev), "dtb_auth_reset", "enter"); + if (adsp->dtb_pas_id || adsp->dtb_fw_name) { + ret = qcom_scm_pas_auth_and_reset(adsp->dtb_pas_id); + if (ret) + panic("Panicking, auth and reset failed for remoteproc %s dtb\n", + rproc->name); + } + + trace_rproc_qcom_event(dev_name(adsp->dev), "Q6_firmware_loading", "enter"); + ret = request_firmware(&fw, rproc->firmware, adsp->dev); if (ret) - goto disable_cx_supply; + goto free_metadata_dtb; + + ret = qcom_mdt_load_no_free(adsp->dev, fw, rproc->firmware, adsp->pas_id, + adsp->mem_region, adsp->mem_phys, adsp->mem_size, + &adsp->mem_reloc, adsp->dma_phys_below_32b, adsp->mdata); + if (ret) + goto free_firmware; + + qcom_pil_info_store(adsp->info_name, adsp->mem_phys, adsp->mem_size); + + adsp_add_coredump_segments(adsp, fw); + trace_rproc_qcom_event(dev_name(adsp->dev), "Q6_auth_reset", "enter"); ret = qcom_scm_pas_auth_and_reset(adsp->pas_id); - if (ret) { - dev_err(adsp->dev, - "failed to authenticate image and release reset\n"); - goto disable_px_supply; + if (ret) + panic("Panicking, auth and reset failed for remoteproc %s\n", rproc->name); + trace_rproc_qcom_event(dev_name(adsp->dev), "Q6_auth_reset", "exit"); + + /* if needed, signal Q6 to continute booting */ + if (adsp->q6v5.rmb_base) { + for (i = 0; i < RMB_POLL_MAX_TIMES || timeout_disabled; i++) { + if (readl_relaxed(adsp->q6v5.rmb_base + RMB_BOOT_WAIT_REG)) { + writel_relaxed(1, adsp->q6v5.rmb_base + RMB_BOOT_CONT_REG); + break; + } + msleep(20); + } + + if (!readl_relaxed(adsp->q6v5.rmb_base + RMB_BOOT_WAIT_REG)) { + dev_err(adsp->dev, "Didn't get rmb signal from %s\n", rproc->name); + goto free_metadata; + } } - ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000)); - if (ret == -ETIMEDOUT) { - dev_err(adsp->dev, "start timed out\n"); - qcom_scm_pas_shutdown(adsp->pas_id); - goto disable_px_supply; + if (!timeout_disabled) { + ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000)); + if (rproc->recovery_disabled && ret) + panic("Panicking, remoteproc %s failed to bootup.\n", adsp->rproc->name); + else if (ret == -ETIMEDOUT) + dev_err(adsp->dev, "start timed out\n"); } - qcom_scm_pas_metadata_release(&adsp->pas_metadata); +free_metadata: + qcom_mdt_free_metadata(adsp->dev, adsp->pas_id, adsp->mdata, + adsp->dma_phys_below_32b, ret); +free_firmware: + if (!fw) + release_firmware(fw); - return 0; +free_metadata_dtb: + if (adsp->dtb_pas_id || adsp->dtb_fw_name) { + qcom_mdt_free_metadata(adsp->dev, adsp->dtb_pas_id, + &adsp->dtb_mdata, adsp->dma_phys_below_32b, ret); + release_firmware(adsp->dtb_firmware); + } -disable_px_supply: - regulator_disable(adsp->px_supply); -disable_cx_supply: - regulator_disable(adsp->cx_supply); + scm_pas_disable_bw(); + if (!ret) + goto exit; + + disable_regulators(adsp); disable_aggre2_clk: clk_disable_unprepare(adsp->aggre2_clk); disable_xo_clk: clk_disable_unprepare(adsp->xo); +disable_load_state: + if (adsp->qmp) + adsp_toggle_load_state(adsp->qmp, adsp->qmp_name, false); disable_proxy_pds: adsp_pds_disable(adsp, adsp->proxy_pds, adsp->proxy_pd_count); +disable_active_pds: + adsp_pds_disable(adsp, adsp->active_pds, adsp->active_pd_count); +unscale_bus: + do_bus_scaling(adsp, false); disable_irqs: qcom_q6v5_unprepare(&adsp->q6v5); - +exit: + trace_rproc_qcom_event(dev_name(adsp->dev), "adsp_start", "exit"); return ret; } @@ -231,11 +517,11 @@ static void qcom_pas_handover(struct qcom_q6v5 *q6v5) { struct qcom_adsp *adsp = container_of(q6v5, struct qcom_adsp, q6v5); - regulator_disable(adsp->px_supply); - regulator_disable(adsp->cx_supply); + disable_regulators(adsp); clk_disable_unprepare(adsp->aggre2_clk); clk_disable_unprepare(adsp->xo); adsp_pds_disable(adsp, adsp->proxy_pds, adsp->proxy_pd_count); + do_bus_scaling(adsp, false); } static int adsp_stop(struct rproc *rproc) @@ -244,18 +530,143 @@ static int adsp_stop(struct rproc *rproc) int handover; int ret; + trace_rproc_qcom_event(dev_name(adsp->dev), "adsp_stop", "enter"); + ret = qcom_q6v5_request_stop(&adsp->q6v5, adsp->sysmon); if (ret == -ETIMEDOUT) dev_err(adsp->dev, "timed out on wait\n"); - ret = qcom_scm_pas_shutdown(adsp->pas_id); + scm_pas_enable_bw(); + if (adsp->retry_shutdown) + ret = qcom_scm_pas_shutdown_retry(adsp->pas_id); + else + ret = qcom_scm_pas_shutdown(adsp->pas_id); if (ret) - dev_err(adsp->dev, "failed to shutdown: %d\n", ret); + panic("Panicking, remoteproc %s failed to shutdown.\n", rproc->name); + if (adsp->dtb_pas_id) { + ret = qcom_scm_pas_shutdown(adsp->dtb_pas_id); + if (ret) + panic("Panicking, remoteproc %s dtb failed to shutdown.\n", rproc->name); + } + + scm_pas_disable_bw(); + adsp_pds_disable(adsp, adsp->active_pds, adsp->active_pd_count); + if (adsp->qmp) + adsp_toggle_load_state(adsp->qmp, adsp->qmp_name, false); handover = qcom_q6v5_unprepare(&adsp->q6v5); if (handover) qcom_pas_handover(&adsp->q6v5); + trace_rproc_qcom_event(dev_name(adsp->dev), "adsp_stop", "exit"); + + return ret; +} + +static int adsp_attach(struct rproc *rproc) +{ + struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; + const struct firmware *fw; + int ret = 0; + int i; + + /* try to register fw for dumps; continue if we fail */ + ret = request_firmware(&fw, rproc->firmware, &rproc->dev); + if (ret < 0) { + dev_err(adsp->dev, "Failed to request DSP firmware\n"); + dev_err(adsp->dev, "Dumps will not be available\n"); + goto begin_attach; + } + + ret = qcom_register_dump_segments(rproc, fw); + if (ret) { + dev_err(adsp->dev, "Failed to register dump segments\n"); + dev_err(adsp->dev, "Dumps will not be available\n"); + } + release_firmware(fw); + +begin_attach: + qcom_q6v5_prepare(&adsp->q6v5); + + ret = do_bus_scaling(adsp, true); + if (ret < 0) + goto disable_irqs; + + ret = adsp_pds_enable(adsp, adsp->active_pds, adsp->active_pd_count); + if (ret < 0) + goto unscale_bus; + + if (!adsp->q6v5.rmb_base || + !readl_relaxed(adsp->q6v5.rmb_base + RMB_BOOT_WAIT_REG)) { + dev_err(adsp->dev, "Remote proc is not ready to attach\n"); + adsp_stop(rproc); + ret = -EBUSY; + goto disable_active_pds; + } + + ret = adsp_pds_enable(adsp, adsp->proxy_pds, adsp->proxy_pd_count); + if (ret < 0) + goto disable_active_pds; + + ret = adsp_toggle_load_state(adsp->qmp, adsp->qmp_name, true); + if (ret) + goto disable_proxy_pds; + + ret = clk_prepare_enable(adsp->xo); + if (ret) + goto disable_load_state; + + ret = clk_prepare_enable(adsp->aggre2_clk); + if (ret) + goto disable_xo_clk; + + ret = enable_regulators(adsp); + if (ret) + goto disable_aggre2_clk; + + /* Signal the Q6 to continue booting */ + for (i = 0; i < RMB_POLL_MAX_TIMES || timeout_disabled; i++) { + if (readl_relaxed(adsp->q6v5.rmb_base + RMB_BOOT_WAIT_REG)) { + writel_relaxed(1, adsp->q6v5.rmb_base + RMB_BOOT_CONT_REG); + break; + } + msleep(20); + } + + if (!readl_relaxed(adsp->q6v5.rmb_base + RMB_BOOT_WAIT_REG)) { + dev_err(adsp->dev, "Didn't get rmb signal from %s\n", rproc->name); + goto disable_regs; + } + + if (!timeout_disabled) { + ret = qcom_q6v5_wait_for_start(&adsp->q6v5, msecs_to_jiffies(5000)); + if (rproc->recovery_disabled && ret) { + panic("Panicking, remoteproc %s failed to bootup.\n", adsp->rproc->name); + } else if (ret == -ETIMEDOUT) { + dev_err(adsp->dev, "start timed out\n"); + goto disable_regs; + } + } + + return ret; + +disable_regs: + disable_regulators(adsp); +disable_aggre2_clk: + clk_disable_unprepare(adsp->aggre2_clk); +disable_xo_clk: + clk_disable_unprepare(adsp->xo); +disable_load_state: + adsp_toggle_load_state(adsp->qmp, adsp->qmp_name, false); +disable_proxy_pds: + adsp_pds_disable(adsp, adsp->proxy_pds, adsp->proxy_pd_count); +disable_active_pds: + adsp_pds_disable(adsp, adsp->active_pds, adsp->active_pd_count); +unscale_bus: + do_bus_scaling(adsp, false); +disable_irqs: + qcom_q6v5_unprepare(&adsp->q6v5); + return ret; } @@ -268,6 +679,9 @@ static void *adsp_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iom if (offset < 0 || offset + len > adsp->mem_size) return NULL; + if (is_iomem) + *is_iomem = true; + return adsp->mem_region + offset; } @@ -279,20 +693,20 @@ static unsigned long adsp_panic(struct rproc *rproc) } static const struct rproc_ops adsp_ops = { - .unprepare = adsp_unprepare, + .attach = adsp_attach, .start = adsp_start, .stop = adsp_stop, .da_to_va = adsp_da_to_va, - .parse_fw = qcom_register_dump_segments, .load = adsp_load, .panic = adsp_panic, }; static const struct rproc_ops adsp_minidump_ops = { - .unprepare = adsp_unprepare, + .attach = adsp_attach, .start = adsp_start, .stop = adsp_stop, .da_to_va = adsp_da_to_va, + .parse_fw = qcom_register_dump_segments, .load = adsp_load, .panic = adsp_panic, .coredump = adsp_minidump, @@ -326,14 +740,71 @@ static int adsp_init_clock(struct qcom_adsp *adsp) static int adsp_init_regulator(struct qcom_adsp *adsp) { - adsp->cx_supply = devm_regulator_get(adsp->dev, "cx"); - if (IS_ERR(adsp->cx_supply)) - return PTR_ERR(adsp->cx_supply); + int len; + int i, rc; + char uv_ua[50]; + u32 uv_ua_vals[2]; + const char *reg_name; - regulator_set_load(adsp->cx_supply, 100000); + adsp->reg_cnt = of_property_count_strings(adsp->dev->of_node, + "reg-names"); + if (adsp->reg_cnt <= 0) { + dev_err(adsp->dev, "No regulators added!\n"); + return 0; + } + + adsp->regs = devm_kzalloc(adsp->dev, + sizeof(struct reg_info) * adsp->reg_cnt, + GFP_KERNEL); + if (!adsp->regs) + return -ENOMEM; + + for (i = 0; i < adsp->reg_cnt; i++) { + of_property_read_string_index(adsp->dev->of_node, "reg-names", + i, ®_name); + + adsp->regs[i].reg = devm_regulator_get(adsp->dev, reg_name); + if (IS_ERR(adsp->regs[i].reg)) { + dev_err(adsp->dev, "failed to get %s reg\n", reg_name); + return PTR_ERR(adsp->regs[i].reg); + } + + /* Read current(uA) and voltage(uV) value */ + snprintf(uv_ua, sizeof(uv_ua), "%s-uV-uA", reg_name); + if (!of_find_property(adsp->dev->of_node, uv_ua, &len)) + continue; + + rc = of_property_read_u32_array(adsp->dev->of_node, uv_ua, + uv_ua_vals, + ARRAY_SIZE(uv_ua_vals)); + if (rc) { + dev_err(adsp->dev, "Failed to read uVuA value(rc:%d)\n", + rc); + return rc; + } + + if (uv_ua_vals[0] > 0) + adsp->regs[i].uV = uv_ua_vals[0]; + if (uv_ua_vals[1] > 0) + adsp->regs[i].uA = uv_ua_vals[1]; + } + return 0; +} + +static void adsp_init_bus_scaling(struct qcom_adsp *adsp) +{ + if (scm_perf_client) + goto get_rproc_client; + + scm_perf_client = of_icc_get(adsp->dev, "crypto_ddr"); + if (IS_ERR(scm_perf_client)) + dev_warn(adsp->dev, "Crypto scaling not setup\n"); + +get_rproc_client: + adsp->bus_client = of_icc_get(adsp->dev, "rproc_ddr"); + if (IS_ERR(adsp->bus_client)) + dev_warn(adsp->dev, "%s: No bus client\n", __func__); - adsp->px_supply = devm_regulator_get(adsp->dev, "px"); - return PTR_ERR_OR_ZERO(adsp->px_supply); } static int adsp_pds_attach(struct device *dev, struct device **devs, @@ -414,9 +885,53 @@ static int adsp_alloc_memory_region(struct qcom_adsp *adsp) return -EBUSY; } + if (!adsp->dtb_pas_id) + return 0; + + node = of_parse_phandle(adsp->dev->of_node, "memory-region", 1); + if (!node) { + dev_err(adsp->dev, "no dtb memory-region specified\n"); + return -EINVAL; + } + + ret = of_address_to_resource(node, 0, &r); + if (ret) + return ret; + + adsp->dtb_mem_phys = adsp->dtb_mem_reloc = r.start; + adsp->dtb_mem_size = resource_size(&r); + adsp->dtb_mem_region = devm_ioremap_wc(adsp->dev, adsp->dtb_mem_phys, adsp->dtb_mem_size); + if (!adsp->dtb_mem_region) { + dev_err(adsp->dev, "unable to map memory region: %pa+%zx\n", + &r.start, adsp->dtb_mem_size); + return -EBUSY; + } + return 0; } +static int adsp_setup_32b_dma_allocs(struct qcom_adsp *adsp) +{ + int ret; + + if (!adsp->dma_phys_below_32b) + return 0; + + ret = of_reserved_mem_device_init_by_idx(adsp->dev, adsp->dev->of_node, 2); + if (ret) { + dev_err(adsp->dev, + "Unable to get the CMA area for performing dma_alloc_* calls\n"); + goto out; + } + + ret = dma_set_mask_and_coherent(adsp->dev, DMA_BIT_MASK(32)); + if (ret) + dev_err(adsp->dev, "Unable to set the coherent mask to 32-bits!\n"); + +out: + return ret; +} + static int adsp_probe(struct platform_device *pdev) { const struct adsp_data *desc; @@ -425,6 +940,7 @@ static int adsp_probe(struct platform_device *pdev) const char *fw_name; const struct rproc_ops *ops = &adsp_ops; int ret; + bool signal_aop; desc = of_device_get_match_data(&pdev->dev); if (!desc) @@ -449,43 +965,87 @@ static int adsp_probe(struct platform_device *pdev) return -ENOMEM; } + rproc->recovery_disabled = true; rproc->auto_boot = desc->auto_boot; - rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); + if (desc->uses_elf64) + rproc_coredump_set_elf_info(rproc, ELFCLASS64, EM_NONE); + else + rproc_coredump_set_elf_info(rproc, ELFCLASS32, EM_NONE); adsp = (struct qcom_adsp *)rproc->priv; adsp->dev = &pdev->dev; adsp->rproc = rproc; adsp->minidump_id = desc->minidump_id; adsp->pas_id = desc->pas_id; + adsp->dtb_pas_id = desc->dtb_pas_id; + adsp->dtb_fw_name = desc->dtb_firmware_name; adsp->has_aggre2_clk = desc->has_aggre2_clk; adsp->info_name = desc->sysmon_name; + adsp->qmp_name = desc->qmp_name; + adsp->dma_phys_below_32b = desc->dma_phys_below_32b; + + if (desc->free_after_auth_reset) { + adsp->mdata = devm_kzalloc(adsp->dev, sizeof(struct qcom_mdt_metadata), GFP_KERNEL); + adsp->retry_shutdown = true; + } platform_set_drvdata(pdev, adsp); - device_wakeup_enable(adsp->dev); + ret = device_init_wakeup(adsp->dev, true); + if (ret) + goto free_rproc; ret = adsp_alloc_memory_region(adsp); if (ret) - goto free_rproc; + goto deinit_wakeup_source; + + ret = adsp_setup_32b_dma_allocs(adsp); + if (ret) + goto deinit_wakeup_source; ret = adsp_init_clock(adsp); if (ret) - goto free_rproc; + goto deinit_wakeup_source; ret = adsp_init_regulator(adsp); if (ret) - goto free_rproc; + goto deinit_wakeup_source; + + adsp_init_bus_scaling(adsp); + + ret = adsp_pds_attach(&pdev->dev, adsp->active_pds, + desc->active_pd_names); + if (ret < 0) + goto deinit_wakeup_source; + adsp->active_pd_count = ret; ret = adsp_pds_attach(&pdev->dev, adsp->proxy_pds, desc->proxy_pd_names); if (ret < 0) - goto free_rproc; + goto detach_active_pds; adsp->proxy_pd_count = ret; - ret = qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem, desc->load_state, + signal_aop = of_property_read_bool(pdev->dev.of_node, + "qcom,signal-aop"); + + if (signal_aop) { + adsp->qmp = qmp_get(adsp->dev); + if (IS_ERR_OR_NULL(adsp->qmp)) + goto detach_proxy_pds; + } + + ret = qcom_q6v5_init(&adsp->q6v5, pdev, rproc, desc->crash_reason_smem, qcom_pas_handover); + if (ret) goto detach_proxy_pds; + qcom_q6v5_register_ssr_subdev(&adsp->q6v5, &adsp->ssr_subdev.subdev); + + if (adsp->q6v5.rmb_base && + readl_relaxed(adsp->q6v5.rmb_base + RMB_Q6_BOOT_STATUS_REG)) + rproc->state = RPROC_DETACHED; + + timeout_disabled = qcom_pil_timeouts_disabled(); qcom_add_glink_subdev(rproc, &adsp->glink_subdev, desc->ssr_name); qcom_add_smd_subdev(rproc, &adsp->smd_subdev); qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); @@ -497,14 +1057,25 @@ static int adsp_probe(struct platform_device *pdev) goto detach_proxy_pds; } + ret = device_create_file(adsp->dev, &dev_attr_txn_id); + if (ret) + goto remove_subdevs; + ret = rproc_add(rproc); if (ret) - goto detach_proxy_pds; + goto remove_attr_txn_id; return 0; - +remove_attr_txn_id: + device_remove_file(adsp->dev, &dev_attr_txn_id); +remove_subdevs: + qcom_remove_sysmon_subdev(adsp->sysmon); detach_proxy_pds: adsp_pds_detach(adsp, adsp->proxy_pds, adsp->proxy_pd_count); +detach_active_pds: + adsp_pds_detach(adsp, adsp->active_pds, adsp->active_pd_count); +deinit_wakeup_source: + device_init_wakeup(adsp->dev, false); free_rproc: rproc_free(rproc); @@ -516,12 +1087,12 @@ static int adsp_remove(struct platform_device *pdev) struct qcom_adsp *adsp = platform_get_drvdata(pdev); rproc_del(adsp->rproc); - - qcom_q6v5_deinit(&adsp->q6v5); + device_remove_file(adsp->dev, &dev_attr_txn_id); qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); qcom_remove_sysmon_subdev(adsp->sysmon); qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev); qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev); + device_init_wakeup(adsp->dev, false); rproc_free(adsp->rproc); return 0; @@ -538,48 +1109,15 @@ static const struct adsp_data adsp_resource_init = { .ssctl_id = 0x14, }; -static const struct adsp_data sdm845_adsp_resource_init = { - .crash_reason_smem = 423, - .firmware_name = "adsp.mdt", - .pas_id = 1, - .has_aggre2_clk = false, - .auto_boot = true, - .load_state = "adsp", - .ssr_name = "lpass", - .sysmon_name = "adsp", - .ssctl_id = 0x14, -}; - -static const struct adsp_data sm6350_adsp_resource = { - .crash_reason_smem = 423, - .firmware_name = "adsp.mdt", - .pas_id = 1, - .has_aggre2_clk = false, - .auto_boot = true, - .proxy_pd_names = (char*[]){ - "lcx", - "lmx", - NULL - }, - .load_state = "adsp", - .ssr_name = "lpass", - .sysmon_name = "adsp", - .ssctl_id = 0x14, -}; - static const struct adsp_data sm8150_adsp_resource = { .crash_reason_smem = 423, .firmware_name = "adsp.mdt", .pas_id = 1, .has_aggre2_clk = false, .auto_boot = true, - .proxy_pd_names = (char*[]){ - "cx", - NULL - }, - .load_state = "adsp", .ssr_name = "lpass", .sysmon_name = "adsp", + .qmp_name = "adsp", .ssctl_id = 0x14, }; @@ -589,12 +1127,15 @@ static const struct adsp_data sm8250_adsp_resource = { .pas_id = 1, .has_aggre2_clk = false, .auto_boot = true, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, .proxy_pd_names = (char*[]){ "lcx", "lmx", NULL }, - .load_state = "adsp", .ssr_name = "lpass", .sysmon_name = "adsp", .ssctl_id = 0x14, @@ -606,18 +1147,62 @@ static const struct adsp_data sm8350_adsp_resource = { .pas_id = 1, .has_aggre2_clk = false, .auto_boot = true, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, .proxy_pd_names = (char*[]){ "lcx", "lmx", NULL }, - .load_state = "adsp", .ssr_name = "lpass", .sysmon_name = "adsp", .ssctl_id = 0x14, }; -static const struct adsp_data msm8996_adsp_resource = { +static const struct adsp_data waipio_adsp_resource = { + .crash_reason_smem = 423, + .firmware_name = "adsp.mdt", + .pas_id = 1, + .minidump_id = 5, + .uses_elf64 = true, + .has_aggre2_clk = false, + .auto_boot = false, + .ssr_name = "lpass", + .sysmon_name = "adsp", + .qmp_name = "adsp", + .ssctl_id = 0x14, +}; + +static const struct adsp_data kalama_adsp_resource = { + .crash_reason_smem = 423, + .firmware_name = "adsp.mdt", + .dtb_firmware_name = "adsp_dtb.mdt", + .pas_id = 1, + .dtb_pas_id = 0x24, + .minidump_id = 5, + .uses_elf64 = true, + .has_aggre2_clk = false, + .auto_boot = false, + .ssr_name = "lpass", + .sysmon_name = "adsp", + .qmp_name = "adsp", + .ssctl_id = 0x14, +}; + +static const struct adsp_data khaje_adsp_resource = { + .crash_reason_smem = 423, + .firmware_name = "adsp.mdt", + .pas_id = 1, + .minidump_id = 5, + .uses_elf64 = false, + .ssr_name = "lpass", + .sysmon_name = "adsp", + .ssctl_id = 0x14, +}; + +static const struct adsp_data msm8998_adsp_resource = { .crash_reason_smem = 423, .firmware_name = "adsp.mdt", .pas_id = 1, @@ -643,48 +1228,15 @@ static const struct adsp_data cdsp_resource_init = { .ssctl_id = 0x17, }; -static const struct adsp_data sdm845_cdsp_resource_init = { - .crash_reason_smem = 601, - .firmware_name = "cdsp.mdt", - .pas_id = 18, - .has_aggre2_clk = false, - .auto_boot = true, - .load_state = "cdsp", - .ssr_name = "cdsp", - .sysmon_name = "cdsp", - .ssctl_id = 0x17, -}; - -static const struct adsp_data sm6350_cdsp_resource = { - .crash_reason_smem = 601, - .firmware_name = "cdsp.mdt", - .pas_id = 18, - .has_aggre2_clk = false, - .auto_boot = true, - .proxy_pd_names = (char*[]){ - "cx", - "mx", - NULL - }, - .load_state = "cdsp", - .ssr_name = "cdsp", - .sysmon_name = "cdsp", - .ssctl_id = 0x17, -}; - static const struct adsp_data sm8150_cdsp_resource = { .crash_reason_smem = 601, .firmware_name = "cdsp.mdt", .pas_id = 18, .has_aggre2_clk = false, .auto_boot = true, - .proxy_pd_names = (char*[]){ - "cx", - NULL - }, - .load_state = "cdsp", .ssr_name = "cdsp", .sysmon_name = "cdsp", + .qmp_name = "cdsp", .ssctl_id = 0x17, }; @@ -694,16 +1246,53 @@ static const struct adsp_data sm8250_cdsp_resource = { .pas_id = 18, .has_aggre2_clk = false, .auto_boot = true, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, .proxy_pd_names = (char*[]){ "cx", NULL }, - .load_state = "cdsp", .ssr_name = "cdsp", .sysmon_name = "cdsp", .ssctl_id = 0x17, }; +static const struct adsp_data sm8350_cdsp_resource = { + .crash_reason_smem = 601, + .firmware_name = "cdsp.mdt", + .pas_id = 18, + .has_aggre2_clk = false, + .auto_boot = true, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, + .proxy_pd_names = (char*[]){ + "cx", + "mxc", + NULL + }, + .ssr_name = "cdsp", + .sysmon_name = "cdsp", + .ssctl_id = 0x17, +}; + +static const struct adsp_data waipio_cdsp_resource = { + .crash_reason_smem = 601, + .firmware_name = "cdsp.mdt", + .pas_id = 18, + .minidump_id = 7, + .uses_elf64 = true, + .has_aggre2_clk = false, + .auto_boot = false, + .ssr_name = "cdsp", + .sysmon_name = "cdsp", + .qmp_name = "cdsp", + .ssctl_id = 0x17, +}; + static const struct adsp_data sc8280xp_nsp0_resource = { .crash_reason_smem = 601, .firmware_name = "cdsp.mdt", @@ -734,18 +1323,28 @@ static const struct adsp_data sc8280xp_nsp1_resource = { .ssctl_id = 0x20, }; -static const struct adsp_data sm8350_cdsp_resource = { +static const struct adsp_data kalama_cdsp_resource = { + .crash_reason_smem = 601, + .firmware_name = "cdsp.mdt", + .dtb_firmware_name = "cdsp_dtb.mdt", + .pas_id = 18, + .dtb_pas_id = 0x25, + .minidump_id = 7, + .uses_elf64 = true, + .has_aggre2_clk = false, + .auto_boot = false, + .ssr_name = "cdsp", + .sysmon_name = "cdsp", + .qmp_name = "cdsp", + .ssctl_id = 0x17, +}; + +static const struct adsp_data khaje_cdsp_resource = { .crash_reason_smem = 601, .firmware_name = "cdsp.mdt", .pas_id = 18, - .has_aggre2_clk = false, - .auto_boot = true, - .proxy_pd_names = (char*[]){ - "cx", - "mxc", - NULL - }, - .load_state = "cdsp", + .minidump_id = 7, + .uses_elf64 = false, .ssr_name = "cdsp", .sysmon_name = "cdsp", .ssctl_id = 0x17, @@ -758,34 +1357,164 @@ static const struct adsp_data mpss_resource_init = { .minidump_id = 3, .has_aggre2_clk = false, .auto_boot = false, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, .proxy_pd_names = (char*[]){ "cx", "mss", NULL }, - .load_state = "modem", .ssr_name = "mpss", .sysmon_name = "modem", .ssctl_id = 0x12, }; -static const struct adsp_data sc8180x_mpss_resource = { +static const struct adsp_data waipio_mpss_resource = { .crash_reason_smem = 421, .firmware_name = "modem.mdt", .pas_id = 4, + .free_after_auth_reset = true, + .minidump_id = 3, + .uses_elf64 = true, .has_aggre2_clk = false, .auto_boot = false, - .proxy_pd_names = (char*[]){ - "cx", - NULL - }, - .load_state = "modem", + .ssr_name = "mpss", + .sysmon_name = "modem", + .qmp_name = "modem", + .ssctl_id = 0x12, +}; + +static const struct adsp_data kalama_mpss_resource = { + .crash_reason_smem = 421, + .firmware_name = "modem.mdt", + .dtb_firmware_name = "modem_dtb.mdt", + .pas_id = 4, + .dtb_pas_id = 0x26, + .free_after_auth_reset = true, + .minidump_id = 3, + .uses_elf64 = true, + .has_aggre2_clk = false, + .auto_boot = false, + .ssr_name = "mpss", + .sysmon_name = "modem", + .qmp_name = "modem", + .ssctl_id = 0x12, + .dma_phys_below_32b = true, +}; + +static const struct adsp_data cinder_mpss_resource = { + .crash_reason_smem = 421, + .firmware_name = "modem.mdt", + .pas_id = 4, + .free_after_auth_reset = true, + .minidump_id = 3, + .uses_elf64 = true, + .has_aggre2_clk = false, + .auto_boot = false, + .ssr_name = "mpss", + .sysmon_name = "modem", + .qmp_name = "modem", + .ssctl_id = 0x12, +}; + +static const struct adsp_data khaje_mpss_resource = { + .crash_reason_smem = 421, + .firmware_name = "modem.mdt", + .pas_id = 4, + .free_after_auth_reset = true, + .minidump_id = 3, + .uses_elf64 = true, .ssr_name = "mpss", .sysmon_name = "modem", .ssctl_id = 0x12, }; static const struct adsp_data slpi_resource_init = { + .crash_reason_smem = 424, + .firmware_name = "slpi.mdt", + .pas_id = 12, + .has_aggre2_clk = true, + .auto_boot = true, + .ssr_name = "dsps", + .sysmon_name = "slpi", + .ssctl_id = 0x16, +}; + +static const struct adsp_data sm8150_slpi_resource = { + .crash_reason_smem = 424, + .firmware_name = "slpi.mdt", + .pas_id = 12, + .has_aggre2_clk = false, + .auto_boot = true, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, + .proxy_pd_names = (char*[]){ + "lcx", + "lmx", + NULL + }, + .ssr_name = "dsps", + .sysmon_name = "slpi", + .ssctl_id = 0x16, +}; + +static const struct adsp_data sm8250_slpi_resource = { + .crash_reason_smem = 424, + .firmware_name = "slpi.mdt", + .pas_id = 12, + .has_aggre2_clk = false, + .auto_boot = true, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, + .proxy_pd_names = (char*[]){ + "lcx", + "lmx", + NULL + }, + .ssr_name = "dsps", + .sysmon_name = "slpi", + .ssctl_id = 0x16, +}; + +static const struct adsp_data sm8350_slpi_resource = { + .crash_reason_smem = 424, + .firmware_name = "slpi.mdt", + .pas_id = 12, + .has_aggre2_clk = false, + .auto_boot = true, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, + .proxy_pd_names = (char*[]){ + "lcx", + "lmx", + NULL + }, + .ssr_name = "dsps", + .sysmon_name = "slpi", + .ssctl_id = 0x16, +}; + +static const struct adsp_data waipio_slpi_resource = { + .crash_reason_smem = 424, + .firmware_name = "slpi.mdt", + .pas_id = 12, + .has_aggre2_clk = false, + .auto_boot = false, + .ssr_name = "dsps", + .sysmon_name = "slpi", + .qmp_name = "slpi", + .ssctl_id = 0x16, +}; + +static const struct adsp_data msm8998_slpi_resource = { .crash_reason_smem = 424, .firmware_name = "slpi.mdt", .pas_id = 12, @@ -800,57 +1529,6 @@ static const struct adsp_data slpi_resource_init = { .ssctl_id = 0x16, }; -static const struct adsp_data sm8150_slpi_resource = { - .crash_reason_smem = 424, - .firmware_name = "slpi.mdt", - .pas_id = 12, - .has_aggre2_clk = false, - .auto_boot = true, - .proxy_pd_names = (char*[]){ - "lcx", - "lmx", - NULL - }, - .load_state = "slpi", - .ssr_name = "dsps", - .sysmon_name = "slpi", - .ssctl_id = 0x16, -}; - -static const struct adsp_data sm8250_slpi_resource = { - .crash_reason_smem = 424, - .firmware_name = "slpi.mdt", - .pas_id = 12, - .has_aggre2_clk = false, - .auto_boot = true, - .proxy_pd_names = (char*[]){ - "lcx", - "lmx", - NULL - }, - .load_state = "slpi", - .ssr_name = "dsps", - .sysmon_name = "slpi", - .ssctl_id = 0x16, -}; - -static const struct adsp_data sm8350_slpi_resource = { - .crash_reason_smem = 424, - .firmware_name = "slpi.mdt", - .pas_id = 12, - .has_aggre2_clk = false, - .auto_boot = true, - .proxy_pd_names = (char*[]){ - "lcx", - "lmx", - NULL - }, - .load_state = "slpi", - .ssr_name = "dsps", - .sysmon_name = "slpi", - .ssctl_id = 0x16, -}; - static const struct adsp_data wcss_resource_init = { .crash_reason_smem = 421, .firmware_name = "wcnss.mdt", @@ -877,18 +1555,60 @@ static const struct adsp_data sdx55_mpss_resource = { .ssctl_id = 0x22, }; +static const struct adsp_data sc8180x_mpss_resource = { + .crash_reason_smem = 421, + .firmware_name = "modem.mdt", + .pas_id = 4, + .has_aggre2_clk = false, + .auto_boot = false, + .active_pd_names = (char*[]){ + "load_state", + NULL + }, + .proxy_pd_names = (char*[]){ + "cx", + NULL + }, + .ssr_name = "mpss", + .sysmon_name = "modem", + .ssctl_id = 0x12, +}; + +static const struct adsp_data sdmshrike_adsp_resource = { + .crash_reason_smem = 423, + .firmware_name = "adsp.mdt", + .pas_id = 1, + .has_aggre2_clk = false, + .auto_boot = true, + .ssr_name = "lpass", + .sysmon_name = "adsp", + .qmp_name = "adsp", + .ssctl_id = 0x14, +}; + +static const struct adsp_data sdmshrike_cdsp_resource = { + .crash_reason_smem = 601, + .firmware_name = "cdsp.mdt", + .pas_id = 18, + .has_aggre2_clk = false, + .auto_boot = true, + .ssr_name = "lpass", + .sysmon_name = "cdsp", + .qmp_name = "cdsp", + .ssctl_id = 0x17, +}; + static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,msm8226-adsp-pil", .data = &adsp_resource_init}, { .compatible = "qcom,msm8974-adsp-pil", .data = &adsp_resource_init}, - { .compatible = "qcom,msm8996-adsp-pil", .data = &msm8996_adsp_resource}, + { .compatible = "qcom,msm8996-adsp-pil", .data = &adsp_resource_init}, { .compatible = "qcom,msm8996-slpi-pil", .data = &slpi_resource_init}, - { .compatible = "qcom,msm8998-adsp-pas", .data = &msm8996_adsp_resource}, - { .compatible = "qcom,msm8998-slpi-pas", .data = &slpi_resource_init}, + { .compatible = "qcom,msm8998-adsp-pas", .data = &msm8998_adsp_resource}, + { .compatible = "qcom,msm8998-slpi-pas", .data = &msm8998_slpi_resource}, { .compatible = "qcom,qcs404-adsp-pas", .data = &adsp_resource_init }, { .compatible = "qcom,qcs404-cdsp-pas", .data = &cdsp_resource_init }, { .compatible = "qcom,qcs404-wcss-pas", .data = &wcss_resource_init }, { .compatible = "qcom,sc7180-mpss-pas", .data = &mpss_resource_init}, - { .compatible = "qcom,sc7280-mpss-pas", .data = &mpss_resource_init}, { .compatible = "qcom,sc8180x-adsp-pas", .data = &sm8150_adsp_resource}, { .compatible = "qcom,sc8180x-cdsp-pas", .data = &sm8150_cdsp_resource}, { .compatible = "qcom,sc8180x-mpss-pas", .data = &sc8180x_mpss_resource}, @@ -896,12 +1616,9 @@ static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,sc8280xp-nsp0-pas", .data = &sc8280xp_nsp0_resource}, { .compatible = "qcom,sc8280xp-nsp1-pas", .data = &sc8280xp_nsp1_resource}, { .compatible = "qcom,sdm660-adsp-pas", .data = &adsp_resource_init}, - { .compatible = "qcom,sdm845-adsp-pas", .data = &sdm845_adsp_resource_init}, - { .compatible = "qcom,sdm845-cdsp-pas", .data = &sdm845_cdsp_resource_init}, + { .compatible = "qcom,sdm845-adsp-pas", .data = &adsp_resource_init}, + { .compatible = "qcom,sdm845-cdsp-pas", .data = &cdsp_resource_init}, { .compatible = "qcom,sdx55-mpss-pas", .data = &sdx55_mpss_resource}, - { .compatible = "qcom,sm6350-adsp-pas", .data = &sm6350_adsp_resource}, - { .compatible = "qcom,sm6350-cdsp-pas", .data = &sm6350_cdsp_resource}, - { .compatible = "qcom,sm6350-mpss-pas", .data = &mpss_resource_init}, { .compatible = "qcom,sm8150-adsp-pas", .data = &sm8150_adsp_resource}, { .compatible = "qcom,sm8150-cdsp-pas", .data = &sm8150_cdsp_resource}, { .compatible = "qcom,sm8150-mpss-pas", .data = &mpss_resource_init}, @@ -913,10 +1630,19 @@ static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,sm8350-cdsp-pas", .data = &sm8350_cdsp_resource}, { .compatible = "qcom,sm8350-slpi-pas", .data = &sm8350_slpi_resource}, { .compatible = "qcom,sm8350-mpss-pas", .data = &mpss_resource_init}, - { .compatible = "qcom,sm8450-adsp-pas", .data = &sm8350_adsp_resource}, - { .compatible = "qcom,sm8450-cdsp-pas", .data = &sm8350_cdsp_resource}, - { .compatible = "qcom,sm8450-slpi-pas", .data = &sm8350_slpi_resource}, - { .compatible = "qcom,sm8450-mpss-pas", .data = &mpss_resource_init}, + { .compatible = "qcom,waipio-adsp-pas", .data = &waipio_adsp_resource}, + { .compatible = "qcom,waipio-cdsp-pas", .data = &waipio_cdsp_resource}, + { .compatible = "qcom,waipio-slpi-pas", .data = &waipio_slpi_resource}, + { .compatible = "qcom,waipio-modem-pas", .data = &waipio_mpss_resource}, + { .compatible = "qcom,kalama-adsp-pas", .data = &kalama_adsp_resource}, + { .compatible = "qcom,kalama-cdsp-pas", .data = &kalama_cdsp_resource}, + { .compatible = "qcom,kalama-modem-pas", .data = &kalama_mpss_resource}, + { .compatible = "qcom,cinder-modem-pas", .data = &cinder_mpss_resource}, + { .compatible = "qcom,khaje-adsp-pas", .data = &khaje_adsp_resource}, + { .compatible = "qcom,khaje-cdsp-pas", .data = &khaje_cdsp_resource}, + { .compatible = "qcom,khaje-modem-pas", .data = &khaje_mpss_resource}, + { .compatible = "qcom,sdmshrike-adsp-pas", .data = &sdmshrike_adsp_resource}, + { .compatible = "qcom,sdmshrike-cdsp-pas", .data = &sdmshrike_cdsp_resource}, { }, }; MODULE_DEVICE_TABLE(of, adsp_of_match); diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c index 9fca81492863..9e25614a912a 100644 --- a/drivers/remoteproc/qcom_sysmon.c +++ b/drivers/remoteproc/qcom_sysmon.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2017, Linaro Ltd. + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */ #include #include @@ -11,13 +12,27 @@ #include #include #include -#include #include +#include #include "qcom_common.h" +#define SYSMON_NOTIF_TIMEOUT CONFIG_RPROC_SYSMON_NOTIF_TIMEOUT + +#define SYSMON_SUBDEV_NAME "sysmon" + +static const char * const notif_timeout_msg = "sysmon msg from %s to %s for %s taking too long"; +static const char * const shutdown_timeout_msg = "sysmon_send_shutdown to %s taking too long"; + static BLOCKING_NOTIFIER_HEAD(sysmon_notifiers); +struct qcom_sysmon; + +struct notif_timeout_data { + struct qcom_sysmon *dest; + struct timer_list timer; +}; + struct qcom_sysmon { struct rproc_subdev subdev; struct rproc *rproc; @@ -33,9 +48,11 @@ struct qcom_sysmon { int ssctl_version; int ssctl_instance; + struct notif_timeout_data timeout_data; struct notifier_block nb; struct device *dev; + uint32_t transaction_id; struct rpmsg_endpoint *ept; struct completion comp; @@ -50,42 +67,29 @@ struct qcom_sysmon { struct sockaddr_qrtr ssctl; }; -enum { - SSCTL_SSR_EVENT_BEFORE_POWERUP, - SSCTL_SSR_EVENT_AFTER_POWERUP, - SSCTL_SSR_EVENT_BEFORE_SHUTDOWN, - SSCTL_SSR_EVENT_AFTER_SHUTDOWN, -}; - -static const char * const sysmon_state_string[] = { - [SSCTL_SSR_EVENT_BEFORE_POWERUP] = "before_powerup", - [SSCTL_SSR_EVENT_AFTER_POWERUP] = "after_powerup", - [SSCTL_SSR_EVENT_BEFORE_SHUTDOWN] = "before_shutdown", - [SSCTL_SSR_EVENT_AFTER_SHUTDOWN] = "after_shutdown", -}; - -struct sysmon_event { - const char *subsys_name; - u32 ssr_event; -}; - static DEFINE_MUTEX(sysmon_lock); static LIST_HEAD(sysmon_list); +uint32_t qcom_sysmon_get_txn_id(struct qcom_sysmon *sysmon) +{ + return sysmon->transaction_id; +} +EXPORT_SYMBOL(qcom_sysmon_get_txn_id); + /** * sysmon_send_event() - send notification of other remote's SSR event * @sysmon: sysmon context * @event: sysmon event context */ static void sysmon_send_event(struct qcom_sysmon *sysmon, - const struct sysmon_event *event) + const struct qcom_sysmon *source) { char req[50]; int len; int ret; - len = snprintf(req, sizeof(req), "ssr:%s:%s", event->subsys_name, - sysmon_state_string[event->ssr_event]); + len = scnprintf(req, sizeof(req), "ssr:%s:%s", source->name, + subdevice_state_string[source->state]); if (len >= sizeof(req)) return; @@ -175,6 +179,7 @@ static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count, #define SSCTL_SHUTDOWN_REQ 0x21 #define SSCTL_SHUTDOWN_READY_IND 0x21 #define SSCTL_SUBSYS_EVENT_REQ 0x23 +#define SSCTL_SUBSYS_EVENT_WITH_TID_REQ 0x25 #define SSCTL_MAX_MSG_LEN 7 @@ -202,22 +207,23 @@ static struct qmi_elem_info ssctl_shutdown_resp_ei[] = { {} }; -struct ssctl_subsys_event_req { +struct ssctl_subsys_event_with_tid_req { u8 subsys_name_len; char subsys_name[SSCTL_SUBSYS_NAME_LENGTH]; u32 event; + uint32_t transaction_id; u8 evt_driven_valid; u32 evt_driven; }; -static struct qmi_elem_info ssctl_subsys_event_req_ei[] = { +static struct qmi_elem_info ssctl_subsys_event_with_tid_req_ei[] = { { .data_type = QMI_DATA_LEN, .elem_len = 1, .elem_size = sizeof(uint8_t), .array_type = NO_ARRAY, .tlv_type = 0x01, - .offset = offsetof(struct ssctl_subsys_event_req, + .offset = offsetof(struct ssctl_subsys_event_with_tid_req, subsys_name_len), .ei_array = NULL, }, @@ -227,7 +233,7 @@ static struct qmi_elem_info ssctl_subsys_event_req_ei[] = { .elem_size = sizeof(char), .array_type = VAR_LEN_ARRAY, .tlv_type = 0x01, - .offset = offsetof(struct ssctl_subsys_event_req, + .offset = offsetof(struct ssctl_subsys_event_with_tid_req, subsys_name), .ei_array = NULL, }, @@ -237,17 +243,27 @@ static struct qmi_elem_info ssctl_subsys_event_req_ei[] = { .elem_size = sizeof(uint32_t), .array_type = NO_ARRAY, .tlv_type = 0x02, - .offset = offsetof(struct ssctl_subsys_event_req, + .offset = offsetof(struct ssctl_subsys_event_with_tid_req, event), .ei_array = NULL, }, + { + .data_type = QMI_UNSIGNED_4_BYTE, + .elem_len = 1, + .elem_size = sizeof(uint32_t), + .array_type = NO_ARRAY, + .tlv_type = 0x03, + .offset = offsetof(struct ssctl_subsys_event_with_tid_req, + transaction_id), + .ei_array = NULL, + }, { .data_type = QMI_OPT_FLAG, .elem_len = 1, .elem_size = sizeof(uint8_t), .array_type = NO_ARRAY, .tlv_type = 0x10, - .offset = offsetof(struct ssctl_subsys_event_req, + .offset = offsetof(struct ssctl_subsys_event_with_tid_req, evt_driven_valid), .ei_array = NULL, }, @@ -257,31 +273,32 @@ static struct qmi_elem_info ssctl_subsys_event_req_ei[] = { .elem_size = sizeof(uint32_t), .array_type = NO_ARRAY, .tlv_type = 0x10, - .offset = offsetof(struct ssctl_subsys_event_req, + .offset = offsetof(struct ssctl_subsys_event_with_tid_req, evt_driven), .ei_array = NULL, }, {} }; -struct ssctl_subsys_event_resp { +struct ssctl_subsys_event_with_tid_resp { struct qmi_response_type_v01 resp; }; -static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = { +static struct qmi_elem_info ssctl_subsys_event_with_tid_resp_ei[] = { { .data_type = QMI_STRUCT, .elem_len = 1, .elem_size = sizeof(struct qmi_response_type_v01), .array_type = NO_ARRAY, .tlv_type = 0x02, - .offset = offsetof(struct ssctl_subsys_event_resp, + .offset = offsetof(struct ssctl_subsys_event_with_tid_resp, resp), .ei_array = qmi_response_type_v01_ei, }, {} }; + static struct qmi_elem_info ssctl_shutdown_ind_ei[] = { {} }; @@ -294,7 +311,7 @@ static void sysmon_ind_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq, complete(&sysmon->ind_comp); } -static const struct qmi_msg_handler qmi_indication_handler[] = { +static struct qmi_msg_handler qmi_indication_handler[] = { { .type = QMI_INDICATION, .msg_id = SSCTL_SHUTDOWN_READY_IND, @@ -334,6 +351,9 @@ static bool ssctl_request_shutdown(struct qcom_sysmon *sysmon) bool acked = false; int ret; + if (sysmon->ssctl_instance == -EINVAL) + return false; + reinit_completion(&sysmon->ind_comp); reinit_completion(&sysmon->shutdown_comp); ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp); @@ -352,9 +372,9 @@ static bool ssctl_request_shutdown(struct qcom_sysmon *sysmon) ret = qmi_txn_wait(&txn, 5 * HZ); if (ret < 0) { - dev_err(sysmon->dev, "timeout waiting for shutdown response\n"); + dev_err(sysmon->dev, "failed receiving QMI response\n"); } else if (resp.resp.result) { - dev_err(sysmon->dev, "shutdown request rejected\n"); + dev_err(sysmon->dev, "shutdown request failed\n"); } else { dev_dbg(sysmon->dev, "shutdown request completed\n"); acked = true; @@ -372,43 +392,49 @@ static bool ssctl_request_shutdown(struct qcom_sysmon *sysmon) * @event: sysmon event context */ static void ssctl_send_event(struct qcom_sysmon *sysmon, - const struct sysmon_event *event) + const struct qcom_sysmon *source) { - struct ssctl_subsys_event_resp resp; - struct ssctl_subsys_event_req req; + struct ssctl_subsys_event_with_tid_resp resp; + struct ssctl_subsys_event_with_tid_req req; struct qmi_txn txn; int ret; + if (sysmon->ssctl_instance == -EINVAL) + return; + memset(&resp, 0, sizeof(resp)); - ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_subsys_event_resp_ei, &resp); + ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_subsys_event_with_tid_resp_ei, &resp); if (ret < 0) { dev_err(sysmon->dev, "failed to allocate QMI txn\n"); return; } memset(&req, 0, sizeof(req)); - strlcpy(req.subsys_name, event->subsys_name, sizeof(req.subsys_name)); + strscpy(req.subsys_name, source->name, sizeof(req.subsys_name)); req.subsys_name_len = strlen(req.subsys_name); - req.event = event->ssr_event; + req.event = source->state; req.evt_driven_valid = true; req.evt_driven = SSCTL_SSR_EVENT_FORCED; + req.transaction_id = sysmon->transaction_id; ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn, - SSCTL_SUBSYS_EVENT_REQ, 40, - ssctl_subsys_event_req_ei, &req); + SSCTL_SUBSYS_EVENT_WITH_TID_REQ, 40, + ssctl_subsys_event_with_tid_req_ei, &req); if (ret < 0) { - dev_err(sysmon->dev, "failed to send subsystem event\n"); + dev_err(sysmon->dev, "failed to send shutdown request\n"); qmi_txn_cancel(&txn); return; } ret = qmi_txn_wait(&txn, 5 * HZ); if (ret < 0) - dev_err(sysmon->dev, "timeout waiting for subsystem event response\n"); + dev_err(sysmon->dev, "failed receiving QMI response\n"); else if (resp.resp.result) - dev_err(sysmon->dev, "subsystem event rejected\n"); + dev_err(sysmon->dev, "failed to receive %s ssr %s event. response result: %d\n", + source->name, subdevice_state_string[source->state], + resp.resp.result); else - dev_dbg(sysmon->dev, "subsystem event accepted\n"); + dev_dbg(sysmon->dev, "ssr event send completed\n"); } /** @@ -465,18 +491,66 @@ static const struct qmi_ops ssctl_ops = { .del_server = ssctl_del_server, }; +static void sysmon_notif_timeout_handler(struct timer_list *t) +{ + struct notif_timeout_data *td = from_timer(td, t, timer); + struct qcom_sysmon *sysmon = container_of(td, struct qcom_sysmon, timeout_data); + + if (IS_ENABLED(CONFIG_QCOM_PANIC_ON_NOTIF_TIMEOUT) && + system_state != SYSTEM_RESTART && + system_state != SYSTEM_POWER_OFF && + system_state != SYSTEM_HALT && + !qcom_device_shutdown_in_progress) + panic(notif_timeout_msg, sysmon->name, td->dest->name, + subdevice_state_string[sysmon->state]); + else + WARN(1, notif_timeout_msg, sysmon->name, td->dest->name, + subdevice_state_string[sysmon->state]); +} + +static void sysmon_shutdown_notif_timeout_handler(struct timer_list *t) +{ + struct notif_timeout_data *td = from_timer(td, t, timer); + struct qcom_sysmon *sysmon = container_of(td, struct qcom_sysmon, timeout_data); + + if (IS_ENABLED(CONFIG_QCOM_PANIC_ON_NOTIF_TIMEOUT) && + system_state != SYSTEM_RESTART && + system_state != SYSTEM_POWER_OFF && + system_state != SYSTEM_HALT && + !qcom_device_shutdown_in_progress) + panic(shutdown_timeout_msg, sysmon->name); + else + WARN(1, shutdown_timeout_msg, sysmon->name); +} + +static inline void send_event(struct qcom_sysmon *sysmon, struct qcom_sysmon *source) +{ + unsigned long timeout; + + source->timeout_data.timer.function = sysmon_notif_timeout_handler; + source->timeout_data.dest = sysmon; + timeout = jiffies + msecs_to_jiffies(SYSMON_NOTIF_TIMEOUT); + mod_timer(&source->timeout_data.timer, timeout); + + /* Only SSCTL version 2 supports SSR events */ + if (sysmon->ssctl_version == 2) + ssctl_send_event(sysmon, source); + else if (sysmon->ept) + sysmon_send_event(sysmon, source); + + del_timer_sync(&source->timeout_data.timer); +} + static int sysmon_prepare(struct rproc_subdev *subdev) { struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev); - struct sysmon_event event = { - .subsys_name = sysmon->name, - .ssr_event = SSCTL_SSR_EVENT_BEFORE_POWERUP - }; + + trace_rproc_qcom_event(dev_name(sysmon->rproc->dev.parent), SYSMON_SUBDEV_NAME, "prepare"); mutex_lock(&sysmon->state_lock); - sysmon->state = SSCTL_SSR_EVENT_BEFORE_POWERUP; - blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); + sysmon->state = QCOM_SSR_BEFORE_POWERUP; + blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)sysmon); mutex_unlock(&sysmon->state_lock); return 0; @@ -496,29 +570,23 @@ static int sysmon_start(struct rproc_subdev *subdev) struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev); struct qcom_sysmon *target; - struct sysmon_event event = { - .subsys_name = sysmon->name, - .ssr_event = SSCTL_SSR_EVENT_AFTER_POWERUP - }; + + trace_rproc_qcom_event(dev_name(sysmon->rproc->dev.parent), SYSMON_SUBDEV_NAME, "start"); mutex_lock(&sysmon->state_lock); - sysmon->state = SSCTL_SSR_EVENT_AFTER_POWERUP; - blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); + sysmon->state = QCOM_SSR_AFTER_POWERUP; + blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)sysmon); mutex_unlock(&sysmon->state_lock); mutex_lock(&sysmon_lock); list_for_each_entry(target, &sysmon_list, node) { - if (target == sysmon) - continue; - mutex_lock(&target->state_lock); - event.subsys_name = target->name; - event.ssr_event = target->state; + if (target == sysmon || target->state != QCOM_SSR_AFTER_POWERUP) { + mutex_unlock(&target->state_lock); + continue; + } - if (sysmon->ssctl_version == 2) - ssctl_send_event(sysmon, &event); - else if (sysmon->ept) - sysmon_send_event(sysmon, &event); + send_event(sysmon, target); mutex_unlock(&target->state_lock); } mutex_unlock(&sysmon_lock); @@ -528,41 +596,51 @@ static int sysmon_start(struct rproc_subdev *subdev) static void sysmon_stop(struct rproc_subdev *subdev, bool crashed) { + unsigned long timeout; struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev); - struct sysmon_event event = { - .subsys_name = sysmon->name, - .ssr_event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN - }; + + trace_rproc_qcom_event(dev_name(sysmon->rproc->dev.parent), SYSMON_SUBDEV_NAME, + crashed ? "crash stop" : "stop"); sysmon->shutdown_acked = false; mutex_lock(&sysmon->state_lock); - sysmon->state = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN; - blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); + sysmon->state = QCOM_SSR_BEFORE_SHUTDOWN; + + sysmon->transaction_id++; + dev_info(sysmon->dev, "Incrementing tid for %s to %d\n", sysmon->name, + sysmon->transaction_id); + + blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)sysmon); mutex_unlock(&sysmon->state_lock); /* Don't request graceful shutdown if we've crashed */ if (crashed) return; + sysmon->timeout_data.timer.function = sysmon_shutdown_notif_timeout_handler; + timeout = jiffies + msecs_to_jiffies(SYSMON_NOTIF_TIMEOUT); + mod_timer(&sysmon->timeout_data.timer, timeout); + if (sysmon->ssctl_version) sysmon->shutdown_acked = ssctl_request_shutdown(sysmon); else if (sysmon->ept) sysmon->shutdown_acked = sysmon_request_shutdown(sysmon); + + del_timer_sync(&sysmon->timeout_data.timer); } static void sysmon_unprepare(struct rproc_subdev *subdev) { struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev); - struct sysmon_event event = { - .subsys_name = sysmon->name, - .ssr_event = SSCTL_SSR_EVENT_AFTER_SHUTDOWN - }; + + trace_rproc_qcom_event(dev_name(sysmon->rproc->dev.parent), SYSMON_SUBDEV_NAME, + "unprepare"); mutex_lock(&sysmon->state_lock); - sysmon->state = SSCTL_SSR_EVENT_AFTER_SHUTDOWN; - blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)&event); + sysmon->state = QCOM_SSR_AFTER_SHUTDOWN; + blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)sysmon); mutex_unlock(&sysmon->state_lock); } @@ -576,20 +654,16 @@ static int sysmon_notify(struct notifier_block *nb, unsigned long event, void *data) { struct qcom_sysmon *sysmon = container_of(nb, struct qcom_sysmon, nb); - struct sysmon_event *sysmon_event = data; + struct qcom_sysmon *source = data; /* Skip non-running rprocs and the originating instance */ - if (sysmon->state != SSCTL_SSR_EVENT_AFTER_POWERUP || - !strcmp(sysmon_event->subsys_name, sysmon->name)) { + if (sysmon->state != QCOM_SSR_AFTER_POWERUP || + !strcmp(source->name, sysmon->name)) { dev_dbg(sysmon->dev, "not notifying %s\n", sysmon->name); return NOTIFY_DONE; } - /* Only SSCTL version 2 supports SSR events */ - if (sysmon->ssctl_version == 2) - ssctl_send_event(sysmon, sysmon_event); - else if (sysmon->ept) - sysmon_send_event(sysmon, sysmon_event); + send_event(sysmon, source); return NOTIFY_DONE; } @@ -603,6 +677,140 @@ static irqreturn_t sysmon_shutdown_interrupt(int irq, void *data) return IRQ_HANDLED; } +#define QMI_SSCTL_GET_FAILURE_REASON_REQ 0x0022 +#define QMI_SSCTL_EMPTY_MSG_LENGTH 0 +#define QMI_SSCTL_ERROR_MSG_LENGTH 90 +#define QMI_EOTI_DATA_TYPE \ +{ \ + .data_type = QMI_EOTI, \ + .elem_len = 0, \ + .elem_size = 0, \ + .array_type = NO_ARRAY,\ + .tlv_type = 0x00, \ + .offset = 0, \ + .ei_array = NULL, \ +}, + +struct qmi_ssctl_get_failure_reason_resp_msg { + struct qmi_response_type_v01 resp; + uint8_t error_message_valid; + uint32_t error_message_len; + char error_message[QMI_SSCTL_ERROR_MSG_LENGTH]; +}; + +static struct qmi_elem_info qmi_ssctl_get_failure_reason_req_msg_ei[] = { + QMI_EOTI_DATA_TYPE +}; + +static struct qmi_elem_info qmi_ssctl_get_failure_reason_resp_msg_ei[] = { + { + .data_type = QMI_STRUCT, + .elem_len = 1, + .elem_size = sizeof(struct qmi_response_type_v01), + .array_type = NO_ARRAY, + .tlv_type = 0x02, + .offset = offsetof( + struct qmi_ssctl_get_failure_reason_resp_msg, + resp), + .ei_array = qmi_response_type_v01_ei, + }, + { + .data_type = QMI_OPT_FLAG, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct qmi_ssctl_get_failure_reason_resp_msg, + error_message_valid), + .ei_array = NULL, + }, + { + .data_type = QMI_DATA_LEN, + .elem_len = 1, + .elem_size = sizeof(uint8_t), + .array_type = NO_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct qmi_ssctl_get_failure_reason_resp_msg, + error_message_len), + .ei_array = NULL, + }, + { + .data_type = QMI_UNSIGNED_1_BYTE, + .elem_len = QMI_SSCTL_ERROR_MSG_LENGTH, + .elem_size = sizeof(char), + .array_type = VAR_LEN_ARRAY, + .tlv_type = 0x10, + .offset = offsetof( + struct qmi_ssctl_get_failure_reason_resp_msg, + error_message), + .ei_array = NULL, + }, + QMI_EOTI_DATA_TYPE +}; + +/** + * qcom_sysmon_get_reason() - Retrieve failure reason from a subsystem. + * @dest_desc: Subsystem descriptor of the subsystem to query + * @buf: Caller-allocated buffer for the returned NUL-terminated reason + * @len: Length of @buf + * + * Reverts to using legacy sysmon API (sysmon_get_reason_no_qmi()) if client + * handle is not set. + * + * Returns 0 for success, -EINVAL for an invalid destination, -ENODEV if + * the SMD transport channel is not open, -ETIMEDOUT if the destination + * subsystem does not respond, and -EPROTO if the destination subsystem + * responds with something unexpected. + * + */ +int qcom_sysmon_get_reason(struct qcom_sysmon *sysmon, char *buf, size_t len) +{ + char req = 0; + struct qmi_ssctl_get_failure_reason_resp_msg resp; + struct qmi_txn txn; + const char *dest_ss; + int ret; + + if (sysmon == NULL || buf == NULL || len == 0) + return -EINVAL; + + dest_ss = sysmon->name; + + ret = qmi_txn_init(&sysmon->qmi, &txn, qmi_ssctl_get_failure_reason_resp_msg_ei, + &resp); + if (ret < 0) { + pr_err("SYSMON QMI tx init failed to dest %s, ret - %d\n", dest_ss, ret); + goto out; + } + + ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn, + QMI_SSCTL_GET_FAILURE_REASON_REQ, + QMI_SSCTL_EMPTY_MSG_LENGTH, + qmi_ssctl_get_failure_reason_req_msg_ei, + &req); + if (ret < 0) { + pr_err("SYSMON QMI send req failed to dest %s, ret - %d\n", dest_ss, ret); + qmi_txn_cancel(&txn); + goto out; + } + + ret = qmi_txn_wait(&txn, 5 * HZ); + if (ret < 0) { + pr_err("SYSMON QMI qmi txn wait failed to dest %s, ret - %d\n", dest_ss, ret); + goto out; + } else if (resp.resp.result) { + dev_err(sysmon->dev, "failed to receive req. response result: %d\n", + resp.resp.result); + goto out; + } + strscpy(buf, resp.error_message, len); +out: + return ret; +} +EXPORT_SYMBOL(qcom_sysmon_get_reason); + /** * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc * @rproc: rproc context to associate the subdev with @@ -631,9 +839,13 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, init_completion(&sysmon->comp); init_completion(&sysmon->ind_comp); init_completion(&sysmon->shutdown_comp); + timer_setup(&sysmon->timeout_data.timer, sysmon_notif_timeout_handler, 0); mutex_init(&sysmon->lock); mutex_init(&sysmon->state_lock); + if (sysmon->ssctl_instance == -EINVAL) + goto add_subdev_callbacks; + sysmon->shutdown_irq = of_irq_get_byname(sysmon->dev->of_node, "shutdown-ack"); if (sysmon->shutdown_irq < 0) { @@ -665,6 +877,7 @@ struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc, qmi_add_lookup(&sysmon->qmi, 43, 0, 0); +add_subdev_callbacks: sysmon->subdev.prepare = sysmon_prepare; sysmon->subdev.start = sysmon_start; sysmon->subdev.stop = sysmon_stop; @@ -700,7 +913,8 @@ void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon) rproc_remove_subdev(sysmon->rproc, &sysmon->subdev); - qmi_handle_release(&sysmon->qmi); + if (sysmon->ssctl_instance != -EINVAL) + qmi_handle_release(&sysmon->qmi); kfree(sysmon); } diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c index 366db493579b..cb467198d2b7 100644 --- a/drivers/soc/qcom/mdt_loader.c +++ b/drivers/soc/qcom/mdt_loader.c @@ -4,7 +4,7 @@ * * Copyright (C) 2016 Linaro Ltd * Copyright (C) 2015 Sony Mobile Communications Inc - * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2012-2013, 2021 The Linux Foundation. All rights reserved. */ #include @@ -15,6 +15,7 @@ #include #include #include +#include #include static bool mdt_phdr_valid(const struct elf32_phdr *phdr) @@ -31,42 +32,24 @@ static bool mdt_phdr_valid(const struct elf32_phdr *phdr) return true; } -static ssize_t mdt_load_split_segment(void *ptr, const struct elf32_phdr *phdrs, - unsigned int segment, const char *fw_name, - struct device *dev) +static bool qcom_mdt_bins_are_split(const struct firmware *fw) { - const struct elf32_phdr *phdr = &phdrs[segment]; - const struct firmware *seg_fw; - char *seg_name; - ssize_t ret; + const struct elf32_phdr *phdrs; + const struct elf32_hdr *ehdr; + uint64_t seg_start, seg_end; + int i; - if (strlen(fw_name) < 4) - return -EINVAL; + ehdr = (struct elf32_hdr *)fw->data; + phdrs = (struct elf32_phdr *)(ehdr + 1); - seg_name = kstrdup(fw_name, GFP_KERNEL); - if (!seg_name) - return -ENOMEM; - - sprintf(seg_name + strlen(fw_name) - 3, "b%02d", segment); - ret = request_firmware_into_buf(&seg_fw, seg_name, dev, - ptr, phdr->p_filesz); - if (ret) { - dev_err(dev, "error %zd loading %s\n", ret, seg_name); - kfree(seg_name); - return ret; + for (i = 0; i < ehdr->e_phnum; i++) { + seg_start = phdrs[i].p_offset; + seg_end = phdrs[i].p_offset + phdrs[i].p_filesz; + if (seg_start > fw->size || seg_end > fw->size) + return true; } - if (seg_fw->size != phdr->p_filesz) { - dev_err(dev, - "failed to load segment %d from truncated file %s\n", - segment, seg_name); - ret = -EINVAL; - } - - release_firmware(seg_fw); - kfree(seg_name); - - return ret; + return false; } /** @@ -106,8 +89,9 @@ EXPORT_SYMBOL_GPL(qcom_mdt_get_size); /** * qcom_mdt_read_metadata() - read header and metadata from mdt or mbn - * @fw: firmware of mdt header or mbn - * @data_len: length of the read metadata blob + * @fw: firmware of mdt header or mbn + * @data_len: length of the read metadata blob + * @metadata_phys: phys address for the assigned metadata buffer * * The mechanism that performs the authentication of the loading firmware * expects an ELF header directly followed by the segment of hashes, with no @@ -121,18 +105,19 @@ EXPORT_SYMBOL_GPL(qcom_mdt_get_size); * * Return: pointer to data, or ERR_PTR() */ -void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len, - const char *fw_name, struct device *dev) +void *qcom_mdt_read_metadata(struct device *dev, const struct firmware *fw, const char *firmware, + size_t *data_len, bool dma_phys_below_32b, dma_addr_t *metadata_phys) { const struct elf32_phdr *phdrs; const struct elf32_hdr *ehdr; - unsigned int hash_segment = 0; - size_t hash_offset; + const struct firmware *seg_fw; + struct device *scm_dev = NULL; + size_t hash_index; size_t hash_size; size_t ehdr_size; - unsigned int i; - ssize_t ret; + char *fw_name; void *data; + int ret; ehdr = (struct elf32_hdr *)fw->data; phdrs = (struct elf32_phdr *)(ehdr + 1); @@ -143,131 +128,99 @@ void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len, if (phdrs[0].p_type == PT_LOAD) return ERR_PTR(-EINVAL); - for (i = 1; i < ehdr->e_phnum; i++) { - if ((phdrs[i].p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) { - hash_segment = i; + for (hash_index = 1; hash_index < ehdr->e_phnum; hash_index++) { + if (phdrs[hash_index].p_type != PT_LOAD && + (phdrs[hash_index].p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH) break; - } } - - if (!hash_segment) { - dev_err(dev, "no hash segment found in %s\n", fw_name); + if (hash_index >= ehdr->e_phnum) return ERR_PTR(-EINVAL); - } ehdr_size = phdrs[0].p_filesz; - hash_size = phdrs[hash_segment].p_filesz; + hash_size = phdrs[hash_index].p_filesz; + + /* + * During the scm call memory protection will be enabled for the metadata + * blob, so make sure it's physically contiguous, 4K aligned and + * non-cachable to avoid XPU violations. + */ + if (metadata_phys) { + if (!dma_phys_below_32b) { + scm_dev = qcom_get_scm_device(); + if (!scm_dev) + return ERR_PTR(-EPROBE_DEFER); + data = dma_alloc_coherent(scm_dev, ehdr_size + hash_size, + metadata_phys, GFP_KERNEL); + } else { + data = dma_alloc_coherent(dev, ehdr_size + hash_size, + metadata_phys, GFP_KERNEL); + } + } else { + data = kmalloc(ehdr_size + hash_size, GFP_KERNEL); + } - data = kmalloc(ehdr_size + hash_size, GFP_KERNEL); if (!data) return ERR_PTR(-ENOMEM); - /* Copy ELF header */ + /* copy elf header */ memcpy(data, fw->data, ehdr_size); - if (ehdr_size + hash_size == fw->size) { - /* Firmware is split and hash is packed following the ELF header */ - hash_offset = phdrs[0].p_filesz; - memcpy(data + ehdr_size, fw->data + hash_offset, hash_size); - } else if (phdrs[hash_segment].p_offset + hash_size <= fw->size) { - /* Hash is in its own segment, but within the loaded file */ - hash_offset = phdrs[hash_segment].p_offset; - memcpy(data + ehdr_size, fw->data + hash_offset, hash_size); - } else { - /* Hash is in its own segment, beyond the loaded file */ - ret = mdt_load_split_segment(data + ehdr_size, phdrs, hash_segment, fw_name, dev); - if (ret) { - kfree(data); - return ERR_PTR(ret); + if (qcom_mdt_bins_are_split(fw)) { + fw_name = kstrdup(firmware, GFP_KERNEL); + if (!fw_name) { + ret = -ENOMEM; + goto free_metadata; + } + snprintf(fw_name + strlen(fw_name) - 3, 4, "b%02d", hash_index); + + ret = request_firmware_into_buf(&seg_fw, fw_name, dev, data + ehdr_size, hash_size); + kfree(fw_name); + + if (ret) + goto free_metadata; + + release_firmware(seg_fw); + } else { + memcpy(data + ehdr_size, fw->data + phdrs[hash_index].p_offset, hash_size); } *data_len = ehdr_size + hash_size; return data; +free_metadata: + if (metadata_phys) { + if (!dma_phys_below_32b) + dma_free_coherent(scm_dev, ehdr_size + hash_size, data, *metadata_phys); + else + dma_free_coherent(dev, ehdr_size + hash_size, data, *metadata_phys); + } else + kfree(data); + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(qcom_mdt_read_metadata); -/** - * qcom_mdt_pas_init() - initialize PAS region for firmware loading - * @dev: device handle to associate resources with - * @fw: firmware object for the mdt file - * @firmware: name of the firmware, for construction of segment file names - * @pas_id: PAS identifier - * @mem_phys: physical address of allocated memory region - * @ctx: PAS metadata context, to be released by caller - * - * Returns 0 on success, negative errno otherwise. - */ -int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_metadata *ctx) -{ - const struct elf32_phdr *phdrs; - const struct elf32_phdr *phdr; - const struct elf32_hdr *ehdr; - phys_addr_t min_addr = PHYS_ADDR_MAX; - phys_addr_t max_addr = 0; - size_t metadata_len; - void *metadata; - int ret; - int i; - - ehdr = (struct elf32_hdr *)fw->data; - phdrs = (struct elf32_phdr *)(ehdr + 1); - - for (i = 0; i < ehdr->e_phnum; i++) { - phdr = &phdrs[i]; - - if (!mdt_phdr_valid(phdr)) - continue; - - if (phdr->p_paddr < min_addr) - min_addr = phdr->p_paddr; - - if (phdr->p_paddr + phdr->p_memsz > max_addr) - max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); - } - - metadata = qcom_mdt_read_metadata(fw, &metadata_len, fw_name, dev); - if (IS_ERR(metadata)) { - ret = PTR_ERR(metadata); - dev_err(dev, "error %d reading firmware %s metadata\n", ret, fw_name); - goto out; - } - - ret = qcom_scm_pas_init_image(pas_id, metadata, metadata_len, ctx); - kfree(metadata); - if (ret) { - /* Invalid firmware metadata */ - dev_err(dev, "error %d initializing firmware %s\n", ret, fw_name); - goto out; - } - - ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, max_addr - min_addr); - if (ret) { - /* Unable to set up relocation */ - dev_err(dev, "error %d setting up firmware %s\n", ret, fw_name); - goto out; - } - -out: - return ret; -} -EXPORT_SYMBOL_GPL(qcom_mdt_pas_init); - -static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, void *mem_region, - phys_addr_t mem_phys, size_t mem_size, - phys_addr_t *reloc_base, bool pas_init) +static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, const char *firmware, + int pas_id, void *mem_region, phys_addr_t mem_phys, size_t mem_size, + phys_addr_t *reloc_base, bool pas_init, bool dma_phys_below_32b, + struct qcom_mdt_metadata *mdata) { const struct elf32_phdr *phdrs; const struct elf32_phdr *phdr; const struct elf32_hdr *ehdr; + const struct firmware *seg_fw; phys_addr_t mem_reloc; phys_addr_t min_addr = PHYS_ADDR_MAX; + phys_addr_t max_addr = 0; + dma_addr_t metadata_phys = 0; + struct device *scm_dev = NULL; + size_t metadata_len = 0; + size_t fw_name_len; ssize_t offset; + void *metadata = NULL; + char *fw_name; bool relocate = false; + bool is_split; void *ptr; int ret = 0; int i; @@ -275,9 +228,41 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, if (!fw || !mem_region || !mem_phys || !mem_size) return -EINVAL; + is_split = qcom_mdt_bins_are_split(fw); ehdr = (struct elf32_hdr *)fw->data; phdrs = (struct elf32_phdr *)(ehdr + 1); + fw_name_len = strlen(firmware); + if (fw_name_len <= 4) + return -EINVAL; + + fw_name = kstrdup(firmware, GFP_KERNEL); + if (!fw_name) + return -ENOMEM; + + if (pas_init) { + metadata = qcom_mdt_read_metadata(dev, fw, firmware, &metadata_len, + dma_phys_below_32b, &metadata_phys); + if (IS_ERR(metadata)) { + ret = PTR_ERR(metadata); + dev_err(dev, "error %d reading firmware %s metadata\n", + ret, fw_name); + goto out; + } + + if (mdata) { + mdata->buf = metadata; + mdata->buf_phys = metadata_phys; + mdata->size = metadata_len; + } + + ret = qcom_scm_pas_init_image(pas_id, metadata_phys); + if (ret) { + dev_err(dev, "invalid firmware metadata\n"); + goto deinit; + } + } + for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; @@ -289,9 +274,21 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, if (phdr->p_paddr < min_addr) min_addr = phdr->p_paddr; + + if (phdr->p_paddr + phdr->p_memsz > max_addr) + max_addr = ALIGN(phdr->p_paddr + phdr->p_memsz, SZ_4K); } if (relocate) { + if (pas_init) { + ret = qcom_scm_pas_mem_setup(pas_id, mem_phys, + max_addr - min_addr); + if (ret) { + dev_err(dev, "unable to setup relocation\n"); + goto deinit; + } + } + /* * The image is relocatable, so offset each segment based on * the lowest segment address. @@ -328,22 +325,31 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, ptr = mem_region + offset; - if (phdr->p_filesz && phdr->p_offset < fw->size && - phdr->p_offset + phdr->p_filesz <= fw->size) { - /* Firmware is large enough to be non-split */ - if (phdr->p_offset + phdr->p_filesz > fw->size) { - dev_err(dev, "file %s segment %d would be truncated\n", - fw_name, i); - ret = -EINVAL; - break; - } + if (phdr->p_filesz) { + if (!is_split) { + /* Firmware is large enough to be non-split */ + memcpy(ptr, fw->data + phdr->p_offset, phdr->p_filesz); + } else { + /* Firmware not large enough, load split-out segments */ + snprintf(fw_name + fw_name_len - 3, 4, "b%02d", i); + ret = request_firmware_into_buf(&seg_fw, fw_name, dev, + ptr, phdr->p_filesz); + if (ret) { + dev_err(dev, "failed to load %s\n", fw_name); + break; + } - memcpy(ptr, fw->data + phdr->p_offset, phdr->p_filesz); - } else if (phdr->p_filesz) { - /* Firmware not large enough, load split-out segments */ - ret = mdt_load_split_segment(ptr, phdrs, i, fw_name, dev); - if (ret) - break; + if (seg_fw->size != phdr->p_filesz) { + dev_err(dev, + "failed to load segment %d from truncated file %s\n", + i, fw_name); + release_firmware(seg_fw); + ret = -EINVAL; + break; + } + + release_firmware(seg_fw); + } } if (phdr->p_memsz > phdr->p_filesz) @@ -352,7 +358,24 @@ static int __qcom_mdt_load(struct device *dev, const struct firmware *fw, if (reloc_base) *reloc_base = mem_reloc; +deinit: + if (ret) + qcom_scm_pas_shutdown(pas_id); + if (!mdata && pas_init) { + if (dma_phys_below_32b) { + dma_free_coherent(dev, metadata_len, metadata, metadata_phys); + } else { + scm_dev = qcom_get_scm_device(); + if (!scm_dev) + goto out; + + dma_free_coherent(scm_dev, metadata_len, metadata, metadata_phys); + } + } + +out: + kfree(fw_name); return ret; } @@ -374,14 +397,8 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw, phys_addr_t mem_phys, size_t mem_size, phys_addr_t *reloc_base) { - int ret; - - ret = qcom_mdt_pas_init(dev, fw, firmware, pas_id, mem_phys, NULL); - if (ret) - return ret; - return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, - mem_size, reloc_base, true); + mem_size, reloc_base, true, false, NULL); } EXPORT_SYMBOL_GPL(qcom_mdt_load); @@ -404,9 +421,68 @@ int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, size_t mem_size, phys_addr_t *reloc_base) { return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, - mem_size, reloc_base, false); + mem_size, reloc_base, false, false, NULL); } EXPORT_SYMBOL_GPL(qcom_mdt_load_no_init); +/** + * qcom_mdt_load_no_free() - load the firmware which header is loaded as fw + * @dev: device handle to associate resources with + * @fw: firmware object for the mdt file + * @firmware: name of the firmware, for construction of segment file names + * @pas_id: PAS identifier + * @mem_region: allocated memory region to load firmware into + * @mem_phys: physical address of allocated memory region + * @mem_size: size of the allocated memory region + * @reloc_base: adjusted physical address after relocation + * + * This function is essentially the same as qcom_mdt_load. The only difference + * between the two is that the metadata is not freed at the end of this call. + * The client must call qcom_mdt_free_metadata for cleanup. + * + * Returns 0 on success, negative errno otherwise. + */ +int qcom_mdt_load_no_free(struct device *dev, const struct firmware *fw, const char *firmware, + int pas_id, void *mem_region, phys_addr_t mem_phys, size_t mem_size, + phys_addr_t *reloc_base, bool dma_phys_below_32b, + struct qcom_mdt_metadata *metadata) +{ + return __qcom_mdt_load(dev, fw, firmware, pas_id, mem_region, mem_phys, + mem_size, reloc_base, true, dma_phys_below_32b, metadata); +} +EXPORT_SYMBOL(qcom_mdt_load_no_free); + +/** + * qcom_mdt_free_metadata() - free the firmware metadata + * @dev: device handle to associate resources with + * @pas_id: PAS identifier + * @mdata: reference to metadata region to be freed + * @err: whether this call was made after an error occurred + * + * Free the metadata that was allocated by mdt loader. + * + */ +void qcom_mdt_free_metadata(struct device *dev, int pas_id, struct qcom_mdt_metadata *mdata, + bool dma_phys_below_32b, int err) +{ + struct device *scm_dev; + + if (err && qcom_scm_pas_shutdown_retry(pas_id)) + panic("Panicking, failed to shutdown peripheral %d\n", pas_id); + if (mdata) { + if (!dma_phys_below_32b) { + scm_dev = qcom_get_scm_device(); + if (!scm_dev) { + pr_err("%s: scm_dev has not been created!\n", __func__); + return; + } + dma_free_coherent(scm_dev, mdata->size, mdata->buf, mdata->buf_phys); + } else { + dma_free_coherent(dev, mdata->size, mdata->buf, mdata->buf_phys); + } + } +} +EXPORT_SYMBOL(qcom_mdt_free_metadata); + MODULE_DESCRIPTION("Firmware parser for Qualcomm MDT format"); MODULE_LICENSE("GPL v2"); diff --git a/include/linux/remoteproc/qcom_rproc.h b/include/linux/remoteproc/qcom_rproc.h index 82b211518136..ca9fad78d68f 100644 --- a/include/linux/remoteproc/qcom_rproc.h +++ b/include/linux/remoteproc/qcom_rproc.h @@ -1,3 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + #ifndef __QCOM_RPROC_H__ #define __QCOM_RPROC_H__ @@ -27,6 +32,8 @@ struct qcom_ssr_notify_data { #if IS_ENABLED(CONFIG_QCOM_RPROC_COMMON) void *qcom_register_ssr_notifier(const char *name, struct notifier_block *nb); +void *qcom_register_early_ssr_notifier(const char *name, struct notifier_block *nb); +int qcom_unregister_early_ssr_notifier(void *notify, struct notifier_block *nb); int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb); #else @@ -37,6 +44,17 @@ static inline void *qcom_register_ssr_notifier(const char *name, return NULL; } +static inline void *qcom_register_early_ssr_notifier(const char *name, struct notifier_block *nb) +{ + return NULL; +} + +static inline int qcom_unregister_early_ssr_notifier(void *notify, + struct notifier_block *nb) +{ + return 0; +} + static inline int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb) { diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h index 9e8e60421192..4c15fd6997c6 100644 --- a/include/linux/soc/qcom/mdt_loader.h +++ b/include/linux/soc/qcom/mdt_loader.h @@ -1,4 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ #ifndef __QCOM_MDT_LOADER_H__ #define __QCOM_MDT_LOADER_H__ @@ -10,14 +13,16 @@ struct device; struct firmware; -struct qcom_scm_pas_metadata; + +struct qcom_mdt_metadata { + void *buf; + dma_addr_t buf_phys; + size_t size; +}; #if IS_ENABLED(CONFIG_QCOM_MDT_LOADER) ssize_t qcom_mdt_get_size(const struct firmware *fw); -int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_metadata *pas_metadata_ctx); int qcom_mdt_load(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, void *mem_region, phys_addr_t mem_phys, size_t mem_size, @@ -27,8 +32,15 @@ int qcom_mdt_load_no_init(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, void *mem_region, phys_addr_t mem_phys, size_t mem_size, phys_addr_t *reloc_base); -void *qcom_mdt_read_metadata(const struct firmware *fw, size_t *data_len, - const char *fw_name, struct device *dev); +void *qcom_mdt_read_metadata(struct device *dev, const struct firmware *fw, + const char *firmware, size_t *data_len, bool dma_phys_below_32b, + dma_addr_t *metadata_phys); +int qcom_mdt_load_no_free(struct device *dev, const struct firmware *fw, const char *firmware, + int pas_id, void *mem_region, phys_addr_t mem_phys, size_t mem_size, + phys_addr_t *reloc_base, bool dma_phys_below_32b, + struct qcom_mdt_metadata *metadata); +void qcom_mdt_free_metadata(struct device *dev, int pas_id, struct qcom_mdt_metadata *mdata, + bool dma_phys_below_32b, int err); #else /* !IS_ENABLED(CONFIG_QCOM_MDT_LOADER) */ @@ -37,13 +49,6 @@ static inline ssize_t qcom_mdt_get_size(const struct firmware *fw) return -ENODEV; } -static inline int qcom_mdt_pas_init(struct device *dev, const struct firmware *fw, - const char *fw_name, int pas_id, phys_addr_t mem_phys, - struct qcom_scm_pas_metadata *pas_metadata_ctx) -{ - return -ENODEV; -} - static inline int qcom_mdt_load(struct device *dev, const struct firmware *fw, const char *fw_name, int pas_id, void *mem_region, phys_addr_t mem_phys, @@ -62,11 +67,23 @@ static inline int qcom_mdt_load_no_init(struct device *dev, return -ENODEV; } -static inline void *qcom_mdt_read_metadata(const struct firmware *fw, - size_t *data_len, const char *fw_name, - struct device *dev) +void *qcom_mdt_read_metadata(struct device *dev, const struct firmware *fw, + const char *firmware, size_t *data_len, bool dma_phys_below_32b, + dma_addr_t *metadata_phys) { - return ERR_PTR(-ENODEV); + return NULL; +} +int qcom_mdt_load_no_free(struct device *dev, const struct firmware *fw, const char *firmware, + int pas_id, void *mem_region, phys_addr_t mem_phys, size_t mem_size, + phys_addr_t *reloc_base, bool dma_phys_below_32b, + struct qcom_mdt_metadata *metadata) +{ + return -ENODEV; +} +void qcom_mdt_free_metadata(struct device *dev, int pas_id, struct qcom_mdt_metadata *mdata, + bool dma_phys_below_32b, int err) +{ + return; } #endif /* !IS_ENABLED(CONFIG_QCOM_MDT_LOADER) */ diff --git a/include/trace/events/rproc_qcom.h b/include/trace/events/rproc_qcom.h new file mode 100644 index 000000000000..a530518f1821 --- /dev/null +++ b/include/trace/events/rproc_qcom.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM rproc_qcom + +#if !defined(_TRACE_RPROC_QCOM_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_RPROC_QCOM_H +#include + +TRACE_EVENT(rproc_qcom_event, + + TP_PROTO(const char *name, const char *event, const char *subevent), + + TP_ARGS(name, event, subevent), + + TP_STRUCT__entry( + __string(name, name) + __string(event, event) + __string(subevent, subevent) + ), + + TP_fast_assign( + __assign_str(name, name); + __assign_str(event, event); + __assign_str(subevent, subevent); + ), + + TP_printk("%s: %s: %s", __get_str(name), __get_str(event), __get_str(subevent)) +); +#endif /* _TRACE_RPROC_QCOM_H */ + +/* This part must be outside protection */ +#include