blob: 48b399af9c8391c6d19cfbea2a06fd6182c570ce [file] [log] [blame]
Thomas Gleixner1802d0b2019-05-27 08:55:21 +02001// SPDX-License-Identifier: GPL-2.0-only
Bjorn Anderssonbde440e2017-01-27 02:28:32 -08002/*
3 * Qualcomm Peripheral Image Loader helpers
4 *
5 * Copyright (C) 2016 Linaro Ltd
6 * Copyright (C) 2015 Sony Mobile Communications Inc
7 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Bjorn Anderssonbde440e2017-01-27 02:28:32 -08008 */
9
10#include <linux/firmware.h>
11#include <linux/kernel.h>
12#include <linux/module.h>
Bjorn Andersson1e140df2017-07-24 22:56:43 -070013#include <linux/notifier.h>
Bjorn Anderssonbde440e2017-01-27 02:28:32 -080014#include <linux/remoteproc.h>
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -070015#include <linux/remoteproc/qcom_rproc.h>
Bjorn Anderssoneea07022017-08-29 16:13:35 -070016#include <linux/rpmsg/qcom_glink.h>
Bjorn Anderssonb90fcfc2017-01-27 07:04:54 -080017#include <linux/rpmsg/qcom_smd.h>
Sarangdhar Joshidcb57ed2018-01-05 16:04:20 -080018#include <linux/soc/qcom/mdt_loader.h>
Bjorn Anderssonbde440e2017-01-27 02:28:32 -080019
20#include "remoteproc_internal.h"
21#include "qcom_common.h"
22
Bjorn Anderssoneea07022017-08-29 16:13:35 -070023#define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev)
Bjorn Anderssonb90fcfc2017-01-27 07:04:54 -080024#define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
Bjorn Andersson1e140df2017-07-24 22:56:43 -070025#define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev)
26
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -070027struct qcom_ssr_subsystem {
28 const char *name;
29 struct srcu_notifier_head notifier_list;
30 struct list_head list;
31};
32
33static LIST_HEAD(qcom_ssr_subsystem_list);
34static DEFINE_MUTEX(qcom_ssr_subsys_lock);
Bjorn Anderssonb90fcfc2017-01-27 07:04:54 -080035
Alex Elder6f8b0372018-06-26 07:11:58 -050036static int glink_subdev_start(struct rproc_subdev *subdev)
Bjorn Anderssoneea07022017-08-29 16:13:35 -070037{
38 struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
39
40 glink->edge = qcom_glink_smem_register(glink->dev, glink->node);
41
Vasyl Gomonovycha1fcc452017-11-28 23:32:40 +010042 return PTR_ERR_OR_ZERO(glink->edge);
Bjorn Anderssoneea07022017-08-29 16:13:35 -070043}
44
Alex Elder6f8b0372018-06-26 07:11:58 -050045static void glink_subdev_stop(struct rproc_subdev *subdev, bool crashed)
Bjorn Anderssoneea07022017-08-29 16:13:35 -070046{
47 struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
48
49 qcom_glink_smem_unregister(glink->edge);
50 glink->edge = NULL;
51}
52
Bjorn Andersson5d1f2e32020-04-22 17:37:34 -070053static void glink_subdev_unprepare(struct rproc_subdev *subdev)
54{
55 struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
56
57 qcom_glink_ssr_notify(glink->ssr_name);
58}
59
Bjorn Anderssoneea07022017-08-29 16:13:35 -070060/**
61 * qcom_add_glink_subdev() - try to add a GLINK subdevice to rproc
62 * @rproc: rproc handle to parent the subdevice
63 * @glink: reference to a GLINK subdev context
Bjorn Anderssoncd9fc8f2020-04-22 17:37:33 -070064 * @ssr_name: identifier of the associated remoteproc for ssr notifications
Bjorn Anderssoneea07022017-08-29 16:13:35 -070065 */
Bjorn Anderssoncd9fc8f2020-04-22 17:37:33 -070066void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink,
67 const char *ssr_name)
Bjorn Anderssoneea07022017-08-29 16:13:35 -070068{
69 struct device *dev = &rproc->dev;
70
71 glink->node = of_get_child_by_name(dev->parent->of_node, "glink-edge");
72 if (!glink->node)
73 return;
74
Bjorn Anderssoncd9fc8f2020-04-22 17:37:33 -070075 glink->ssr_name = kstrdup_const(ssr_name, GFP_KERNEL);
76 if (!glink->ssr_name)
77 return;
78
Bjorn Anderssoneea07022017-08-29 16:13:35 -070079 glink->dev = dev;
Alex Elder6f8b0372018-06-26 07:11:58 -050080 glink->subdev.start = glink_subdev_start;
81 glink->subdev.stop = glink_subdev_stop;
Bjorn Andersson5d1f2e32020-04-22 17:37:34 -070082 glink->subdev.unprepare = glink_subdev_unprepare;
Bjorn Andersson49026762018-06-26 07:11:57 -050083
84 rproc_add_subdev(rproc, &glink->subdev);
Bjorn Anderssoneea07022017-08-29 16:13:35 -070085}
86EXPORT_SYMBOL_GPL(qcom_add_glink_subdev);
87
88/**
89 * qcom_remove_glink_subdev() - remove a GLINK subdevice from rproc
90 * @rproc: rproc handle
91 * @glink: reference to a GLINK subdev context
92 */
93void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink)
94{
Sibi Sankar730b2ad2018-04-03 23:45:15 +053095 if (!glink->node)
96 return;
97
Bjorn Anderssoneea07022017-08-29 16:13:35 -070098 rproc_remove_subdev(rproc, &glink->subdev);
Bjorn Anderssoncd9fc8f2020-04-22 17:37:33 -070099 kfree_const(glink->ssr_name);
Bjorn Anderssoneea07022017-08-29 16:13:35 -0700100 of_node_put(glink->node);
101}
102EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev);
103
Sarangdhar Joshidcb57ed2018-01-05 16:04:20 -0800104/**
105 * qcom_register_dump_segments() - register segments for coredump
106 * @rproc: remoteproc handle
107 * @fw: firmware header
108 *
109 * Register all segments of the ELF in the remoteproc coredump segment list
110 *
111 * Return: 0 on success, negative errno on failure.
112 */
113int qcom_register_dump_segments(struct rproc *rproc,
114 const struct firmware *fw)
115{
116 const struct elf32_phdr *phdrs;
117 const struct elf32_phdr *phdr;
118 const struct elf32_hdr *ehdr;
119 int ret;
120 int i;
121
122 ehdr = (struct elf32_hdr *)fw->data;
123 phdrs = (struct elf32_phdr *)(ehdr + 1);
124
125 for (i = 0; i < ehdr->e_phnum; i++) {
126 phdr = &phdrs[i];
127
128 if (phdr->p_type != PT_LOAD)
129 continue;
130
131 if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
132 continue;
133
134 if (!phdr->p_memsz)
135 continue;
136
137 ret = rproc_coredump_add_segment(rproc, phdr->p_paddr,
138 phdr->p_memsz);
139 if (ret)
140 return ret;
141 }
142
143 return 0;
144}
145EXPORT_SYMBOL_GPL(qcom_register_dump_segments);
146
Alex Elder6f8b0372018-06-26 07:11:58 -0500147static int smd_subdev_start(struct rproc_subdev *subdev)
Bjorn Anderssonb90fcfc2017-01-27 07:04:54 -0800148{
149 struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
150
151 smd->edge = qcom_smd_register_edge(smd->dev, smd->node);
152
Himanshu Jhac76929b2017-08-29 19:13:18 +0530153 return PTR_ERR_OR_ZERO(smd->edge);
Bjorn Anderssonb90fcfc2017-01-27 07:04:54 -0800154}
155
Alex Elder6f8b0372018-06-26 07:11:58 -0500156static void smd_subdev_stop(struct rproc_subdev *subdev, bool crashed)
Bjorn Anderssonb90fcfc2017-01-27 07:04:54 -0800157{
158 struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
159
160 qcom_smd_unregister_edge(smd->edge);
161 smd->edge = NULL;
162}
163
164/**
165 * qcom_add_smd_subdev() - try to add a SMD subdevice to rproc
166 * @rproc: rproc handle to parent the subdevice
167 * @smd: reference to a Qualcomm subdev context
168 */
169void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
170{
171 struct device *dev = &rproc->dev;
172
173 smd->node = of_get_child_by_name(dev->parent->of_node, "smd-edge");
174 if (!smd->node)
175 return;
176
177 smd->dev = dev;
Alex Elder6f8b0372018-06-26 07:11:58 -0500178 smd->subdev.start = smd_subdev_start;
179 smd->subdev.stop = smd_subdev_stop;
Bjorn Andersson49026762018-06-26 07:11:57 -0500180
181 rproc_add_subdev(rproc, &smd->subdev);
Bjorn Anderssonb90fcfc2017-01-27 07:04:54 -0800182}
183EXPORT_SYMBOL_GPL(qcom_add_smd_subdev);
184
185/**
186 * qcom_remove_smd_subdev() - remove the smd subdevice from rproc
187 * @rproc: rproc handle
188 * @smd: the SMD subdevice to remove
189 */
190void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
191{
Sibi Sankar730b2ad2018-04-03 23:45:15 +0530192 if (!smd->node)
193 return;
194
Bjorn Anderssonb90fcfc2017-01-27 07:04:54 -0800195 rproc_remove_subdev(rproc, &smd->subdev);
196 of_node_put(smd->node);
197}
198EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev);
199
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700200static struct qcom_ssr_subsystem *qcom_ssr_get_subsys(const char *name)
201{
202 struct qcom_ssr_subsystem *info;
203
204 mutex_lock(&qcom_ssr_subsys_lock);
205 /* Match in the global qcom_ssr_subsystem_list with name */
206 list_for_each_entry(info, &qcom_ssr_subsystem_list, list)
207 if (!strcmp(info->name, name))
208 goto out;
209
210 info = kzalloc(sizeof(*info), GFP_KERNEL);
211 if (!info) {
212 info = ERR_PTR(-ENOMEM);
213 goto out;
214 }
215 info->name = kstrdup_const(name, GFP_KERNEL);
216 srcu_init_notifier_head(&info->notifier_list);
217
218 /* Add to global notification list */
219 list_add_tail(&info->list, &qcom_ssr_subsystem_list);
220
221out:
222 mutex_unlock(&qcom_ssr_subsys_lock);
223 return info;
224}
225
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700226/**
227 * qcom_register_ssr_notifier() - register SSR notification handler
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700228 * @name: Subsystem's SSR name
229 * @nb: notifier_block to be invoked upon subsystem's state change
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700230 *
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700231 * This registers the @nb notifier block as part the notifier chain for a
232 * remoteproc associated with @name. The notifier block's callback
233 * will be invoked when the remote processor's SSR events occur
234 * (pre/post startup and pre/post shutdown).
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700235 *
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700236 * Return: a subsystem cookie on success, ERR_PTR on failure.
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700237 */
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700238void *qcom_register_ssr_notifier(const char *name, struct notifier_block *nb)
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700239{
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700240 struct qcom_ssr_subsystem *info;
241
242 info = qcom_ssr_get_subsys(name);
243 if (IS_ERR(info))
244 return info;
245
246 srcu_notifier_chain_register(&info->notifier_list, nb);
247
248 return &info->notifier_list;
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700249}
250EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier);
251
252/**
253 * qcom_unregister_ssr_notifier() - unregister SSR notification handler
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700254 * @notify: subsystem cookie returned from qcom_register_ssr_notifier
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700255 * @nb: notifier_block to unregister
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700256 *
257 * This function will unregister the notifier from the particular notifier
258 * chain.
259 *
260 * Return: 0 on success, %ENOENT otherwise.
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700261 */
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700262int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb)
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700263{
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700264 return srcu_notifier_chain_unregister(notify, nb);
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700265}
266EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier);
267
Bjorn Andersson1417dba2019-07-15 22:03:27 -0700268static void ssr_notify_unprepare(struct rproc_subdev *subdev)
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700269{
270 struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700271 struct qcom_ssr_notify_data data = {
272 .name = ssr->info->name,
273 .crashed = false,
274 };
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700275
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700276 srcu_notifier_call_chain(&ssr->info->notifier_list, 0, &data);
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700277}
278
279/**
280 * qcom_add_ssr_subdev() - register subdevice as restart notification source
281 * @rproc: rproc handle
282 * @ssr: SSR subdevice handle
283 * @ssr_name: identifier to use for notifications originating from @rproc
284 *
285 * As the @ssr is registered with the @rproc SSR events will be sent to all
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700286 * registered listeners for the remoteproc when it's SSR events occur
287 * (pre/post startup and pre/post shutdown).
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700288 */
289void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
290 const char *ssr_name)
291{
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700292 struct qcom_ssr_subsystem *info;
293
294 info = qcom_ssr_get_subsys(ssr_name);
295 if (IS_ERR(info)) {
296 dev_err(&rproc->dev, "Failed to add ssr subdevice\n");
297 return;
298 }
299
300 ssr->info = info;
Bjorn Andersson1417dba2019-07-15 22:03:27 -0700301 ssr->subdev.unprepare = ssr_notify_unprepare;
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700302
Bjorn Andersson49026762018-06-26 07:11:57 -0500303 rproc_add_subdev(rproc, &ssr->subdev);
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700304}
305EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev);
306
307/**
308 * qcom_remove_ssr_subdev() - remove subdevice as restart notification source
309 * @rproc: rproc handle
310 * @ssr: SSR subdevice handle
311 */
312void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr)
313{
314 rproc_remove_subdev(rproc, &ssr->subdev);
Rishabh Bhatnagar5abfe5c2020-06-23 19:23:27 -0700315 ssr->info = NULL;
Bjorn Andersson1e140df2017-07-24 22:56:43 -0700316}
317EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev);
318
Bjorn Anderssonbde440e2017-01-27 02:28:32 -0800319MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver");
320MODULE_LICENSE("GPL v2");