diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-11 21:17:45 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-11 21:17:45 +0300 |
commit | 617e7481d7bfb807273d0f1b1983de032a725220 (patch) | |
tree | 626d4acde63a517001f75c39c19fc03e3af3a798 /drivers/remoteproc/qcom_q6v5_mss.c | |
parent | dded87afdacf8fe129fe13fe61d0a21e2afff3f6 (diff) | |
parent | 62b8f9e99329c92286534d05dac9dc0a6e0ba0cf (diff) | |
download | linux-617e7481d7bfb807273d0f1b1983de032a725220.tar.xz |
Merge tag 'rproc-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc
Pull remoteproc updates from Bjorn Andersson:
"This introduces a new "detached" state for remote processors that are
deemed to be running at the time Linux boots and the infrastructure
for "attaching" to these. It then introduces the support for
performing this operation for the STM32 platform.
The coredump functionality is moved out from the core file and gains
support for an optional mode where the recovery phase awaits the
notification from devcoredump that the dump should be released. This
allows userspace to grab the coredump in scenarios where vmalloc space
is too low for creating a complete copy of the coredump before handing
this to devcoredump.
A new character device based interface is introduced to allow tying
the stoppage of a remote processor to the termination of a user space
process. This is useful in situations when such process provides
crucial resources/operations for the firmware running on the remote
processor.
The Texas Instrument K3 driver gains support for the C66x and C71x
DSPs.
Qualcomm remoteprocs gains support for stashing relocation information
in IMEM, to aid post mortem debugging and the crash notification
mechanism is generalized to be reusable in cases where loosely coupled
drivers needs to know about the status of a remote processor. One such
example is the IPA hardware block, which is jointly owned with the
modem and migrated to this improved interface.
It also introduces a number of bug fixes and debug improvements for
the Qualcomm modem remoteproc driver.
And it cleans up the inconsistent interface for remoteproc drivers to
implement power management"
* tag 'rproc-v5.9' of git://git.kernel.org/pub/scm/linux/kernel/git/andersson/remoteproc: (56 commits)
remoteproc: core: Register the character device interface
remoteproc: Add remoteproc character device interface
remoteproc: kill IPA notify code
net: ipa: new notification infrastructure
remoteproc: k3-dsp: Add support for C71x DSPs
dt-bindings: remoteproc: k3-dsp: Update bindings for C71x DSPs
remoteproc: k3-dsp: Add support for L2RAM loading on C66x DSPs
remoteproc: k3-dsp: Add a remoteproc driver of K3 C66x DSPs
dt-bindings: remoteproc: Add bindings for C66x DSPs on TI K3 SoCs
remoteproc: k3: Add TI-SCI processor control helper functions
remoteproc: Introduce rproc_of_parse_firmware() helper
dt-bindings: arm: keystone: Add common TI SCI bindings
remoteproc: qcom_q6v5_mss: Remove redundant running state
remoteproc: qcom: q6v5: Update running state before requesting stop
remoteproc: qcom_q6v5_mss: Add modem debug policy support
remoteproc: qcom_q6v5_mss: Validate modem blob firmware size before load
remoteproc: qcom_q6v5_mss: Validate MBA firmware size before load
rpmsg: update documentation
remoteproc: qcom_q6v5_mss: Add MBA log extraction support
remoteproc: Add coredump debugfs entry
...
Diffstat (limited to 'drivers/remoteproc/qcom_q6v5_mss.c')
-rw-r--r-- | drivers/remoteproc/qcom_q6v5_mss.c | 157 |
1 files changed, 90 insertions, 67 deletions
diff --git a/drivers/remoteproc/qcom_q6v5_mss.c b/drivers/remoteproc/qcom_q6v5_mss.c index 903b2bb97e12..c401bcc263fa 100644 --- a/drivers/remoteproc/qcom_q6v5_mss.c +++ b/drivers/remoteproc/qcom_q6v5_mss.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <linux/delay.h> +#include <linux/devcoredump.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> #include <linux/kernel.h> @@ -22,7 +23,6 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/remoteproc.h> -#include "linux/remoteproc/qcom_q6v5_ipa_notify.h" #include <linux/reset.h> #include <linux/soc/qcom/mdt_loader.h> #include <linux/iopoll.h> @@ -30,12 +30,15 @@ #include "remoteproc_internal.h" #include "qcom_common.h" +#include "qcom_pil_info.h" #include "qcom_q6v5.h" #include <linux/qcom_scm.h> #define MPSS_CRASH_REASON_SMEM 421 +#define MBA_LOG_SIZE SZ_4K + /* RMB Status Register Values */ #define RMB_PBL_SUCCESS 0x1 @@ -112,8 +115,6 @@ #define QDSP6SS_SLEEP 0x3C #define QDSP6SS_BOOT_CORE_START 0x400 #define QDSP6SS_BOOT_CMD 0x404 -#define QDSP6SS_BOOT_STATUS 0x408 -#define BOOT_STATUS_TIMEOUT_US 200 #define BOOT_FSM_TIMEOUT 10000 struct reg_info { @@ -140,6 +141,7 @@ struct rproc_hexagon_res { int version; bool need_mem_protection; bool has_alt_reset; + bool has_mba_logs; bool has_spare_reg; }; @@ -179,15 +181,14 @@ struct q6v5 { int active_reg_count; int proxy_reg_count; - bool running; - bool dump_mba_loaded; - unsigned long dump_segment_mask; - unsigned long dump_complete_mask; + size_t current_dump_size; + size_t total_dump_size; phys_addr_t mba_phys; void *mba_region; size_t mba_size; + size_t dp_size; phys_addr_t mpss_phys; phys_addr_t mpss_reloc; @@ -196,10 +197,10 @@ struct q6v5 { struct qcom_rproc_glink glink_subdev; struct qcom_rproc_subdev smd_subdev; struct qcom_rproc_ssr ssr_subdev; - struct qcom_rproc_ipa_notify ipa_notify_subdev; struct qcom_sysmon *sysmon; bool need_mem_protection; bool has_alt_reset; + bool has_mba_logs; bool has_spare_reg; int mpss_perm; int mba_perm; @@ -404,11 +405,33 @@ static int q6v5_xfer_mem_ownership(struct q6v5 *qproc, int *current_perm, current_perm, next, perms); } +static void q6v5_debug_policy_load(struct q6v5 *qproc) +{ + const struct firmware *dp_fw; + + if (request_firmware_direct(&dp_fw, "msadp", qproc->dev)) + return; + + if (SZ_1M + dp_fw->size <= qproc->mba_size) { + memcpy(qproc->mba_region + SZ_1M, dp_fw->data, dp_fw->size); + qproc->dp_size = dp_fw->size; + } + + release_firmware(dp_fw); +} + static int q6v5_load(struct rproc *rproc, const struct firmware *fw) { struct q6v5 *qproc = rproc->priv; + /* MBA is restricted to a maximum size of 1M */ + if (fw->size > qproc->mba_size || fw->size > SZ_1M) { + dev_err(qproc->dev, "MBA firmware load failed\n"); + return -EINVAL; + } + memcpy(qproc->mba_region, fw->data, fw->size); + q6v5_debug_policy_load(qproc); return 0; } @@ -511,6 +534,26 @@ static int q6v5_rmb_mba_wait(struct q6v5 *qproc, u32 status, int ms) return val; } +static void q6v5_dump_mba_logs(struct q6v5 *qproc) +{ + struct rproc *rproc = qproc->rproc; + void *data; + + if (!qproc->has_mba_logs) + return; + + if (q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true, false, qproc->mba_phys, + qproc->mba_size)) + return; + + data = vmalloc(MBA_LOG_SIZE); + if (!data) + return; + + memcpy(data, qproc->mba_region, MBA_LOG_SIZE); + dev_coredumpv(&rproc->dev, data, MBA_LOG_SIZE, GFP_KERNEL); +} + static int q6v5proc_reset(struct q6v5 *qproc) { u32 val; @@ -579,13 +622,15 @@ static int q6v5proc_reset(struct q6v5 *qproc) /* De-assert the Q6 stop core signal */ writel(1, qproc->reg_base + QDSP6SS_BOOT_CORE_START); + /* Wait for 10 us for any staggering logic to settle */ + usleep_range(10, 20); + /* Trigger the boot FSM to start the Q6 out-of-reset sequence */ writel(1, qproc->reg_base + QDSP6SS_BOOT_CMD); - /* Poll the QDSP6SS_BOOT_STATUS for FSM completion */ - ret = readl_poll_timeout(qproc->reg_base + QDSP6SS_BOOT_STATUS, - val, (val & BIT(0)) != 0, 1, - BOOT_STATUS_TIMEOUT_US); + /* Poll the MSS_STATUS for FSM completion */ + ret = readl_poll_timeout(qproc->rmb_base + RMB_MBA_MSS_STATUS, + val, (val & BIT(0)) != 0, 10, BOOT_FSM_TIMEOUT); if (ret) { dev_err(qproc->dev, "Boot FSM failed to complete.\n"); /* Reset the modem so that boot FSM is in reset state */ @@ -829,6 +874,7 @@ static int q6v5_mba_load(struct q6v5 *qproc) { int ret; int xfermemop_ret; + bool mba_load_err = false; qcom_q6v5_prepare(&qproc->q6v5); @@ -895,6 +941,10 @@ static int q6v5_mba_load(struct q6v5 *qproc) } writel(qproc->mba_phys, qproc->rmb_base + RMB_MBA_IMAGE_REG); + if (qproc->dp_size) { + writel(qproc->mba_phys + SZ_1M, qproc->rmb_base + RMB_PMI_CODE_START_REG); + writel(qproc->dp_size, qproc->rmb_base + RMB_PMI_CODE_LENGTH_REG); + } ret = q6v5proc_reset(qproc); if (ret) @@ -918,7 +968,7 @@ halt_axi_ports: q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_nc); - + mba_load_err = true; reclaim_mba: xfermemop_ret = q6v5_xfer_mem_ownership(qproc, &qproc->mba_perm, true, false, qproc->mba_phys, @@ -926,6 +976,8 @@ reclaim_mba: if (xfermemop_ret) { dev_err(qproc->dev, "Failed to reclaim mba buffer, system may become unstable\n"); + } else if (mba_load_err) { + q6v5_dump_mba_logs(qproc); } disable_active_clks: @@ -961,6 +1013,7 @@ static void q6v5_mba_reclaim(struct q6v5 *qproc) u32 val; qproc->dump_mba_loaded = false; + qproc->dp_size = 0; q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_q6); q6v5proc_halt_axi_port(qproc, qproc->halt_map, qproc->halt_modem); @@ -1139,15 +1192,14 @@ static int q6v5_mpss_load(struct q6v5 *qproc) } else if (phdr->p_filesz) { /* Replace "xxx.xxx" with "xxx.bxx" */ sprintf(fw_name + fw_name_len - 3, "b%02d", i); - ret = request_firmware(&seg_fw, fw_name, qproc->dev); + ret = request_firmware_into_buf(&seg_fw, fw_name, qproc->dev, + ptr, phdr->p_filesz); if (ret) { dev_err(qproc->dev, "failed to load %s\n", fw_name); iounmap(ptr); goto release_firmware; } - memcpy(ptr, seg_fw->data, seg_fw->size); - release_firmware(seg_fw); } @@ -1190,6 +1242,8 @@ static int q6v5_mpss_load(struct q6v5 *qproc) else if (ret < 0) dev_err(qproc->dev, "MPSS authentication failed: %d\n", ret); + qcom_pil_info_store("modem", qproc->mpss_phys, qproc->mpss_size); + release_firmware: release_firmware(fw); out: @@ -1200,11 +1254,10 @@ out: static void qcom_q6v5_dump_segment(struct rproc *rproc, struct rproc_dump_segment *segment, - void *dest) + void *dest, size_t cp_offset, size_t size) { int ret = 0; struct q6v5 *qproc = rproc->priv; - unsigned long mask = BIT((unsigned long)segment->priv); int offset = segment->da - qproc->mpss_reloc; void *ptr = NULL; @@ -1221,19 +1274,19 @@ static void qcom_q6v5_dump_segment(struct rproc *rproc, } if (!ret) - ptr = ioremap_wc(qproc->mpss_phys + offset, segment->size); + ptr = ioremap_wc(qproc->mpss_phys + offset + cp_offset, size); if (ptr) { - memcpy(dest, ptr, segment->size); + memcpy(dest, ptr, size); iounmap(ptr); } else { - memset(dest, 0xff, segment->size); + memset(dest, 0xff, size); } - qproc->dump_segment_mask |= mask; + qproc->current_dump_size += size; /* Reclaim mba after copying segments */ - if (qproc->dump_segment_mask == qproc->dump_complete_mask) { + if (qproc->current_dump_size == qproc->total_dump_size) { if (qproc->dump_mba_loaded) { /* Try to reset ownership back to Q6 */ q6v5_xfer_mem_ownership(qproc, &qproc->mpss_perm, @@ -1255,7 +1308,8 @@ static int q6v5_start(struct rproc *rproc) if (ret) return ret; - dev_info(qproc->dev, "MBA booted, loading mpss\n"); + dev_info(qproc->dev, "MBA booted with%s debug policy, loading mpss\n", + qproc->dp_size ? "" : "out"); ret = q6v5_mpss_load(qproc); if (ret) @@ -1275,13 +1329,13 @@ static int q6v5_start(struct rproc *rproc) "Failed to reclaim mba buffer system may become unstable\n"); /* Reset Dump Segment Mask */ - qproc->dump_segment_mask = 0; - qproc->running = true; + qproc->current_dump_size = 0; return 0; reclaim_mpss: q6v5_mba_reclaim(qproc); + q6v5_dump_mba_logs(qproc); return ret; } @@ -1291,8 +1345,6 @@ static int q6v5_stop(struct rproc *rproc) struct q6v5 *qproc = (struct q6v5 *)rproc->priv; int ret; - qproc->running = false; - ret = qcom_q6v5_request_stop(&qproc->q6v5); if (ret == -ETIMEDOUT) dev_err(qproc->dev, "timed out on wait\n"); @@ -1324,7 +1376,7 @@ static int qcom_q6v5_register_dump_segments(struct rproc *rproc, ehdr = (struct elf32_hdr *)fw->data; phdrs = (struct elf32_phdr *)(ehdr + 1); - qproc->dump_complete_mask = 0; + qproc->total_dump_size = 0; for (i = 0; i < ehdr->e_phnum; i++) { phdr = &phdrs[i]; @@ -1335,11 +1387,11 @@ static int qcom_q6v5_register_dump_segments(struct rproc *rproc, ret = rproc_coredump_add_custom_segment(rproc, phdr->p_paddr, phdr->p_memsz, qcom_q6v5_dump_segment, - (void *)i); + NULL); if (ret) break; - qproc->dump_complete_mask |= BIT(i); + qproc->total_dump_size += phdr->p_memsz; } release_firmware(fw); @@ -1554,39 +1606,6 @@ static int q6v5_alloc_memory_region(struct q6v5 *qproc) return 0; } -#if IS_ENABLED(CONFIG_QCOM_Q6V5_IPA_NOTIFY) - -/* Register IPA notification function */ -int qcom_register_ipa_notify(struct rproc *rproc, qcom_ipa_notify_t notify, - void *data) -{ - struct qcom_rproc_ipa_notify *ipa_notify; - struct q6v5 *qproc = rproc->priv; - - if (!notify) - return -EINVAL; - - ipa_notify = &qproc->ipa_notify_subdev; - if (ipa_notify->notify) - return -EBUSY; - - ipa_notify->notify = notify; - ipa_notify->data = data; - - return 0; -} -EXPORT_SYMBOL_GPL(qcom_register_ipa_notify); - -/* Deregister IPA notification function */ -void qcom_deregister_ipa_notify(struct rproc *rproc) -{ - struct q6v5 *qproc = rproc->priv; - - qproc->ipa_notify_subdev.notify = NULL; -} -EXPORT_SYMBOL_GPL(qcom_deregister_ipa_notify); -#endif /* !IS_ENABLED(CONFIG_QCOM_Q6V5_IPA_NOTIFY) */ - static int q6v5_probe(struct platform_device *pdev) { const struct rproc_hexagon_res *desc; @@ -1701,6 +1720,7 @@ static int q6v5_probe(struct platform_device *pdev) qproc->version = desc->version; qproc->need_mem_protection = desc->need_mem_protection; + qproc->has_mba_logs = desc->has_mba_logs; ret = qcom_q6v5_init(&qproc->q6v5, pdev, rproc, MPSS_CRASH_REASON_SMEM, qcom_msa_handover); @@ -1712,7 +1732,6 @@ static int q6v5_probe(struct platform_device *pdev) qcom_add_glink_subdev(rproc, &qproc->glink_subdev, "mpss"); qcom_add_smd_subdev(rproc, &qproc->smd_subdev); qcom_add_ssr_subdev(rproc, &qproc->ssr_subdev, "mpss"); - qcom_add_ipa_notify_subdev(rproc, &qproc->ipa_notify_subdev); qproc->sysmon = qcom_add_sysmon_subdev(rproc, "modem", 0x12); if (IS_ERR(qproc->sysmon)) { ret = PTR_ERR(qproc->sysmon); @@ -1728,7 +1747,6 @@ static int q6v5_probe(struct platform_device *pdev) remove_sysmon_subdev: qcom_remove_sysmon_subdev(qproc->sysmon); remove_subdevs: - qcom_remove_ipa_notify_subdev(qproc->rproc, &qproc->ipa_notify_subdev); qcom_remove_ssr_subdev(rproc, &qproc->ssr_subdev); qcom_remove_smd_subdev(rproc, &qproc->smd_subdev); qcom_remove_glink_subdev(rproc, &qproc->glink_subdev); @@ -1750,7 +1768,6 @@ static int q6v5_remove(struct platform_device *pdev) rproc_del(rproc); qcom_remove_sysmon_subdev(qproc->sysmon); - qcom_remove_ipa_notify_subdev(rproc, &qproc->ipa_notify_subdev); qcom_remove_ssr_subdev(rproc, &qproc->ssr_subdev); qcom_remove_smd_subdev(rproc, &qproc->smd_subdev); qcom_remove_glink_subdev(rproc, &qproc->glink_subdev); @@ -1792,6 +1809,7 @@ static const struct rproc_hexagon_res sc7180_mss = { }, .need_mem_protection = true, .has_alt_reset = false, + .has_mba_logs = true, .has_spare_reg = true, .version = MSS_SC7180, }; @@ -1827,6 +1845,7 @@ static const struct rproc_hexagon_res sdm845_mss = { }, .need_mem_protection = true, .has_alt_reset = true, + .has_mba_logs = false, .has_spare_reg = false, .version = MSS_SDM845, }; @@ -1854,6 +1873,7 @@ static const struct rproc_hexagon_res msm8998_mss = { }, .need_mem_protection = true, .has_alt_reset = false, + .has_mba_logs = false, .has_spare_reg = false, .version = MSS_MSM8998, }; @@ -1884,6 +1904,7 @@ static const struct rproc_hexagon_res msm8996_mss = { }, .need_mem_protection = true, .has_alt_reset = false, + .has_mba_logs = false, .has_spare_reg = false, .version = MSS_MSM8996, }; @@ -1917,6 +1938,7 @@ static const struct rproc_hexagon_res msm8916_mss = { }, .need_mem_protection = false, .has_alt_reset = false, + .has_mba_logs = false, .has_spare_reg = false, .version = MSS_MSM8916, }; @@ -1958,6 +1980,7 @@ static const struct rproc_hexagon_res msm8974_mss = { }, .need_mem_protection = false, .has_alt_reset = false, + .has_mba_logs = false, .has_spare_reg = false, .version = MSS_MSM8974, }; |