diff options
Diffstat (limited to 'drivers/remoteproc/imx_rproc.c')
| -rw-r--r-- | drivers/remoteproc/imx_rproc.c | 789 |
1 files changed, 450 insertions, 339 deletions
diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index f9874fc5a80f..3be8790c14a2 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -13,11 +13,13 @@ #include <linux/mailbox_client.h> #include <linux/mfd/syscon.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/of_address.h> #include <linux/of_reserved_mem.h> -#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> +#include <linux/pm_runtime.h> +#include <linux/reboot.h> #include <linux/regmap.h> #include <linux/remoteproc.h> #include <linux/workqueue.h> @@ -40,6 +42,12 @@ #define IMX7D_M4_STOP (IMX7D_ENABLE_M4 | IMX7D_SW_M4C_RST | \ IMX7D_SW_M4C_NON_SCLR_RST) +#define IMX8M_M7_STOP (IMX7D_ENABLE_M4 | IMX7D_SW_M4C_RST) +#define IMX8M_M7_POLL IMX7D_ENABLE_M4 + +#define IMX8M_GPR22 0x58 +#define IMX8M_GPR22_CM7_CPUWAIT BIT(0) + /* Address: 0x020D8000 */ #define IMX6SX_SRC_SCR 0x00 #define IMX6SX_ENABLE_M4 BIT(22) @@ -84,13 +92,13 @@ 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 void imx_rproc_free_mbox(struct rproc *rproc); -static int imx_rproc_detach_pd(struct rproc *rproc); +static int imx_rproc_xtr_mbox_init(struct rproc *rproc, bool tx_block); +static void imx_rproc_free_mbox(void *data); struct imx_rproc { struct device *dev; struct regmap *regmap; + struct regmap *gpr; struct rproc *rproc; const struct imx_rproc_dcfg *dcfg; struct imx_rproc_mem mem[IMX_RPROC_MEM_MAX]; @@ -106,29 +114,23 @@ struct imx_rproc { u32 rproc_pt; /* partition id */ u32 rsrc_id; /* resource id */ u32 entry; /* cpu start address */ - int num_pd; u32 core_index; - struct device **pd_dev; - struct device_link **pd_dev_link; + struct dev_pm_domain_list *pd_list; }; 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 }, @@ -206,11 +208,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 */ @@ -285,132 +285,101 @@ static const struct imx_rproc_att imx_rproc_att_imx6sx[] = { { 0x80000000, 0x80000000, 0x60000000, 0 }, }; -static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn = { - .att = imx_rproc_att_imx8mn, - .att_size = ARRAY_SIZE(imx_rproc_att_imx8mn), - .method = IMX_RPROC_SMC, -}; - -static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mq = { - .src_reg = IMX7D_SRC_SCR, - .src_mask = IMX7D_M4_RST_MASK, - .src_start = IMX7D_M4_START, - .src_stop = IMX7D_M4_STOP, - .att = imx_rproc_att_imx8mq, - .att_size = ARRAY_SIZE(imx_rproc_att_imx8mq), - .method = IMX_RPROC_MMIO, -}; +static int imx_rproc_arm_smc_start(struct rproc *rproc) +{ + struct arm_smccc_res res; -static const struct imx_rproc_dcfg imx_rproc_cfg_imx8qm = { - .att = imx_rproc_att_imx8qm, - .att_size = ARRAY_SIZE(imx_rproc_att_imx8qm), - .method = IMX_RPROC_SCU_API, -}; + arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_START, 0, 0, 0, 0, 0, 0, &res); -static const struct imx_rproc_dcfg imx_rproc_cfg_imx8qxp = { - .att = imx_rproc_att_imx8qxp, - .att_size = ARRAY_SIZE(imx_rproc_att_imx8qxp), - .method = IMX_RPROC_SCU_API, -}; + return res.a0; +} -static const struct imx_rproc_dcfg imx_rproc_cfg_imx8ulp = { - .att = imx_rproc_att_imx8ulp, - .att_size = ARRAY_SIZE(imx_rproc_att_imx8ulp), - .method = IMX_RPROC_NONE, -}; +static int imx_rproc_mmio_start(struct rproc *rproc) +{ + struct imx_rproc *priv = rproc->priv; + const struct imx_rproc_dcfg *dcfg = priv->dcfg; -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, -}; + if (priv->gpr) + return regmap_clear_bits(priv->gpr, dcfg->gpr_reg, dcfg->gpr_wait); -static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = { - .src_reg = IMX7D_SRC_SCR, - .src_mask = IMX7D_M4_RST_MASK, - .src_start = IMX7D_M4_START, - .src_stop = IMX7D_M4_STOP, - .att = imx_rproc_att_imx7d, - .att_size = ARRAY_SIZE(imx_rproc_att_imx7d), - .method = IMX_RPROC_MMIO, -}; + return regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, dcfg->src_start); +} -static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = { - .src_reg = IMX6SX_SRC_SCR, - .src_mask = IMX6SX_M4_RST_MASK, - .src_start = IMX6SX_M4_START, - .src_stop = IMX6SX_M4_STOP, - .att = imx_rproc_att_imx6sx, - .att_size = ARRAY_SIZE(imx_rproc_att_imx6sx), - .method = IMX_RPROC_MMIO, -}; +static int imx_rproc_scu_api_start(struct rproc *rproc) +{ + struct imx_rproc *priv = rproc->priv; -static const struct imx_rproc_dcfg imx_rproc_cfg_imx93 = { - .att = imx_rproc_att_imx93, - .att_size = ARRAY_SIZE(imx_rproc_att_imx93), - .method = IMX_RPROC_SMC, -}; + return imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, true, priv->entry); +} static int imx_rproc_start(struct rproc *rproc) { struct imx_rproc *priv = rproc->priv; const struct imx_rproc_dcfg *dcfg = priv->dcfg; struct device *dev = priv->dev; - 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; - switch (dcfg->method) { - case IMX_RPROC_MMIO: - ret = regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, - dcfg->src_start); - break; - case IMX_RPROC_SMC: - arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_START, 0, 0, 0, 0, 0, 0, &res); - ret = res.a0; - break; - case IMX_RPROC_SCU_API: - ret = imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, true, priv->entry); - break; - default: + if (!dcfg->ops || !dcfg->ops->start) return -EOPNOTSUPP; - } + ret = dcfg->ops->start(rproc); if (ret) dev_err(dev, "Failed to enable remote core!\n"); return ret; } +static int imx_rproc_arm_smc_stop(struct rproc *rproc) +{ + struct imx_rproc *priv = rproc->priv; + struct arm_smccc_res res; + + arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_STOP, 0, 0, 0, 0, 0, 0, &res); + if (res.a1) + dev_info(priv->dev, "Not in wfi, force stopped\n"); + + return res.a0; +} + +static int imx_rproc_mmio_stop(struct rproc *rproc) +{ + struct imx_rproc *priv = rproc->priv; + const struct imx_rproc_dcfg *dcfg = priv->dcfg; + int ret; + + if (priv->gpr) { + ret = regmap_set_bits(priv->gpr, dcfg->gpr_reg, dcfg->gpr_wait); + if (ret) { + dev_err(priv->dev, "Failed to quiescence M4 platform!\n"); + return ret; + } + } + + return regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, dcfg->src_stop); +} + +static int imx_rproc_scu_api_stop(struct rproc *rproc) +{ + struct imx_rproc *priv = rproc->priv; + + return imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, false, priv->entry); +} + static int imx_rproc_stop(struct rproc *rproc) { struct imx_rproc *priv = rproc->priv; const struct imx_rproc_dcfg *dcfg = priv->dcfg; struct device *dev = priv->dev; - struct arm_smccc_res res; int ret; - switch (dcfg->method) { - case IMX_RPROC_MMIO: - ret = regmap_update_bits(priv->regmap, dcfg->src_reg, dcfg->src_mask, - dcfg->src_stop); - break; - case IMX_RPROC_SMC: - arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_STOP, 0, 0, 0, 0, 0, 0, &res); - ret = res.a0; - if (res.a1) - dev_info(dev, "Not in wfi, force stopped\n"); - break; - case IMX_RPROC_SCU_API: - ret = imx_sc_pm_cpu_start(priv->ipc_handle, priv->rsrc_id, false, priv->entry); - break; - default: + if (!dcfg->ops || !dcfg->ops->stop) return -EOPNOTSUPP; - } + ret = dcfg->ops->stop(rproc); if (ret) dev_err(dev, "Failed to stop remote core\n"); else @@ -521,50 +490,44 @@ static int imx_rproc_prepare(struct rproc *rproc) { struct imx_rproc *priv = rproc->priv; struct device_node *np = priv->dev->of_node; - struct of_phandle_iterator it; struct rproc_mem_entry *mem; - struct reserved_mem *rmem; + int i = 0; u32 da; /* Register associated reserved memory regions */ - of_phandle_iterator_init(&it, np, "memory-region", NULL, 0); - while (of_phandle_iterator_next(&it) == 0) { + while (1) { + int err; + struct resource res; + + err = of_reserved_mem_region_to_resource(np, i++, &res); + if (err) + return 0; + /* * Ignore the first memory region which will be used vdev buffer. * No need to do extra handlings, rproc_add_virtio_dev will handle it. */ - if (!strcmp(it.node->name, "vdev0buffer")) + if (strstarts(res.name, "vdev0buffer")) continue; - if (!strcmp(it.node->name, "rsc-table")) + if (strstarts(res.name, "rsc-table")) continue; - rmem = of_reserved_mem_lookup(it.node); - if (!rmem) { - of_node_put(it.node); - dev_err(priv->dev, "unable to acquire memory-region\n"); - return -EINVAL; - } - /* No need to translate pa to da, i.MX use same map */ - da = rmem->base; + da = res.start; /* Register memory region */ - mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)rmem->base, rmem->size, da, + mem = rproc_mem_entry_init(priv->dev, NULL, (dma_addr_t)res.start, + resource_size(&res), da, imx_rproc_mem_alloc, imx_rproc_mem_release, - it.node->name); - - if (mem) { - rproc_coredump_add_segment(rproc, da, rmem->size); - } else { - of_node_put(it.node); + "%.*s", strchrnul(res.name, '@') - res.name, + res.name); + if (!mem) return -ENOMEM; - } + rproc_coredump_add_segment(rproc, da, resource_size(&res)); rproc_add_carveout(rproc, mem); } - - return 0; } static int imx_rproc_parse_fw(struct rproc *rproc, const struct firmware *fw) @@ -603,16 +566,12 @@ 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) +static int imx_rproc_scu_api_detach(struct rproc *rproc) { struct imx_rproc *priv = rproc->priv; - const struct imx_rproc_dcfg *dcfg = priv->dcfg; - - if (dcfg->method != IMX_RPROC_SCU_API) - return -EOPNOTSUPP; if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id)) return -EOPNOTSUPP; @@ -622,6 +581,17 @@ static int imx_rproc_detach(struct rproc *rproc) return 0; } +static int imx_rproc_detach(struct rproc *rproc) +{ + struct imx_rproc *priv = rproc->priv; + const struct imx_rproc_dcfg *dcfg = priv->dcfg; + + if (!dcfg->ops || !dcfg->ops->detach) + return -EOPNOTSUPP; + + return dcfg->ops->detach(rproc); +} + static struct resource_table *imx_rproc_get_loaded_rsc_table(struct rproc *rproc, size_t *table_sz) { struct imx_rproc *priv = rproc->priv; @@ -634,6 +604,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, @@ -644,7 +625,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, @@ -684,40 +665,36 @@ static int imx_rproc_addr_init(struct imx_rproc *priv, } /* memory-region is optional property */ - nph = of_count_phandle_with_args(np, "memory-region", NULL); + nph = of_reserved_mem_region_count(np); if (nph <= 0) return 0; /* remap optional addresses */ for (a = 0; a < nph; a++) { - struct device_node *node; struct resource res; - node = of_parse_phandle(np, "memory-region", a); - /* Not map vdevbuffer, vdevring region */ - if (!strncmp(node->name, "vdev", strlen("vdev"))) { - of_node_put(node); - continue; - } - err = of_address_to_resource(node, 0, &res); - of_node_put(node); + err = of_reserved_mem_region_to_resource(np, a, &res); if (err) { dev_err(dev, "unable to resolve memory region\n"); return err; } + /* Not map vdevbuffer, vdevring region */ + if (strstarts(res.name, "vdev")) + continue; + if (b >= IMX_RPROC_MEM_MAX) break; /* Not use resource version, because we might share region */ - priv->mem[b].cpu_addr = devm_ioremap_wc(&pdev->dev, res.start, resource_size(&res)); + priv->mem[b].cpu_addr = devm_ioremap_resource_wc(&pdev->dev, &res); if (!priv->mem[b].cpu_addr) { dev_err(dev, "failed to remap %pr\n", &res); return -ENOMEM; } priv->mem[b].sys_addr = res.start; priv->mem[b].size = resource_size(&res); - if (!strcmp(node->name, "rsc-table")) + if (!strcmp(res.name, "rsc-table")) priv->rsc_table = priv->mem[b].cpu_addr; b++; } @@ -725,13 +702,22 @@ static int imx_rproc_addr_init(struct imx_rproc *priv, return 0; } +static int imx_rproc_notified_idr_cb(int id, void *ptr, void *data) +{ + struct rproc *rproc = data; + + rproc_vq_interrupt(rproc, id); + + return 0; +} + static void imx_rproc_vq_work(struct work_struct *work) { struct imx_rproc *priv = container_of(work, struct imx_rproc, rproc_work); + struct rproc *rproc = priv->rproc; - rproc_vq_interrupt(priv->rproc, 0); - rproc_vq_interrupt(priv->rproc, 1); + idr_for_each(&rproc->notifyids, imx_rproc_notified_idr_cb, rproc); } static void imx_rproc_rx_callback(struct mbox_client *cl, void *msg) @@ -742,7 +728,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; @@ -760,12 +746,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; @@ -785,8 +771,9 @@ static int imx_rproc_xtr_mbox_init(struct rproc *rproc) return 0; } -static void imx_rproc_free_mbox(struct rproc *rproc) +static void imx_rproc_free_mbox(void *data) { + struct rproc *rproc = data; struct imx_rproc *priv = rproc->priv; if (priv->tx_ch) { @@ -800,16 +787,12 @@ static void imx_rproc_free_mbox(struct rproc *rproc) } } -static void imx_rproc_put_scu(struct rproc *rproc) +static void imx_rproc_put_scu(void *data) { - struct imx_rproc *priv = rproc->priv; - const struct imx_rproc_dcfg *dcfg = priv->dcfg; - - if (dcfg->method != IMX_RPROC_SCU_API) - return; + struct imx_rproc *priv = data; if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id)) { - imx_rproc_detach_pd(rproc); + dev_pm_domain_detach_list(priv->pd_list); return; } @@ -837,151 +820,61 @@ static int imx_rproc_attach_pd(struct imx_rproc *priv) { struct device *dev = priv->dev; int ret, i; + bool detached = true; /* * If there is only one power-domain entry, the platform driver framework * will handle it, no need handle it in this driver. */ - priv->num_pd = of_count_phandle_with_args(dev->of_node, "power-domains", - "#power-domain-cells"); - if (priv->num_pd <= 1) + if (dev->pm_domain) return 0; - priv->pd_dev = devm_kmalloc_array(dev, priv->num_pd, sizeof(*priv->pd_dev), GFP_KERNEL); - if (!priv->pd_dev) - return -ENOMEM; - - priv->pd_dev_link = devm_kmalloc_array(dev, priv->num_pd, sizeof(*priv->pd_dev_link), - GFP_KERNEL); - - if (!priv->pd_dev_link) - return -ENOMEM; - - for (i = 0; i < priv->num_pd; i++) { - priv->pd_dev[i] = dev_pm_domain_attach_by_id(dev, i); - if (IS_ERR(priv->pd_dev[i])) { - ret = PTR_ERR(priv->pd_dev[i]); - goto detach_pd; - } - - priv->pd_dev_link[i] = device_link_add(dev, priv->pd_dev[i], DL_FLAG_STATELESS | - DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE); - if (!priv->pd_dev_link[i]) { - dev_pm_domain_detach(priv->pd_dev[i], false); - ret = -EINVAL; - goto detach_pd; + ret = dev_pm_domain_attach_list(dev, NULL, &priv->pd_list); + if (ret < 0) + return ret; + /* + * If all the power domain devices are already turned on, the remote + * core is already powered up and running when the kernel booted (e.g., + * started by U-Boot's bootaux command). In this case attach to it. + */ + for (i = 0; i < ret; i++) { + if (!dev_pm_genpd_is_on(priv->pd_list->pd_devs[i])) { + detached = false; + break; } } - return 0; - -detach_pd: - while (--i >= 0) { - device_link_del(priv->pd_dev_link[i]); - dev_pm_domain_detach(priv->pd_dev[i], false); - } + if (detached) + priv->rproc->state = RPROC_DETACHED; - return ret; + return 0; } -static int imx_rproc_detach_pd(struct rproc *rproc) +static int imx_rproc_arm_smc_detect_mode(struct rproc *rproc) { struct imx_rproc *priv = rproc->priv; - int i; - - /* - * If there is only one power-domain entry, the platform driver framework - * will handle it, no need handle it in this driver. - */ - if (priv->num_pd <= 1) - return 0; + struct arm_smccc_res res; - for (i = 0; i < priv->num_pd; i++) { - device_link_del(priv->pd_dev_link[i]); - dev_pm_domain_detach(priv->pd_dev[i], false); - } + arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_STARTED, 0, 0, 0, 0, 0, 0, &res); + if (res.a0) + priv->rproc->state = RPROC_DETACHED; return 0; } -static int imx_rproc_detect_mode(struct imx_rproc *priv) +static int imx_rproc_mmio_detect_mode(struct rproc *rproc) { - struct regmap_config config = { .name = "imx-rproc" }; + const struct regmap_config config = { .name = "imx-rproc" }; + struct imx_rproc *priv = rproc->priv; const struct imx_rproc_dcfg *dcfg = priv->dcfg; struct device *dev = priv->dev; struct regmap *regmap; - struct arm_smccc_res res; - int ret; u32 val; - u8 pt; - - switch (dcfg->method) { - case IMX_RPROC_NONE: - priv->rproc->state = RPROC_DETACHED; - return 0; - case IMX_RPROC_SMC: - arm_smccc_smc(IMX_SIP_RPROC, IMX_SIP_RPROC_STARTED, 0, 0, 0, 0, 0, 0, &res); - if (res.a0) - priv->rproc->state = RPROC_DETACHED; - return 0; - case IMX_RPROC_SCU_API: - ret = imx_scu_get_handle(&priv->ipc_handle); - if (ret) - return ret; - ret = of_property_read_u32(dev->of_node, "fsl,resource-id", &priv->rsrc_id); - if (ret) { - dev_err(dev, "No fsl,resource-id property\n"); - return ret; - } - - if (priv->rsrc_id == IMX_SC_R_M4_1_PID0) - priv->core_index = 1; - else - priv->core_index = 0; - - /* - * If Mcore resource is not owned by Acore partition, It is kicked by ROM, - * and Linux could only do IPC with Mcore and nothing else. - */ - if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id)) { - if (of_property_read_u32(dev->of_node, "fsl,entry-address", &priv->entry)) - return -EINVAL; - - return imx_rproc_attach_pd(priv); - } - - priv->rproc->state = RPROC_DETACHED; - priv->rproc->recovery_disabled = false; - rproc_set_feature(priv->rproc, RPROC_FEAT_ATTACH_ON_RECOVERY); - - /* Get partition id and enable irq in SCFW */ - ret = imx_sc_rm_get_resource_owner(priv->ipc_handle, priv->rsrc_id, &pt); - if (ret) { - dev_err(dev, "not able to get resource owner\n"); - return ret; - } - - priv->rproc_pt = pt; - priv->rproc_nb.notifier_call = imx_rproc_partition_notify; - - ret = imx_scu_irq_register_notifier(&priv->rproc_nb); - if (ret) { - dev_err(dev, "register scu notifier failed, %d\n", ret); - return ret; - } - - ret = imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_REBOOTED, BIT(priv->rproc_pt), - true); - if (ret) { - imx_scu_irq_unregister_notifier(&priv->rproc_nb); - dev_err(dev, "Enable irq failed, %d\n", ret); - return ret; - } + int ret; - return 0; - default: - break; - } + priv->gpr = syscon_regmap_lookup_by_phandle(dev->of_node, "fsl,iomuxc-gpr"); + if (IS_ERR(priv->gpr)) + priv->gpr = NULL; regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); if (IS_ERR(regmap)) { @@ -992,6 +885,19 @@ static int imx_rproc_detect_mode(struct imx_rproc *priv) priv->regmap = regmap; regmap_attach_dev(dev, regmap, &config); + if (priv->gpr) { + ret = regmap_read(priv->gpr, dcfg->gpr_reg, &val); + if (val & dcfg->gpr_wait) { + /* + * After cold boot, the CM indicates its in wait + * state, but not fully powered off. Power it off + * fully so firmware can be loaded into it. + */ + imx_rproc_stop(priv->rproc); + return 0; + } + } + ret = regmap_read(regmap, dcfg->src_reg, &val); if (ret) { dev_err(dev, "Failed to read src\n"); @@ -1004,35 +910,112 @@ static int imx_rproc_detect_mode(struct imx_rproc *priv) return 0; } -static int imx_rproc_clk_enable(struct imx_rproc *priv) +static int imx_rproc_scu_api_detect_mode(struct rproc *rproc) { - const struct imx_rproc_dcfg *dcfg = priv->dcfg; + struct imx_rproc *priv = rproc->priv; struct device *dev = priv->dev; int ret; + u8 pt; - /* Remote core is not under control of Linux */ - if (dcfg->method == IMX_RPROC_NONE) - return 0; - - priv->clk = devm_clk_get(dev, NULL); - if (IS_ERR(priv->clk)) { - dev_err(dev, "Failed to get clock\n"); - return PTR_ERR(priv->clk); + ret = imx_scu_get_handle(&priv->ipc_handle); + if (ret) + return ret; + ret = of_property_read_u32(dev->of_node, "fsl,resource-id", &priv->rsrc_id); + if (ret) { + dev_err(dev, "No fsl,resource-id property\n"); + return ret; } + if (priv->rsrc_id == IMX_SC_R_M4_1_PID0) + priv->core_index = 1; + else + priv->core_index = 0; + + ret = devm_add_action_or_reset(dev, imx_rproc_put_scu, priv); + if (ret) + return dev_err_probe(dev, ret, "Failed to add action for put scu\n"); + /* - * clk for M4 block including memory. Should be - * enabled before .start for FW transfer. + * If Mcore resource is not owned by Acore partition, It is kicked by ROM, + * and Linux could only do IPC with Mcore and nothing else. */ - ret = clk_prepare_enable(priv->clk); + if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id)) { + if (of_property_read_u32(dev->of_node, "fsl,entry-address", &priv->entry)) + return -EINVAL; + + return imx_rproc_attach_pd(priv); + } + + priv->rproc->state = RPROC_DETACHED; + priv->rproc->recovery_disabled = false; + rproc_set_feature(priv->rproc, RPROC_FEAT_ATTACH_ON_RECOVERY); + + /* Get partition id and enable irq in SCFW */ + ret = imx_sc_rm_get_resource_owner(priv->ipc_handle, priv->rsrc_id, &pt); + if (ret) { + dev_err(dev, "not able to get resource owner\n"); + return ret; + } + + priv->rproc_pt = pt; + priv->rproc_nb.notifier_call = imx_rproc_partition_notify; + + ret = imx_scu_irq_register_notifier(&priv->rproc_nb); if (ret) { - dev_err(dev, "Failed to enable clock\n"); + dev_err(dev, "register scu notifier failed, %d\n", ret); + return ret; + } + + ret = imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_REBOOTED, BIT(priv->rproc_pt), + true); + if (ret) { + imx_scu_irq_unregister_notifier(&priv->rproc_nb); + dev_err(dev, "Enable irq failed, %d\n", ret); return ret; } return 0; } +static int imx_rproc_detect_mode(struct imx_rproc *priv) +{ + const struct imx_rproc_dcfg *dcfg = priv->dcfg; + + /* + * To i.MX{7,8} ULP, Linux is under control of RTOS, no need + * dcfg->ops or dcfg->ops->detect_mode, it is state RPROC_DETACHED. + */ + if (!dcfg->ops || !dcfg->ops->detect_mode) { + priv->rproc->state = RPROC_DETACHED; + return 0; + } + + return dcfg->ops->detect_mode(priv->rproc); +} + +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 void imx_rproc_destroy_workqueue(void *data) +{ + struct workqueue_struct *workqueue = data; + + destroy_workqueue(workqueue); +} + static int imx_rproc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1043,16 +1026,14 @@ static int imx_rproc_probe(struct platform_device *pdev) int ret; /* set some other name then imx */ - rproc = rproc_alloc(dev, "imx-rproc", &imx_rproc_ops, - NULL, sizeof(*priv)); + rproc = devm_rproc_alloc(dev, "imx-rproc", &imx_rproc_ops, + NULL, sizeof(*priv)); if (!rproc) return -ENOMEM; dcfg = of_device_get_match_data(dev); - if (!dcfg) { - ret = -EINVAL; - goto err_put_rproc; - } + if (!dcfg) + return -EINVAL; priv = rproc->priv; priv->rproc = rproc; @@ -1063,51 +1044,81 @@ static int imx_rproc_probe(struct platform_device *pdev) priv->workqueue = create_workqueue(dev_name(dev)); if (!priv->workqueue) { dev_err(dev, "cannot create workqueue\n"); - ret = -ENOMEM; - goto err_put_rproc; + return -ENOMEM; } - ret = imx_rproc_xtr_mbox_init(rproc); + ret = devm_add_action_or_reset(dev, imx_rproc_destroy_workqueue, priv->workqueue); if (ret) - goto err_put_wkq; + return dev_err_probe(dev, ret, "Failed to add devm destroy workqueue action\n"); - ret = imx_rproc_addr_init(priv, pdev); - if (ret) { - dev_err(dev, "failed on imx_rproc_addr_init\n"); - goto err_put_mbox; - } + INIT_WORK(&priv->rproc_work, imx_rproc_vq_work); - ret = imx_rproc_detect_mode(priv); + ret = imx_rproc_xtr_mbox_init(rproc, true); if (ret) - goto err_put_mbox; + return ret; - ret = imx_rproc_clk_enable(priv); + ret = devm_add_action_or_reset(dev, imx_rproc_free_mbox, rproc); if (ret) - goto err_put_scu; + return dev_err_probe(dev, ret, + "Failed to add devm free mbox action: %d\n", ret); - INIT_WORK(&priv->rproc_work, imx_rproc_vq_work); + ret = imx_rproc_addr_init(priv, pdev); + if (ret) + return dev_err_probe(dev, ret, "failed on imx_rproc_addr_init\n"); + + ret = imx_rproc_detect_mode(priv); + if (ret) + return dev_err_probe(dev, ret, "failed on detect mode\n"); + + /* + * Handle clocks when remote core is under control of Linux AND the + * clocks are not managed by system firmware. + */ + if (dcfg->flags & IMX_RPROC_NEED_CLKS) { + priv->clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to enable clock\n"); + } if (rproc->state != RPROC_DETACHED) rproc->auto_boot = of_property_read_bool(np, "fsl,auto-boot"); - ret = rproc_add(rproc); + 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) + return dev_err_probe(dev, ret, "register power off handler failure\n"); + + ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART_PREPARE, + SYS_OFF_PRIO_DEFAULT, + imx_rproc_sys_off_handler, rproc); + if (ret) + return dev_err_probe(dev, ret, "register restart handler failure\n"); + } + + pm_runtime_enable(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return dev_err_probe(dev, ret, "pm_runtime get failed\n"); + + ret = devm_rproc_add(dev, rproc); if (ret) { dev_err(dev, "rproc_add failed\n"); - goto err_put_clk; + goto err_put_pm; } return 0; -err_put_clk: - clk_disable_unprepare(priv->clk); -err_put_scu: - imx_rproc_put_scu(rproc); -err_put_mbox: - imx_rproc_free_mbox(rproc); -err_put_wkq: - destroy_workqueue(priv->workqueue); -err_put_rproc: - rproc_free(rproc); +err_put_pm: + pm_runtime_disable(dev); + pm_runtime_put_noidle(dev); return ret; } @@ -1117,14 +1128,112 @@ static void imx_rproc_remove(struct platform_device *pdev) struct rproc *rproc = platform_get_drvdata(pdev); struct imx_rproc *priv = rproc->priv; - clk_disable_unprepare(priv->clk); - rproc_del(rproc); - imx_rproc_put_scu(rproc); - imx_rproc_free_mbox(rproc); - destroy_workqueue(priv->workqueue); - rproc_free(rproc); + pm_runtime_disable(priv->dev); + pm_runtime_put_noidle(priv->dev); } +static const struct imx_rproc_plat_ops imx_rproc_ops_arm_smc = { + .start = imx_rproc_arm_smc_start, + .stop = imx_rproc_arm_smc_stop, + .detect_mode = imx_rproc_arm_smc_detect_mode, +}; + +static const struct imx_rproc_plat_ops imx_rproc_ops_mmio = { + .start = imx_rproc_mmio_start, + .stop = imx_rproc_mmio_stop, + .detect_mode = imx_rproc_mmio_detect_mode, +}; + +static const struct imx_rproc_plat_ops imx_rproc_ops_scu_api = { + .start = imx_rproc_scu_api_start, + .stop = imx_rproc_scu_api_stop, + .detach = imx_rproc_scu_api_detach, + .detect_mode = imx_rproc_scu_api_detect_mode, +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn_mmio = { + .src_reg = IMX7D_SRC_SCR, + .src_mask = IMX7D_M4_RST_MASK, + .src_start = IMX7D_M4_START, + .src_stop = IMX8M_M7_STOP, + .gpr_reg = IMX8M_GPR22, + .gpr_wait = IMX8M_GPR22_CM7_CPUWAIT, + .att = imx_rproc_att_imx8mn, + .att_size = ARRAY_SIZE(imx_rproc_att_imx8mn), + .ops = &imx_rproc_ops_mmio, + .flags = IMX_RPROC_NEED_CLKS, +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mn = { + .att = imx_rproc_att_imx8mn, + .att_size = ARRAY_SIZE(imx_rproc_att_imx8mn), + .ops = &imx_rproc_ops_arm_smc, + .flags = IMX_RPROC_NEED_CLKS, +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx8mq = { + .src_reg = IMX7D_SRC_SCR, + .src_mask = IMX7D_M4_RST_MASK, + .src_start = IMX7D_M4_START, + .src_stop = IMX7D_M4_STOP, + .att = imx_rproc_att_imx8mq, + .att_size = ARRAY_SIZE(imx_rproc_att_imx8mq), + .ops = &imx_rproc_ops_mmio, + .flags = IMX_RPROC_NEED_CLKS, +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx8qm = { + .att = imx_rproc_att_imx8qm, + .att_size = ARRAY_SIZE(imx_rproc_att_imx8qm), + .ops = &imx_rproc_ops_scu_api, +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx8qxp = { + .att = imx_rproc_att_imx8qxp, + .att_size = ARRAY_SIZE(imx_rproc_att_imx8qxp), + .ops = &imx_rproc_ops_scu_api, +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx8ulp = { + .att = imx_rproc_att_imx8ulp, + .att_size = ARRAY_SIZE(imx_rproc_att_imx8ulp), +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx7ulp = { + .att = imx_rproc_att_imx7ulp, + .att_size = ARRAY_SIZE(imx_rproc_att_imx7ulp), + .flags = IMX_RPROC_NEED_SYSTEM_OFF, +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx7d = { + .src_reg = IMX7D_SRC_SCR, + .src_mask = IMX7D_M4_RST_MASK, + .src_start = IMX7D_M4_START, + .src_stop = IMX7D_M4_STOP, + .att = imx_rproc_att_imx7d, + .att_size = ARRAY_SIZE(imx_rproc_att_imx7d), + .ops = &imx_rproc_ops_mmio, + .flags = IMX_RPROC_NEED_CLKS, +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx6sx = { + .src_reg = IMX6SX_SRC_SCR, + .src_mask = IMX6SX_M4_RST_MASK, + .src_start = IMX6SX_M4_START, + .src_stop = IMX6SX_M4_STOP, + .att = imx_rproc_att_imx6sx, + .att_size = ARRAY_SIZE(imx_rproc_att_imx6sx), + .ops = &imx_rproc_ops_mmio, + .flags = IMX_RPROC_NEED_CLKS, +}; + +static const struct imx_rproc_dcfg imx_rproc_cfg_imx93 = { + .att = imx_rproc_att_imx93, + .att_size = ARRAY_SIZE(imx_rproc_att_imx93), + .ops = &imx_rproc_ops_arm_smc, + .flags = IMX_RPROC_NEED_CLKS, +}; + static const struct of_device_id imx_rproc_of_match[] = { { .compatible = "fsl,imx7ulp-cm4", .data = &imx_rproc_cfg_imx7ulp }, { .compatible = "fsl,imx7d-cm4", .data = &imx_rproc_cfg_imx7d }, @@ -1133,6 +1242,8 @@ static const struct of_device_id imx_rproc_of_match[] = { { .compatible = "fsl,imx8mm-cm4", .data = &imx_rproc_cfg_imx8mq }, { .compatible = "fsl,imx8mn-cm7", .data = &imx_rproc_cfg_imx8mn }, { .compatible = "fsl,imx8mp-cm7", .data = &imx_rproc_cfg_imx8mn }, + { .compatible = "fsl,imx8mn-cm7-mmio", .data = &imx_rproc_cfg_imx8mn_mmio }, + { .compatible = "fsl,imx8mp-cm7-mmio", .data = &imx_rproc_cfg_imx8mn_mmio }, { .compatible = "fsl,imx8qxp-cm4", .data = &imx_rproc_cfg_imx8qxp }, { .compatible = "fsl,imx8qm-cm4", .data = &imx_rproc_cfg_imx8qm }, { .compatible = "fsl,imx8ulp-cm33", .data = &imx_rproc_cfg_imx8ulp }, @@ -1143,7 +1254,7 @@ MODULE_DEVICE_TABLE(of, imx_rproc_of_match); static struct platform_driver imx_rproc_driver = { .probe = imx_rproc_probe, - .remove_new = imx_rproc_remove, + .remove = imx_rproc_remove, .driver = { .name = "imx-rproc", .of_match_table = imx_rproc_of_match, |
