#include <linux/of_reserved_mem.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
#include <linux/regmap.h>
#include <linux/remoteproc.h>
#include <linux/workqueue.h>
#define ATT_OWN BIT(1)
#define ATT_IOMEM BIT(2)
+static int imx_rproc_detach_pd(struct rproc *rproc);
+
struct imx_rproc {
struct device *dev;
struct regmap *regmap;
struct notifier_block rproc_nb;
u32 rproc_pt; /* partition id */
u32 rsrc_id; /* resource id */
+ u32 entry; /* cpu start address */
+ int num_pd;
+ struct device **pd_dev;
+ struct device_link **pd_dev_link;
};
static const struct imx_rproc_att imx_rproc_att_imx93[] = {
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:
return -EOPNOTSUPP;
}
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:
return -EOPNOTSUPP;
}
if (dcfg->method != IMX_RPROC_SCU_API)
return;
- if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id))
+ if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id)) {
+ imx_rproc_detach_pd(rproc);
return;
+ }
imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_REBOOTED, BIT(priv->rproc_pt), false);
imx_scu_irq_unregister_notifier(&priv->rproc_nb);
return 0;
}
+static int imx_rproc_attach_pd(struct imx_rproc *priv)
+{
+ struct device *dev = priv->dev;
+ int ret, i;
+
+ /*
+ * 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)
+ 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;
+ }
+ }
+
+ return 0;
+
+detach_pd:
+ while (--i >= 0) {
+ device_link_del(priv->pd_dev_link[i]);
+ dev_pm_domain_detach(priv->pd_dev[i], false);
+ }
+
+ return ret;
+}
+
+static int imx_rproc_detach_pd(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;
+
+ 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);
+ }
+
+ return 0;
+}
+
static int imx_rproc_detect_mode(struct imx_rproc *priv)
{
struct regmap_config config = { .name = "imx-rproc" };
* 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))
- return 0;
+ 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 = true;