aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/msm/adreno/a5xx_gpu.c4
-rw-r--r--drivers/media/platform/qcom/venus/firmware.c2
-rw-r--r--drivers/remoteproc/Kconfig19
-rw-r--r--drivers/remoteproc/Makefile1
-rw-r--r--drivers/remoteproc/imx_rproc.c23
-rw-r--r--drivers/remoteproc/qcom_adsp_pil.c20
-rw-r--r--drivers/remoteproc/qcom_common.c56
-rw-r--r--drivers/remoteproc/qcom_common.h23
-rw-r--r--drivers/remoteproc/qcom_q6v5_pil.c9
-rw-r--r--drivers/remoteproc/qcom_sysmon.c579
-rw-r--r--drivers/remoteproc/qcom_wcnss.c11
-rw-r--r--drivers/remoteproc/remoteproc_core.c152
-rw-r--r--drivers/remoteproc/remoteproc_internal.h7
-rw-r--r--drivers/soc/qcom/Kconfig2
-rw-r--r--drivers/soc/qcom/mdt_loader.c7
-rw-r--r--include/linux/remoteproc.h27
-rw-r--r--include/linux/soc/qcom/mdt_loader.h3
-rw-r--r--samples/Kconfig10
-rw-r--r--samples/Makefile2
-rw-r--r--samples/qmi/Makefile1
-rw-r--r--samples/qmi/qmi_sample_client.c622
21 files changed, 1524 insertions, 56 deletions
diff --git a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
index a4f68affc13b..d39400e5bc42 100644
--- a/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a5xx_gpu.c
@@ -89,14 +89,14 @@ static int zap_shader_load_mdt(struct msm_gpu *gpu, const char *fwname)
89 */ 89 */
90 if (to_adreno_gpu(gpu)->fwloc == FW_LOCATION_LEGACY) { 90 if (to_adreno_gpu(gpu)->fwloc == FW_LOCATION_LEGACY) {
91 ret = qcom_mdt_load(dev, fw, fwname, GPU_PAS_ID, 91 ret = qcom_mdt_load(dev, fw, fwname, GPU_PAS_ID,
92 mem_region, mem_phys, mem_size); 92 mem_region, mem_phys, mem_size, NULL);
93 } else { 93 } else {
94 char newname[strlen("qcom/") + strlen(fwname) + 1]; 94 char newname[strlen("qcom/") + strlen(fwname) + 1];
95 95
96 sprintf(newname, "qcom/%s", fwname); 96 sprintf(newname, "qcom/%s", fwname);
97 97
98 ret = qcom_mdt_load(dev, fw, newname, GPU_PAS_ID, 98 ret = qcom_mdt_load(dev, fw, newname, GPU_PAS_ID,
99 mem_region, mem_phys, mem_size); 99 mem_region, mem_phys, mem_size, NULL);
100 } 100 }
101 if (ret) 101 if (ret)
102 goto out; 102 goto out;
diff --git a/drivers/media/platform/qcom/venus/firmware.c b/drivers/media/platform/qcom/venus/firmware.c
index 521d4b36c090..c4a577848dd7 100644
--- a/drivers/media/platform/qcom/venus/firmware.c
+++ b/drivers/media/platform/qcom/venus/firmware.c
@@ -76,7 +76,7 @@ int venus_boot(struct device *dev, const char *fwname)
76 } 76 }
77 77
78 ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID, mem_va, mem_phys, 78 ret = qcom_mdt_load(dev, mdt, fwname, VENUS_PAS_ID, mem_va, mem_phys,
79 mem_size); 79 mem_size, NULL);
80 80
81 release_firmware(mdt); 81 release_firmware(mdt);
82 82
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index b609e1d3654b..027274008b08 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -6,6 +6,7 @@ config REMOTEPROC
6 select CRC32 6 select CRC32
7 select FW_LOADER 7 select FW_LOADER
8 select VIRTIO 8 select VIRTIO
9 select WANT_DEV_COREDUMP
9 help 10 help
10 Support for remote processors (such as DSP coprocessors). These 11 Support for remote processors (such as DSP coprocessors). These
11 are mainly used on embedded systems. 12 are mainly used on embedded systems.
@@ -90,6 +91,7 @@ config QCOM_ADSP_PIL
90 depends on QCOM_SMEM 91 depends on QCOM_SMEM
91 depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) 92 depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
92 depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n 93 depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
94 depends on QCOM_SYSMON || QCOM_SYSMON=n
93 select MFD_SYSCON 95 select MFD_SYSCON
94 select QCOM_MDT_LOADER 96 select QCOM_MDT_LOADER
95 select QCOM_RPROC_COMMON 97 select QCOM_RPROC_COMMON
@@ -107,6 +109,7 @@ config QCOM_Q6V5_PIL
107 depends on QCOM_SMEM 109 depends on QCOM_SMEM
108 depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) 110 depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
109 depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n 111 depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
112 depends on QCOM_SYSMON || QCOM_SYSMON=n
110 select MFD_SYSCON 113 select MFD_SYSCON
111 select QCOM_RPROC_COMMON 114 select QCOM_RPROC_COMMON
112 select QCOM_SCM 115 select QCOM_SCM
@@ -114,12 +117,28 @@ config QCOM_Q6V5_PIL
114 Say y here to support the Qualcomm Peripherial Image Loader for the 117 Say y here to support the Qualcomm Peripherial Image Loader for the
115 Hexagon V5 based remote processors. 118 Hexagon V5 based remote processors.
116 119
120config QCOM_SYSMON
121 tristate "Qualcomm sysmon driver"
122 depends on RPMSG
123 depends on ARCH_QCOM
124 depends on NET
125 select QCOM_QMI_HELPERS
126 help
127 The sysmon driver implements a sysmon QMI client and a handler for
128 the sys_mon SMD and GLINK channel, which are used for graceful
129 shutdown, retrieving failure information and propagating information
130 about other subsystems being shut down.
131
132 Say y here if your system runs firmware on any other subsystems, e.g.
133 modem or DSP.
134
117config QCOM_WCNSS_PIL 135config QCOM_WCNSS_PIL
118 tristate "Qualcomm WCNSS Peripheral Image Loader" 136 tristate "Qualcomm WCNSS Peripheral Image Loader"
119 depends on OF && ARCH_QCOM 137 depends on OF && ARCH_QCOM
120 depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n) 138 depends on RPMSG_QCOM_SMD || (COMPILE_TEST && RPMSG_QCOM_SMD=n)
121 depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n 139 depends on RPMSG_QCOM_GLINK_SMEM || RPMSG_QCOM_GLINK_SMEM=n
122 depends on QCOM_SMEM 140 depends on QCOM_SMEM
141 depends on QCOM_SYSMON || QCOM_SYSMON=n
123 select QCOM_MDT_LOADER 142 select QCOM_MDT_LOADER
124 select QCOM_RPROC_COMMON 143 select QCOM_RPROC_COMMON
125 select QCOM_SCM 144 select QCOM_SCM
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 6e16450ce11f..02627ede8d4a 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_KEYSTONE_REMOTEPROC) += keystone_remoteproc.o
17obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o 17obj-$(CONFIG_QCOM_ADSP_PIL) += qcom_adsp_pil.o
18obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o 18obj-$(CONFIG_QCOM_RPROC_COMMON) += qcom_common.o
19obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o 19obj-$(CONFIG_QCOM_Q6V5_PIL) += qcom_q6v5_pil.o
20obj-$(CONFIG_QCOM_SYSMON) += qcom_sysmon.o
20obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o 21obj-$(CONFIG_QCOM_WCNSS_PIL) += qcom_wcnss_pil.o
21qcom_wcnss_pil-y += qcom_wcnss.o 22qcom_wcnss_pil-y += qcom_wcnss.o
22qcom_wcnss_pil-y += qcom_wcnss_iris.o 23qcom_wcnss_pil-y += qcom_wcnss_iris.o
diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
index 633268e9d550..54c07fd3f204 100644
--- a/drivers/remoteproc/imx_rproc.c
+++ b/drivers/remoteproc/imx_rproc.c
@@ -333,14 +333,14 @@ static int imx_rproc_probe(struct platform_device *pdev)
333 /* set some other name then imx */ 333 /* set some other name then imx */
334 rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops, 334 rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops,
335 NULL, sizeof(*priv)); 335 NULL, sizeof(*priv));
336 if (!rproc) { 336 if (!rproc)
337 ret = -ENOMEM; 337 return -ENOMEM;
338 goto err;
339 }
340 338
341 dcfg = of_device_get_match_data(dev); 339 dcfg = of_device_get_match_data(dev);
342 if (!dcfg) 340 if (!dcfg) {
343 return -EINVAL; 341 ret = -EINVAL;
342 goto err_put_rproc;
343 }
344 344
345 priv = rproc->priv; 345 priv = rproc->priv;
346 priv->rproc = rproc; 346 priv->rproc = rproc;
@@ -359,8 +359,8 @@ static int imx_rproc_probe(struct platform_device *pdev)
359 priv->clk = devm_clk_get(dev, NULL); 359 priv->clk = devm_clk_get(dev, NULL);
360 if (IS_ERR(priv->clk)) { 360 if (IS_ERR(priv->clk)) {
361 dev_err(dev, "Failed to get clock\n"); 361 dev_err(dev, "Failed to get clock\n");
362 rproc_free(rproc); 362 ret = PTR_ERR(priv->clk);
363 return PTR_ERR(priv->clk); 363 goto err_put_rproc;
364 } 364 }
365 365
366 /* 366 /*
@@ -370,8 +370,7 @@ static int imx_rproc_probe(struct platform_device *pdev)
370 ret = clk_prepare_enable(priv->clk); 370 ret = clk_prepare_enable(priv->clk);
371 if (ret) { 371 if (ret) {
372 dev_err(&rproc->dev, "Failed to enable clock\n"); 372 dev_err(&rproc->dev, "Failed to enable clock\n");
373 rproc_free(rproc); 373 goto err_put_rproc;
374 return ret;
375 } 374 }
376 375
377 ret = rproc_add(rproc); 376 ret = rproc_add(rproc);
@@ -380,13 +379,13 @@ static int imx_rproc_probe(struct platform_device *pdev)
380 goto err_put_clk; 379 goto err_put_clk;
381 } 380 }
382 381
383 return ret; 382 return 0;
384 383
385err_put_clk: 384err_put_clk:
386 clk_disable_unprepare(priv->clk); 385 clk_disable_unprepare(priv->clk);
387err_put_rproc: 386err_put_rproc:
388 rproc_free(rproc); 387 rproc_free(rproc);
389err: 388
390 return ret; 389 return ret;
391} 390}
392 391
diff --git a/drivers/remoteproc/qcom_adsp_pil.c b/drivers/remoteproc/qcom_adsp_pil.c
index 373c167892d7..89a86ce07f99 100644
--- a/drivers/remoteproc/qcom_adsp_pil.c
+++ b/drivers/remoteproc/qcom_adsp_pil.c
@@ -38,7 +38,10 @@ struct adsp_data {
38 const char *firmware_name; 38 const char *firmware_name;
39 int pas_id; 39 int pas_id;
40 bool has_aggre2_clk; 40 bool has_aggre2_clk;
41
41 const char *ssr_name; 42 const char *ssr_name;
43 const char *sysmon_name;
44 int ssctl_id;
42}; 45};
43 46
44struct qcom_adsp { 47struct qcom_adsp {
@@ -75,6 +78,7 @@ struct qcom_adsp {
75 struct qcom_rproc_glink glink_subdev; 78 struct qcom_rproc_glink glink_subdev;
76 struct qcom_rproc_subdev smd_subdev; 79 struct qcom_rproc_subdev smd_subdev;
77 struct qcom_rproc_ssr ssr_subdev; 80 struct qcom_rproc_ssr ssr_subdev;
81 struct qcom_sysmon *sysmon;
78}; 82};
79 83
80static int adsp_load(struct rproc *rproc, const struct firmware *fw) 84static int adsp_load(struct rproc *rproc, const struct firmware *fw)
@@ -82,7 +86,9 @@ static int adsp_load(struct rproc *rproc, const struct firmware *fw)
82 struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv; 86 struct qcom_adsp *adsp = (struct qcom_adsp *)rproc->priv;
83 87
84 return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id, 88 return qcom_mdt_load(adsp->dev, fw, rproc->firmware, adsp->pas_id,
85 adsp->mem_region, adsp->mem_phys, adsp->mem_size); 89 adsp->mem_region, adsp->mem_phys, adsp->mem_size,
90 &adsp->mem_reloc);
91
86} 92}
87 93
88static int adsp_start(struct rproc *rproc) 94static int adsp_start(struct rproc *rproc)
@@ -177,6 +183,7 @@ static const struct rproc_ops adsp_ops = {
177 .start = adsp_start, 183 .start = adsp_start,
178 .stop = adsp_stop, 184 .stop = adsp_stop,
179 .da_to_va = adsp_da_to_va, 185 .da_to_va = adsp_da_to_va,
186 .parse_fw = qcom_register_dump_segments,
180 .load = adsp_load, 187 .load = adsp_load,
181}; 188};
182 189
@@ -201,9 +208,6 @@ static irqreturn_t adsp_fatal_interrupt(int irq, void *dev)
201 208
202 rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR); 209 rproc_report_crash(adsp->rproc, RPROC_FATAL_ERROR);
203 210
204 if (!IS_ERR(msg))
205 msg[0] = '\0';
206
207 return IRQ_HANDLED; 211 return IRQ_HANDLED;
208} 212}
209 213
@@ -398,6 +402,9 @@ static int adsp_probe(struct platform_device *pdev)
398 qcom_add_glink_subdev(rproc, &adsp->glink_subdev); 402 qcom_add_glink_subdev(rproc, &adsp->glink_subdev);
399 qcom_add_smd_subdev(rproc, &adsp->smd_subdev); 403 qcom_add_smd_subdev(rproc, &adsp->smd_subdev);
400 qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name); 404 qcom_add_ssr_subdev(rproc, &adsp->ssr_subdev, desc->ssr_name);
405 adsp->sysmon = qcom_add_sysmon_subdev(rproc,
406 desc->sysmon_name,
407 desc->ssctl_id);
401 408
402 ret = rproc_add(rproc); 409 ret = rproc_add(rproc);
403 if (ret) 410 if (ret)
@@ -419,6 +426,7 @@ static int adsp_remove(struct platform_device *pdev)
419 rproc_del(adsp->rproc); 426 rproc_del(adsp->rproc);
420 427
421 qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev); 428 qcom_remove_glink_subdev(adsp->rproc, &adsp->glink_subdev);
429 qcom_remove_sysmon_subdev(adsp->sysmon);
422 qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev); 430 qcom_remove_smd_subdev(adsp->rproc, &adsp->smd_subdev);
423 qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev); 431 qcom_remove_ssr_subdev(adsp->rproc, &adsp->ssr_subdev);
424 rproc_free(adsp->rproc); 432 rproc_free(adsp->rproc);
@@ -432,6 +440,8 @@ static const struct adsp_data adsp_resource_init = {
432 .pas_id = 1, 440 .pas_id = 1,
433 .has_aggre2_clk = false, 441 .has_aggre2_clk = false,
434 .ssr_name = "lpass", 442 .ssr_name = "lpass",
443 .sysmon_name = "adsp",
444 .ssctl_id = 0x14,
435}; 445};
436 446
437static const struct adsp_data slpi_resource_init = { 447static const struct adsp_data slpi_resource_init = {
@@ -440,6 +450,8 @@ static const struct adsp_data slpi_resource_init = {
440 .pas_id = 12, 450 .pas_id = 12,
441 .has_aggre2_clk = true, 451 .has_aggre2_clk = true,
442 .ssr_name = "dsps", 452 .ssr_name = "dsps",
453 .sysmon_name = "slpi",
454 .ssctl_id = 0x16,
443}; 455};
444 456
445static const struct of_device_id adsp_of_match[] = { 457static const struct of_device_id adsp_of_match[] = {
diff --git a/drivers/remoteproc/qcom_common.c b/drivers/remoteproc/qcom_common.c
index 00602499713f..acfc99f82fb8 100644
--- a/drivers/remoteproc/qcom_common.c
+++ b/drivers/remoteproc/qcom_common.c
@@ -22,6 +22,7 @@
22#include <linux/remoteproc.h> 22#include <linux/remoteproc.h>
23#include <linux/rpmsg/qcom_glink.h> 23#include <linux/rpmsg/qcom_glink.h>
24#include <linux/rpmsg/qcom_smd.h> 24#include <linux/rpmsg/qcom_smd.h>
25#include <linux/soc/qcom/mdt_loader.h>
25 26
26#include "remoteproc_internal.h" 27#include "remoteproc_internal.h"
27#include "qcom_common.h" 28#include "qcom_common.h"
@@ -41,7 +42,7 @@ static int glink_subdev_probe(struct rproc_subdev *subdev)
41 return PTR_ERR_OR_ZERO(glink->edge); 42 return PTR_ERR_OR_ZERO(glink->edge);
42} 43}
43 44
44static void glink_subdev_remove(struct rproc_subdev *subdev) 45static void glink_subdev_remove(struct rproc_subdev *subdev, bool crashed)
45{ 46{
46 struct qcom_rproc_glink *glink = to_glink_subdev(subdev); 47 struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
47 48
@@ -74,11 +75,57 @@ EXPORT_SYMBOL_GPL(qcom_add_glink_subdev);
74 */ 75 */
75void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink) 76void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink)
76{ 77{
78 if (!glink->node)
79 return;
80
77 rproc_remove_subdev(rproc, &glink->subdev); 81 rproc_remove_subdev(rproc, &glink->subdev);
78 of_node_put(glink->node); 82 of_node_put(glink->node);
79} 83}
80EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev); 84EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev);
81 85
86/**
87 * qcom_register_dump_segments() - register segments for coredump
88 * @rproc: remoteproc handle
89 * @fw: firmware header
90 *
91 * Register all segments of the ELF in the remoteproc coredump segment list
92 *
93 * Return: 0 on success, negative errno on failure.
94 */
95int qcom_register_dump_segments(struct rproc *rproc,
96 const struct firmware *fw)
97{
98 const struct elf32_phdr *phdrs;
99 const struct elf32_phdr *phdr;
100 const struct elf32_hdr *ehdr;
101 int ret;
102 int i;
103
104 ehdr = (struct elf32_hdr *)fw->data;
105 phdrs = (struct elf32_phdr *)(ehdr + 1);
106
107 for (i = 0; i < ehdr->e_phnum; i++) {
108 phdr = &phdrs[i];
109
110 if (phdr->p_type != PT_LOAD)
111 continue;
112
113 if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
114 continue;
115
116 if (!phdr->p_memsz)
117 continue;
118
119 ret = rproc_coredump_add_segment(rproc, phdr->p_paddr,
120 phdr->p_memsz);
121 if (ret)
122 return ret;
123 }
124
125 return 0;
126}
127EXPORT_SYMBOL_GPL(qcom_register_dump_segments);
128
82static int smd_subdev_probe(struct rproc_subdev *subdev) 129static int smd_subdev_probe(struct rproc_subdev *subdev)
83{ 130{
84 struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); 131 struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
@@ -88,7 +135,7 @@ static int smd_subdev_probe(struct rproc_subdev *subdev)
88 return PTR_ERR_OR_ZERO(smd->edge); 135 return PTR_ERR_OR_ZERO(smd->edge);
89} 136}
90 137
91static void smd_subdev_remove(struct rproc_subdev *subdev) 138static void smd_subdev_remove(struct rproc_subdev *subdev, bool crashed)
92{ 139{
93 struct qcom_rproc_subdev *smd = to_smd_subdev(subdev); 140 struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
94 141
@@ -121,6 +168,9 @@ EXPORT_SYMBOL_GPL(qcom_add_smd_subdev);
121 */ 168 */
122void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd) 169void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
123{ 170{
171 if (!smd->node)
172 return;
173
124 rproc_remove_subdev(rproc, &smd->subdev); 174 rproc_remove_subdev(rproc, &smd->subdev);
125 of_node_put(smd->node); 175 of_node_put(smd->node);
126} 176}
@@ -157,7 +207,7 @@ static int ssr_notify_start(struct rproc_subdev *subdev)
157 return 0; 207 return 0;
158} 208}
159 209
160static void ssr_notify_stop(struct rproc_subdev *subdev) 210static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed)
161{ 211{
162 struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev); 212 struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
163 213
diff --git a/drivers/remoteproc/qcom_common.h b/drivers/remoteproc/qcom_common.h
index 728be9834d8b..58de71e4781c 100644
--- a/drivers/remoteproc/qcom_common.h
+++ b/drivers/remoteproc/qcom_common.h
@@ -4,6 +4,9 @@
4 4
5#include <linux/remoteproc.h> 5#include <linux/remoteproc.h>
6#include "remoteproc_internal.h" 6#include "remoteproc_internal.h"
7#include <linux/soc/qcom/qmi.h>
8
9struct qcom_sysmon;
7 10
8struct qcom_rproc_glink { 11struct qcom_rproc_glink {
9 struct rproc_subdev subdev; 12 struct rproc_subdev subdev;
@@ -30,6 +33,8 @@ struct qcom_rproc_ssr {
30void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink); 33void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink);
31void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink); 34void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink);
32 35
36int qcom_register_dump_segments(struct rproc *rproc, const struct firmware *fw);
37
33void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); 38void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
34void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd); 39void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd);
35 40
@@ -37,4 +42,22 @@ void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
37 const char *ssr_name); 42 const char *ssr_name);
38void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr); 43void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr);
39 44
45#if IS_ENABLED(CONFIG_QCOM_SYSMON)
46struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
47 const char *name,
48 int ssctl_instance);
49void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon);
50#else
51static inline struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
52 const char *name,
53 int ssctl_instance)
54{
55 return NULL;
56}
57
58static inline void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon)
59{
60}
61#endif
62
40#endif 63#endif
diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c
index b4e5e725848d..8e70a627e0bb 100644
--- a/drivers/remoteproc/qcom_q6v5_pil.c
+++ b/drivers/remoteproc/qcom_q6v5_pil.c
@@ -168,6 +168,7 @@ struct q6v5 {
168 168
169 struct qcom_rproc_subdev smd_subdev; 169 struct qcom_rproc_subdev smd_subdev;
170 struct qcom_rproc_ssr ssr_subdev; 170 struct qcom_rproc_ssr ssr_subdev;
171 struct qcom_sysmon *sysmon;
171 bool need_mem_protection; 172 bool need_mem_protection;
172 int mpss_perm; 173 int mpss_perm;
173 int mba_perm; 174 int mba_perm;
@@ -939,9 +940,6 @@ static irqreturn_t q6v5_wdog_interrupt(int irq, void *dev)
939 940
940 rproc_report_crash(qproc->rproc, RPROC_WATCHDOG); 941 rproc_report_crash(qproc->rproc, RPROC_WATCHDOG);
941 942
942 if (!IS_ERR(msg))
943 msg[0] = '\0';
944
945 return IRQ_HANDLED; 943 return IRQ_HANDLED;
946} 944}
947 945
@@ -959,9 +957,6 @@ static irqreturn_t q6v5_fatal_interrupt(int irq, void *dev)
959 957
960 rproc_report_crash(qproc->rproc, RPROC_FATAL_ERROR); 958 rproc_report_crash(qproc->rproc, RPROC_FATAL_ERROR);
961 959
962 if (!IS_ERR(msg))
963 msg[0] = '\0';
964
965 return IRQ_HANDLED; 960 return IRQ_HANDLED;
966} 961}
967 962
@@ -1215,6 +1210,7 @@ static int q6v5_probe(struct platform_device *pdev)
1215 qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS); 1210 qproc->mba_perm = BIT(QCOM_SCM_VMID_HLOS);
1216 qcom_add_smd_subdev(rproc, &qproc->smd_subdev); 1211 qcom_add_smd_subdev(rproc, &qproc->smd_subdev);
1217 qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); 1212 qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss");
1213 qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12);
1218 1214
1219 ret = rproc_add(rproc); 1215 ret = rproc_add(rproc);
1220 if (ret) 1216 if (ret)
@@ -1234,6 +1230,7 @@ static int q6v5_remove(struct platform_device *pdev)
1234 1230
1235 rproc_del(qproc->rproc); 1231 rproc_del(qproc->rproc);
1236 1232
1233 qcom_remove_sysmon_subdev(qproc->sysmon);
1237 qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev); 1234 qcom_remove_smd_subdev(qproc->rproc, &qproc->smd_subdev);
1238 qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev); 1235 qcom_remove_ssr_subdev(qproc->rproc, &qproc->ssr_subdev);
1239 rproc_free(qproc->rproc); 1236 rproc_free(qproc->rproc);
diff --git a/drivers/remoteproc/qcom_sysmon.c b/drivers/remoteproc/qcom_sysmon.c
new file mode 100644
index 000000000000..f085545d7da5
--- /dev/null
+++ b/drivers/remoteproc/qcom_sysmon.c
@@ -0,0 +1,579 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2017, Linaro Ltd.
4 */
5#include <linux/firmware.h>
6#include <linux/module.h>
7#include <linux/notifier.h>
8#include <linux/slab.h>
9#include <linux/io.h>
10#include <linux/notifier.h>
11#include <linux/of_platform.h>
12#include <linux/platform_device.h>
13#include <linux/remoteproc/qcom_rproc.h>
14#include <linux/rpmsg.h>
15
16#include "qcom_common.h"
17
18static BLOCKING_NOTIFIER_HEAD(sysmon_notifiers);
19
20struct qcom_sysmon {
21 struct rproc_subdev subdev;
22 struct rproc *rproc;
23
24 struct list_head node;
25
26 const char *name;
27
28 int ssctl_version;
29 int ssctl_instance;
30
31 struct notifier_block nb;
32
33 struct device *dev;
34
35 struct rpmsg_endpoint *ept;
36 struct completion comp;
37 struct mutex lock;
38
39 bool ssr_ack;
40
41 struct qmi_handle qmi;
42 struct sockaddr_qrtr ssctl;
43};
44
45static DEFINE_MUTEX(sysmon_lock);
46static LIST_HEAD(sysmon_list);
47
48/**
49 * sysmon_send_event() - send notification of other remote's SSR event
50 * @sysmon: sysmon context
51 * @name: other remote's name
52 */
53static void sysmon_send_event(struct qcom_sysmon *sysmon, const char *name)
54{
55 char req[50];
56 int len;
57 int ret;
58
59 len = snprintf(req, sizeof(req), "ssr:%s:before_shutdown", name);
60 if (len >= sizeof(req))
61 return;
62
63 mutex_lock(&sysmon->lock);
64 reinit_completion(&sysmon->comp);
65 sysmon->ssr_ack = false;
66
67 ret = rpmsg_send(sysmon->ept, req, len);
68 if (ret < 0) {
69 dev_err(sysmon->dev, "failed to send sysmon event\n");
70 goto out_unlock;
71 }
72
73 ret = wait_for_completion_timeout(&sysmon->comp,
74 msecs_to_jiffies(5000));
75 if (!ret) {
76 dev_err(sysmon->dev, "timeout waiting for sysmon ack\n");
77 goto out_unlock;
78 }
79
80 if (!sysmon->ssr_ack)
81 dev_err(sysmon->dev, "unexpected response to sysmon event\n");
82
83out_unlock:
84 mutex_unlock(&sysmon->lock);
85}
86
87/**
88 * sysmon_request_shutdown() - request graceful shutdown of remote
89 * @sysmon: sysmon context
90 */
91static void sysmon_request_shutdown(struct qcom_sysmon *sysmon)
92{
93 char *req = "ssr:shutdown";
94 int ret;
95
96 mutex_lock(&sysmon->lock);
97 reinit_completion(&sysmon->comp);
98 sysmon->ssr_ack = false;
99
100 ret = rpmsg_send(sysmon->ept, req, strlen(req) + 1);
101 if (ret < 0) {
102 dev_err(sysmon->dev, "send sysmon shutdown request failed\n");
103 goto out_unlock;
104 }
105
106 ret = wait_for_completion_timeout(&sysmon->comp,
107 msecs_to_jiffies(5000));
108 if (!ret) {
109 dev_err(sysmon->dev, "timeout waiting for sysmon ack\n");
110 goto out_unlock;
111 }
112
113 if (!sysmon->ssr_ack)
114 dev_err(sysmon->dev,
115 "unexpected response to sysmon shutdown request\n");
116
117out_unlock:
118 mutex_unlock(&sysmon->lock);
119}
120
121static int sysmon_callback(struct rpmsg_device *rpdev, void *data, int count,
122 void *priv, u32 addr)
123{
124 struct qcom_sysmon *sysmon = priv;
125 const char *ssr_ack = "ssr:ack";
126 const int ssr_ack_len = strlen(ssr_ack) + 1;
127
128 if (!sysmon)
129 return -EINVAL;
130
131 if (count >= ssr_ack_len && !memcmp(data, ssr_ack, ssr_ack_len))
132 sysmon->ssr_ack = true;
133
134 complete(&sysmon->comp);
135
136 return 0;
137}
138
139#define SSCTL_SHUTDOWN_REQ 0x21
140#define SSCTL_SUBSYS_EVENT_REQ 0x23
141
142#define SSCTL_MAX_MSG_LEN 7
143
144#define SSCTL_SUBSYS_NAME_LENGTH 15
145
146enum {
147 SSCTL_SSR_EVENT_BEFORE_POWERUP,
148 SSCTL_SSR_EVENT_AFTER_POWERUP,
149 SSCTL_SSR_EVENT_BEFORE_SHUTDOWN,
150 SSCTL_SSR_EVENT_AFTER_SHUTDOWN,
151};
152
153enum {
154 SSCTL_SSR_EVENT_FORCED,
155 SSCTL_SSR_EVENT_GRACEFUL,
156};
157
158struct ssctl_shutdown_resp {
159 struct qmi_response_type_v01 resp;
160};
161
162static struct qmi_elem_info ssctl_shutdown_resp_ei[] = {
163 {
164 .data_type = QMI_STRUCT,
165 .elem_len = 1,
166 .elem_size = sizeof(struct qmi_response_type_v01),
167 .array_type = NO_ARRAY,
168 .tlv_type = 0x02,
169 .offset = offsetof(struct ssctl_shutdown_resp, resp),
170 .ei_array = qmi_response_type_v01_ei,
171 },
172 {}
173};
174
175struct ssctl_subsys_event_req {
176 u8 subsys_name_len;
177 char subsys_name[SSCTL_SUBSYS_NAME_LENGTH];
178 u32 event;
179 u8 evt_driven_valid;
180 u32 evt_driven;
181};
182
183static struct qmi_elem_info ssctl_subsys_event_req_ei[] = {
184 {
185 .data_type = QMI_DATA_LEN,
186 .elem_len = 1,
187 .elem_size = sizeof(uint8_t),
188 .array_type = NO_ARRAY,
189 .tlv_type = 0x01,
190 .offset = offsetof(struct ssctl_subsys_event_req,
191 subsys_name_len),
192 .ei_array = NULL,
193 },
194 {
195 .data_type = QMI_UNSIGNED_1_BYTE,
196 .elem_len = SSCTL_SUBSYS_NAME_LENGTH,
197 .elem_size = sizeof(char),
198 .array_type = VAR_LEN_ARRAY,
199 .tlv_type = 0x01,
200 .offset = offsetof(struct ssctl_subsys_event_req,
201 subsys_name),
202 .ei_array = NULL,
203 },
204 {
205 .data_type = QMI_SIGNED_4_BYTE_ENUM,
206 .elem_len = 1,
207 .elem_size = sizeof(uint32_t),
208 .array_type = NO_ARRAY,
209 .tlv_type = 0x02,
210 .offset = offsetof(struct ssctl_subsys_event_req,
211 event),
212 .ei_array = NULL,
213 },
214 {
215 .data_type = QMI_OPT_FLAG,
216 .elem_len = 1,
217 .elem_size = sizeof(uint8_t),
218 .array_type = NO_ARRAY,
219 .tlv_type = 0x10,
220 .offset = offsetof(struct ssctl_subsys_event_req,
221 evt_driven_valid),
222 .ei_array = NULL,
223 },
224 {
225 .data_type = QMI_SIGNED_4_BYTE_ENUM,
226 .elem_len = 1,
227 .elem_size = sizeof(uint32_t),
228 .array_type = NO_ARRAY,
229 .tlv_type = 0x10,
230 .offset = offsetof(struct ssctl_subsys_event_req,
231 evt_driven),
232 .ei_array = NULL,
233 },
234 {}
235};
236
237struct ssctl_subsys_event_resp {
238 struct qmi_response_type_v01 resp;
239};
240
241static struct qmi_elem_info ssctl_subsys_event_resp_ei[] = {
242 {
243 .data_type = QMI_STRUCT,
244 .elem_len = 1,
245 .elem_size = sizeof(struct qmi_response_type_v01),
246 .array_type = NO_ARRAY,
247 .tlv_type = 0x02,
248 .offset = offsetof(struct ssctl_subsys_event_resp,
249 resp),
250 .ei_array = qmi_response_type_v01_ei,
251 },
252 {}
253};
254
255/**
256 * ssctl_request_shutdown() - request shutdown via SSCTL QMI service
257 * @sysmon: sysmon context
258 */
259static void ssctl_request_shutdown(struct qcom_sysmon *sysmon)
260{
261 struct ssctl_shutdown_resp resp;
262 struct qmi_txn txn;
263 int ret;
264
265 ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_shutdown_resp_ei, &resp);
266 if (ret < 0) {
267 dev_err(sysmon->dev, "failed to allocate QMI txn\n");
268 return;
269 }
270
271 ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
272 SSCTL_SHUTDOWN_REQ, 0, NULL, NULL);
273 if (ret < 0) {
274 dev_err(sysmon->dev, "failed to send shutdown request\n");
275 qmi_txn_cancel(&txn);
276 return;
277 }
278
279 ret = qmi_txn_wait(&txn, 5 * HZ);
280 if (ret < 0)
281 dev_err(sysmon->dev, "failed receiving QMI response\n");
282 else if (resp.resp.result)
283 dev_err(sysmon->dev, "shutdown request failed\n");
284 else
285 dev_dbg(sysmon->dev, "shutdown request completed\n");
286}
287
288/**
289 * ssctl_send_event() - send notification of other remote's SSR event
290 * @sysmon: sysmon context
291 * @name: other remote's name
292 */
293static void ssctl_send_event(struct qcom_sysmon *sysmon, const char *name)
294{
295 struct ssctl_subsys_event_resp resp;
296 struct ssctl_subsys_event_req req;
297 struct qmi_txn txn;
298 int ret;
299
300 memset(&resp, 0, sizeof(resp));
301 ret = qmi_txn_init(&sysmon->qmi, &txn, ssctl_subsys_event_resp_ei, &resp);
302 if (ret < 0) {
303 dev_err(sysmon->dev, "failed to allocate QMI txn\n");
304 return;
305 }
306
307 memset(&req, 0, sizeof(req));
308 strlcpy(req.subsys_name, name, sizeof(req.subsys_name));
309 req.subsys_name_len = strlen(req.subsys_name);
310 req.event = SSCTL_SSR_EVENT_BEFORE_SHUTDOWN;
311 req.evt_driven_valid = true;
312 req.evt_driven = SSCTL_SSR_EVENT_FORCED;
313
314 ret = qmi_send_request(&sysmon->qmi, &sysmon->ssctl, &txn,
315 SSCTL_SUBSYS_EVENT_REQ, 40,
316 ssctl_subsys_event_req_ei, &req);
317 if (ret < 0) {
318 dev_err(sysmon->dev, "failed to send shutdown request\n");
319 qmi_txn_cancel(&txn);
320 return;
321 }
322
323 ret = qmi_txn_wait(&txn, 5 * HZ);
324 if (ret < 0)
325 dev_err(sysmon->dev, "failed receiving QMI response\n");
326 else if (resp.resp.result)
327 dev_err(sysmon->dev, "ssr event send failed\n");
328 else
329 dev_dbg(sysmon->dev, "ssr event send completed\n");
330}
331
332/**
333 * ssctl_new_server() - QMI callback indicating a new service
334 * @qmi: QMI handle
335 * @svc: service information
336 *
337 * Return: 0 if we're interested in this service, -EINVAL otherwise.
338 */
339static int ssctl_new_server(struct qmi_handle *qmi, struct qmi_service *svc)
340{
341 struct qcom_sysmon *sysmon = container_of(qmi, struct qcom_sysmon, qmi);
342
343 switch (svc->version) {
344 case 1:
345 if (svc->instance != 0)
346 return -EINVAL;
347 if (strcmp(sysmon->name, "modem"))
348 return -EINVAL;
349 break;
350 case 2:
351 if (svc->instance != sysmon->ssctl_instance)
352 return -EINVAL;
353 break;
354 default:
355 return -EINVAL;
356 };
357
358 sysmon->ssctl_version = svc->version;
359
360 sysmon->ssctl.sq_family = AF_QIPCRTR;
361 sysmon->ssctl.sq_node = svc->node;
362 sysmon->ssctl.sq_port = svc->port;
363
364 svc->priv = sysmon;
365
366 return 0;
367}
368
369/**
370 * ssctl_del_server() - QMI callback indicating that @svc is removed
371 * @qmi: QMI handle
372 * @svc: service information
373 */
374static void ssctl_del_server(struct qmi_handle *qmi, struct qmi_service *svc)
375{
376 struct qcom_sysmon *sysmon = svc->priv;
377
378 sysmon->ssctl_version = 0;
379}
380
381static const struct qmi_ops ssctl_ops = {
382 .new_server = ssctl_new_server,
383 .del_server = ssctl_del_server,
384};
385
386static int sysmon_start(struct rproc_subdev *subdev)
387{
388 return 0;
389}
390
391static void sysmon_stop(struct rproc_subdev *subdev, bool crashed)
392{
393 struct qcom_sysmon *sysmon = container_of(subdev, struct qcom_sysmon, subdev);
394
395 blocking_notifier_call_chain(&sysmon_notifiers, 0, (void *)sysmon->name);
396
397 /* Don't request graceful shutdown if we've crashed */
398 if (crashed)
399 return;
400
401 if (sysmon->ssctl_version)
402 ssctl_request_shutdown(sysmon);
403 else if (sysmon->ept)
404 sysmon_request_shutdown(sysmon);
405}
406
407/**
408 * sysmon_notify() - notify sysmon target of another's SSR
409 * @nb: notifier_block associated with sysmon instance
410 * @event: unused
411 * @data: SSR identifier of the remote that is going down
412 */
413static int sysmon_notify(struct notifier_block *nb, unsigned long event,
414 void *data)
415{
416 struct qcom_sysmon *sysmon = container_of(nb, struct qcom_sysmon, nb);
417 struct rproc *rproc = sysmon->rproc;
418 const char *ssr_name = data;
419
420 /* Skip non-running rprocs and the originating instance */
421 if (rproc->state != RPROC_RUNNING || !strcmp(data, sysmon->name)) {
422 dev_dbg(sysmon->dev, "not notifying %s\n", sysmon->name);
423 return NOTIFY_DONE;
424 }
425
426 /* Only SSCTL version 2 supports SSR events */
427 if (sysmon->ssctl_version == 2)
428 ssctl_send_event(sysmon, ssr_name);
429 else if (sysmon->ept)
430 sysmon_send_event(sysmon, ssr_name);
431
432 return NOTIFY_DONE;
433}
434
435/**
436 * qcom_add_sysmon_subdev() - create a sysmon subdev for the given remoteproc
437 * @rproc: rproc context to associate the subdev with
438 * @name: name of this subdev, to use in SSR
439 * @ssctl_instance: instance id of the ssctl QMI service
440 *
441 * Return: A new qcom_sysmon object, or NULL on failure
442 */
443struct qcom_sysmon *qcom_add_sysmon_subdev(struct rproc *rproc,
444 const char *name,
445 int ssctl_instance)
446{
447 struct qcom_sysmon *sysmon;
448 int ret;
449
450 sysmon = kzalloc(sizeof(*sysmon), GFP_KERNEL);
451 if (!sysmon)
452 return NULL;
453
454 sysmon->dev = rproc->dev.parent;
455 sysmon->rproc = rproc;
456
457 sysmon->name = name;
458 sysmon->ssctl_instance = ssctl_instance;
459
460 init_completion(&sysmon->comp);
461 mutex_init(&sysmon->lock);
462
463 ret = qmi_handle_init(&sysmon->qmi, SSCTL_MAX_MSG_LEN, &ssctl_ops, NULL);
464 if (ret < 0) {
465 dev_err(sysmon->dev, "failed to initialize qmi handle\n");
466 kfree(sysmon);
467 return NULL;
468 }
469
470 qmi_add_lookup(&sysmon->qmi, 43, 0, 0);
471
472 rproc_add_subdev(rproc, &sysmon->subdev, sysmon_start, sysmon_stop);
473
474 sysmon->nb.notifier_call = sysmon_notify;
475 blocking_notifier_chain_register(&sysmon_notifiers, &sysmon->nb);
476
477 mutex_lock(&sysmon_lock);
478 list_add(&sysmon->node, &sysmon_list);
479 mutex_unlock(&sysmon_lock);
480
481 return sysmon;
482}
483EXPORT_SYMBOL_GPL(qcom_add_sysmon_subdev);
484
485/**
486 * qcom_remove_sysmon_subdev() - release a qcom_sysmon
487 * @sysmon: sysmon context, as retrieved by qcom_add_sysmon_subdev()
488 */
489void qcom_remove_sysmon_subdev(struct qcom_sysmon *sysmon)
490{
491 if (!sysmon)
492 return;
493
494 mutex_lock(&sysmon_lock);
495 list_del(&sysmon->node);
496 mutex_unlock(&sysmon_lock);
497
498 blocking_notifier_chain_unregister(&sysmon_notifiers, &sysmon->nb);
499
500 rproc_remove_subdev(sysmon->rproc, &sysmon->subdev);
501
502 qmi_handle_release(&sysmon->qmi);
503
504 kfree(sysmon);
505}
506EXPORT_SYMBOL_GPL(qcom_remove_sysmon_subdev);
507
508/**
509 * sysmon_probe() - probe sys_mon channel
510 * @rpdev: rpmsg device handle
511 *
512 * Find the sysmon context associated with the ancestor remoteproc and assign
513 * this rpmsg device with said sysmon context.
514 *
515 * Return: 0 on success, negative errno on failure.
516 */
517static int sysmon_probe(struct rpmsg_device *rpdev)
518{
519 struct qcom_sysmon *sysmon;
520 struct rproc *rproc;
521
522 rproc = rproc_get_by_child(&rpdev->dev);
523 if (!rproc) {
524 dev_err(&rpdev->dev, "sysmon device not child of rproc\n");
525 return -EINVAL;
526 }
527
528 mutex_lock(&sysmon_lock);
529 list_for_each_entry(sysmon, &sysmon_list, node) {
530 if (sysmon->rproc == rproc)
531 goto found;
532 }
533 mutex_unlock(&sysmon_lock);
534
535 dev_err(&rpdev->dev, "no sysmon associated with parent rproc\n");
536
537 return -EINVAL;
538
539found:
540 mutex_unlock(&sysmon_lock);
541
542 rpdev->ept->priv = sysmon;
543 sysmon->ept = rpdev->ept;
544
545 return 0;
546}
547
548/**
549 * sysmon_remove() - sys_mon channel remove handler
550 * @rpdev: rpmsg device handle
551 *
552 * Disassociate the rpmsg device with the sysmon instance.
553 */
554static void sysmon_remove(struct rpmsg_device *rpdev)
555{
556 struct qcom_sysmon *sysmon = rpdev->ept->priv;
557
558 sysmon->ept = NULL;
559}
560
561static const struct rpmsg_device_id sysmon_match[] = {
562 { "sys_mon" },
563 {}
564};
565
566static struct rpmsg_driver sysmon_driver = {
567 .probe = sysmon_probe,
568 .remove = sysmon_remove,
569 .callback = sysmon_callback,
570 .id_table = sysmon_match,
571 .drv = {
572 .name = "qcom_sysmon",
573 },
574};
575
576module_rpmsg_driver(sysmon_driver);
577
578MODULE_DESCRIPTION("Qualcomm sysmon driver");
579MODULE_LICENSE("GPL v2");
diff --git a/drivers/remoteproc/qcom_wcnss.c b/drivers/remoteproc/qcom_wcnss.c
index 3f0609236a76..b0e07e9f42d5 100644
--- a/drivers/remoteproc/qcom_wcnss.c
+++ b/drivers/remoteproc/qcom_wcnss.c
@@ -40,6 +40,7 @@
40#define WCNSS_CRASH_REASON_SMEM 422 40#define WCNSS_CRASH_REASON_SMEM 422
41#define WCNSS_FIRMWARE_NAME "wcnss.mdt" 41#define WCNSS_FIRMWARE_NAME "wcnss.mdt"
42#define WCNSS_PAS_ID 6 42#define WCNSS_PAS_ID 6
43#define WCNSS_SSCTL_ID 0x13
43 44
44#define WCNSS_SPARE_NVBIN_DLND BIT(25) 45#define WCNSS_SPARE_NVBIN_DLND BIT(25)
45 46
@@ -98,6 +99,7 @@ struct qcom_wcnss {
98 size_t mem_size; 99 size_t mem_size;
99 100
100 struct qcom_rproc_subdev smd_subdev; 101 struct qcom_rproc_subdev smd_subdev;
102 struct qcom_sysmon *sysmon;
101}; 103};
102 104
103static const struct wcnss_data riva_data = { 105static const struct wcnss_data riva_data = {
@@ -153,7 +155,8 @@ static int wcnss_load(struct rproc *rproc, const struct firmware *fw)
153 struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv; 155 struct qcom_wcnss *wcnss = (struct qcom_wcnss *)rproc->priv;
154 156
155 return qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID, 157 return qcom_mdt_load(wcnss->dev, fw, rproc->firmware, WCNSS_PAS_ID,
156 wcnss->mem_region, wcnss->mem_phys, wcnss->mem_size); 158 wcnss->mem_region, wcnss->mem_phys,
159 wcnss->mem_size, &wcnss->mem_reloc);
157} 160}
158 161
159static void wcnss_indicate_nv_download(struct qcom_wcnss *wcnss) 162static void wcnss_indicate_nv_download(struct qcom_wcnss *wcnss)
@@ -308,6 +311,7 @@ static const struct rproc_ops wcnss_ops = {
308 .start = wcnss_start, 311 .start = wcnss_start,
309 .stop = wcnss_stop, 312 .stop = wcnss_stop,
310 .da_to_va = wcnss_da_to_va, 313 .da_to_va = wcnss_da_to_va,
314 .parse_fw = qcom_register_dump_segments,
311 .load = wcnss_load, 315 .load = wcnss_load,
312}; 316};
313 317
@@ -332,9 +336,6 @@ static irqreturn_t wcnss_fatal_interrupt(int irq, void *dev)
332 336
333 rproc_report_crash(wcnss->rproc, RPROC_FATAL_ERROR); 337 rproc_report_crash(wcnss->rproc, RPROC_FATAL_ERROR);
334 338
335 if (!IS_ERR(msg))
336 msg[0] = '\0';
337
338 return IRQ_HANDLED; 339 return IRQ_HANDLED;
339} 340}
340 341
@@ -551,6 +552,7 @@ static int wcnss_probe(struct platform_device *pdev)
551 } 552 }
552 553
553 qcom_add_smd_subdev(rproc, &wcnss->smd_subdev); 554 qcom_add_smd_subdev(rproc, &wcnss->smd_subdev);
555 wcnss->sysmon = qcom_add_sysmon_subdev(rproc, "wcnss", WCNSS_SSCTL_ID);
554 556
555 ret = rproc_add(rproc); 557 ret = rproc_add(rproc);
556 if (ret) 558 if (ret)
@@ -573,6 +575,7 @@ static int wcnss_remove(struct platform_device *pdev)
573 qcom_smem_state_put(wcnss->state); 575 qcom_smem_state_put(wcnss->state);
574 rproc_del(wcnss->rproc); 576 rproc_del(wcnss->rproc);
575 577
578 qcom_remove_sysmon_subdev(wcnss->sysmon);
576 qcom_remove_smd_subdev(wcnss->rproc, &wcnss->smd_subdev); 579 qcom_remove_smd_subdev(wcnss->rproc, &wcnss->smd_subdev);
577 rproc_free(wcnss->rproc); 580 rproc_free(wcnss->rproc);
578 581
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index 4170dfbd93bd..6d9c5832ce47 100644
--- a/drivers/remoteproc/remoteproc_core.c
+++ b/drivers/remoteproc/remoteproc_core.c
@@ -33,6 +33,7 @@
33#include <linux/firmware.h> 33#include <linux/firmware.h>
34#include <linux/string.h> 34#include <linux/string.h>
35#include <linux/debugfs.h> 35#include <linux/debugfs.h>
36#include <linux/devcoredump.h>
36#include <linux/remoteproc.h> 37#include <linux/remoteproc.h>
37#include <linux/iommu.h> 38#include <linux/iommu.h>
38#include <linux/idr.h> 39#include <linux/idr.h>
@@ -307,7 +308,7 @@ static int rproc_vdev_do_probe(struct rproc_subdev *subdev)
307 return rproc_add_virtio_dev(rvdev, rvdev->id); 308 return rproc_add_virtio_dev(rvdev, rvdev->id);
308} 309}
309 310
310static void rproc_vdev_do_remove(struct rproc_subdev *subdev) 311static void rproc_vdev_do_remove(struct rproc_subdev *subdev, bool crashed)
311{ 312{
312 struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev); 313 struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev);
313 314
@@ -788,17 +789,31 @@ static int rproc_probe_subdevices(struct rproc *rproc)
788 789
789unroll_registration: 790unroll_registration:
790 list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node) 791 list_for_each_entry_continue_reverse(subdev, &rproc->subdevs, node)
791 subdev->remove(subdev); 792 subdev->remove(subdev, true);
792 793
793 return ret; 794 return ret;
794} 795}
795 796
796static void rproc_remove_subdevices(struct rproc *rproc) 797static void rproc_remove_subdevices(struct rproc *rproc, bool crashed)
797{ 798{
798 struct rproc_subdev *subdev; 799 struct rproc_subdev *subdev;
799 800
800 list_for_each_entry_reverse(subdev, &rproc->subdevs, node) 801 list_for_each_entry_reverse(subdev, &rproc->subdevs, node)
801 subdev->remove(subdev); 802 subdev->remove(subdev, crashed);
803}
804
805/**
806 * rproc_coredump_cleanup() - clean up dump_segments list
807 * @rproc: the remote processor handle
808 */
809static void rproc_coredump_cleanup(struct rproc *rproc)
810{
811 struct rproc_dump_segment *entry, *tmp;
812
813 list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) {
814 list_del(&entry->node);
815 kfree(entry);
816 }
802} 817}
803 818
804/** 819/**
@@ -848,6 +863,8 @@ static void rproc_resource_cleanup(struct rproc *rproc)
848 /* clean up remote vdev entries */ 863 /* clean up remote vdev entries */
849 list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) 864 list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node)
850 kref_put(&rvdev->refcount, rproc_vdev_release); 865 kref_put(&rvdev->refcount, rproc_vdev_release);
866
867 rproc_coredump_cleanup(rproc);
851} 868}
852 869
853static int rproc_start(struct rproc *rproc, const struct firmware *fw) 870static int rproc_start(struct rproc *rproc, const struct firmware *fw)
@@ -927,8 +944,8 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
927 944
928 rproc->bootaddr = rproc_get_boot_addr(rproc, fw); 945 rproc->bootaddr = rproc_get_boot_addr(rproc, fw);
929 946
930 /* load resource table */ 947 /* Load resource table, core dump segment list etc from the firmware */
931 ret = rproc_load_rsc_table(rproc, fw); 948 ret = rproc_parse_fw(rproc, fw);
932 if (ret) 949 if (ret)
933 goto disable_iommu; 950 goto disable_iommu;
934 951
@@ -992,13 +1009,13 @@ static int rproc_trigger_auto_boot(struct rproc *rproc)
992 return ret; 1009 return ret;
993} 1010}
994 1011
995static int rproc_stop(struct rproc *rproc) 1012static int rproc_stop(struct rproc *rproc, bool crashed)
996{ 1013{
997 struct device *dev = &rproc->dev; 1014 struct device *dev = &rproc->dev;
998 int ret; 1015 int ret;
999 1016
1000 /* remove any subdevices for the remote processor */ 1017 /* remove any subdevices for the remote processor */
1001 rproc_remove_subdevices(rproc); 1018 rproc_remove_subdevices(rproc, crashed);
1002 1019
1003 /* the installed resource table is no longer accessible */ 1020 /* the installed resource table is no longer accessible */
1004 rproc->table_ptr = rproc->cached_table; 1021 rproc->table_ptr = rproc->cached_table;
@@ -1018,6 +1035,113 @@ static int rproc_stop(struct rproc *rproc)
1018} 1035}
1019 1036
1020/** 1037/**
1038 * rproc_coredump_add_segment() - add segment of device memory to coredump
1039 * @rproc: handle of a remote processor
1040 * @da: device address
1041 * @size: size of segment
1042 *
1043 * Add device memory to the list of segments to be included in a coredump for
1044 * the remoteproc.
1045 *
1046 * Return: 0 on success, negative errno on error.
1047 */
1048int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size)
1049{
1050 struct rproc_dump_segment *segment;
1051
1052 segment = kzalloc(sizeof(*segment), GFP_KERNEL);
1053 if (!segment)
1054 return -ENOMEM;
1055
1056 segment->da = da;
1057 segment->size = size;
1058
1059 list_add_tail(&segment->node, &rproc->dump_segments);
1060
1061 return 0;
1062}
1063EXPORT_SYMBOL(rproc_coredump_add_segment);
1064
1065/**
1066 * rproc_coredump() - perform coredump
1067 * @rproc: rproc handle
1068 *
1069 * This function will generate an ELF header for the registered segments
1070 * and create a devcoredump device associated with rproc.
1071 */
1072static void rproc_coredump(struct rproc *rproc)
1073{
1074 struct rproc_dump_segment *segment;
1075 struct elf32_phdr *phdr;
1076 struct elf32_hdr *ehdr;
1077 size_t data_size;
1078 size_t offset;
1079 void *data;
1080 void *ptr;
1081 int phnum = 0;
1082
1083 if (list_empty(&rproc->dump_segments))
1084 return;
1085
1086 data_size = sizeof(*ehdr);
1087 list_for_each_entry(segment, &rproc->dump_segments, node) {
1088 data_size += sizeof(*phdr) + segment->size;
1089
1090 phnum++;
1091 }
1092
1093 data = vmalloc(data_size);
1094 if (!data)
1095 return;
1096
1097 ehdr = data;
1098
1099 memset(ehdr, 0, sizeof(*ehdr));
1100 memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
1101 ehdr->e_ident[EI_CLASS] = ELFCLASS32;
1102 ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
1103 ehdr->e_ident[EI_VERSION] = EV_CURRENT;
1104 ehdr->e_ident[EI_OSABI] = ELFOSABI_NONE;
1105 ehdr->e_type = ET_CORE;
1106 ehdr->e_machine = EM_NONE;
1107 ehdr->e_version = EV_CURRENT;
1108 ehdr->e_entry = rproc->bootaddr;
1109 ehdr->e_phoff = sizeof(*ehdr);
1110 ehdr->e_ehsize = sizeof(*ehdr);
1111 ehdr->e_phentsize = sizeof(*phdr);
1112 ehdr->e_phnum = phnum;
1113
1114 phdr = data + ehdr->e_phoff;
1115 offset = ehdr->e_phoff + sizeof(*phdr) * ehdr->e_phnum;
1116 list_for_each_entry(segment, &rproc->dump_segments, node) {
1117 memset(phdr, 0, sizeof(*phdr));
1118 phdr->p_type = PT_LOAD;
1119 phdr->p_offset = offset;
1120 phdr->p_vaddr = segment->da;
1121 phdr->p_paddr = segment->da;
1122 phdr->p_filesz = segment->size;
1123 phdr->p_memsz = segment->size;
1124 phdr->p_flags = PF_R | PF_W | PF_X;
1125 phdr->p_align = 0;
1126
1127 ptr = rproc_da_to_va(rproc, segment->da, segment->size);
1128 if (!ptr) {
1129 dev_err(&rproc->dev,
1130 "invalid coredump segment (%pad, %zu)\n",
1131 &segment->da, segment->size);
1132 memset(data + offset, 0xff, segment->size);
1133 } else {
1134 memcpy(data + offset, ptr, segment->size);
1135 }
1136
1137 offset += phdr->p_filesz;
1138 phdr++;
1139 }
1140
1141 dev_coredumpv(&rproc->dev, data, data_size, GFP_KERNEL);
1142}
1143
1144/**
1021 * rproc_trigger_recovery() - recover a remoteproc 1145 * rproc_trigger_recovery() - recover a remoteproc
1022 * @rproc: the remote processor 1146 * @rproc: the remote processor
1023 * 1147 *
@@ -1039,10 +1163,13 @@ int rproc_trigger_recovery(struct rproc *rproc)
1039 if (ret) 1163 if (ret)
1040 return ret; 1164 return ret;
1041 1165
1042 ret = rproc_stop(rproc); 1166 ret = rproc_stop(rproc, false);
1043 if (ret) 1167 if (ret)
1044 goto unlock_mutex; 1168 goto unlock_mutex;
1045 1169
1170 /* generate coredump */
1171 rproc_coredump(rproc);
1172
1046 /* load firmware */ 1173 /* load firmware */
1047 ret = request_firmware(&firmware_p, rproc->firmware, dev); 1174 ret = request_firmware(&firmware_p, rproc->firmware, dev);
1048 if (ret < 0) { 1175 if (ret < 0) {
@@ -1189,7 +1316,7 @@ void rproc_shutdown(struct rproc *rproc)
1189 if (!atomic_dec_and_test(&rproc->power)) 1316 if (!atomic_dec_and_test(&rproc->power))
1190 goto out; 1317 goto out;
1191 1318
1192 ret = rproc_stop(rproc); 1319 ret = rproc_stop(rproc, true);
1193 if (ret) { 1320 if (ret) {
1194 atomic_inc(&rproc->power); 1321 atomic_inc(&rproc->power);
1195 goto out; 1322 goto out;
@@ -1428,7 +1555,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
1428 /* Default to ELF loader if no load function is specified */ 1555 /* Default to ELF loader if no load function is specified */
1429 if (!rproc->ops->load) { 1556 if (!rproc->ops->load) {
1430 rproc->ops->load = rproc_elf_load_segments; 1557 rproc->ops->load = rproc_elf_load_segments;
1431 rproc->ops->load_rsc_table = rproc_elf_load_rsc_table; 1558 rproc->ops->parse_fw = rproc_elf_load_rsc_table;
1432 rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table; 1559 rproc->ops->find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table;
1433 rproc->ops->sanity_check = rproc_elf_sanity_check; 1560 rproc->ops->sanity_check = rproc_elf_sanity_check;
1434 rproc->ops->get_boot_addr = rproc_elf_get_boot_addr; 1561 rproc->ops->get_boot_addr = rproc_elf_get_boot_addr;
@@ -1443,6 +1570,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
1443 INIT_LIST_HEAD(&rproc->traces); 1570 INIT_LIST_HEAD(&rproc->traces);
1444 INIT_LIST_HEAD(&rproc->rvdevs); 1571 INIT_LIST_HEAD(&rproc->rvdevs);
1445 INIT_LIST_HEAD(&rproc->subdevs); 1572 INIT_LIST_HEAD(&rproc->subdevs);
1573 INIT_LIST_HEAD(&rproc->dump_segments);
1446 1574
1447 INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work); 1575 INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work);
1448 1576
@@ -1535,7 +1663,7 @@ EXPORT_SYMBOL(rproc_del);
1535void rproc_add_subdev(struct rproc *rproc, 1663void rproc_add_subdev(struct rproc *rproc,
1536 struct rproc_subdev *subdev, 1664 struct rproc_subdev *subdev,
1537 int (*probe)(struct rproc_subdev *subdev), 1665 int (*probe)(struct rproc_subdev *subdev),
1538 void (*remove)(struct rproc_subdev *subdev)) 1666 void (*remove)(struct rproc_subdev *subdev, bool crashed))
1539{ 1667{
1540 subdev->probe = probe; 1668 subdev->probe = probe;
1541 subdev->remove = remove; 1669 subdev->remove = remove;
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index 55a2950c5cb7..7570beb035b5 100644
--- a/drivers/remoteproc/remoteproc_internal.h
+++ b/drivers/remoteproc/remoteproc_internal.h
@@ -88,11 +88,10 @@ int rproc_load_segments(struct rproc *rproc, const struct firmware *fw)
88 return -EINVAL; 88 return -EINVAL;
89} 89}
90 90
91static inline int rproc_load_rsc_table(struct rproc *rproc, 91static inline int rproc_parse_fw(struct rproc *rproc, const struct firmware *fw)
92 const struct firmware *fw)
93{ 92{
94 if (rproc->ops->load_rsc_table) 93 if (rproc->ops->parse_fw)
95 return rproc->ops->load_rsc_table(rproc, fw); 94 return rproc->ops->parse_fw(rproc, fw);
96 95
97 return 0; 96 return 0;
98} 97}
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index a993d19fa562..5c4535b545cc 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -37,7 +37,7 @@ config QCOM_PM
37 37
38config QCOM_QMI_HELPERS 38config QCOM_QMI_HELPERS
39 tristate 39 tristate
40 depends on ARCH_QCOM 40 depends on ARCH_QCOM && NET
41 help 41 help
42 Helper library for handling QMI encoded messages. QMI encoded 42 Helper library for handling QMI encoded messages. QMI encoded
43 messages are used in communication between the majority of QRTR 43 messages are used in communication between the majority of QRTR
diff --git a/drivers/soc/qcom/mdt_loader.c b/drivers/soc/qcom/mdt_loader.c
index 08bd8549242a..17b314d9a148 100644
--- a/drivers/soc/qcom/mdt_loader.c
+++ b/drivers/soc/qcom/mdt_loader.c
@@ -83,12 +83,14 @@ EXPORT_SYMBOL_GPL(qcom_mdt_get_size);
83 * @mem_region: allocated memory region to load firmware into 83 * @mem_region: allocated memory region to load firmware into
84 * @mem_phys: physical address of allocated memory region 84 * @mem_phys: physical address of allocated memory region
85 * @mem_size: size of the allocated memory region 85 * @mem_size: size of the allocated memory region
86 * @reloc_base: adjusted physical address after relocation
86 * 87 *
87 * Returns 0 on success, negative errno otherwise. 88 * Returns 0 on success, negative errno otherwise.
88 */ 89 */
89int qcom_mdt_load(struct device *dev, const struct firmware *fw, 90int qcom_mdt_load(struct device *dev, const struct firmware *fw,
90 const char *firmware, int pas_id, void *mem_region, 91 const char *firmware, int pas_id, void *mem_region,
91 phys_addr_t mem_phys, size_t mem_size) 92 phys_addr_t mem_phys, size_t mem_size,
93 phys_addr_t *reloc_base)
92{ 94{
93 const struct elf32_phdr *phdrs; 95 const struct elf32_phdr *phdrs;
94 const struct elf32_phdr *phdr; 96 const struct elf32_phdr *phdr;
@@ -192,6 +194,9 @@ int qcom_mdt_load(struct device *dev, const struct firmware *fw,
192 memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz); 194 memset(ptr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz);
193 } 195 }
194 196
197 if (reloc_base)
198 *reloc_base = mem_reloc;
199
195out: 200out:
196 kfree(fw_name); 201 kfree(fw_name);
197 202
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index 728d421fffe9..d09a9c7af109 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -344,7 +344,7 @@ struct rproc_ops {
344 int (*stop)(struct rproc *rproc); 344 int (*stop)(struct rproc *rproc);
345 void (*kick)(struct rproc *rproc, int vqid); 345 void (*kick)(struct rproc *rproc, int vqid);
346 void * (*da_to_va)(struct rproc *rproc, u64 da, int len); 346 void * (*da_to_va)(struct rproc *rproc, u64 da, int len);
347 int (*load_rsc_table)(struct rproc *rproc, const struct firmware *fw); 347 int (*parse_fw)(struct rproc *rproc, const struct firmware *fw);
348 struct resource_table *(*find_loaded_rsc_table)( 348 struct resource_table *(*find_loaded_rsc_table)(
349 struct rproc *rproc, const struct firmware *fw); 349 struct rproc *rproc, const struct firmware *fw);
350 int (*load)(struct rproc *rproc, const struct firmware *fw); 350 int (*load)(struct rproc *rproc, const struct firmware *fw);
@@ -395,6 +395,21 @@ enum rproc_crash_type {
395}; 395};
396 396
397/** 397/**
398 * struct rproc_dump_segment - segment info from ELF header
399 * @node: list node related to the rproc segment list
400 * @da: device address of the segment
401 * @size: size of the segment
402 */
403struct rproc_dump_segment {
404 struct list_head node;
405
406 dma_addr_t da;
407 size_t size;
408
409 loff_t offset;
410};
411
412/**
398 * struct rproc - represents a physical remote processor device 413 * struct rproc - represents a physical remote processor device
399 * @node: list node of this rproc object 414 * @node: list node of this rproc object
400 * @domain: iommu domain 415 * @domain: iommu domain
@@ -424,6 +439,7 @@ enum rproc_crash_type {
424 * @cached_table: copy of the resource table 439 * @cached_table: copy of the resource table
425 * @table_sz: size of @cached_table 440 * @table_sz: size of @cached_table
426 * @has_iommu: flag to indicate if remote processor is behind an MMU 441 * @has_iommu: flag to indicate if remote processor is behind an MMU
442 * @dump_segments: list of segments in the firmware
427 */ 443 */
428struct rproc { 444struct rproc {
429 struct list_head node; 445 struct list_head node;
@@ -455,19 +471,21 @@ struct rproc {
455 size_t table_sz; 471 size_t table_sz;
456 bool has_iommu; 472 bool has_iommu;
457 bool auto_boot; 473 bool auto_boot;
474 struct list_head dump_segments;
458}; 475};
459 476
460/** 477/**
461 * struct rproc_subdev - subdevice tied to a remoteproc 478 * struct rproc_subdev - subdevice tied to a remoteproc
462 * @node: list node related to the rproc subdevs list 479 * @node: list node related to the rproc subdevs list
463 * @probe: probe function, called as the rproc is started 480 * @probe: probe function, called as the rproc is started
464 * @remove: remove function, called as the rproc is stopped 481 * @remove: remove function, called as the rproc is being stopped, the @crashed
482 * parameter indicates if this originates from the a recovery
465 */ 483 */
466struct rproc_subdev { 484struct rproc_subdev {
467 struct list_head node; 485 struct list_head node;
468 486
469 int (*probe)(struct rproc_subdev *subdev); 487 int (*probe)(struct rproc_subdev *subdev);
470 void (*remove)(struct rproc_subdev *subdev); 488 void (*remove)(struct rproc_subdev *subdev, bool crashed);
471}; 489};
472 490
473/* we currently support only two vrings per rvdev */ 491/* we currently support only two vrings per rvdev */
@@ -534,6 +552,7 @@ void rproc_free(struct rproc *rproc);
534int rproc_boot(struct rproc *rproc); 552int rproc_boot(struct rproc *rproc);
535void rproc_shutdown(struct rproc *rproc); 553void rproc_shutdown(struct rproc *rproc);
536void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type); 554void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type);
555int rproc_coredump_add_segment(struct rproc *rproc, dma_addr_t da, size_t size);
537 556
538static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev) 557static inline struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev)
539{ 558{
@@ -550,7 +569,7 @@ static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev)
550void rproc_add_subdev(struct rproc *rproc, 569void rproc_add_subdev(struct rproc *rproc,
551 struct rproc_subdev *subdev, 570 struct rproc_subdev *subdev,
552 int (*probe)(struct rproc_subdev *subdev), 571 int (*probe)(struct rproc_subdev *subdev),
553 void (*remove)(struct rproc_subdev *subdev)); 572 void (*remove)(struct rproc_subdev *subdev, bool graceful));
554 573
555void rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev); 574void rproc_remove_subdev(struct rproc *rproc, struct rproc_subdev *subdev);
556 575
diff --git a/include/linux/soc/qcom/mdt_loader.h b/include/linux/soc/qcom/mdt_loader.h
index bd8e0864b059..5b98bbdabc25 100644
--- a/include/linux/soc/qcom/mdt_loader.h
+++ b/include/linux/soc/qcom/mdt_loader.h
@@ -14,6 +14,7 @@ struct firmware;
14ssize_t qcom_mdt_get_size(const struct firmware *fw); 14ssize_t qcom_mdt_get_size(const struct firmware *fw);
15int qcom_mdt_load(struct device *dev, const struct firmware *fw, 15int qcom_mdt_load(struct device *dev, const struct firmware *fw,
16 const char *fw_name, int pas_id, void *mem_region, 16 const char *fw_name, int pas_id, void *mem_region,
17 phys_addr_t mem_phys, size_t mem_size); 17 phys_addr_t mem_phys, size_t mem_size,
18 phys_addr_t *reloc_base);
18 19
19#endif 20#endif
diff --git a/samples/Kconfig b/samples/Kconfig
index f524f551718e..3db002b9e1d3 100644
--- a/samples/Kconfig
+++ b/samples/Kconfig
@@ -62,6 +62,16 @@ config SAMPLE_KDB
62 Build an example of how to dynamically add the hello 62 Build an example of how to dynamically add the hello
63 command to the kdb shell. 63 command to the kdb shell.
64 64
65config SAMPLE_QMI_CLIENT
66 tristate "Build qmi client sample -- loadable modules only"
67 depends on m
68 depends on ARCH_QCOM
69 depends on NET
70 select QCOM_QMI_HELPERS
71 help
72 Build an QMI client sample driver, which demonstrates how to
73 communicate with a remote QRTR service, using QMI encoded messages.
74
65config SAMPLE_RPMSG_CLIENT 75config SAMPLE_RPMSG_CLIENT
66 tristate "Build rpmsg client sample -- loadable modules only" 76 tristate "Build rpmsg client sample -- loadable modules only"
67 depends on RPMSG && m 77 depends on RPMSG && m
diff --git a/samples/Makefile b/samples/Makefile
index 70cf3758dcf2..bd601c038b86 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -3,4 +3,4 @@
3obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \ 3obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \
4 hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \ 4 hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \
5 configfs/ connector/ v4l/ trace_printk/ \ 5 configfs/ connector/ v4l/ trace_printk/ \
6 vfio-mdev/ statx/ 6 vfio-mdev/ statx/ qmi/
diff --git a/samples/qmi/Makefile b/samples/qmi/Makefile
new file mode 100644
index 000000000000..2b111d2769df
--- /dev/null
+++ b/samples/qmi/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_SAMPLE_QMI_CLIENT) += qmi_sample_client.o
diff --git a/samples/qmi/qmi_sample_client.c b/samples/qmi/qmi_sample_client.c
new file mode 100644
index 000000000000..c9e7276c3d83
--- /dev/null
+++ b/samples/qmi/qmi_sample_client.c
@@ -0,0 +1,622 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Sample in-kernel QMI client driver
4 *
5 * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
6 * Copyright (C) 2017 Linaro Ltd.
7 */
8#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/debugfs.h>
11#include <linux/device.h>
12#include <linux/platform_device.h>
13#include <linux/qrtr.h>
14#include <linux/net.h>
15#include <linux/completion.h>
16#include <linux/idr.h>
17#include <linux/string.h>
18#include <net/sock.h>
19#include <linux/soc/qcom/qmi.h>
20
21#define PING_REQ1_TLV_TYPE 0x1
22#define PING_RESP1_TLV_TYPE 0x2
23#define PING_OPT1_TLV_TYPE 0x10
24#define PING_OPT2_TLV_TYPE 0x11
25
26#define DATA_REQ1_TLV_TYPE 0x1
27#define DATA_RESP1_TLV_TYPE 0x2
28#define DATA_OPT1_TLV_TYPE 0x10
29#define DATA_OPT2_TLV_TYPE 0x11
30
31#define TEST_MED_DATA_SIZE_V01 8192
32#define TEST_MAX_NAME_SIZE_V01 255
33
34#define TEST_PING_REQ_MSG_ID_V01 0x20
35#define TEST_DATA_REQ_MSG_ID_V01 0x21
36
37#define TEST_PING_REQ_MAX_MSG_LEN_V01 266
38#define TEST_DATA_REQ_MAX_MSG_LEN_V01 8456
39
40struct test_name_type_v01 {
41 u32 name_len;
42 char name[TEST_MAX_NAME_SIZE_V01];
43};
44
45static struct qmi_elem_info test_name_type_v01_ei[] = {
46 {
47 .data_type = QMI_DATA_LEN,
48 .elem_len = 1,
49 .elem_size = sizeof(u8),
50 .array_type = NO_ARRAY,
51 .tlv_type = QMI_COMMON_TLV_TYPE,
52 .offset = offsetof(struct test_name_type_v01,
53 name_len),
54 },
55 {
56 .data_type = QMI_UNSIGNED_1_BYTE,
57 .elem_len = TEST_MAX_NAME_SIZE_V01,
58 .elem_size = sizeof(char),
59 .array_type = VAR_LEN_ARRAY,
60 .tlv_type = QMI_COMMON_TLV_TYPE,
61 .offset = offsetof(struct test_name_type_v01,
62 name),
63 },
64 {}
65};
66
67struct test_ping_req_msg_v01 {
68 char ping[4];
69
70 u8 client_name_valid;
71 struct test_name_type_v01 client_name;
72};
73
74static struct qmi_elem_info test_ping_req_msg_v01_ei[] = {
75 {
76 .data_type = QMI_UNSIGNED_1_BYTE,
77 .elem_len = 4,
78 .elem_size = sizeof(char),
79 .array_type = STATIC_ARRAY,
80 .tlv_type = PING_REQ1_TLV_TYPE,
81 .offset = offsetof(struct test_ping_req_msg_v01,
82 ping),
83 },
84 {
85 .data_type = QMI_OPT_FLAG,
86 .elem_len = 1,
87 .elem_size = sizeof(u8),
88 .array_type = NO_ARRAY,
89 .tlv_type = PING_OPT1_TLV_TYPE,
90 .offset = offsetof(struct test_ping_req_msg_v01,
91 client_name_valid),
92 },
93 {
94 .data_type = QMI_STRUCT,
95 .elem_len = 1,
96 .elem_size = sizeof(struct test_name_type_v01),
97 .array_type = NO_ARRAY,
98 .tlv_type = PING_OPT1_TLV_TYPE,
99 .offset = offsetof(struct test_ping_req_msg_v01,
100 client_name),
101 .ei_array = test_name_type_v01_ei,
102 },
103 {}
104};
105
106struct test_ping_resp_msg_v01 {
107 struct qmi_response_type_v01 resp;
108
109 u8 pong_valid;
110 char pong[4];
111
112 u8 service_name_valid;
113 struct test_name_type_v01 service_name;
114};
115
116static struct qmi_elem_info test_ping_resp_msg_v01_ei[] = {
117 {
118 .data_type = QMI_STRUCT,
119 .elem_len = 1,
120 .elem_size = sizeof(struct qmi_response_type_v01),
121 .array_type = NO_ARRAY,
122 .tlv_type = PING_RESP1_TLV_TYPE,
123 .offset = offsetof(struct test_ping_resp_msg_v01,
124 resp),
125 .ei_array = qmi_response_type_v01_ei,
126 },
127 {
128 .data_type = QMI_OPT_FLAG,
129 .elem_len = 1,
130 .elem_size = sizeof(u8),
131 .array_type = NO_ARRAY,
132 .tlv_type = PING_OPT1_TLV_TYPE,
133 .offset = offsetof(struct test_ping_resp_msg_v01,
134 pong_valid),
135 },
136 {
137 .data_type = QMI_UNSIGNED_1_BYTE,
138 .elem_len = 4,
139 .elem_size = sizeof(char),
140 .array_type = STATIC_ARRAY,
141 .tlv_type = PING_OPT1_TLV_TYPE,
142 .offset = offsetof(struct test_ping_resp_msg_v01,
143 pong),
144 },
145 {
146 .data_type = QMI_OPT_FLAG,
147 .elem_len = 1,
148 .elem_size = sizeof(u8),
149 .array_type = NO_ARRAY,
150 .tlv_type = PING_OPT2_TLV_TYPE,
151 .offset = offsetof(struct test_ping_resp_msg_v01,
152 service_name_valid),
153 },
154 {
155 .data_type = QMI_STRUCT,
156 .elem_len = 1,
157 .elem_size = sizeof(struct test_name_type_v01),
158 .array_type = NO_ARRAY,
159 .tlv_type = PING_OPT2_TLV_TYPE,
160 .offset = offsetof(struct test_ping_resp_msg_v01,
161 service_name),
162 .ei_array = test_name_type_v01_ei,
163 },
164 {}
165};
166
167struct test_data_req_msg_v01 {
168 u32 data_len;
169 u8 data[TEST_MED_DATA_SIZE_V01];
170
171 u8 client_name_valid;
172 struct test_name_type_v01 client_name;
173};
174
175static struct qmi_elem_info test_data_req_msg_v01_ei[] = {
176 {
177 .data_type = QMI_DATA_LEN,
178 .elem_len = 1,
179 .elem_size = sizeof(u32),
180 .array_type = NO_ARRAY,
181 .tlv_type = DATA_REQ1_TLV_TYPE,
182 .offset = offsetof(struct test_data_req_msg_v01,
183 data_len),
184 },
185 {
186 .data_type = QMI_UNSIGNED_1_BYTE,
187 .elem_len = TEST_MED_DATA_SIZE_V01,
188 .elem_size = sizeof(u8),
189 .array_type = VAR_LEN_ARRAY,
190 .tlv_type = DATA_REQ1_TLV_TYPE,
191 .offset = offsetof(struct test_data_req_msg_v01,
192 data),
193 },
194 {
195 .data_type = QMI_OPT_FLAG,
196 .elem_len = 1,
197 .elem_size = sizeof(u8),
198 .array_type = NO_ARRAY,
199 .tlv_type = DATA_OPT1_TLV_TYPE,
200 .offset = offsetof(struct test_data_req_msg_v01,
201 client_name_valid),
202 },
203 {
204 .data_type = QMI_STRUCT,
205 .elem_len = 1,
206 .elem_size = sizeof(struct test_name_type_v01),
207 .array_type = NO_ARRAY,
208 .tlv_type = DATA_OPT1_TLV_TYPE,
209 .offset = offsetof(struct test_data_req_msg_v01,
210 client_name),
211 .ei_array = test_name_type_v01_ei,
212 },
213 {}
214};
215
216struct test_data_resp_msg_v01 {
217 struct qmi_response_type_v01 resp;
218
219 u8 data_valid;
220 u32 data_len;
221 u8 data[TEST_MED_DATA_SIZE_V01];
222
223 u8 service_name_valid;
224 struct test_name_type_v01 service_name;
225};
226
227static struct qmi_elem_info test_data_resp_msg_v01_ei[] = {
228 {
229 .data_type = QMI_STRUCT,
230 .elem_len = 1,
231 .elem_size = sizeof(struct qmi_response_type_v01),
232 .array_type = NO_ARRAY,
233 .tlv_type = DATA_RESP1_TLV_TYPE,
234 .offset = offsetof(struct test_data_resp_msg_v01,
235 resp),
236 .ei_array = qmi_response_type_v01_ei,
237 },
238 {
239 .data_type = QMI_OPT_FLAG,
240 .elem_len = 1,
241 .elem_size = sizeof(u8),
242 .array_type = NO_ARRAY,
243 .tlv_type = DATA_OPT1_TLV_TYPE,
244 .offset = offsetof(struct test_data_resp_msg_v01,
245 data_valid),
246 },
247 {
248 .data_type = QMI_DATA_LEN,
249 .elem_len = 1,
250 .elem_size = sizeof(u32),
251 .array_type = NO_ARRAY,
252 .tlv_type = DATA_OPT1_TLV_TYPE,
253 .offset = offsetof(struct test_data_resp_msg_v01,
254 data_len),
255 },
256 {
257 .data_type = QMI_UNSIGNED_1_BYTE,
258 .elem_len = TEST_MED_DATA_SIZE_V01,
259 .elem_size = sizeof(u8),
260 .array_type = VAR_LEN_ARRAY,
261 .tlv_type = DATA_OPT1_TLV_TYPE,
262 .offset = offsetof(struct test_data_resp_msg_v01,
263 data),
264 },
265 {
266 .data_type = QMI_OPT_FLAG,
267 .elem_len = 1,
268 .elem_size = sizeof(u8),
269 .array_type = NO_ARRAY,
270 .tlv_type = DATA_OPT2_TLV_TYPE,
271 .offset = offsetof(struct test_data_resp_msg_v01,
272 service_name_valid),
273 },
274 {
275 .data_type = QMI_STRUCT,
276 .elem_len = 1,
277 .elem_size = sizeof(struct test_name_type_v01),
278 .array_type = NO_ARRAY,
279 .tlv_type = DATA_OPT2_TLV_TYPE,
280 .offset = offsetof(struct test_data_resp_msg_v01,
281 service_name),
282 .ei_array = test_name_type_v01_ei,
283 },
284 {}
285};
286
287/*
288 * ping_write() - ping_pong debugfs file write handler
289 * @file: debugfs file context
290 * @user_buf: reference to the user data (ignored)
291 * @count: number of bytes in @user_buf
292 * @ppos: offset in @file to write
293 *
294 * This function allows user space to send out a ping_pong QMI encoded message
295 * to the associated remote test service and will return with the result of the
296 * transaction. It serves as an example of how to provide a custom response
297 * handler.
298 *
299 * Return: @count, or negative errno on failure.
300 */
301static ssize_t ping_write(struct file *file, const char __user *user_buf,
302 size_t count, loff_t *ppos)
303{
304 struct qmi_handle *qmi = file->private_data;
305 struct test_ping_req_msg_v01 req = {};
306 struct qmi_txn txn;
307 int ret;
308
309 memcpy(req.ping, "ping", sizeof(req.ping));
310
311 ret = qmi_txn_init(qmi, &txn, NULL, NULL);
312 if (ret < 0)
313 return ret;
314
315 ret = qmi_send_request(qmi, NULL, &txn,
316 TEST_PING_REQ_MSG_ID_V01,
317 TEST_PING_REQ_MAX_MSG_LEN_V01,
318 test_ping_req_msg_v01_ei, &req);
319 if (ret < 0) {
320 qmi_txn_cancel(&txn);
321 return ret;
322 }
323
324 ret = qmi_txn_wait(&txn, 5 * HZ);
325 if (ret < 0)
326 count = ret;
327
328 return count;
329}
330
331static const struct file_operations ping_fops = {
332 .open = simple_open,
333 .write = ping_write,
334};
335
336static void ping_pong_cb(struct qmi_handle *qmi, struct sockaddr_qrtr *sq,
337 struct qmi_txn *txn, const void *data)
338{
339 const struct test_ping_resp_msg_v01 *resp = data;
340
341 if (!txn) {
342 pr_err("spurious ping response\n");
343 return;
344 }
345
346 if (resp->resp.result == QMI_RESULT_FAILURE_V01)
347 txn->result = -ENXIO;
348 else if (!resp->pong_valid || memcmp(resp->pong, "pong", 4))
349 txn->result = -EINVAL;
350
351 complete(&txn->completion);
352}
353
354/*
355 * data_write() - data debugfs file write handler
356 * @file: debugfs file context
357 * @user_buf: reference to the user data
358 * @count: number of bytes in @user_buf
359 * @ppos: offset in @file to write
360 *
361 * This function allows user space to send out a data QMI encoded message to
362 * the associated remote test service and will return with the result of the
363 * transaction. It serves as an example of how to have the QMI helpers decode a
364 * transaction response into a provided object automatically.
365 *
366 * Return: @count, or negative errno on failure.
367 */
368static ssize_t data_write(struct file *file, const char __user *user_buf,
369 size_t count, loff_t *ppos)
370
371{
372 struct qmi_handle *qmi = file->private_data;
373 struct test_data_resp_msg_v01 *resp;
374 struct test_data_req_msg_v01 *req;
375 struct qmi_txn txn;
376 int ret;
377
378 req = kzalloc(sizeof(*req), GFP_KERNEL);
379 if (!req)
380 return -ENOMEM;
381
382 resp = kzalloc(sizeof(*resp), GFP_KERNEL);
383 if (!resp) {
384 kfree(req);
385 return -ENOMEM;
386 }
387
388 req->data_len = min_t(size_t, sizeof(req->data), count);
389 if (copy_from_user(req->data, user_buf, req->data_len)) {
390 ret = -EFAULT;
391 goto out;
392 }
393
394 ret = qmi_txn_init(qmi, &txn, test_data_resp_msg_v01_ei, resp);
395 if (ret < 0)
396 goto out;
397
398 ret = qmi_send_request(qmi, NULL, &txn,
399 TEST_DATA_REQ_MSG_ID_V01,
400 TEST_DATA_REQ_MAX_MSG_LEN_V01,
401 test_data_req_msg_v01_ei, req);
402 if (ret < 0) {
403 qmi_txn_cancel(&txn);
404 goto out;
405 }
406
407 ret = qmi_txn_wait(&txn, 5 * HZ);
408 if (ret < 0) {
409 goto out;
410 } else if (!resp->data_valid ||
411 resp->data_len != req->data_len ||
412 memcmp(resp->data, req->data, req->data_len)) {
413 pr_err("response data doesn't match expectation\n");
414 ret = -EINVAL;
415 goto out;
416 }
417
418 ret = count;
419
420out:
421 kfree(resp);
422 kfree(req);
423
424 return ret;
425}
426
427static const struct file_operations data_fops = {
428 .open = simple_open,
429 .write = data_write,
430};
431
432static struct qmi_msg_handler qmi_sample_handlers[] = {
433 {
434 .type = QMI_RESPONSE,
435 .msg_id = TEST_PING_REQ_MSG_ID_V01,
436 .ei = test_ping_resp_msg_v01_ei,
437 .decoded_size = sizeof(struct test_ping_req_msg_v01),
438 .fn = ping_pong_cb
439 },
440 {}
441};
442
443struct qmi_sample {
444 struct qmi_handle qmi;
445
446 struct dentry *de_dir;
447 struct dentry *de_data;
448 struct dentry *de_ping;
449};
450
451static struct dentry *qmi_debug_dir;
452
453static int qmi_sample_probe(struct platform_device *pdev)
454{
455 struct sockaddr_qrtr *sq;
456 struct qmi_sample *sample;
457 char path[20];
458 int ret;
459
460 sample = devm_kzalloc(&pdev->dev, sizeof(*sample), GFP_KERNEL);
461 if (!sample)
462 return -ENOMEM;
463
464 ret = qmi_handle_init(&sample->qmi, TEST_DATA_REQ_MAX_MSG_LEN_V01,
465 NULL,
466 qmi_sample_handlers);
467 if (ret < 0)
468 return ret;
469
470 sq = dev_get_platdata(&pdev->dev);
471 ret = kernel_connect(sample->qmi.sock, (struct sockaddr *)sq,
472 sizeof(*sq), 0);
473 if (ret < 0) {
474 pr_err("failed to connect to remote service port\n");
475 goto err_release_qmi_handle;
476 }
477
478 snprintf(path, sizeof(path), "%d:%d", sq->sq_node, sq->sq_port);
479
480 sample->de_dir = debugfs_create_dir(path, qmi_debug_dir);
481 if (IS_ERR(sample->de_dir)) {
482 ret = PTR_ERR(sample->de_dir);
483 goto err_release_qmi_handle;
484 }
485
486 sample->de_data = debugfs_create_file("data", 0600, sample->de_dir,
487 sample, &data_fops);
488 if (IS_ERR(sample->de_data)) {
489 ret = PTR_ERR(sample->de_data);
490 goto err_remove_de_dir;
491 }
492
493 sample->de_ping = debugfs_create_file("ping", 0600, sample->de_dir,
494 sample, &ping_fops);
495 if (IS_ERR(sample->de_ping)) {
496 ret = PTR_ERR(sample->de_ping);
497 goto err_remove_de_data;
498 }
499
500 platform_set_drvdata(pdev, sample);
501
502 return 0;
503
504err_remove_de_data:
505 debugfs_remove(sample->de_data);
506err_remove_de_dir:
507 debugfs_remove(sample->de_dir);
508err_release_qmi_handle:
509 qmi_handle_release(&sample->qmi);
510
511 return ret;
512}
513
514static int qmi_sample_remove(struct platform_device *pdev)
515{
516 struct qmi_sample *sample = platform_get_drvdata(pdev);
517
518 debugfs_remove(sample->de_ping);
519 debugfs_remove(sample->de_data);
520 debugfs_remove(sample->de_dir);
521
522 qmi_handle_release(&sample->qmi);
523
524 return 0;
525}
526
527static struct platform_driver qmi_sample_driver = {
528 .probe = qmi_sample_probe,
529 .remove = qmi_sample_remove,
530 .driver = {
531 .name = "qmi_sample_client",
532 },
533};
534
535static int qmi_sample_new_server(struct qmi_handle *qmi,
536 struct qmi_service *service)
537{
538 struct platform_device *pdev;
539 struct sockaddr_qrtr sq = { AF_QIPCRTR, service->node, service->port };
540 int ret;
541
542 pdev = platform_device_alloc("qmi_sample_client", PLATFORM_DEVID_AUTO);
543 if (!pdev)
544 return -ENOMEM;
545
546 ret = platform_device_add_data(pdev, &sq, sizeof(sq));
547 if (ret)
548 goto err_put_device;
549
550 ret = platform_device_add(pdev);
551 if (ret)
552 goto err_put_device;
553
554 service->priv = pdev;
555
556 return 0;
557
558err_put_device:
559 platform_device_put(pdev);
560
561 return ret;
562}
563
564static void qmi_sample_del_server(struct qmi_handle *qmi,
565 struct qmi_service *service)
566{
567 struct platform_device *pdev = service->priv;
568
569 platform_device_unregister(pdev);
570}
571
572static struct qmi_handle lookup_client;
573
574static struct qmi_ops lookup_ops = {
575 .new_server = qmi_sample_new_server,
576 .del_server = qmi_sample_del_server,
577};
578
579static int qmi_sample_init(void)
580{
581 int ret;
582
583 qmi_debug_dir = debugfs_create_dir("qmi_sample", NULL);
584 if (IS_ERR(qmi_debug_dir)) {
585 pr_err("failed to create qmi_sample dir\n");
586 return PTR_ERR(qmi_debug_dir);
587 }
588
589 ret = platform_driver_register(&qmi_sample_driver);
590 if (ret)
591 goto err_remove_debug_dir;
592
593 ret = qmi_handle_init(&lookup_client, 0, &lookup_ops, NULL);
594 if (ret < 0)
595 goto err_unregister_driver;
596
597 qmi_add_lookup(&lookup_client, 15, 0, 0);
598
599 return 0;
600
601err_unregister_driver:
602 platform_driver_unregister(&qmi_sample_driver);
603err_remove_debug_dir:
604 debugfs_remove(qmi_debug_dir);
605
606 return ret;
607}
608
609static void qmi_sample_exit(void)
610{
611 qmi_handle_release(&lookup_client);
612
613 platform_driver_unregister(&qmi_sample_driver);
614
615 debugfs_remove(qmi_debug_dir);
616}
617
618module_init(qmi_sample_init);
619module_exit(qmi_sample_exit);
620
621MODULE_DESCRIPTION("Sample QMI client driver");
622MODULE_LICENSE("GPL v2");