0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #include <linux/firmware.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/notifier.h>
0014 #include <linux/remoteproc.h>
0015 #include <linux/remoteproc/qcom_rproc.h>
0016 #include <linux/rpmsg/qcom_glink.h>
0017 #include <linux/rpmsg/qcom_smd.h>
0018 #include <linux/slab.h>
0019 #include <linux/soc/qcom/mdt_loader.h>
0020 #include <linux/soc/qcom/smem.h>
0021
0022 #include "remoteproc_internal.h"
0023 #include "qcom_common.h"
0024
0025 #define to_glink_subdev(d) container_of(d, struct qcom_rproc_glink, subdev)
0026 #define to_smd_subdev(d) container_of(d, struct qcom_rproc_subdev, subdev)
0027 #define to_ssr_subdev(d) container_of(d, struct qcom_rproc_ssr, subdev)
0028
0029 #define MAX_NUM_OF_SS 10
0030 #define MAX_REGION_NAME_LENGTH 16
0031 #define SBL_MINIDUMP_SMEM_ID 602
0032 #define MD_REGION_VALID ('V' << 24 | 'A' << 16 | 'L' << 8 | 'I' << 0)
0033 #define MD_SS_ENCR_DONE ('D' << 24 | 'O' << 16 | 'N' << 8 | 'E' << 0)
0034 #define MD_SS_ENABLED ('E' << 24 | 'N' << 16 | 'B' << 8 | 'L' << 0)
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044 struct minidump_region {
0045 char name[MAX_REGION_NAME_LENGTH];
0046 __le32 seq_num;
0047 __le32 valid;
0048 __le64 address;
0049 __le64 size;
0050 };
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061 struct minidump_subsystem {
0062 __le32 status;
0063 __le32 enabled;
0064 __le32 encryption_status;
0065 __le32 encryption_required;
0066 __le32 region_count;
0067 __le64 regions_baseptr;
0068 };
0069
0070
0071
0072
0073
0074
0075
0076
0077 struct minidump_global_toc {
0078 __le32 status;
0079 __le32 md_revision;
0080 __le32 enabled;
0081 struct minidump_subsystem subsystems[MAX_NUM_OF_SS];
0082 };
0083
0084 struct qcom_ssr_subsystem {
0085 const char *name;
0086 struct srcu_notifier_head notifier_list;
0087 struct list_head list;
0088 };
0089
0090 static LIST_HEAD(qcom_ssr_subsystem_list);
0091 static DEFINE_MUTEX(qcom_ssr_subsys_lock);
0092
0093 static void qcom_minidump_cleanup(struct rproc *rproc)
0094 {
0095 struct rproc_dump_segment *entry, *tmp;
0096
0097 list_for_each_entry_safe(entry, tmp, &rproc->dump_segments, node) {
0098 list_del(&entry->node);
0099 kfree(entry->priv);
0100 kfree(entry);
0101 }
0102 }
0103
0104 static int qcom_add_minidump_segments(struct rproc *rproc, struct minidump_subsystem *subsystem)
0105 {
0106 struct minidump_region __iomem *ptr;
0107 struct minidump_region region;
0108 int seg_cnt, i;
0109 dma_addr_t da;
0110 size_t size;
0111 char *name;
0112
0113 if (WARN_ON(!list_empty(&rproc->dump_segments))) {
0114 dev_err(&rproc->dev, "dump segment list already populated\n");
0115 return -EUCLEAN;
0116 }
0117
0118 seg_cnt = le32_to_cpu(subsystem->region_count);
0119 ptr = ioremap((unsigned long)le64_to_cpu(subsystem->regions_baseptr),
0120 seg_cnt * sizeof(struct minidump_region));
0121 if (!ptr)
0122 return -EFAULT;
0123
0124 for (i = 0; i < seg_cnt; i++) {
0125 memcpy_fromio(®ion, ptr + i, sizeof(region));
0126 if (region.valid == MD_REGION_VALID) {
0127 name = kstrdup(region.name, GFP_KERNEL);
0128 if (!name) {
0129 iounmap(ptr);
0130 return -ENOMEM;
0131 }
0132 da = le64_to_cpu(region.address);
0133 size = le32_to_cpu(region.size);
0134 rproc_coredump_add_custom_segment(rproc, da, size, NULL, name);
0135 }
0136 }
0137
0138 iounmap(ptr);
0139 return 0;
0140 }
0141
0142 void qcom_minidump(struct rproc *rproc, unsigned int minidump_id)
0143 {
0144 int ret;
0145 struct minidump_subsystem *subsystem;
0146 struct minidump_global_toc *toc;
0147
0148
0149 toc = qcom_smem_get(QCOM_SMEM_HOST_ANY, SBL_MINIDUMP_SMEM_ID, NULL);
0150
0151
0152 if (IS_ERR(toc) || !toc->status) {
0153 dev_err(&rproc->dev, "Minidump TOC not found in SMEM\n");
0154 return;
0155 }
0156
0157
0158 subsystem = &toc->subsystems[minidump_id];
0159
0160
0161
0162
0163
0164 if (subsystem->regions_baseptr == 0 ||
0165 le32_to_cpu(subsystem->status) != 1 ||
0166 le32_to_cpu(subsystem->enabled) != MD_SS_ENABLED ||
0167 le32_to_cpu(subsystem->encryption_status) != MD_SS_ENCR_DONE) {
0168 dev_err(&rproc->dev, "Minidump not ready, skipping\n");
0169 return;
0170 }
0171
0172 ret = qcom_add_minidump_segments(rproc, subsystem);
0173 if (ret) {
0174 dev_err(&rproc->dev, "Failed with error: %d while adding minidump entries\n", ret);
0175 goto clean_minidump;
0176 }
0177 rproc_coredump_using_sections(rproc);
0178 clean_minidump:
0179 qcom_minidump_cleanup(rproc);
0180 }
0181 EXPORT_SYMBOL_GPL(qcom_minidump);
0182
0183 static int glink_subdev_start(struct rproc_subdev *subdev)
0184 {
0185 struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
0186
0187 glink->edge = qcom_glink_smem_register(glink->dev, glink->node);
0188
0189 return PTR_ERR_OR_ZERO(glink->edge);
0190 }
0191
0192 static void glink_subdev_stop(struct rproc_subdev *subdev, bool crashed)
0193 {
0194 struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
0195
0196 qcom_glink_smem_unregister(glink->edge);
0197 glink->edge = NULL;
0198 }
0199
0200 static void glink_subdev_unprepare(struct rproc_subdev *subdev)
0201 {
0202 struct qcom_rproc_glink *glink = to_glink_subdev(subdev);
0203
0204 qcom_glink_ssr_notify(glink->ssr_name);
0205 }
0206
0207
0208
0209
0210
0211
0212
0213 void qcom_add_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink,
0214 const char *ssr_name)
0215 {
0216 struct device *dev = &rproc->dev;
0217
0218 glink->node = of_get_child_by_name(dev->parent->of_node, "glink-edge");
0219 if (!glink->node)
0220 return;
0221
0222 glink->ssr_name = kstrdup_const(ssr_name, GFP_KERNEL);
0223 if (!glink->ssr_name)
0224 return;
0225
0226 glink->dev = dev;
0227 glink->subdev.start = glink_subdev_start;
0228 glink->subdev.stop = glink_subdev_stop;
0229 glink->subdev.unprepare = glink_subdev_unprepare;
0230
0231 rproc_add_subdev(rproc, &glink->subdev);
0232 }
0233 EXPORT_SYMBOL_GPL(qcom_add_glink_subdev);
0234
0235
0236
0237
0238
0239
0240 void qcom_remove_glink_subdev(struct rproc *rproc, struct qcom_rproc_glink *glink)
0241 {
0242 if (!glink->node)
0243 return;
0244
0245 rproc_remove_subdev(rproc, &glink->subdev);
0246 kfree_const(glink->ssr_name);
0247 of_node_put(glink->node);
0248 }
0249 EXPORT_SYMBOL_GPL(qcom_remove_glink_subdev);
0250
0251
0252
0253
0254
0255
0256
0257
0258
0259
0260 int qcom_register_dump_segments(struct rproc *rproc,
0261 const struct firmware *fw)
0262 {
0263 const struct elf32_phdr *phdrs;
0264 const struct elf32_phdr *phdr;
0265 const struct elf32_hdr *ehdr;
0266 int ret;
0267 int i;
0268
0269 ehdr = (struct elf32_hdr *)fw->data;
0270 phdrs = (struct elf32_phdr *)(ehdr + 1);
0271
0272 for (i = 0; i < ehdr->e_phnum; i++) {
0273 phdr = &phdrs[i];
0274
0275 if (phdr->p_type != PT_LOAD)
0276 continue;
0277
0278 if ((phdr->p_flags & QCOM_MDT_TYPE_MASK) == QCOM_MDT_TYPE_HASH)
0279 continue;
0280
0281 if (!phdr->p_memsz)
0282 continue;
0283
0284 ret = rproc_coredump_add_segment(rproc, phdr->p_paddr,
0285 phdr->p_memsz);
0286 if (ret)
0287 return ret;
0288 }
0289
0290 return 0;
0291 }
0292 EXPORT_SYMBOL_GPL(qcom_register_dump_segments);
0293
0294 static int smd_subdev_start(struct rproc_subdev *subdev)
0295 {
0296 struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
0297
0298 smd->edge = qcom_smd_register_edge(smd->dev, smd->node);
0299
0300 return PTR_ERR_OR_ZERO(smd->edge);
0301 }
0302
0303 static void smd_subdev_stop(struct rproc_subdev *subdev, bool crashed)
0304 {
0305 struct qcom_rproc_subdev *smd = to_smd_subdev(subdev);
0306
0307 qcom_smd_unregister_edge(smd->edge);
0308 smd->edge = NULL;
0309 }
0310
0311
0312
0313
0314
0315
0316 void qcom_add_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
0317 {
0318 struct device *dev = &rproc->dev;
0319
0320 smd->node = of_get_child_by_name(dev->parent->of_node, "smd-edge");
0321 if (!smd->node)
0322 return;
0323
0324 smd->dev = dev;
0325 smd->subdev.start = smd_subdev_start;
0326 smd->subdev.stop = smd_subdev_stop;
0327
0328 rproc_add_subdev(rproc, &smd->subdev);
0329 }
0330 EXPORT_SYMBOL_GPL(qcom_add_smd_subdev);
0331
0332
0333
0334
0335
0336
0337 void qcom_remove_smd_subdev(struct rproc *rproc, struct qcom_rproc_subdev *smd)
0338 {
0339 if (!smd->node)
0340 return;
0341
0342 rproc_remove_subdev(rproc, &smd->subdev);
0343 of_node_put(smd->node);
0344 }
0345 EXPORT_SYMBOL_GPL(qcom_remove_smd_subdev);
0346
0347 static struct qcom_ssr_subsystem *qcom_ssr_get_subsys(const char *name)
0348 {
0349 struct qcom_ssr_subsystem *info;
0350
0351 mutex_lock(&qcom_ssr_subsys_lock);
0352
0353 list_for_each_entry(info, &qcom_ssr_subsystem_list, list)
0354 if (!strcmp(info->name, name))
0355 goto out;
0356
0357 info = kzalloc(sizeof(*info), GFP_KERNEL);
0358 if (!info) {
0359 info = ERR_PTR(-ENOMEM);
0360 goto out;
0361 }
0362 info->name = kstrdup_const(name, GFP_KERNEL);
0363 srcu_init_notifier_head(&info->notifier_list);
0364
0365
0366 list_add_tail(&info->list, &qcom_ssr_subsystem_list);
0367
0368 out:
0369 mutex_unlock(&qcom_ssr_subsys_lock);
0370 return info;
0371 }
0372
0373
0374
0375
0376
0377
0378
0379
0380
0381
0382
0383
0384
0385 void *qcom_register_ssr_notifier(const char *name, struct notifier_block *nb)
0386 {
0387 struct qcom_ssr_subsystem *info;
0388
0389 info = qcom_ssr_get_subsys(name);
0390 if (IS_ERR(info))
0391 return info;
0392
0393 srcu_notifier_chain_register(&info->notifier_list, nb);
0394
0395 return &info->notifier_list;
0396 }
0397 EXPORT_SYMBOL_GPL(qcom_register_ssr_notifier);
0398
0399
0400
0401
0402
0403
0404
0405
0406
0407
0408
0409 int qcom_unregister_ssr_notifier(void *notify, struct notifier_block *nb)
0410 {
0411 return srcu_notifier_chain_unregister(notify, nb);
0412 }
0413 EXPORT_SYMBOL_GPL(qcom_unregister_ssr_notifier);
0414
0415 static int ssr_notify_prepare(struct rproc_subdev *subdev)
0416 {
0417 struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
0418 struct qcom_ssr_notify_data data = {
0419 .name = ssr->info->name,
0420 .crashed = false,
0421 };
0422
0423 srcu_notifier_call_chain(&ssr->info->notifier_list,
0424 QCOM_SSR_BEFORE_POWERUP, &data);
0425 return 0;
0426 }
0427
0428 static int ssr_notify_start(struct rproc_subdev *subdev)
0429 {
0430 struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
0431 struct qcom_ssr_notify_data data = {
0432 .name = ssr->info->name,
0433 .crashed = false,
0434 };
0435
0436 srcu_notifier_call_chain(&ssr->info->notifier_list,
0437 QCOM_SSR_AFTER_POWERUP, &data);
0438 return 0;
0439 }
0440
0441 static void ssr_notify_stop(struct rproc_subdev *subdev, bool crashed)
0442 {
0443 struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
0444 struct qcom_ssr_notify_data data = {
0445 .name = ssr->info->name,
0446 .crashed = crashed,
0447 };
0448
0449 srcu_notifier_call_chain(&ssr->info->notifier_list,
0450 QCOM_SSR_BEFORE_SHUTDOWN, &data);
0451 }
0452
0453 static void ssr_notify_unprepare(struct rproc_subdev *subdev)
0454 {
0455 struct qcom_rproc_ssr *ssr = to_ssr_subdev(subdev);
0456 struct qcom_ssr_notify_data data = {
0457 .name = ssr->info->name,
0458 .crashed = false,
0459 };
0460
0461 srcu_notifier_call_chain(&ssr->info->notifier_list,
0462 QCOM_SSR_AFTER_SHUTDOWN, &data);
0463 }
0464
0465
0466
0467
0468
0469
0470
0471
0472
0473
0474
0475 void qcom_add_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr,
0476 const char *ssr_name)
0477 {
0478 struct qcom_ssr_subsystem *info;
0479
0480 info = qcom_ssr_get_subsys(ssr_name);
0481 if (IS_ERR(info)) {
0482 dev_err(&rproc->dev, "Failed to add ssr subdevice\n");
0483 return;
0484 }
0485
0486 ssr->info = info;
0487 ssr->subdev.prepare = ssr_notify_prepare;
0488 ssr->subdev.start = ssr_notify_start;
0489 ssr->subdev.stop = ssr_notify_stop;
0490 ssr->subdev.unprepare = ssr_notify_unprepare;
0491
0492 rproc_add_subdev(rproc, &ssr->subdev);
0493 }
0494 EXPORT_SYMBOL_GPL(qcom_add_ssr_subdev);
0495
0496
0497
0498
0499
0500
0501 void qcom_remove_ssr_subdev(struct rproc *rproc, struct qcom_rproc_ssr *ssr)
0502 {
0503 rproc_remove_subdev(rproc, &ssr->subdev);
0504 ssr->info = NULL;
0505 }
0506 EXPORT_SYMBOL_GPL(qcom_remove_ssr_subdev);
0507
0508 MODULE_DESCRIPTION("Qualcomm Remoteproc helper driver");
0509 MODULE_LICENSE("GPL v2");