diff options
Diffstat (limited to 'drivers/remoteproc')
-rw-r--r-- | drivers/remoteproc/Kconfig | 13 | ||||
-rw-r--r-- | drivers/remoteproc/Makefile | 1 | ||||
-rw-r--r-- | drivers/remoteproc/da8xx_remoteproc.c | 10 | ||||
-rw-r--r-- | drivers/remoteproc/imx_dsp_rproc.c | 2 | ||||
-rw-r--r-- | drivers/remoteproc/imx_rproc.c | 93 | ||||
-rw-r--r-- | drivers/remoteproc/imx_rproc.h | 4 | ||||
-rw-r--r-- | drivers/remoteproc/ingenic_rproc.c | 3 | ||||
-rw-r--r-- | drivers/remoteproc/keystone_remoteproc.c | 21 | ||||
-rw-r--r-- | drivers/remoteproc/qcom_q6v5_pas.c | 93 | ||||
-rw-r--r-- | drivers/remoteproc/st_slim_rproc.c | 6 | ||||
-rw-r--r-- | drivers/remoteproc/ti_k3_dsp_remoteproc.c | 108 | ||||
-rw-r--r-- | drivers/remoteproc/ti_k3_m4_remoteproc.c | 667 | ||||
-rw-r--r-- | drivers/remoteproc/ti_k3_r5_remoteproc.c | 130 | ||||
-rw-r--r-- | drivers/remoteproc/ti_sci_proc.h | 26 | ||||
-rw-r--r-- | drivers/remoteproc/xlnx_r5_remoteproc.c | 141 |
15 files changed, 1096 insertions, 222 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index dda2ada215b7..7a80c92b785e 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -340,6 +340,19 @@ config TI_K3_DSP_REMOTEPROC It's safe to say N here if you're not interested in utilizing the DSP slave processors. +config TI_K3_M4_REMOTEPROC + tristate "TI K3 M4 remoteproc support" + depends on ARCH_OMAP2PLUS || ARCH_K3 + select MAILBOX + select OMAP2PLUS_MBOX + help + Say m here to support TI's M4 remote processor subsystems + on various TI K3 family of SoCs through the remote processor + framework. + + It's safe to say N here if you're not interested in utilizing + a remote processor. + config TI_K3_R5_REMOTEPROC tristate "TI K3 R5 remoteproc support" depends on ARCH_K3 diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 91314a9b43ce..5ff4e2fee4ab 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -37,5 +37,6 @@ obj-$(CONFIG_ST_REMOTEPROC) += st_remoteproc.o obj-$(CONFIG_ST_SLIM_REMOTEPROC) += st_slim_rproc.o obj-$(CONFIG_STM32_RPROC) += stm32_rproc.o obj-$(CONFIG_TI_K3_DSP_REMOTEPROC) += ti_k3_dsp_remoteproc.o +obj-$(CONFIG_TI_K3_M4_REMOTEPROC) += ti_k3_m4_remoteproc.o obj-$(CONFIG_TI_K3_R5_REMOTEPROC) += ti_k3_r5_remoteproc.o obj-$(CONFIG_XLNX_R5_REMOTEPROC) += xlnx_r5_remoteproc.o diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c index 9041a0e07fb2..8770d0cf1255 100644 --- a/drivers/remoteproc/da8xx_remoteproc.c +++ b/drivers/remoteproc/da8xx_remoteproc.c @@ -239,8 +239,6 @@ static int da8xx_rproc_probe(struct platform_device *pdev) struct da8xx_rproc *drproc; struct rproc *rproc; struct irq_data *irq_data; - struct resource *bootreg_res; - struct resource *chipsig_res; struct clk *dsp_clk; struct reset_control *dsp_reset; void __iomem *chipsig; @@ -258,15 +256,11 @@ static int da8xx_rproc_probe(struct platform_device *pdev) return -EINVAL; } - bootreg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "host1cfg"); - bootreg = devm_ioremap_resource(dev, bootreg_res); + bootreg = devm_platform_ioremap_resource_byname(pdev, "host1cfg"); if (IS_ERR(bootreg)) return PTR_ERR(bootreg); - chipsig_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, - "chipsig"); - chipsig = devm_ioremap_resource(dev, chipsig_res); + chipsig = devm_platform_ioremap_resource_byname(pdev, "chipsig"); if (IS_ERR(chipsig)) return PTR_ERR(chipsig); diff --git a/drivers/remoteproc/imx_dsp_rproc.c b/drivers/remoteproc/imx_dsp_rproc.c index 087506e21508..376187ad5754 100644 --- a/drivers/remoteproc/imx_dsp_rproc.c +++ b/drivers/remoteproc/imx_dsp_rproc.c @@ -509,7 +509,7 @@ static int imx_dsp_rproc_mbox_alloc(struct imx_dsp_rproc *priv) struct mbox_client *cl; int ret; - if (!of_get_property(dev->of_node, "mbox-names", NULL)) + if (!of_property_present(dev->of_node, "mbox-names")) return 0; cl = &priv->cl; diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 144c8e9a642e..800015ff7ff9 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -18,6 +18,7 @@ #include <linux/of_reserved_mem.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> +#include <linux/reboot.h> #include <linux/regmap.h> #include <linux/remoteproc.h> #include <linux/workqueue.h> @@ -90,7 +91,7 @@ struct imx_rproc_mem { #define ATT_CORE_MASK 0xffff #define ATT_CORE(I) BIT((I)) -static int imx_rproc_xtr_mbox_init(struct rproc *rproc); +static int imx_rproc_xtr_mbox_init(struct rproc *rproc, bool tx_block); static void imx_rproc_free_mbox(struct rproc *rproc); struct imx_rproc { @@ -119,20 +120,16 @@ struct imx_rproc { static const struct imx_rproc_att imx_rproc_att_imx93[] = { /* dev addr , sys addr , size , flags */ /* TCM CODE NON-SECURE */ - { 0x0FFC0000, 0x201C0000, 0x00020000, ATT_OWN | ATT_IOMEM }, - { 0x0FFE0000, 0x201E0000, 0x00020000, ATT_OWN | ATT_IOMEM }, + { 0x0FFC0000, 0x201C0000, 0x00040000, ATT_OWN | ATT_IOMEM }, /* TCM CODE SECURE */ - { 0x1FFC0000, 0x201C0000, 0x00020000, ATT_OWN | ATT_IOMEM }, - { 0x1FFE0000, 0x201E0000, 0x00020000, ATT_OWN | ATT_IOMEM }, + { 0x1FFC0000, 0x201C0000, 0x00040000, ATT_OWN | ATT_IOMEM }, /* TCM SYS NON-SECURE*/ - { 0x20000000, 0x20200000, 0x00020000, ATT_OWN | ATT_IOMEM }, - { 0x20020000, 0x20220000, 0x00020000, ATT_OWN | ATT_IOMEM }, + { 0x20000000, 0x20200000, 0x00040000, ATT_OWN | ATT_IOMEM }, /* TCM SYS SECURE*/ - { 0x30000000, 0x20200000, 0x00020000, ATT_OWN | ATT_IOMEM }, - { 0x30020000, 0x20220000, 0x00020000, ATT_OWN | ATT_IOMEM }, + { 0x30000000, 0x20200000, 0x00040000, ATT_OWN | ATT_IOMEM }, /* DDR */ { 0x80000000, 0x80000000, 0x10000000, 0 }, @@ -210,11 +207,9 @@ static const struct imx_rproc_att imx_rproc_att_imx8mq[] = { /* QSPI Code - alias */ { 0x08000000, 0x08000000, 0x08000000, 0 }, /* DDR (Code) - alias */ - { 0x10000000, 0x80000000, 0x0FFE0000, 0 }, - /* TCML */ - { 0x1FFE0000, 0x007E0000, 0x00020000, ATT_OWN | ATT_IOMEM}, - /* TCMU */ - { 0x20000000, 0x00800000, 0x00020000, ATT_OWN | ATT_IOMEM}, + { 0x10000000, 0x40000000, 0x0FFE0000, 0 }, + /* TCML/U */ + { 0x1FFE0000, 0x007E0000, 0x00040000, ATT_OWN | ATT_IOMEM}, /* OCRAM_S */ { 0x20180000, 0x00180000, 0x00008000, ATT_OWN }, /* OCRAM */ @@ -339,6 +334,7 @@ static const struct imx_rproc_dcfg imx_rproc_cfg_imx7ulp = { .att = imx_rproc_att_imx7ulp, .att_size = ARRAY_SIZE(imx_rproc_att_imx7ulp), .method = IMX_RPROC_NONE, + .flags = IMX_RPROC_NEED_SYSTEM_OFF, }; static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = { @@ -375,7 +371,7 @@ static int imx_rproc_start(struct rproc *rproc) struct arm_smccc_res res; int ret; - ret = imx_rproc_xtr_mbox_init(rproc); + ret = imx_rproc_xtr_mbox_init(rproc, true); if (ret) return ret; @@ -635,7 +631,7 @@ static void imx_rproc_kick(struct rproc *rproc, int vqid) static int imx_rproc_attach(struct rproc *rproc) { - return imx_rproc_xtr_mbox_init(rproc); + return imx_rproc_xtr_mbox_init(rproc, true); } static int imx_rproc_detach(struct rproc *rproc) @@ -666,6 +662,17 @@ static struct resource_table *imx_rproc_get_loaded_rsc_table(struct rproc *rproc return (struct resource_table *)priv->rsc_table; } +static struct resource_table * +imx_rproc_elf_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw) +{ + struct imx_rproc *priv = rproc->priv; + + if (priv->rsc_table) + return (struct resource_table *)priv->rsc_table; + + return rproc_elf_find_loaded_rsc_table(rproc, fw); +} + static const struct rproc_ops imx_rproc_ops = { .prepare = imx_rproc_prepare, .attach = imx_rproc_attach, @@ -676,7 +683,7 @@ static const struct rproc_ops imx_rproc_ops = { .da_to_va = imx_rproc_da_to_va, .load = rproc_elf_load_segments, .parse_fw = imx_rproc_parse_fw, - .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table, + .find_loaded_rsc_table = imx_rproc_elf_find_loaded_rsc_table, .get_loaded_rsc_table = imx_rproc_get_loaded_rsc_table, .sanity_check = rproc_elf_sanity_check, .get_boot_addr = rproc_elf_get_boot_addr, @@ -789,7 +796,7 @@ static void imx_rproc_rx_callback(struct mbox_client *cl, void *msg) queue_work(priv->workqueue, &priv->rproc_work); } -static int imx_rproc_xtr_mbox_init(struct rproc *rproc) +static int imx_rproc_xtr_mbox_init(struct rproc *rproc, bool tx_block) { struct imx_rproc *priv = rproc->priv; struct device *dev = priv->dev; @@ -807,12 +814,12 @@ static int imx_rproc_xtr_mbox_init(struct rproc *rproc) if (priv->tx_ch && priv->rx_ch) return 0; - if (!of_get_property(dev->of_node, "mbox-names", NULL)) + if (!of_property_present(dev->of_node, "mbox-names")) return 0; cl = &priv->cl; cl->dev = dev; - cl->tx_block = true; + cl->tx_block = tx_block; cl->tx_tout = 100; cl->knows_txdone = false; cl->rx_callback = imx_rproc_rx_callback; @@ -1045,6 +1052,22 @@ static int imx_rproc_clk_enable(struct imx_rproc *priv) return 0; } +static int imx_rproc_sys_off_handler(struct sys_off_data *data) +{ + struct rproc *rproc = data->cb_data; + int ret; + + imx_rproc_free_mbox(rproc); + + ret = imx_rproc_xtr_mbox_init(rproc, false); + if (ret) { + dev_err(&rproc->dev, "Failed to request non-blocking mbox\n"); + return NOTIFY_BAD; + } + + return NOTIFY_DONE; +} + static int imx_rproc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1076,7 +1099,9 @@ static int imx_rproc_probe(struct platform_device *pdev) return -ENOMEM; } - ret = imx_rproc_xtr_mbox_init(rproc); + INIT_WORK(&priv->rproc_work, imx_rproc_vq_work); + + ret = imx_rproc_xtr_mbox_init(rproc, true); if (ret) goto err_put_wkq; @@ -1094,11 +1119,33 @@ static int imx_rproc_probe(struct platform_device *pdev) if (ret) goto err_put_scu; - INIT_WORK(&priv->rproc_work, imx_rproc_vq_work); - if (rproc->state != RPROC_DETACHED) rproc->auto_boot = of_property_read_bool(np, "fsl,auto-boot"); + if (dcfg->flags & IMX_RPROC_NEED_SYSTEM_OFF) { + /* + * setup mailbox to non-blocking mode in + * [SYS_OFF_MODE_POWER_OFF_PREPARE, SYS_OFF_MODE_RESTART_PREPARE] + * phase before invoking [SYS_OFF_MODE_POWER_OFF, SYS_OFF_MODE_RESTART] + * atomic chain, see kernel/reboot.c. + */ + ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF_PREPARE, + SYS_OFF_PRIO_DEFAULT, + imx_rproc_sys_off_handler, rproc); + if (ret) { + dev_err(dev, "register power off handler failure\n"); + goto err_put_clk; + } + + ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART_PREPARE, + SYS_OFF_PRIO_DEFAULT, + imx_rproc_sys_off_handler, rproc); + if (ret) { + dev_err(dev, "register restart handler failure\n"); + goto err_put_clk; + } + } + ret = rproc_add(rproc); if (ret) { dev_err(dev, "rproc_add failed\n"); diff --git a/drivers/remoteproc/imx_rproc.h b/drivers/remoteproc/imx_rproc.h index 79a1b8956d14..17a7d051c531 100644 --- a/drivers/remoteproc/imx_rproc.h +++ b/drivers/remoteproc/imx_rproc.h @@ -26,6 +26,9 @@ enum imx_rproc_method { IMX_RPROC_SCU_API, }; +/* dcfg flags */ +#define IMX_RPROC_NEED_SYSTEM_OFF BIT(0) + struct imx_rproc_dcfg { u32 src_reg; u32 src_mask; @@ -36,6 +39,7 @@ struct imx_rproc_dcfg { const struct imx_rproc_att *att; size_t att_size; enum imx_rproc_method method; + u32 flags; }; #endif /* _IMX_RPROC_H */ diff --git a/drivers/remoteproc/ingenic_rproc.c b/drivers/remoteproc/ingenic_rproc.c index 9902cce28692..1b78d8ddeacf 100644 --- a/drivers/remoteproc/ingenic_rproc.c +++ b/drivers/remoteproc/ingenic_rproc.c @@ -183,8 +183,7 @@ static int ingenic_rproc_probe(struct platform_device *pdev) vpu->dev = &pdev->dev; platform_set_drvdata(pdev, vpu); - mem = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aux"); - vpu->aux_base = devm_ioremap_resource(dev, mem); + vpu->aux_base = devm_platform_ioremap_resource_byname(pdev, "aux"); if (IS_ERR(vpu->aux_base)) { dev_err(dev, "Failed to ioremap\n"); return PTR_ERR(vpu->aux_base); diff --git a/drivers/remoteproc/keystone_remoteproc.c b/drivers/remoteproc/keystone_remoteproc.c index 7e57b90bcaf8..8f0f7a4cfef2 100644 --- a/drivers/remoteproc/keystone_remoteproc.c +++ b/drivers/remoteproc/keystone_remoteproc.c @@ -366,8 +366,6 @@ static int keystone_rproc_probe(struct platform_device *pdev) struct rproc *rproc; int dsp_id; char *fw_name = NULL; - char *template = "keystone-dsp%d-fw"; - int name_len = 0; int ret = 0; if (!np) { @@ -382,14 +380,12 @@ static int keystone_rproc_probe(struct platform_device *pdev) } /* construct a custom default fw name - subject to change in future */ - name_len = strlen(template); /* assuming a single digit alias */ - fw_name = devm_kzalloc(dev, name_len, GFP_KERNEL); + fw_name = devm_kasprintf(dev, GFP_KERNEL, "keystone-dsp%d-fw", dsp_id); if (!fw_name) return -ENOMEM; - snprintf(fw_name, name_len, template, dsp_id); - rproc = rproc_alloc(dev, dev_name(dev), &keystone_rproc_ops, fw_name, - sizeof(*ksproc)); + rproc = devm_rproc_alloc(dev, dev_name(dev), &keystone_rproc_ops, + fw_name, sizeof(*ksproc)); if (!rproc) return -ENOMEM; @@ -400,13 +396,11 @@ static int keystone_rproc_probe(struct platform_device *pdev) ret = keystone_rproc_of_get_dev_syscon(pdev, ksproc); if (ret) - goto free_rproc; + return ret; ksproc->reset = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(ksproc->reset)) { - ret = PTR_ERR(ksproc->reset); - goto free_rproc; - } + if (IS_ERR(ksproc->reset)) + return PTR_ERR(ksproc->reset); /* enable clock for accessing DSP internal memories */ pm_runtime_enable(dev); @@ -471,8 +465,6 @@ disable_clk: pm_runtime_put_sync(dev); disable_rpm: pm_runtime_disable(dev); -free_rproc: - rproc_free(rproc); return ret; } @@ -484,7 +476,6 @@ static void keystone_rproc_remove(struct platform_device *pdev) gpiod_put(ksproc->kick_gpio); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); - rproc_free(ksproc->rproc); of_reserved_mem_device_release(&pdev->dev); } diff --git a/drivers/remoteproc/qcom_q6v5_pas.c b/drivers/remoteproc/qcom_q6v5_pas.c index 88e7b84f223c..ef82835e98a4 100644 --- a/drivers/remoteproc/qcom_q6v5_pas.c +++ b/drivers/remoteproc/qcom_q6v5_pas.c @@ -829,6 +829,23 @@ static const struct adsp_data adsp_resource_init = { .ssctl_id = 0x14, }; +static const struct adsp_data sa8775p_adsp_resource = { + .crash_reason_smem = 423, + .firmware_name = "adsp.mbn", + .pas_id = 1, + .minidump_id = 5, + .auto_boot = true, + .proxy_pd_names = (char*[]){ + "lcx", + "lmx", + NULL + }, + .load_state = "adsp", + .ssr_name = "lpass", + .sysmon_name = "adsp", + .ssctl_id = 0x14, +}; + static const struct adsp_data sdm845_adsp_resource_init = { .crash_reason_smem = 423, .firmware_name = "adsp.mdt", @@ -942,6 +959,42 @@ static const struct adsp_data cdsp_resource_init = { .ssctl_id = 0x17, }; +static const struct adsp_data sa8775p_cdsp0_resource = { + .crash_reason_smem = 601, + .firmware_name = "cdsp0.mbn", + .pas_id = 18, + .minidump_id = 7, + .auto_boot = true, + .proxy_pd_names = (char*[]){ + "cx", + "mxc", + "nsp", + NULL + }, + .load_state = "cdsp", + .ssr_name = "cdsp", + .sysmon_name = "cdsp", + .ssctl_id = 0x17, +}; + +static const struct adsp_data sa8775p_cdsp1_resource = { + .crash_reason_smem = 633, + .firmware_name = "cdsp1.mbn", + .pas_id = 30, + .minidump_id = 20, + .auto_boot = true, + .proxy_pd_names = (char*[]){ + "cx", + "mxc", + "nsp", + NULL + }, + .load_state = "nsp", + .ssr_name = "cdsp1", + .sysmon_name = "cdsp1", + .ssctl_id = 0x20, +}; + static const struct adsp_data sdm845_cdsp_resource_init = { .crash_reason_smem = 601, .firmware_name = "cdsp.mdt", @@ -1083,6 +1136,40 @@ static const struct adsp_data sm8350_cdsp_resource = { .ssctl_id = 0x17, }; +static const struct adsp_data sa8775p_gpdsp0_resource = { + .crash_reason_smem = 640, + .firmware_name = "gpdsp0.mbn", + .pas_id = 39, + .minidump_id = 21, + .auto_boot = true, + .proxy_pd_names = (char*[]){ + "cx", + "mxc", + NULL + }, + .load_state = "gpdsp0", + .ssr_name = "gpdsp0", + .sysmon_name = "gpdsp0", + .ssctl_id = 0x21, +}; + +static const struct adsp_data sa8775p_gpdsp1_resource = { + .crash_reason_smem = 641, + .firmware_name = "gpdsp1.mbn", + .pas_id = 40, + .minidump_id = 22, + .auto_boot = true, + .proxy_pd_names = (char*[]){ + "cx", + "mxc", + NULL + }, + .load_state = "gpdsp1", + .ssr_name = "gpdsp1", + .sysmon_name = "gpdsp1", + .ssctl_id = 0x22, +}; + static const struct adsp_data mpss_resource_init = { .crash_reason_smem = 421, .firmware_name = "modem.mdt", @@ -1329,6 +1416,11 @@ static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,qcs404-adsp-pas", .data = &adsp_resource_init }, { .compatible = "qcom,qcs404-cdsp-pas", .data = &cdsp_resource_init }, { .compatible = "qcom,qcs404-wcss-pas", .data = &wcss_resource_init }, + { .compatible = "qcom,sa8775p-adsp-pas", .data = &sa8775p_adsp_resource}, + { .compatible = "qcom,sa8775p-cdsp0-pas", .data = &sa8775p_cdsp0_resource}, + { .compatible = "qcom,sa8775p-cdsp1-pas", .data = &sa8775p_cdsp1_resource}, + { .compatible = "qcom,sa8775p-gpdsp0-pas", .data = &sa8775p_gpdsp0_resource}, + { .compatible = "qcom,sa8775p-gpdsp1-pas", .data = &sa8775p_gpdsp1_resource}, { .compatible = "qcom,sc7180-adsp-pas", .data = &sm8250_adsp_resource}, { .compatible = "qcom,sc7180-mpss-pas", .data = &mpss_resource_init}, { .compatible = "qcom,sc7280-adsp-pas", .data = &sm8350_adsp_resource}, @@ -1346,6 +1438,7 @@ static const struct of_device_id adsp_of_match[] = { { .compatible = "qcom,sdm845-cdsp-pas", .data = &sdm845_cdsp_resource_init}, { .compatible = "qcom,sdm845-slpi-pas", .data = &sdm845_slpi_resource_init}, { .compatible = "qcom,sdx55-mpss-pas", .data = &sdx55_mpss_resource}, + { .compatible = "qcom,sdx75-mpss-pas", .data = &sm8650_mpss_resource}, { .compatible = "qcom,sm6115-adsp-pas", .data = &adsp_resource_init}, { .compatible = "qcom,sm6115-cdsp-pas", .data = &cdsp_resource_init}, { .compatible = "qcom,sm6115-mpss-pas", .data = &sc8180x_mpss_resource}, diff --git a/drivers/remoteproc/st_slim_rproc.c b/drivers/remoteproc/st_slim_rproc.c index d17719384c16..5412beb0a692 100644 --- a/drivers/remoteproc/st_slim_rproc.c +++ b/drivers/remoteproc/st_slim_rproc.c @@ -259,16 +259,14 @@ struct st_slim_rproc *st_slim_rproc_alloc(struct platform_device *pdev, slim_rproc->mem[i].size = resource_size(res); } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "slimcore"); - slim_rproc->slimcore = devm_ioremap_resource(dev, res); + slim_rproc->slimcore = devm_platform_ioremap_resource_byname(pdev, "slimcore"); if (IS_ERR(slim_rproc->slimcore)) { dev_err(&pdev->dev, "failed to ioremap slimcore IO\n"); err = PTR_ERR(slim_rproc->slimcore); goto err; } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "peripherals"); - slim_rproc->peri = devm_ioremap_resource(dev, res); + slim_rproc->peri = devm_platform_ioremap_resource_byname(pdev, "peripherals"); if (IS_ERR(slim_rproc->peri)) { dev_err(&pdev->dev, "failed to ioremap peripherals IO\n"); err = PTR_ERR(slim_rproc->peri); diff --git a/drivers/remoteproc/ti_k3_dsp_remoteproc.c b/drivers/remoteproc/ti_k3_dsp_remoteproc.c index a22d41689a7d..8be3f631c192 100644 --- a/drivers/remoteproc/ti_k3_dsp_remoteproc.c +++ b/drivers/remoteproc/ti_k3_dsp_remoteproc.c @@ -115,6 +115,10 @@ static void k3_dsp_rproc_mbox_callback(struct mbox_client *client, void *data) const char *name = kproc->rproc->name; u32 msg = omap_mbox_message(data); + /* Do not forward messages from a detached core */ + if (kproc->rproc->state == RPROC_DETACHED) + return; + dev_dbg(dev, "mbox msg: 0x%x\n", msg); switch (msg) { @@ -155,6 +159,10 @@ static void k3_dsp_rproc_kick(struct rproc *rproc, int vqid) mbox_msg_t msg = (mbox_msg_t)vqid; int ret; + /* Do not forward messages to a detached core */ + if (kproc->rproc->state == RPROC_DETACHED) + return; + /* send the index of the triggered virtqueue in the mailbox payload */ ret = mbox_send_message(kproc->mbox, (void *)msg); if (ret < 0) @@ -230,12 +238,9 @@ static int k3_dsp_rproc_request_mbox(struct rproc *rproc) client->knows_txdone = false; kproc->mbox = mbox_request_channel(client, 0); - if (IS_ERR(kproc->mbox)) { - ret = -EBUSY; - dev_err(dev, "mbox_request_channel failed: %ld\n", - PTR_ERR(kproc->mbox)); - return ret; - } + if (IS_ERR(kproc->mbox)) + return dev_err_probe(dev, PTR_ERR(kproc->mbox), + "mbox_request_channel failed\n"); /* * Ping the remote processor, this is only for sanity-sake for now; @@ -315,32 +320,23 @@ static int k3_dsp_rproc_start(struct rproc *rproc) u32 boot_addr; int ret; - ret = k3_dsp_rproc_request_mbox(rproc); - if (ret) - return ret; - boot_addr = rproc->bootaddr; if (boot_addr & (kproc->data->boot_align_addr - 1)) { dev_err(dev, "invalid boot address 0x%x, must be aligned on a 0x%x boundary\n", boot_addr, kproc->data->boot_align_addr); - ret = -EINVAL; - goto put_mbox; + return -EINVAL; } dev_dbg(dev, "booting DSP core using boot addr = 0x%x\n", boot_addr); ret = ti_sci_proc_set_config(kproc->tsp, boot_addr, 0, 0); if (ret) - goto put_mbox; + return ret; ret = k3_dsp_rproc_release(kproc); if (ret) - goto put_mbox; + return ret; return 0; - -put_mbox: - mbox_free_channel(kproc->mbox); - return ret; } /* @@ -353,8 +349,6 @@ static int k3_dsp_rproc_stop(struct rproc *rproc) { struct k3_dsp_rproc *kproc = rproc->priv; - mbox_free_channel(kproc->mbox); - k3_dsp_rproc_reset(kproc); return 0; @@ -363,42 +357,22 @@ static int k3_dsp_rproc_stop(struct rproc *rproc) /* * Attach to a running DSP remote processor (IPC-only mode) * - * This rproc attach callback only needs to request the mailbox, the remote - * processor is already booted, so there is no need to issue any TI-SCI - * commands to boot the DSP core. This callback is invoked only in IPC-only - * mode. + * This rproc attach callback is a NOP. The remote processor is already booted, + * and all required resources have been acquired during probe routine, so there + * is no need to issue any TI-SCI commands to boot the DSP core. This callback + * is invoked only in IPC-only mode and exists because rproc_validate() checks + * for its existence. */ -static int k3_dsp_rproc_attach(struct rproc *rproc) -{ - struct k3_dsp_rproc *kproc = rproc->priv; - struct device *dev = kproc->dev; - int ret; - - ret = k3_dsp_rproc_request_mbox(rproc); - if (ret) - return ret; - - dev_info(dev, "DSP initialized in IPC-only mode\n"); - return 0; -} +static int k3_dsp_rproc_attach(struct rproc *rproc) { return 0; } /* * Detach from a running DSP remote processor (IPC-only mode) * - * This rproc detach callback performs the opposite operation to attach callback - * and only needs to release the mailbox, the DSP core is not stopped and will - * be left to continue to run its booted firmware. This callback is invoked only - * in IPC-only mode. + * This rproc detach callback is a NOP. The DSP core is not stopped and will be + * left to continue to run its booted firmware. This callback is invoked only in + * IPC-only mode and exists for sanity sake. */ -static int k3_dsp_rproc_detach(struct rproc *rproc) -{ - struct k3_dsp_rproc *kproc = rproc->priv; - struct device *dev = kproc->dev; - - mbox_free_channel(kproc->mbox); - dev_info(dev, "DSP deinitialized in IPC-only mode\n"); - return 0; -} +static int k3_dsp_rproc_detach(struct rproc *rproc) { return 0; } /* * This function implements the .get_loaded_rsc_table() callback and is used @@ -636,32 +610,6 @@ static void k3_dsp_release_tsp(void *data) ti_sci_proc_release(tsp); } -static -struct ti_sci_proc *k3_dsp_rproc_of_get_tsp(struct device *dev, - const struct ti_sci_handle *sci) -{ - struct ti_sci_proc *tsp; - u32 temp[2]; - int ret; - - ret = of_property_read_u32_array(dev->of_node, "ti,sci-proc-ids", - temp, 2); - if (ret < 0) - return ERR_PTR(ret); - - tsp = devm_kzalloc(dev, sizeof(*tsp), GFP_KERNEL); - if (!tsp) - return ERR_PTR(-ENOMEM); - - tsp->dev = dev; - tsp->sci = sci; - tsp->ops = &sci->ops.proc_ops; - tsp->proc_id = temp[0]; - tsp->host_id = temp[1]; - - return tsp; -} - static int k3_dsp_rproc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -697,6 +645,10 @@ static int k3_dsp_rproc_probe(struct platform_device *pdev) kproc->dev = dev; kproc->data = data; + ret = k3_dsp_rproc_request_mbox(rproc); + if (ret) + return ret; + kproc->ti_sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); if (IS_ERR(kproc->ti_sci)) return dev_err_probe(dev, PTR_ERR(kproc->ti_sci), @@ -711,7 +663,7 @@ static int k3_dsp_rproc_probe(struct platform_device *pdev) return dev_err_probe(dev, PTR_ERR(kproc->reset), "failed to get reset\n"); - kproc->tsp = k3_dsp_rproc_of_get_tsp(dev, kproc->ti_sci); + kproc->tsp = ti_sci_proc_of_get_tsp(dev, kproc->ti_sci); if (IS_ERR(kproc->tsp)) return dev_err_probe(dev, PTR_ERR(kproc->tsp), "failed to construct ti-sci proc control\n"); @@ -789,6 +741,8 @@ static void k3_dsp_rproc_remove(struct platform_device *pdev) if (ret) dev_err(dev, "failed to detach proc (%pe)\n", ERR_PTR(ret)); } + + mbox_free_channel(kproc->mbox); } static const struct k3_dsp_mem_data c66_mems[] = { diff --git a/drivers/remoteproc/ti_k3_m4_remoteproc.c b/drivers/remoteproc/ti_k3_m4_remoteproc.c new file mode 100644 index 000000000000..09f0484a90e1 --- /dev/null +++ b/drivers/remoteproc/ti_k3_m4_remoteproc.c @@ -0,0 +1,667 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI K3 Cortex-M4 Remote Processor(s) driver + * + * Copyright (C) 2021-2024 Texas Instruments Incorporated - https://www.ti.com/ + * Hari Nagalla <hnagalla@ti.com> + */ + +#include <linux/io.h> +#include <linux/mailbox_client.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> +#include <linux/remoteproc.h> +#include <linux/reset.h> +#include <linux/slab.h> + +#include "omap_remoteproc.h" +#include "remoteproc_internal.h" +#include "ti_sci_proc.h" + +#define K3_M4_IRAM_DEV_ADDR 0x00000 +#define K3_M4_DRAM_DEV_ADDR 0x30000 + +/** + * struct k3_m4_rproc_mem - internal memory structure + * @cpu_addr: MPU virtual address of the memory region + * @bus_addr: Bus address used to access the memory region + * @dev_addr: Device address of the memory region from remote processor view + * @size: Size of the memory region + */ +struct k3_m4_rproc_mem { + void __iomem *cpu_addr; + phys_addr_t bus_addr; + u32 dev_addr; + size_t size; +}; + +/** + * struct k3_m4_rproc_mem_data - memory definitions for a remote processor + * @name: name for this memory entry + * @dev_addr: device address for the memory entry + */ +struct k3_m4_rproc_mem_data { + const char *name; + const u32 dev_addr; +}; + +/** + * struct k3_m4_rproc - k3 remote processor driver structure + * @dev: cached device pointer + * @mem: internal memory regions data + * @num_mems: number of internal memory regions + * @rmem: reserved memory regions data + * @num_rmems: number of reserved memory regions + * @reset: reset control handle + * @tsp: TI-SCI processor control handle + * @ti_sci: TI-SCI handle + * @ti_sci_id: TI-SCI device identifier + * @mbox: mailbox channel handle + * @client: mailbox client to request the mailbox channel + */ +struct k3_m4_rproc { + struct device *dev; + struct k3_m4_rproc_mem *mem; + int num_mems; + struct k3_m4_rproc_mem *rmem; + int num_rmems; + struct reset_control *reset; + struct ti_sci_proc *tsp; + const struct ti_sci_handle *ti_sci; + u32 ti_sci_id; + struct mbox_chan *mbox; + struct mbox_client client; +}; + +/** + * k3_m4_rproc_mbox_callback() - inbound mailbox message handler + * @client: mailbox client pointer used for requesting the mailbox channel + * @data: mailbox payload + * + * This handler is invoked by the K3 mailbox driver whenever a mailbox + * message is received. Usually, the mailbox payload simply contains + * the index of the virtqueue that is kicked by the remote processor, + * and we let remoteproc core handle it. + * + * In addition to virtqueue indices, we also have some out-of-band values + * that indicate different events. Those values are deliberately very + * large so they don't coincide with virtqueue indices. + */ +static void k3_m4_rproc_mbox_callback(struct mbox_client *client, void *data) +{ + struct device *dev = client->dev; + struct rproc *rproc = dev_get_drvdata(dev); + u32 msg = (u32)(uintptr_t)(data); + + dev_dbg(dev, "mbox msg: 0x%x\n", msg); + + switch (msg) { + case RP_MBOX_CRASH: + /* + * remoteproc detected an exception, but error recovery is not + * supported. So, just log this for now + */ + dev_err(dev, "K3 rproc %s crashed\n", rproc->name); + break; + case RP_MBOX_ECHO_REPLY: + dev_info(dev, "received echo reply from %s\n", rproc->name); + break; + default: + /* silently handle all other valid messages */ + if (msg >= RP_MBOX_READY && msg < RP_MBOX_END_MSG) + return; + if (msg > rproc->max_notifyid) { + dev_dbg(dev, "dropping unknown message 0x%x", msg); + return; + } + /* msg contains the index of the triggered vring */ + if (rproc_vq_interrupt(rproc, msg) == IRQ_NONE) + dev_dbg(dev, "no message was found in vqid %d\n", msg); + } +} + +/* + * Kick the remote processor to notify about pending unprocessed messages. + * The vqid usage is not used and is inconsequential, as the kick is performed + * through a simulated GPIO (a bit in an IPC interrupt-triggering register), + * the remote processor is expected to process both its Tx and Rx virtqueues. + */ +static void k3_m4_rproc_kick(struct rproc *rproc, int vqid) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + u32 msg = (u32)vqid; + int ret; + + /* + * Send the index of the triggered virtqueue in the mailbox payload. + * NOTE: msg is cast to uintptr_t to prevent compiler warnings when + * void* is 64bit. It is safely cast back to u32 in the mailbox driver. + */ + ret = mbox_send_message(kproc->mbox, (void *)(uintptr_t)msg); + if (ret < 0) + dev_err(dev, "failed to send mailbox message, status = %d\n", + ret); +} + +static int k3_m4_rproc_ping_mbox(struct k3_m4_rproc *kproc) +{ + struct device *dev = kproc->dev; + int ret; + + /* + * Ping the remote processor, this is only for sanity-sake for now; + * there is no functional effect whatsoever. + * + * Note that the reply will _not_ arrive immediately: this message + * will wait in the mailbox fifo until the remote processor is booted. + */ + ret = mbox_send_message(kproc->mbox, (void *)RP_MBOX_ECHO_REQUEST); + if (ret < 0) { + dev_err(dev, "mbox_send_message failed: %d\n", ret); + return ret; + } + + return 0; +} + +/* + * The M4 cores have a local reset that affects only the CPU, and a + * generic module reset that powers on the device and allows the internal + * memories to be accessed while the local reset is asserted. This function is + * used to release the global reset on remote cores to allow loading into the + * internal RAMs. The .prepare() ops is invoked by remoteproc core before any + * firmware loading, and is followed by the .start() ops after loading to + * actually let the remote cores to run. + */ +static int k3_m4_rproc_prepare(struct rproc *rproc) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + int ret; + + /* If the core is running already no need to deassert the module reset */ + if (rproc->state == RPROC_DETACHED) + return 0; + + /* + * Ensure the local reset is asserted so the core doesn't + * execute bogus code when the module reset is released. + */ + ret = reset_control_assert(kproc->reset); + if (ret) { + dev_err(dev, "could not assert local reset\n"); + return ret; + } + + ret = reset_control_status(kproc->reset); + if (ret <= 0) { + dev_err(dev, "local reset still not asserted\n"); + return ret; + } + + ret = kproc->ti_sci->ops.dev_ops.get_device(kproc->ti_sci, + kproc->ti_sci_id); + if (ret) { + dev_err(dev, "could not deassert module-reset for internal RAM loading\n"); + return ret; + } + + return 0; +} + +/* + * This function implements the .unprepare() ops and performs the complimentary + * operations to that of the .prepare() ops. The function is used to assert the + * global reset on applicable cores. This completes the second portion of + * powering down the remote core. The cores themselves are only halted in the + * .stop() callback through the local reset, and the .unprepare() ops is invoked + * by the remoteproc core after the remoteproc is stopped to balance the global + * reset. + */ +static int k3_m4_rproc_unprepare(struct rproc *rproc) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + int ret; + + /* If the core is going to be detached do not assert the module reset */ + if (rproc->state == RPROC_ATTACHED) + return 0; + + ret = kproc->ti_sci->ops.dev_ops.put_device(kproc->ti_sci, + kproc->ti_sci_id); + if (ret) { + dev_err(dev, "module-reset assert failed\n"); + return ret; + } + + return 0; +} + +/* + * This function implements the .get_loaded_rsc_table() callback and is used + * to provide the resource table for a booted remote processor in IPC-only + * mode. The remote processor firmwares follow a design-by-contract approach + * and are expected to have the resource table at the base of the DDR region + * reserved for firmware usage. This provides flexibility for the remote + * processor to be booted by different bootloaders that may or may not have the + * ability to publish the resource table address and size through a DT + * property. + */ +static struct resource_table *k3_m4_get_loaded_rsc_table(struct rproc *rproc, + size_t *rsc_table_sz) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + + if (!kproc->rmem[0].cpu_addr) { + dev_err(dev, "memory-region #1 does not exist, loaded rsc table can't be found"); + return ERR_PTR(-ENOMEM); + } + + /* + * NOTE: The resource table size is currently hard-coded to a maximum + * of 256 bytes. The most common resource table usage for K3 firmwares + * is to only have the vdev resource entry and an optional trace entry. + * The exact size could be computed based on resource table address, but + * the hard-coded value suffices to support the IPC-only mode. + */ + *rsc_table_sz = 256; + return (__force struct resource_table *)kproc->rmem[0].cpu_addr; +} + +/* + * Custom function to translate a remote processor device address (internal + * RAMs only) to a kernel virtual address. The remote processors can access + * their RAMs at either an internal address visible only from a remote + * processor, or at the SoC-level bus address. Both these addresses need to be + * looked through for translation. The translated addresses can be used either + * by the remoteproc core for loading (when using kernel remoteproc loader), or + * by any rpmsg bus drivers. + */ +static void *k3_m4_rproc_da_to_va(struct rproc *rproc, u64 da, size_t len, bool *is_iomem) +{ + struct k3_m4_rproc *kproc = rproc->priv; + void __iomem *va = NULL; + phys_addr_t bus_addr; + u32 dev_addr, offset; + size_t size; + int i; + + if (len == 0) + return NULL; + + for (i = 0; i < kproc->num_mems; i++) { + bus_addr = kproc->mem[i].bus_addr; + dev_addr = kproc->mem[i].dev_addr; + size = kproc->mem[i].size; + + /* handle M4-view addresses */ + if (da >= dev_addr && ((da + len) <= (dev_addr + size))) { + offset = da - dev_addr; + va = kproc->mem[i].cpu_addr + offset; + return (__force void *)va; + } + + /* handle SoC-view addresses */ + if (da >= bus_addr && ((da + len) <= (bus_addr + size))) { + offset = da - bus_addr; + va = kproc->mem[i].cpu_addr + offset; + return (__force void *)va; + } + } + + /* handle static DDR reserved memory regions */ + for (i = 0; i < kproc->num_rmems; i++) { + dev_addr = kproc->rmem[i].dev_addr; + size = kproc->rmem[i].size; + + if (da >= dev_addr && ((da + len) <= (dev_addr + size))) { + offset = da - dev_addr; + va = kproc->rmem[i].cpu_addr + offset; + return (__force void *)va; + } + } + + return NULL; +} + +static int k3_m4_rproc_of_get_memories(struct platform_device *pdev, + struct k3_m4_rproc *kproc) +{ + static const char * const mem_names[] = { "iram", "dram" }; + static const u32 mem_addrs[] = { K3_M4_IRAM_DEV_ADDR, K3_M4_DRAM_DEV_ADDR }; + struct device *dev = &pdev->dev; + struct resource *res; + int num_mems; + int i; + + num_mems = ARRAY_SIZE(mem_names); + kproc->mem = devm_kcalloc(kproc->dev, num_mems, + sizeof(*kproc->mem), GFP_KERNEL); + if (!kproc->mem) + return -ENOMEM; + + for (i = 0; i < num_mems; i++) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + mem_names[i]); + if (!res) { + dev_err(dev, "found no memory resource for %s\n", + mem_names[i]); + return -EINVAL; + } + if (!devm_request_mem_region(dev, res->start, + resource_size(res), + dev_name(dev))) { + dev_err(dev, "could not request %s region for resource\n", + mem_names[i]); + return -EBUSY; + } + + kproc->mem[i].cpu_addr = devm_ioremap_wc(dev, res->start, + resource_size(res)); + if (!kproc->mem[i].cpu_addr) { + dev_err(dev, "failed to map %s memory\n", + mem_names[i]); + return -ENOMEM; + } + kproc->mem[i].bus_addr = res->start; + kproc->mem[i].dev_addr = mem_addrs[i]; + kproc->mem[i].size = resource_size(res); + + dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %pK da 0x%x\n", + mem_names[i], &kproc->mem[i].bus_addr, + kproc->mem[i].size, kproc->mem[i].cpu_addr, + kproc->mem[i].dev_addr); + } + kproc->num_mems = num_mems; + + return 0; +} + +static void k3_m4_rproc_dev_mem_release(void *data) +{ + struct device *dev = data; + + of_reserved_mem_device_release(dev); +} + +static int k3_m4_reserved_mem_init(struct k3_m4_rproc *kproc) +{ + struct device *dev = kproc->dev; + struct device_node *np = dev->of_node; + struct device_node *rmem_np; + struct reserved_mem *rmem; + int num_rmems; + int ret, i; + + num_rmems = of_property_count_elems_of_size(np, "memory-region", + sizeof(phandle)); + if (num_rmems < 0) { + dev_err(dev, "device does not reserved memory regions (%d)\n", + num_rmems); + return -EINVAL; + } + if (num_rmems < 2) { + dev_err(dev, "device needs at least two memory regions to be defined, num = %d\n", + num_rmems); + return -EINVAL; + } + + /* use reserved memory region 0 for vring DMA allocations */ + ret = of_reserved_mem_device_init_by_idx(dev, np, 0); + if (ret) { + dev_err(dev, "device cannot initialize DMA pool (%d)\n", ret); + return ret; + } + ret = devm_add_action_or_reset(dev, k3_m4_rproc_dev_mem_release, dev); + if (ret) + return ret; + + num_rmems--; + kproc->rmem = devm_kcalloc(dev, num_rmems, sizeof(*kproc->rmem), GFP_KERNEL); + if (!kproc->rmem) + return -ENOMEM; + + /* use remaining reserved memory regions for static carveouts */ + for (i = 0; i < num_rmems; i++) { + rmem_np = of_parse_phandle(np, "memory-region", i + 1); + if (!rmem_np) + return -EINVAL; + + rmem = of_reserved_mem_lookup(rmem_np); + if (!rmem) { + of_node_put(rmem_np); + return -EINVAL; + } + of_node_put(rmem_np); + + kproc->rmem[i].bus_addr = rmem->base; + /* 64-bit address regions currently not supported */ + kproc->rmem[i].dev_addr = (u32)rmem->base; + kproc->rmem[i].size = rmem->size; + kproc->rmem[i].cpu_addr = devm_ioremap_wc(dev, rmem->base, rmem->size); + if (!kproc->rmem[i].cpu_addr) { + dev_err(dev, "failed to map reserved memory#%d at %pa of size %pa\n", + i + 1, &rmem->base, &rmem->size); + return -ENOMEM; + } + + dev_dbg(dev, "reserved memory%d: bus addr %pa size 0x%zx va %pK da 0x%x\n", + i + 1, &kproc->rmem[i].bus_addr, + kproc->rmem[i].size, kproc->rmem[i].cpu_addr, + kproc->rmem[i].dev_addr); + } + kproc->num_rmems = num_rmems; + + return 0; +} + +static void k3_m4_release_tsp(void *data) +{ + struct ti_sci_proc *tsp = data; + + ti_sci_proc_release(tsp); +} + +/* + * Power up the M4 remote processor. + * + * This function will be invoked only after the firmware for this rproc + * was loaded, parsed successfully, and all of its resource requirements + * were met. This callback is invoked only in remoteproc mode. + */ +static int k3_m4_rproc_start(struct rproc *rproc) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + int ret; + + ret = k3_m4_rproc_ping_mbox(kproc); + if (ret) + return ret; + + ret = reset_control_deassert(kproc->reset); + if (ret) { + dev_err(dev, "local-reset deassert failed, ret = %d\n", ret); + return ret; + } + + return 0; +} + +/* + * Stop the M4 remote processor. + * + * This function puts the M4 processor into reset, and finishes processing + * of any pending messages. This callback is invoked only in remoteproc mode. + */ +static int k3_m4_rproc_stop(struct rproc *rproc) +{ + struct k3_m4_rproc *kproc = rproc->priv; + struct device *dev = kproc->dev; + int ret; + + ret = reset_control_assert(kproc->reset); + if (ret) { + dev_err(dev, "local-reset assert failed, ret = %d\n", ret); + return ret; + } + + return 0; +} + +/* + * Attach to a running M4 remote processor (IPC-only mode) + * + * The remote processor is already booted, so there is no need to issue any + * TI-SCI commands to boot the M4 core. This callback is used only in IPC-only + * mode. + */ +static int k3_m4_rproc_attach(struct rproc *rproc) +{ + struct k3_m4_rproc *kproc = rproc->priv; + int ret; + + ret = k3_m4_rproc_ping_mbox(kproc); + if (ret) + return ret; + + return 0; +} + +/* + * Detach from a running M4 remote processor (IPC-only mode) + * + * This rproc detach callback performs the opposite operation to attach + * callback, the M4 core is not stopped and will be left to continue to + * run its booted firmware. This callback is invoked only in IPC-only mode. + */ +static int k3_m4_rproc_detach(struct rproc *rproc) +{ + return 0; +} + +static const struct rproc_ops k3_m4_rproc_ops = { + .prepare = k3_m4_rproc_prepare, + .unprepare = k3_m4_rproc_unprepare, + .start = k3_m4_rproc_start, + .stop = k3_m4_rproc_stop, + .attach = k3_m4_rproc_attach, + .detach = k3_m4_rproc_detach, + .kick = k3_m4_rproc_kick, + .da_to_va = k3_m4_rproc_da_to_va, + .get_loaded_rsc_table = k3_m4_get_loaded_rsc_table, +}; + +static int k3_m4_rproc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct k3_m4_rproc *kproc; + struct rproc *rproc; + const char *fw_name; + bool r_state = false; + bool p_state = false; + int ret; + + ret = rproc_of_parse_firmware(dev, 0, &fw_name); + if (ret) + return dev_err_probe(dev, ret, "failed to parse firmware-name property\n"); + + rproc = devm_rproc_alloc(dev, dev_name(dev), &k3_m4_rproc_ops, fw_name, + sizeof(*kproc)); + if (!rproc) + return -ENOMEM; + + rproc->has_iommu = false; + rproc->recovery_disabled = true; + kproc = rproc->priv; + kproc->dev = dev; + platform_set_drvdata(pdev, rproc); + + kproc->ti_sci = devm_ti_sci_get_by_phandle(dev, "ti,sci"); + if (IS_ERR(kproc->ti_sci)) + return dev_err_probe(dev, PTR_ERR(kproc->ti_sci), + "failed to get ti-sci handle\n"); + + ret = of_property_read_u32(dev->of_node, "ti,sci-dev-id", &kproc->ti_sci_id); + if (ret) + return dev_err_probe(dev, ret, "missing 'ti,sci-dev-id' property\n"); + + kproc->reset = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(kproc->reset)) + return dev_err_probe(dev, PTR_ERR(kproc->reset), "failed to get reset\n"); + + kproc->tsp = ti_sci_proc_of_get_tsp(dev, kproc->ti_sci); + if (IS_ERR(kproc->tsp)) + return dev_err_probe(dev, PTR_ERR(kproc->tsp), + "failed to construct ti-sci proc control\n"); + + ret = ti_sci_proc_request(kproc->tsp); + if (ret < 0) + return dev_err_probe(dev, ret, "ti_sci_proc_request failed\n"); + ret = devm_add_action_or_reset(dev, k3_m4_release_tsp, kproc->tsp); + if (ret) + return ret; + + ret = k3_m4_rproc_of_get_memories(pdev, kproc); + if (ret) + return ret; + + ret = k3_m4_reserved_mem_init(kproc); + if (ret) + return dev_err_probe(dev, ret, "reserved memory init failed\n"); + + ret = kproc->ti_sci->ops.dev_ops.is_on(kproc->ti_sci, kproc->ti_sci_id, + &r_state, &p_state); + if (ret) + return dev_err_probe(dev, ret, + "failed to get initial state, mode cannot be determined\n"); + + /* configure devices for either remoteproc or IPC-only mode */ + if (p_state) { + rproc->state = RPROC_DETACHED; + dev_info(dev, "configured M4F for IPC-only mode\n"); + } else { + dev_info(dev, "configured M4F for remoteproc mode\n"); + } + + kproc->client.dev = dev; + kproc->client.tx_done = NULL; + kproc->client.rx_callback = k3_m4_rproc_mbox_callback; + kproc->client.tx_block = false; + kproc->client.knows_txdone = false; + kproc->mbox = mbox_request_channel(&kproc->client, 0); + if (IS_ERR(kproc->mbox)) + return dev_err_probe(dev, PTR_ERR(kproc->mbox), + "mbox_request_channel failed\n"); + + ret = devm_rproc_add(dev, rproc); + if (ret) + return dev_err_probe(dev, ret, + "failed to register device with remoteproc core\n"); + + return 0; +} + +static const struct of_device_id k3_m4_of_match[] = { + { .compatible = "ti,am64-m4fss", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, k3_m4_of_match); + +static struct platform_driver k3_m4_rproc_driver = { + .probe = k3_m4_rproc_probe, + .driver = { + .name = "k3-m4-rproc", + .of_match_table = k3_m4_of_match, + }, +}; +module_platform_driver(k3_m4_rproc_driver); + +MODULE_AUTHOR("Hari Nagalla <hnagalla@ti.com>"); +MODULE_DESCRIPTION("TI K3 M4 Remoteproc driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/remoteproc/ti_k3_r5_remoteproc.c b/drivers/remoteproc/ti_k3_r5_remoteproc.c index 39a47540c590..747ee467da88 100644 --- a/drivers/remoteproc/ti_k3_r5_remoteproc.c +++ b/drivers/remoteproc/ti_k3_r5_remoteproc.c @@ -194,6 +194,10 @@ static void k3_r5_rproc_mbox_callback(struct mbox_client *client, void *data) const char *name = kproc->rproc->name; u32 msg = omap_mbox_message(data); + /* Do not forward message from a detached core */ + if (kproc->rproc->state == RPROC_DETACHED) + return; + dev_dbg(dev, "mbox msg: 0x%x\n", msg); switch (msg) { @@ -229,6 +233,10 @@ static void k3_r5_rproc_kick(struct rproc *rproc, int vqid) mbox_msg_t msg = (mbox_msg_t)vqid; int ret; + /* Do not forward message to a detached core */ + if (kproc->rproc->state == RPROC_DETACHED) + return; + /* send the index of the triggered virtqueue in the mailbox payload */ ret = mbox_send_message(kproc->mbox, (void *)msg); if (ret < 0) @@ -399,12 +407,9 @@ static int k3_r5_rproc_request_mbox(struct rproc *rproc) client->knows_txdone = false; kproc->mbox = mbox_request_channel(client, 0); - if (IS_ERR(kproc->mbox)) { - ret = -EBUSY; - dev_err(dev, "mbox_request_channel failed: %ld\n", - PTR_ERR(kproc->mbox)); - return ret; - } + if (IS_ERR(kproc->mbox)) + return dev_err_probe(dev, PTR_ERR(kproc->mbox), + "mbox_request_channel failed\n"); /* * Ping the remote processor, this is only for sanity-sake for now; @@ -464,8 +469,6 @@ static int k3_r5_rproc_prepare(struct rproc *rproc) ret); return ret; } - core->released_from_reset = true; - wake_up_interruptible(&cluster->core_transition); /* * Newer IP revisions like on J7200 SoCs support h/w auto-initialization @@ -552,10 +555,6 @@ static int k3_r5_rproc_start(struct rproc *rproc) u32 boot_addr; int ret; - ret = k3_r5_rproc_request_mbox(rproc); - if (ret) - return ret; - boot_addr = rproc->bootaddr; /* TODO: add boot_addr sanity checking */ dev_dbg(dev, "booting R5F core using boot addr = 0x%x\n", boot_addr); @@ -564,7 +563,7 @@ static int k3_r5_rproc_start(struct rproc *rproc) core = kproc->core; ret = ti_sci_proc_set_config(core->tsp, boot_addr, 0, 0); if (ret) - goto put_mbox; + return ret; /* unhalt/run all applicable cores */ if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { @@ -580,13 +579,15 @@ static int k3_r5_rproc_start(struct rproc *rproc) if (core != core0 && core0->rproc->state == RPROC_OFFLINE) { dev_err(dev, "%s: can not start core 1 before core 0\n", __func__); - ret = -EPERM; - goto put_mbox; + return -EPERM; } ret = k3_r5_core_run(core); if (ret) - goto put_mbox; + return ret; + + core->released_from_reset = true; + wake_up_interruptible(&cluster->core_transition); } return 0; @@ -596,8 +597,6 @@ unroll_core_run: if (k3_r5_core_halt(core)) dev_warn(core->dev, "core halt back failed\n"); } -put_mbox: - mbox_free_channel(kproc->mbox); return ret; } @@ -658,8 +657,6 @@ static int k3_r5_rproc_stop(struct rproc *rproc) goto out; } - mbox_free_channel(kproc->mbox); - return 0; unroll_core_halt: @@ -674,42 +671,22 @@ out: /* * Attach to a running R5F remote processor (IPC-only mode) * - * The R5F attach callback only needs to request the mailbox, the remote - * processor is already booted, so there is no need to issue any TI-SCI - * commands to boot the R5F cores in IPC-only mode. This callback is invoked - * only in IPC-only mode. + * The R5F attach callback is a NOP. The remote processor is already booted, and + * all required resources have been acquired during probe routine, so there is + * no need to issue any TI-SCI commands to boot the R5F cores in IPC-only mode. + * This callback is invoked only in IPC-only mode and exists because + * rproc_validate() checks for its existence. */ -static int k3_r5_rproc_attach(struct rproc *rproc) -{ - struct k3_r5_rproc *kproc = rproc->priv; - struct device *dev = kproc->dev; - int ret; - - ret = k3_r5_rproc_request_mbox(rproc); - if (ret) - return ret; - - dev_info(dev, "R5F core initialized in IPC-only mode\n"); - return 0; -} +static int k3_r5_rproc_attach(struct rproc *rproc) { return 0; } /* * Detach from a running R5F remote processor (IPC-only mode) * - * The R5F detach callback performs the opposite operation to attach callback - * and only needs to release the mailbox, the R5F cores are not stopped and - * will be left in booted state in IPC-only mode. This callback is invoked - * only in IPC-only mode. + * The R5F detach callback is a NOP. The R5F cores are not stopped and will be + * left in booted state in IPC-only mode. This callback is invoked only in + * IPC-only mode and exists for sanity sake. */ -static int k3_r5_rproc_detach(struct rproc *rproc) -{ - struct k3_r5_rproc *kproc = rproc->priv; - struct device *dev = kproc->dev; - - mbox_free_channel(kproc->mbox); - dev_info(dev, "R5F core deinitialized in IPC-only mode\n"); - return 0; -} +static int k3_r5_rproc_detach(struct rproc *rproc) { return 0; } /* * This function implements the .get_loaded_rsc_table() callback and is used @@ -1259,8 +1236,8 @@ static int k3_r5_cluster_rproc_init(struct platform_device *pdev) goto out; } - rproc = rproc_alloc(cdev, dev_name(cdev), &k3_r5_rproc_ops, - fw_name, sizeof(*kproc)); + rproc = devm_rproc_alloc(cdev, dev_name(cdev), &k3_r5_rproc_ops, + fw_name, sizeof(*kproc)); if (!rproc) { ret = -ENOMEM; goto out; @@ -1278,9 +1255,13 @@ static int k3_r5_cluster_rproc_init(struct platform_device *pdev) kproc->rproc = rproc; core->rproc = rproc; + ret = k3_r5_rproc_request_mbox(rproc); + if (ret) + return ret; + ret = k3_r5_rproc_configure_mode(kproc); if (ret < 0) - goto err_config; + goto out; if (ret) goto init_rmem; @@ -1288,7 +1269,7 @@ static int k3_r5_cluster_rproc_init(struct platform_device *pdev) if (ret) { dev_err(dev, "initial configure failed, ret = %d\n", ret); - goto err_config; + goto out; } init_rmem: @@ -1298,7 +1279,7 @@ init_rmem: if (ret) { dev_err(dev, "reserved memory init failed, ret = %d\n", ret); - goto err_config; + goto out; } ret = rproc_add(rproc); @@ -1332,7 +1313,7 @@ init_rmem: dev_err(dev, "Timed out waiting for %s core to power up!\n", rproc->name); - return ret; + goto err_powerup; } } @@ -1348,12 +1329,10 @@ err_split: } } +err_powerup: rproc_del(rproc); err_add: k3_r5_reserved_mem_exit(kproc); -err_config: - rproc_free(rproc); - core->rproc = NULL; out: /* undo core0 upon any failures on core1 in split-mode */ if (cluster->mode == CLUSTER_MODE_SPLIT && core == core1) { @@ -1395,12 +1374,11 @@ static void k3_r5_cluster_rproc_exit(void *data) } } + mbox_free_channel(kproc->mbox); + rproc_del(rproc); k3_r5_reserved_mem_exit(kproc); - - rproc_free(rproc); - core->rproc = NULL; } } @@ -1533,32 +1511,6 @@ static int k3_r5_core_of_get_sram_memories(struct platform_device *pdev, return 0; } -static -struct ti_sci_proc *k3_r5_core_of_get_tsp(struct device *dev, - const struct ti_sci_handle *sci) -{ - struct ti_sci_proc *tsp; - u32 temp[2]; - int ret; - - ret = of_property_read_u32_array(dev_of_node(dev), "ti,sci-proc-ids", - temp, 2); - if (ret < 0) - return ERR_PTR(ret); - - tsp = devm_kzalloc(dev, sizeof(*tsp), GFP_KERNEL); - if (!tsp) - return ERR_PTR(-ENOMEM); - - tsp->dev = dev; - tsp->sci = sci; - tsp->ops = &sci->ops.proc_ops; - tsp->proc_id = temp[0]; - tsp->host_id = temp[1]; - - return tsp; -} - static int k3_r5_core_of_init(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1633,7 +1585,7 @@ static int k3_r5_core_of_init(struct platform_device *pdev) goto err; } - core->tsp = k3_r5_core_of_get_tsp(dev, core->ti_sci); + core->tsp = ti_sci_proc_of_get_tsp(dev, core->ti_sci); if (IS_ERR(core->tsp)) { ret = PTR_ERR(core->tsp); dev_err(dev, "failed to construct ti-sci proc control, ret = %d\n", diff --git a/drivers/remoteproc/ti_sci_proc.h b/drivers/remoteproc/ti_sci_proc.h index 778558abcdcc..f3911ce75252 100644 --- a/drivers/remoteproc/ti_sci_proc.h +++ b/drivers/remoteproc/ti_sci_proc.h @@ -28,6 +28,32 @@ struct ti_sci_proc { u8 host_id; }; +static inline +struct ti_sci_proc *ti_sci_proc_of_get_tsp(struct device *dev, + const struct ti_sci_handle *sci) +{ + struct ti_sci_proc *tsp; + u32 temp[2]; + int ret; + + ret = of_property_read_u32_array(dev_of_node(dev), "ti,sci-proc-ids", + temp, 2); + if (ret < 0) + return ERR_PTR(ret); + + tsp = devm_kzalloc(dev, sizeof(*tsp), GFP_KERNEL); + if (!tsp) + return ERR_PTR(-ENOMEM); + + tsp->dev = dev; + tsp->sci = sci; + tsp->ops = &sci->ops.proc_ops; + tsp->proc_id = temp[0]; + tsp->host_id = temp[1]; + + return tsp; +} + static inline int ti_sci_proc_request(struct ti_sci_proc *tsp) { int ret; diff --git a/drivers/remoteproc/xlnx_r5_remoteproc.c b/drivers/remoteproc/xlnx_r5_remoteproc.c index 596f3ffb8935..5aeedeaf3c41 100644 --- a/drivers/remoteproc/xlnx_r5_remoteproc.c +++ b/drivers/remoteproc/xlnx_r5_remoteproc.c @@ -57,6 +57,17 @@ struct mem_bank_data { }; /** + * struct zynqmp_sram_bank - sram bank description + * + * @sram_res: sram address region information + * @da: device address of sram + */ +struct zynqmp_sram_bank { + struct resource sram_res; + u32 da; +}; + +/** * struct mbox_info * * @rx_mc_buf: to copy data from mailbox rx channel @@ -120,6 +131,8 @@ static const struct mem_bank_data zynqmp_tcm_banks_lockstep[] = { * struct zynqmp_r5_core * * @rsc_tbl_va: resource table virtual address + * @sram: Array of sram memories assigned to this core + * @num_sram: number of sram for this core * @dev: device of RPU instance * @np: device node of RPU instance * @tcm_bank_count: number TCM banks accessible to this RPU @@ -131,6 +144,8 @@ static const struct mem_bank_data zynqmp_tcm_banks_lockstep[] = { */ struct zynqmp_r5_core { void __iomem *rsc_tbl_va; + struct zynqmp_sram_bank *sram; + int num_sram; struct device *dev; struct device_node *np; int tcm_bank_count; @@ -494,6 +509,45 @@ static int add_mem_regions_carveout(struct rproc *rproc) return 0; } +static int add_sram_carveouts(struct rproc *rproc) +{ + struct zynqmp_r5_core *r5_core = rproc->priv; + struct rproc_mem_entry *rproc_mem; + struct zynqmp_sram_bank *sram; + dma_addr_t dma_addr; + size_t len; + int da, i; + + for (i = 0; i < r5_core->num_sram; i++) { + sram = &r5_core->sram[i]; + + dma_addr = (dma_addr_t)sram->sram_res.start; + + len = resource_size(&sram->sram_res); + da = sram->da; + + rproc_mem = rproc_mem_entry_init(&rproc->dev, NULL, + dma_addr, + len, da, + zynqmp_r5_mem_region_map, + zynqmp_r5_mem_region_unmap, + sram->sram_res.name); + if (!rproc_mem) { + dev_err(&rproc->dev, "failed to add sram %s da=0x%x, size=0x%lx", + sram->sram_res.name, da, len); + return -ENOMEM; + } + + rproc_add_carveout(rproc, rproc_mem); + rproc_coredump_add_segment(rproc, da, len); + + dev_dbg(&rproc->dev, "sram carveout %s addr=%llx, da=0x%x, size=0x%lx", + sram->sram_res.name, dma_addr, da, len); + } + + return 0; +} + /* * tcm_mem_unmap() * @rproc: single R5 core's corresponding rproc instance @@ -669,6 +723,12 @@ static int zynqmp_r5_rproc_prepare(struct rproc *rproc) return ret; } + ret = add_sram_carveouts(rproc); + if (ret) { + dev_err(&rproc->dev, "failed to get sram carveout %d\n", ret); + return ret; + } + return 0; } @@ -881,6 +941,77 @@ free_rproc: return ERR_PTR(ret); } +static int zynqmp_r5_get_sram_banks(struct zynqmp_r5_core *r5_core) +{ + struct device_node *np = r5_core->np; + struct device *dev = r5_core->dev; + struct zynqmp_sram_bank *sram; + struct device_node *sram_np; + int num_sram, i, ret; + u64 abs_addr, size; + + /* "sram" is optional property. Do not fail, if unavailable. */ + if (!of_property_present(r5_core->np, "sram")) + return 0; + + num_sram = of_property_count_elems_of_size(np, "sram", sizeof(phandle)); + if (num_sram <= 0) { + dev_err(dev, "Invalid sram property, ret = %d\n", + num_sram); + return -EINVAL; + } + + sram = devm_kcalloc(dev, num_sram, + sizeof(struct zynqmp_sram_bank), GFP_KERNEL); + if (!sram) + return -ENOMEM; + + for (i = 0; i < num_sram; i++) { + sram_np = of_parse_phandle(np, "sram", i); + if (!sram_np) { + dev_err(dev, "failed to get sram %d phandle\n", i); + return -EINVAL; + } + + if (!of_device_is_available(sram_np)) { + dev_err(dev, "sram device not available\n"); + ret = -EINVAL; + goto fail_sram_get; + } + + ret = of_address_to_resource(sram_np, 0, &sram[i].sram_res); + if (ret) { + dev_err(dev, "addr to res failed\n"); + goto fail_sram_get; + } + + /* Get SRAM device address */ + ret = of_property_read_reg(sram_np, i, &abs_addr, &size); + if (ret) { + dev_err(dev, "failed to get reg property\n"); + goto fail_sram_get; + } + + sram[i].da = (u32)abs_addr; + + of_node_put(sram_np); + + dev_dbg(dev, "sram %d: name=%s, addr=0x%llx, da=0x%x, size=0x%llx\n", + i, sram[i].sram_res.name, sram[i].sram_res.start, + sram[i].da, resource_size(&sram[i].sram_res)); + } + + r5_core->sram = sram; + r5_core->num_sram = num_sram; + + return 0; + +fail_sram_get: + of_node_put(sram_np); + + return ret; +} + static int zynqmp_r5_get_tcm_node_from_dt(struct zynqmp_r5_cluster *cluster) { int i, j, tcm_bank_count, ret, tcm_pd_idx, pd_count; @@ -1059,7 +1190,7 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, r5_core = cluster->r5_cores[0]; /* Maintain backward compatibility for zynqmp by using hardcode TCM address. */ - if (of_find_property(r5_core->np, "reg", NULL)) + if (of_property_present(r5_core->np, "reg")) ret = zynqmp_r5_get_tcm_node_from_dt(cluster); else if (device_is_compatible(dev, "xlnx,zynqmp-r5fss")) ret = zynqmp_r5_get_tcm_node(cluster); @@ -1086,7 +1217,7 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, return ret; } - if (of_find_property(dev_of_node(dev), "xlnx,tcm-mode", NULL) || + if (of_property_present(dev_of_node(dev), "xlnx,tcm-mode") || device_is_compatible(dev, "xlnx,zynqmp-r5fss")) { ret = zynqmp_pm_set_tcm_config(r5_core->pm_domain_id, tcm_mode); @@ -1095,6 +1226,10 @@ static int zynqmp_r5_core_init(struct zynqmp_r5_cluster *cluster, return ret; } } + + ret = zynqmp_r5_get_sram_banks(r5_core); + if (ret) + return ret; } return 0; @@ -1147,7 +1282,7 @@ static int zynqmp_r5_cluster_init(struct zynqmp_r5_cluster *cluster) return -EINVAL; } - if (of_find_property(dev_node, "xlnx,tcm-mode", NULL)) { + if (of_property_present(dev_node, "xlnx,tcm-mode")) { ret = of_property_read_u32(dev_node, "xlnx,tcm-mode", (u32 *)&tcm_mode); if (ret) return ret; |