^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Qualcomm Peripheral Image Loader helpers
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2016 Linaro Ltd
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (C) 2015 Sony Mobile Communications Inc
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/firmware.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/notifier.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/remoteproc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/remoteproc/qcom_rproc.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/rpmsg/qcom_glink.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/rpmsg/qcom_smd.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <linux/soc/qcom/mdt_loader.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #include "remoteproc_internal.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) #include "qcom_common.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) #define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) struct qcom_ssr_subsystem {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) const char *name;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) struct srcu_notifier_head notifier_list;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) struct list_head list;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) static LIST_HEAD(qcom_ssr_subsystem_list);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) static DEFINE_MUTEX(qcom_ssr_subsys_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) static int glink_subdev_start(struct rproc_subdev *subdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) glink->edge = qcom_glink_smem_register(glink->dev, glink->node);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) return PTR_ERR_OR_ZERO(glink->edge);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) static void glink_subdev_stop(struct rproc_subdev *subdev, bool crashed)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) qcom_glink_smem_unregister(glink->edge);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) glink->edge = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) static void glink_subdev_unprepare(struct rproc_subdev *subdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) qcom_glink_ssr_notify(glink->ssr_name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) * qcom_add_glink_subdev() - try to add a GLINK subdevice to rproc
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) * @rproc: rproc handle to parent the subdevice
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) * @glink: reference to a GLINK subdev context
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) * @ssr_name: identifier of the associated remoteproc for ssr notifications
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) const char *ssr_name)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) struct device *dev = &rproc->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) glink->node = of_get_child_by_name(dev->parent->of_node, "glink-edge");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) if (!glink->node)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) glink->ssr_name = kstrdup_const(ssr_name, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) if (!glink->ssr_name)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) glink->dev = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) glink->subdev.start = glink_subdev_start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) glink->subdev.stop = glink_subdev_stop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) glink->subdev.unprepare = glink_subdev_unprepare;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) rproc_add_subdev(rproc, &glink->subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) EXPORT_SYMBOL_GPL(qcom_add_glink_subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) * qcom_remove_glink_subdev() - remove a GLINK subdevice from rproc
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) * @rproc: rproc handle
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) * @glink: reference to a GLINK subdev context
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) if (!glink->node)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) rproc_remove_subdev(rproc, &glink->subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) kfree_const(glink->ssr_name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) of_node_put(glink->node);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) * qcom_register_dump_segments() - register segments for coredump
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) * @rproc: remoteproc handle
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) * @fw: firmware header
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) * Register all segments of the ELF in the remoteproc coredump segment list
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) * Return: 0 on success, negative errno on failure.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) int qcom_register_dump_segments(struct rproc *rproc,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) const struct firmware *fw)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) const struct elf32_phdr *phdrs;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) const struct elf32_phdr *phdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) const struct elf32_hdr *ehdr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) ehdr = (struct elf32_hdr *)fw->data;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) phdrs = (struct elf32_phdr *)(ehdr + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) for (i = 0; i < ehdr->e_phnum; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) phdr = &phdrs[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) if (phdr->p_type != PT_LOAD)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) if (!phdr->p_memsz)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) ret = rproc_coredump_add_segment(rproc, phdr->p_paddr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) phdr->p_memsz);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) EXPORT_SYMBOL_GPL(qcom_register_dump_segments);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) static int smd_subdev_start(struct rproc_subdev *subdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) smd->edge = qcom_smd_register_edge(smd->dev, smd->node);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) return PTR_ERR_OR_ZERO(smd->edge);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) static void smd_subdev_stop(struct rproc_subdev *subdev, bool crashed)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) qcom_smd_unregister_edge(smd->edge);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) smd->edge = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) * qcom_add_smd_subdev() - try to add a SMD subdevice to rproc
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) * @rproc: rproc handle to parent the subdevice
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) * @smd: reference to a Qualcomm subdev context
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172) struct device *dev = &rproc->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) smd->node = of_get_child_by_name(dev->parent->of_node, "smd-edge");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) if (!smd->node)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) smd->dev = dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) smd->subdev.start = smd_subdev_start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) smd->subdev.stop = smd_subdev_stop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) rproc_add_subdev(rproc, &smd->subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) EXPORT_SYMBOL_GPL(qcom_add_smd_subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) * qcom_remove_smd_subdev() - remove the smd subdevice from rproc
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) * @rproc: rproc handle
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) * @smd: the SMD subdevice to remove
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) if (!smd->node)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) rproc_remove_subdev(rproc, &smd->subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) of_node_put(smd->node);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) static struct qcom_ssr_subsystem *qcom_ssr_get_subsys(const char *name)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) struct qcom_ssr_subsystem *info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) mutex_lock(&qcom_ssr_subsys_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) /* Match in the global qcom_ssr_subsystem_list with name */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) list_for_each_entry(info, &qcom_ssr_subsystem_list, list)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) if (!strcmp(info->name, name))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) info = kzalloc(sizeof(*info), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) if (!info) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) info = ERR_PTR(-ENOMEM);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) goto out;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) info->name = kstrdup_const(name, GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) srcu_init_notifier_head(&info->notifier_list);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) /* Add to global notification list */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) list_add_tail(&info->list, &qcom_ssr_subsystem_list);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) out:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) mutex_unlock(&qcom_ssr_subsys_lock);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) return info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) * qcom_register_ssr_notifier() - register SSR notification handler
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) * @name: Subsystem's SSR name
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) * @nb: notifier_block to be invoked upon subsystem's state change
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) * This registers the @nb notifier block as part the notifier chain for a
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233) * remoteproc associated with @name. The notifier block's callback
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) * will be invoked when the remote processor's SSR events occur
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) * (pre/post startup and pre/post shutdown).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237) * Return: a subsystem cookie on success, ERR_PTR on failure.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 239) void *qcom_register_ssr_notifier(const char *name, struct notifier_block *nb)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 240) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 241) struct qcom_ssr_subsystem *info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 242)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 243) info = qcom_ssr_get_subsys(name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 244) if (IS_ERR(info))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 245) return info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 246)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 247) srcu_notifier_chain_register(&info->notifier_list, nb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 248)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 249) return &info->notifier_list;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 250) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 251) EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 252)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 253) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 254) * qcom_unregister_ssr_notifier() - unregister SSR notification handler
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 255) * @notify: subsystem cookie returned from qcom_register_ssr_notifier
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 256) * @nb: notifier_block to unregister
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 257) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 258) * This function will unregister the notifier from the particular notifier
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 259) * chain.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 260) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 261) * Return: 0 on success, %ENOENT otherwise.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 262) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 263) int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 264) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 265) return srcu_notifier_chain_unregister(notify, nb);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 266) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 267) EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 268)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 269) static int ssr_notify_prepare(struct rproc_subdev *subdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 270) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 271) struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 272) struct qcom_ssr_notify_data data = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 273) .name = ssr->info->name,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 274) .crashed = false,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 275) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 276)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 277) srcu_notifier_call_chain(&ssr->info->notifier_list,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 278) QCOM_SSR_BEFORE_POWERUP, &data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 279) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 280) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 281)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 282) static int ssr_notify_start(struct rproc_subdev *subdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 283) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 284) struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 285) struct qcom_ssr_notify_data data = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 286) .name = ssr->info->name,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 287) .crashed = false,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 288) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 289)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 290) srcu_notifier_call_chain(&ssr->info->notifier_list,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 291) QCOM_SSR_AFTER_POWERUP, &data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 292) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 293) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 294)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 295) static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 296) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 297) struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 298) struct qcom_ssr_notify_data data = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 299) .name = ssr->info->name,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 300) .crashed = crashed,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 301) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 302)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 303) srcu_notifier_call_chain(&ssr->info->notifier_list,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 304) QCOM_SSR_BEFORE_SHUTDOWN, &data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 305) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 306)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 307) static void ssr_notify_unprepare(struct rproc_subdev *subdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 308) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 309) struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 310) struct qcom_ssr_notify_data data = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 311) .name = ssr->info->name,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 312) .crashed = false,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 313) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 314)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 315) srcu_notifier_call_chain(&ssr->info->notifier_list,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 316) QCOM_SSR_AFTER_SHUTDOWN, &data);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 317) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 318)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 319) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 320) * qcom_add_ssr_subdev() - register subdevice as restart notification source
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 321) * @rproc: rproc handle
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 322) * @ssr: SSR subdevice handle
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 323) * @ssr_name: identifier to use for notifications originating from @rproc
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 324) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 325) * As the @ssr is registered with the @rproc SSR events will be sent to all
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 326) * registered listeners for the remoteproc when it's SSR events occur
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 327) * (pre/post startup and pre/post shutdown).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 328) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 329) void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 330) const char *ssr_name)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 331) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 332) struct qcom_ssr_subsystem *info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 333)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 334) info = qcom_ssr_get_subsys(ssr_name);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 335) if (IS_ERR(info)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 336) dev_err(&rproc->dev, "Failed to add ssr subdevice\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 337) return;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 338) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 339)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 340) ssr->info = info;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 341) ssr->subdev.prepare = ssr_notify_prepare;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 342) ssr->subdev.start = ssr_notify_start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 343) ssr->subdev.stop = ssr_notify_stop;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 344) ssr->subdev.unprepare = ssr_notify_unprepare;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 345)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 346) rproc_add_subdev(rproc, &ssr->subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 347) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 348) EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 349)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 350) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 351) * qcom_remove_ssr_subdev() - remove subdevice as restart notification source
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 352) * @rproc: rproc handle
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 353) * @ssr: SSR subdevice handle
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 354) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 355) void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 356) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 357) rproc_remove_subdev(rproc, &ssr->subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 358) ssr->info = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 359) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 360) EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 361)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 362) MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 363) MODULE_LICENSE("GPL v2");